Skip to content

Commit 1b551f5

Browse files
Funkenjaegerqu1ck
authored andcommitted
(Eagle) Handle edges in footprints (fix #366)
1 parent f3f24af commit 1b551f5

File tree

1 file changed

+116
-111
lines changed

1 file changed

+116
-111
lines changed

InteractiveHtmlBom/ecad/fusion_eagle.py

Lines changed: 116 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -468,117 +468,122 @@ def _rotate(x, y, angle, mirrored=False):
468468
else:
469469
return xr, yr
470470

471-
def _add_silk_fab(self, el, x, y, angle, mirrored, populate):
472-
if el.tag == 'hole':
473-
dwg_layer = self.pcbdata['edges']
474-
elif el.attrib['layer'] in [self.TOP_PLACE_LAYER, self.BOT_PLACE_LAYER]:
475-
dwg_layer = self.pcbdata['drawings']['silkscreen']
476-
top = el.attrib['layer'] == self.TOP_PLACE_LAYER
477-
elif el.attrib['layer'] in [self.TOP_DOCU_LAYER, self.BOT_DOCU_LAYER]:
478-
if not populate:
479-
return
480-
dwg_layer = self.pcbdata['drawings']['fabrication']
481-
top = el.attrib['layer'] == self.TOP_DOCU_LAYER
482-
else:
483-
return
484-
485-
dwg = None
486-
487-
if el.tag == 'wire':
488-
_dx1 = float(el.attrib['x1'])
489-
_dx2 = float(el.attrib['x2'])
490-
_dy1 = -float(el.attrib['y1'])
491-
_dy2 = -float(el.attrib['y2'])
492-
493-
dx1, dy1 = self._rotate(_dx1, _dy1, -angle, mirrored)
494-
dx2, dy2 = self._rotate(_dx2, _dy2, -angle, mirrored)
495-
496-
x1, y1 = x + dx1, -y + dy1
497-
x2, y2 = x + dx2, -y + dy2
498-
499-
if el.get('curve'):
500-
dwg = {
501-
'type': 'arc',
502-
'width': float(el.attrib['width']),
503-
'svgpath': self._curve_to_svgpath(el, x, y, angle)
504-
}
505-
else:
506-
dwg = {
507-
'type': 'segment',
508-
'start': [x1, y1],
509-
'end': [x2, y2],
510-
'width': float(el.attrib['width'])
511-
}
471+
def _process_footprint(self, package, x, y, angle, mirrored, populate):
472+
for el in package.iter():
473+
if el.tag in ['wire', 'rectangle', 'circle', 'hole',
474+
'polygonshape', 'polygon', 'hole']:
475+
if el.tag == 'hole':
476+
dwg_layer = self.pcbdata['edges']
477+
elif el.attrib['layer'] in [self.TOP_PLACE_LAYER,
478+
self.BOT_PLACE_LAYER]:
479+
dwg_layer = self.pcbdata['drawings']['silkscreen']
480+
top = el.attrib['layer'] == self.TOP_PLACE_LAYER
481+
elif el.attrib['layer'] in [self.TOP_DOCU_LAYER,
482+
self.BOT_DOCU_LAYER]:
483+
if not populate:
484+
return
485+
dwg_layer = self.pcbdata['drawings']['fabrication']
486+
top = el.attrib['layer'] == self.TOP_DOCU_LAYER
487+
elif el.tag == 'wire' and \
488+
el.attrib['layer'] == self.DIMENSION_LAYER:
489+
dwg_layer = self.pcbdata['edges']
490+
top = True
491+
else:
492+
return
512493

513-
elif el.tag == 'rectangle':
514-
_dv = self._rectangle_vertices(el)
494+
dwg = None
515495

516-
# Rotate rectangle about component origin based on component angle
517-
dv = [self._rotate(_x, _y, -angle, mirrored) for (_x, _y) in _dv]
496+
if el.tag == 'wire':
497+
_dx1 = float(el.attrib['x1'])
498+
_dx2 = float(el.attrib['x2'])
499+
_dy1 = -float(el.attrib['y1'])
500+
_dy2 = -float(el.attrib['y2'])
518501

519-
# Map vertices back to absolute coordinates
520-
v = [(x + _x, -y + _y) for (_x, _y) in dv]
502+
dx1, dy1 = self._rotate(_dx1, _dy1, -angle, mirrored)
503+
dx2, dy2 = self._rotate(_dx2, _dy2, -angle, mirrored)
521504

522-
dwg = {
523-
'type': 'polygon',
524-
'filled': 1,
525-
'pos': [0, 0],
526-
'polygons': [v]
527-
}
505+
x1, y1 = x + dx1, -y + dy1
506+
x2, y2 = x + dx2, -y + dy2
528507

529-
elif el.tag in ['circle', 'hole']:
530-
_x = float(el.attrib['x'])
531-
_y = -float(el.attrib['y'])
532-
dxc, dyc = self._rotate(_x, _y, -angle, mirrored)
533-
xc, yc = x + dxc, -y + dyc
508+
if el.get('curve'):
509+
dwg = {
510+
'type': 'arc',
511+
'width': float(el.attrib['width']),
512+
'svgpath': self._curve_to_svgpath(el, x, y, angle)
513+
}
514+
else:
515+
dwg = {
516+
'type': 'segment',
517+
'start': [x1, y1],
518+
'end': [x2, y2],
519+
'width': float(el.attrib['width'])
520+
}
521+
522+
elif el.tag == 'rectangle':
523+
_dv = self._rectangle_vertices(el)
524+
525+
# Rotate rectangle about component origin based on component angle
526+
dv = [self._rotate(_x, _y, -angle, mirrored) for (_x, _y) in
527+
_dv]
528+
529+
# Map vertices back to absolute coordinates
530+
v = [(x + _x, -y + _y) for (_x, _y) in dv]
531+
532+
dwg = {
533+
'type': 'polygon',
534+
'filled': 1,
535+
'pos': [0, 0],
536+
'polygons': [v]
537+
}
534538

535-
if el.tag == 'circle':
536-
radius = float(el.attrib['radius'])
537-
width = float(el.attrib['width'])
538-
else:
539-
radius = float(el.attrib['drill']) / 2
540-
width = 0
541-
542-
dwg = {
543-
'type': 'circle',
544-
'start': [xc, yc],
545-
'radius': radius,
546-
'width': width
547-
}
539+
elif el.tag in ['circle', 'hole']:
540+
_x = float(el.attrib['x'])
541+
_y = -float(el.attrib['y'])
542+
dxc, dyc = self._rotate(_x, _y, -angle, mirrored)
543+
xc, yc = x + dxc, -y + dyc
548544

549-
elif el.tag in ['polygonshape', 'polygon']:
550-
segs = el if el.tag == 'polygon' \
551-
else el.find('polygonoutlinesegments')
545+
if el.tag == 'circle':
546+
radius = float(el.attrib['radius'])
547+
width = float(el.attrib['width'])
548+
else:
549+
radius = float(el.attrib['drill']) / 2
550+
width = 0
551+
552+
dwg = {
553+
'type': 'circle',
554+
'start': [xc, yc],
555+
'radius': radius,
556+
'width': width
557+
}
552558

553-
dv = self._segments_to_polygon(segs, angle, mirrored)
559+
elif el.tag in ['polygonshape', 'polygon']:
560+
segs = el if el.tag == 'polygon' \
561+
else el.find('polygonoutlinesegments')
554562

555-
polygon = [[x + v[0], -y + v[1]] for v in dv]
563+
dv = self._segments_to_polygon(segs, angle, mirrored)
556564

557-
dwg = {
558-
'type': 'polygon',
559-
'filled': 1,
560-
'pos': [0, 0],
561-
'polygons': [polygon]
562-
}
565+
polygon = [[x + v[0], -y + v[1]] for v in dv]
563566

564-
if dwg is not None:
565-
if el.tag == 'hole':
566-
dwg_layer.append(dwg)
567-
else:
568-
bot = not top
567+
dwg = {
568+
'type': 'polygon',
569+
'filled': 1,
570+
'pos': [0, 0],
571+
'polygons': [polygon]
572+
}
569573

570-
# Note that in Eagle terminology, 'mirrored' essentially means
571-
# 'flipped' (i.e. to the opposite side of the board)
572-
if (mirrored and bot) or (not mirrored and top):
573-
dwg_layer['F'].append(dwg)
574-
elif (mirrored and top) or (not mirrored and bot):
575-
dwg_layer['B'].append(dwg)
574+
if dwg is not None:
575+
if el.tag == 'hole' or \
576+
el.attrib['layer'] == self.DIMENSION_LAYER:
577+
dwg_layer.append(dwg)
578+
else:
579+
bot = not top
576580

577-
def _process_footprint(self, package, x, y, angle, mirrored, populate):
578-
for el in package.iter():
579-
if el.tag in ['wire', 'rectangle', 'circle', 'hole',
580-
'polygonshape', 'polygon', 'hole']:
581-
self._add_silk_fab(el, x, y, angle, mirrored, populate)
581+
# Note that in Eagle terminology, 'mirrored' essentially means
582+
# 'flipped' (i.e. to the opposite side of the board)
583+
if (mirrored and bot) or (not mirrored and top):
584+
dwg_layer['F'].append(dwg)
585+
elif (mirrored and top) or (not mirrored and bot):
586+
dwg_layer['B'].append(dwg)
582587

583588
def _element_refdes_to_silk(self, el):
584589
for attr in el.iter('attribute'):
@@ -731,17 +736,6 @@ def _parse(self, brdfile):
731736
self.logger.error("Unsupported units %s on rlMinViaOuter",
732737
mv_units)
733738

734-
# Edges & silkscreen (partial)
735-
for el in plain.iter():
736-
self._add_drawing(el)
737-
# identify board bounding box based on edges
738-
board_outline_bbox = BoundingBox()
739-
740-
for drawing in self.pcbdata['edges']:
741-
self.add_drawing_bounding_box(drawing, board_outline_bbox)
742-
if board_outline_bbox.initialized():
743-
self.pcbdata['edges_bbox'] = board_outline_bbox.to_dict()
744-
745739
# Signals --> nets
746740
if self.config.include_nets:
747741
self.pcbdata['nets'] = []
@@ -762,7 +756,7 @@ def _parse(self, brdfile):
762756
for poly in signal.iter('polygon'):
763757
self._add_zone(poly, signal.attrib['name'])
764758

765-
# Elements --> components, footprints, silkscreen
759+
# Elements --> components, footprints, silkscreen, edges
766760
for el in elements.iter('element'):
767761
populate = el.get('populate') != 'no'
768762
elr = self.Rot(el.get('rot'))
@@ -825,14 +819,25 @@ def _parse(self, brdfile):
825819
elr.mirrored)
826820
self.pcbdata['footprints'].append(footprint)
827821

828-
# Add silkscreen for component footprint & refdes
822+
# Add silkscreen, edges for component footprint & refdes
829823
self._process_footprint(package, elx, ely, elr.angle, elr.mirrored,
830824
populate)
831825
self._element_refdes_to_silk(el)
832826

833827
if populate:
834828
self.components.append(comp)
835829

830+
# Edges & silkscreen (independent of elements)
831+
for el in plain.iter():
832+
self._add_drawing(el)
833+
# identify board bounding box based on edges
834+
board_outline_bbox = BoundingBox()
835+
836+
for drawing in self.pcbdata['edges']:
837+
self.add_drawing_bounding_box(drawing, board_outline_bbox)
838+
if board_outline_bbox.initialized():
839+
self.pcbdata['edges_bbox'] = board_outline_bbox.to_dict()
840+
836841
self._add_parsed_font_data()
837842

838843
# Fabrication & metadata

0 commit comments

Comments
 (0)