""" Exporting of GNU PCB files to DXF. """ import ezdxf import sys import pcb_types class DxfFootprintWriter(): """ Writer for DXF files from GNU PCB footprints. """ def __init__(self): self._doc = ezdxf.new('R12') self._msp = self._doc.modelspace() self._layers = {} self._addLayer('clearance', 8) # gray self._addLayer('hole_drill', 3) # green self._addLayer('mask', 16) # dark red self._addLayer('name', 7) # white self._addLayer('number', 7) # white self._addLayer('pin_copper', 1) # red self._addLayer('pin_drill', 5) # blue self._addLayer('silk_center', 2) # yellow def _addCircle(self, x: pcb_types.Coordinate, y: pcb_types.Coordinate, d: pcb_types.Coordinate, layer_name: str): self._msp.add_circle((x.mm, -y.mm), radius=d.mm / 2, dxfattribs={'layer': layer_name}) def _addOctagon(self, cx: pcb_types.Coordinate, cy: pcb_types.Coordinate, size: pcb_types.Coordinate, layer_name: str): l = size.mm / 2 s = l / (1 + 2**.5) points = [(cx.mm - s, -cy.mm - l), (cx.mm + s, -cy.mm - l), (cx.mm + l, -cy.mm - s), (cx.mm + l, -cy.mm + s), (cx.mm + s, -cy.mm + l), (cx.mm - s, -cy.mm + l), (cx.mm - l, -cy.mm + s), (cx.mm - l, -cy.mm - s), (cx.mm - s, -cy.mm - l)] self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name}) def _addSquare(self, cx: pcb_types.Coordinate, cy: pcb_types.Coordinate, size: pcb_types.Coordinate, layer_name: str): points = [(cx.mm - size.mm / 2, -cy.mm - size.mm / 2), (cx.mm + size.mm / 2, -cy.mm - size.mm / 2), (cx.mm + size.mm / 2, -cy.mm + size.mm / 2), (cx.mm - size.mm / 2, -cy.mm + size.mm / 2), (cx.mm - size.mm / 2, -cy.mm - size.mm / 2)] self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name}) def _addLayer(self, name: str, color: int = 7): if name in self._layers: return self._layers[name] = self._doc.layers.new(name=name, dxfattribs={ 'linetype': 'Continuous', 'color': color }) def _addLine(self, x1: pcb_types.Coordinate, y1: pcb_types.Coordinate, x2: pcb_types.Coordinate, y2: pcb_types.Coordinate, layer_name: str): self._msp.add_line((x1.mm, -y1.mm), (x2.mm, -y2.mm), dxfattribs={'layer': layer_name}) def _addText(self, x: pcb_types.Coordinate, y: pcb_types.Coordinate, t: str, layer_name: str, height: pcb_types.Coordinate = None, align: str = None): dxfattribs = {'layer': layer_name} if height is not None: dxfattribs['height'] = height.mm text = self._msp.add_text(t, dxfattribs=dxfattribs) kwargs = {} if align is not None: kwargs['align'] = align text.set_pos((x.mm, -y.mm), **kwargs) def _drawHole(self, pin: pcb_types.Pin): self._addCircle(pin.r_x, pin.r_y, pin.drill, 'hole_drill') self._addCircle(pin.r_x, pin.r_y, pin.thickness + pin.clearance, 'clearance') self._addCircle(pin.r_x, pin.r_y, pin.mask, 'mask') self._drawNameNumber(pin.r_x, pin.r_y, pin.name, pin.number, height=pin.drill / 2) def _drawNameNumber(self, x: pcb_types.Coordinate, y: pcb_types.Coordinate, name: str, number: str, height: pcb_types.Coordinate = None): self._addText(x, y, name, 'name', height=height, align='BOTTOM_CENTER') self._addText(x, y, number, 'number', height=height, align='TOP_CENTER') def _drawPinOctagon(self, pin: pcb_types.Pin): self._addOctagon(pin.r_x, pin.r_y, pin.thickness, 'pin_copper') self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill') self._addOctagon(pin.r_x, pin.r_y, pin.thickness + pin.clearance, 'clearance') self._addOctagon(pin.r_x, pin.r_y, pin.mask, 'mask') self._drawNameNumber(pin.r_x, pin.r_y, pin.name, pin.number, height=pin.drill / 2) def _drawPinRound(self, pin: pcb_types.Pin): self._addCircle(pin.r_x, pin.r_y, pin.thickness, 'pin_copper') self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill') self._addCircle(pin.r_x, pin.r_y, pin.thickness + pin.clearance, 'clearance') self._addCircle(pin.r_x, pin.r_y, pin.mask, 'mask') self._drawNameNumber(pin.r_x, pin.r_y, pin.name, pin.number, height=pin.drill / 2) def _drawPinSquare(self, pin: pcb_types.Pin): self._addSquare(pin.r_x, pin.r_y, pin.thickness, 'pin_copper') self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill') self._addSquare(pin.r_x, pin.r_y, pin.thickness + pin.clearance, 'clearance') self._addSquare(pin.r_x, pin.r_y, pin.mask, 'mask') self._drawNameNumber(pin.r_x, pin.r_y, pin.name, pin.number, height=pin.drill / 2) def drawElementLine(self, element_line: pcb_types.ElementLine): """ Draw element line to DXF. """ self._addLine(element_line.r_x1, element_line.r_y1, element_line.r_x2, element_line.r_y2, 'silk_center') # TODO element_line.thickness # TODO element_line.s_flags onsolder def drawFootprint(self, fp: pcb_types.Element): """ Draw footprint to DXF. """ # draw entities in header # TODO # draw entities in body type2method = { pcb_types.ElementLine: self.drawElementLine, pcb_types.Pin: self.drawPin } for entity in fp.body: try: method = type2method[type(entity)] method(entity) except KeyError: # TODO print( 'warning: ignoring unknown entity of' f' type {type(entity).__name__:s}', file=sys.stderr) def drawPin(self, pin: pcb_types.Pin): """ Draw pin to DXF. """ if 'hole' in pin.s_flags: return self._drawHole(pin) if 'octagon' in pin.s_flags: return self._drawPinOctagon(pin) if 'square' in pin.s_flags: return self._drawPinSquare(pin) return self._drawPinRound(pin) def writeDxf(self, file_name: str): """ Write DXF file. """ self._doc.saveas(file_name)