Sí, es posible. No nativamente sin embargo, necesitaba un guión. Como he adivinado, debe agregar las coordenadas de las almohadillas con las coordenadas del elemento; las coordenadas de la almohadilla son relativas a su elemento que contiene.
Aquí hay un ejemplo:
Element["" "3.7x4-pad" "" "3.7 x 4 pad" 17.1500mm 11.8500mm 10.00mil 2.6540mm 0 100 ""]
(
Pad[0.0000 -0.1500mm 0.0000 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "1" "square"]
)
Element["" "3.7x4-pad" "" "3.7 x 4 pad" 4.3500mm 11.8500mm 3.3540mm 1.5540mm 0 100 ""]
(
Pad[0.0000 -0.1500mm 0.0000 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "1" "square"]
)
Para hacer una huella de esta PCB, debe desplazar todos los pads con el origen de su elemento contenedor. No me molesté en hacerlo a mano, especialmente si es probable que esto se convierta en una tarea recurrente. Python scripting al rescate:
#!/usr/bin/env python
"""
This script parses a PCB file and outputs a comoponent footprint. This is
to be used whenever designers need to build SMD footprints for parts not
yet distributed with gEDA or registered at gedafootprints.org.
Note: the source PCB is assumed valid!
"""
import re
from sys import stdin, stdout, version
from contextlib import contextmanager
# Included in Python 3.4: ignore certain exceptions while doing something
if version < '3.4':
@contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
# Add numbers with their respective units, forcing millimeter
def add( *items ):
return "{:.4f}mm".format( sum(items) )
class ComponentFactory(object):
# Enclose captured matches with parentheses
descriptors = {
'PAD': '\s*',
'SEP': '\s+',
'DIM': '(-?[0-9.]+\w+)',
'TXT': '"([^"]*)"',
'DIG': '(\d)',
'INT': '(-?\d+)',
'UINT': '(\d+)',
}
# List of fields for a given component
components = {
'PCB[]': [
('name', 'TXT'),
('width', 'DIM'),
('height', 'DIM'),
],
'Element[]': [
('flags', 'TXT'),
('desc', 'TXT'),
('name', 'TXT'),
('value', 'TXT'),
('x' , 'DIM'),
('y' , 'DIM'),
('tx' , 'DIM'),
('ty' , 'DIM'),
('dir', 'DIG'),
('scale', 'UINT'),
('tflags', 'TXT'),
],
'Pad[]': [
('x1', 'DIM'),
('y1', 'DIM'),
('x2', 'DIM'),
('y2', 'DIM'),
('thick', 'DIM'),
('clear', 'DIM'),
('mask', 'DIM'),
('name', 'TXT'),
('number', 'TXT'),
('flags', 'TXT'),
],
'Layer()': [
('id', 'UINT'),
('name', 'TXT'),
],
'Line[]': [
('x1', 'DIM'),
('y1', 'DIM'),
('x2', 'DIM'),
('y2', 'DIM'),
('thick', 'DIM'),
('clear', 'DIM'),
('flags', 'TXT'),
],
'Arc[]': [
('x' , 'DIM'),
('y' , 'DIM'),
('width', 'DIM'),
('height', 'DIM'),
('thick', 'DIM'),
('clear', 'DIM'),
('angle', 'INT'),
('delta', 'INT'),
('flags', 'TXT')
]
}
def __init__(self, name):
# Figure out the pattern given the name
pattern = [ n for n in self.components.keys() if n[:-2] == name ][0]
# Store name and fields for later use
self.name = name
self.fields, types = zip(*self.components[pattern])
# Then compile a regular expression for each matching line
self.re = re.compile(
'{}\{}\s*{}\s*\{}'.format(name, pattern[-2], self.descriptors['SEP'].join(self.descriptors[t] for t in types), pattern[-1])
)
class Component(dict):
def __getitem__(self, name):
""" Returns a signed float with fields that have units. Returns
the raw (text) value otherwise """
value = super(self.__class__, self).__getitem__(name)
m = re.match('(-?[0-9.]+)(mil|mm)?', value)
if not m:
return value
if m.group(2) == 'mil':
return float(m.group(1)) * 0.0254
return float(m.group(1))
def __getattr__(self, name):
""" Return a dict item as if it were an attribute """
return self.__getitem__(name)
def __setattr__(self, name, value):
""" Careful while setting attributes though: check attribute doesn't
exist before passing it to the dict! """
try:
self.__getitem__(name)
self.__setitem__(name, value)
except AttributeError:
super(self.__class__, self).__setattr__(name, value)
def parse(self, text):
try:
return ComponentFactory.Component( zip( self.fields, re.search(self.re, text).groups() ) )
except AttributeError:
raise ValueError
# Component parsers
PCB = ComponentFactory('PCB')
Element = ComponentFactory('Element')
Pad = ComponentFactory('Pad')
Layer = ComponentFactory('Layer')
Line = ComponentFactory('Line')
Arc = ComponentFactory('Arc')
# State variables
description = ''
number = 1
origin = ()
offset = ()
silk = False
while 1:
try:
text = stdin.readline()
except KeyboardInterrupt:
break
if not text:
break
# Strip carriage return
text = text.strip("\r\n");
# Use PCB name as element name
with ignored(ValueError):
description = PCB.parse(text).name
# Try parsing an element
with ignored(ValueError):
e = Element.parse(text)
offset = ( e.x, e.y )
# Mark origin at the middle of the first encountered element
if not origin:
origin = -offset[0], -offset[1]
# Write element header (not sure about usefulness of x and y though)
stdout.write( '\nElement["" "{name}" "" "" {x}mm {y}mm 10mil 10mil 0 100 ""]\n(\n'.format(name=description, x=offset[0], y=offset[1]) )
# Try parsing a pad, use last offset to compute pad position
# Note: first pad by design is always centered around (0, 0)
with ignored(ValueError):
p = Pad.parse(text)
# Offset the pad to the origin
p.x1, p.y1, p.x2, p.y2 = add(origin[0], p.x1, offset[0]), add(origin[1], p.y1, offset[1]), add(origin[0], p.x2, offset[0]), add(origin[1], p.y2, offset[1])
# Set pad number
p.number = number
number = number + 1
stdout.write( '\tPad[{x1} {y1} {x2} {y2} {thick} {clear} {mask} "{name}" "{number}" "{flags}"]\n'.format(**p) )
# Match silk layers to output offset ElementLines
with ignored(ValueError):
lay = Layer.parse(text)
silk = lay.name in ['silk']
if silk:
# Transform each line and arc inside the silk layer
with ignored(ValueError):
ln = Line.parse(text)
ln.x1, ln.y1, ln.x2, ln.y2 = add(origin[0], ln.x1), add(origin[1], ln.y1), add(origin[0], ln.x2), add(origin[1], ln.y2)
stdout.write( '\tElementLine[{x1} {y1} {x2} {y2} {thick}]\n'.format(**ln) )
with ignored(ValueError):
arc = Arc.parse(text)
arc.x, arc.y = add(origin[0], arc.x), add(origin[1], arc.y)
stdout.write( '\tElementArc[{x} {y} {width} {height} {angle} {delta} {thick}]\n'.format(**arc) )
if description:
stdout.write( '\n)\n' )
Guarda el script como build-element.py
, por ejemplo. Para crear una huella de un PCB existente (y válido):
python build-element.py < your-circuit.pcb > element.fp
Usted tendrá que abrir la huella generada en pcb
, opcionalmente cambiar la asignación de pin y guardar el archivo. Personalmente, siempre edito la huella en un editor de texto para eliminar los números del pin no utilizado, es más rápido.
Tenga en cuenta que la secuencia de comandos utiliza el nombre de PCB como la descripción de la huella. Así es como se ve la huella final:
Laimagenmuestralospinesrealesenunaversióneditadadelintentoquepubliquéenmipregunta.Aquíestáelarchivodelahuella,despuésdequeeditéelresultadoparaeliminarmanualmentelospinesnoutilizados:
Element["" "Bourns PEC11S SMD Incremental Encoder" "" "" 17.15mm 11.85mm 10mil 10mil 0 100 ""]
(
Pad[0.0000mm -0.1500mm 0.0000mm 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "" "square"]
Pad[-12.8000mm -0.1500mm -12.8000mm 0.1500mm 3.7000mm 30.00mil 3.7000mm "" "" "square"]
Pad[-11.4000mm -8.2500mm -11.4000mm -7.5500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
Pad[-1.4000mm -8.2500mm -1.4000mm -7.5500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
Pad[-3.9000mm -7.5500mm -3.9000mm -6.4500mm 1.3000mm 30.00mil 1.3000mm "E" "5" "square"]
Pad[-8.9000mm -7.5500mm -8.9000mm -6.4500mm 1.3000mm 30.00mil 1.3000mm "D" "4" "square"]
Pad[-8.9000mm 6.7500mm -8.9000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "A" "1" "square"]
Pad[-6.4000mm 6.7500mm -6.4000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "C" "2" "square"]
Pad[-3.9000mm 6.7500mm -3.9000mm 7.8500mm 1.3000mm 30.00mil 1.3000mm "B" "3" "square"]
Pad[-1.4000mm 7.5500mm -1.4000mm 8.2500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
Pad[-11.4000mm 7.5500mm -11.4000mm 8.2500mm 2.5000mm 30.00mil 2.5000mm "" "" "square"]
ElementLine[-7.8500mm -8.1500mm -4.9500mm -8.1500mm 6.00mil]
ElementLine[-7.8500mm 8.4500mm -7.4500mm 8.4500mm 6.00mil]
ElementLine[-5.3500mm 8.4500mm -5.0500mm 8.4500mm 6.00mil]
ElementLine[-12.6500mm 2.3500mm -12.6500mm 5.9500mm 6.00mil]
ElementLine[-0.1500mm 5.9500mm -0.1500mm 2.3500mm 6.00mil]
ElementLine[-12.6500mm -2.3500mm -12.6500mm -5.9500mm 6.00mil]
ElementLine[-0.1500mm -5.9500mm -0.1500mm -2.3500mm 6.00mil]
)