def parse_symbol_body(self, body): """ Extract a body of a symbol. """ bdy = SBody() for pin in body.get('pins'): parsed_pin = self.parse_pin(pin) bdy.add_pin(parsed_pin) for shape in body.get('shapes'): parsed_shape = self.parse_shape(shape) bdy.add_shape(parsed_shape) return bdy
class ComponentParser(object): """I parse components from Fritzing libraries.""" # The svg files in fritzing libraries are specified in pixels that # are 72dpi. The schematics are in 90dpi. svg_mult = 90.0 / 72.0 def __init__(self, idref): self.component = Component(idref) self.next_pin_number = 0 self.cid2termid = {} # connid -> termid self.termid2pin = {} # termid -> Pin self.terminals = set() self.width = 0.0 self.height = 0.0 def parse_fzp(self, fzp_file): """ Parse the Fritzing component file """ tree = ElementTree(file=fzp_file) try: prefix = tree.find('label').text except AttributeError: pass else: self.component.add_attribute('_prefix', prefix) symbol = Symbol() self.component.add_symbol(symbol) self.body = SBody() symbol.add_body(self.body) self.cid2termid.update(self.parse_terminals(tree)) self.terminals.update(self.cid2termid.values()) layers = tree.find('views/schematicView/layers') if layers is None: self.image = None else: self.image = layers.get('image') def connect_point(self, cid, inst, point): """ Given a connector id, instance id, and a NetPoint, add the appropriate ConnectedComponent to the point """ termid = self.cid2termid.get(cid) pin = self.termid2pin.get(termid) if pin is not None: ccpt = ConnectedComponent(inst.instance_id, pin.pin_number) point.add_connected_component(ccpt) def get_next_pin_number(self): """ Return the next pin number """ nextpn = self.next_pin_number self.next_pin_number += 1 return str(nextpn) def parse_terminals(self, tree): """ Return a dictionary mapping connector id's to terminal id's """ cid2termid = {} for conn in tree.findall('connectors/connector'): plug = conn.find('views/schematicView/p') if plug is None: continue termid = plug.get('terminalId') if termid is None: termid = plug.get('svgId') if termid is not None: cid2termid[conn.get('id')] = termid return cid2termid def parse_svg(self, svg_file): """ Parse the shapes and pins from an svg file """ tree = ElementTree(file=svg_file) viewbox = tree.getroot().get('viewBox') if viewbox != None: self.width, self.height = [float(v) for v in viewbox.split()[-2:]] self.width *= self.svg_mult self.height *= self.svg_mult _iter = tree.getroot().getiterator() for element in _iter: for shape in self.parse_shapes(element): self.body.add_shape(shape) if element.get('id') in self.terminals: pin = get_pin(shape) if pin is not None: pin.pin_number = self.get_next_pin_number() self.termid2pin[element.get('id')] = pin self.body.add_pin(pin) def parse_shapes(self, element): """ Parse a list of shapes from an svg element """ tag = element.tag.rsplit('}', -1)[-1] if tag == 'circle': return self.parse_circle(element) elif tag == 'rect': return self.parse_rect(element) elif tag == 'line': return self.parse_line(element) elif tag == 'path': return self.parse_path(element) elif tag == 'polygon': return self.parse_polygon(element) elif tag == 'polyline': return self.parse_polyline(element) else: return [] def parse_rect(self, rect): """ Parse a rect element """ x, y = (get_x(rect, mult=self.svg_mult), get_y(rect, mult=self.svg_mult)) width, height = (get_length(rect, 'width', self.svg_mult), get_length(rect, 'height', self.svg_mult)) return [Rectangle(x, y, width, height)] def parse_line(self, rect): """ Parse a line element """ return [Line((get_x(rect, 'x1', self.svg_mult), get_y(rect, 'y1', self.svg_mult)), (get_x(rect, 'x2', self.svg_mult), get_y(rect, 'y2', self.svg_mult)))] def parse_path(self, path): """ Parse a path element """ return PathParser(path).parse() def parse_polygon(self, poly): """ Parse a polygon element """ shape = Polygon() for point in poly.get('points', '').split(): if point: x, y = point.split(',') shape.add_point(make_x(x, self.svg_mult), make_y(y, self.svg_mult)) if shape.points: shape.add_point(shape.points[0].x, shape.points[0].y) return [shape] def parse_polyline(self, poly): """ Parse a polyline element """ shapes = [] last_point = None for point in poly.get('points', '').split(): if point: x, y = point.split(',') point = (make_x(x, self.svg_mult), make_y(y, self.svg_mult)) if last_point is not None: shapes.append(Line(last_point, point)) last_point = point return shapes def parse_circle(self, circle): """ Parse a circle element """ return [Circle(get_x(circle, 'cx', self.svg_mult), get_y(circle, 'cy', self.svg_mult), get_length(circle, 'r', self.svg_mult))]
class ComponentParser(object): """I parse components from Fritzing libraries.""" # The svg files in fritzing libraries are specified in pixels that # are 72dpi. The schematics are in 90dpi. svg_mult = 90.0 / 26.0 def __init__(self, idref): self.component = Component(idref) self.next_pin_number = 0 self.cid2termid = {} # connid -> termid self.termid2pin = {} # termid -> Pin self.terminals = set() self.width = 0.0 self.height = 0.0 def parse_fzp(self, fzp_file): """ Parse the Fritzing component file """ tree = ElementTree(file=fzp_file) try: prefix = tree.find('label').text except AttributeError: pass else: self.component.add_attribute('_prefix', prefix) symbol = Symbol() self.component.add_symbol(symbol) self.body = SBody() symbol.add_body(self.body) self.cid2termid.update(self.parse_terminals(tree)) self.terminals.update(self.cid2termid.values()) layers = tree.find('views/schematicView/layers') if layers is None: self.image = None else: self.image = layers.get('image') if self.image == 'schematic/dcpower.svg' : self.image='schematic/dc_powersupply.svg' #print self.image def connect_point(self, cid, inst, point): """ Given a connector id, instance id, and a NetPoint, add the appropriate ConnectedComponent to the point """ termid = self.cid2termid.get(cid) pin = self.termid2pin.get(termid) if pin is not None: ccpt = ConnectedComponent(inst.instance_id, pin.pin_number) point.add_connected_component(ccpt) def get_next_pin_number(self): """ Return the next pin number """ nextpn = self.next_pin_number self.next_pin_number += 1 return str(nextpn) def parse_terminals(self, tree): """ Return a dictionary mapping connector id's to terminal id's """ cid2termid = {} for conn in tree.findall('connectors/connector'): plug = conn.find('views/schematicView/p') if plug is None: continue termid = plug.get('terminalId') if termid is None: termid = plug.get('svgId') if termid is not None: cid2termid[conn.get('id')] = termid return cid2termid def parse_svg(self, svg_file): """ Parse the shapes and pins from an svg file """ tree = ElementTree(file=svg_file) viewbox = tree.getroot().get('viewBox') #self.svg_mult = self.svg_mult*3 if viewbox != None: self.width, self.height = [float(v) for v in viewbox.split()[-2:]] self.width *= self.svg_mult self.height *= self.svg_mult _iter = tree.getroot().getiterator() #print _iter for element in _iter: for shape in self.parse_shapes(element): self.body.add_shape(shape) if element.get('id') in self.terminals: pin = get_pin(shape) if pin is not None: pin.pin_number = self.get_next_pin_number() self.termid2pin[element.get('id')] = pin self.body.add_pin(pin) def parse_shapes(self, element): """ Parse a list of shapes from an svg element """ tag = element.tag.rsplit('}', -1)[-1] #print element if tag == 'circle': return self.parse_circle(element) elif tag == 'rect': return self.parse_rect(element) elif tag == 'line': return self.parse_line(element) elif tag == 'path': return self.parse_path(element) elif tag == 'polygon': return self.parse_polygon(element) elif tag == 'polyline': return self.parse_polyline(element) else: return [] def parse_rect(self, rect): """ Parse a rect element """ x, y = (get_x(rect, mult=self.svg_mult), get_y(rect, mult=self.svg_mult)) width, height = (get_length(rect, 'width', self.svg_mult),get_length(rect, 'height', self.svg_mult)) return [Rectangle(x, y, width, height)] def parse_line(self, rect): """ Parse a line element """ return [Line((get_x(rect, 'x1', self.svg_mult), get_y(rect, 'y1', self.svg_mult)), (get_x(rect, 'x2', self.svg_mult), get_y(rect, 'y2', self.svg_mult)))] def parse_path(self, path): """ Parse a path element """ return PathParser(path).parse() def parse_polygon(self, poly): """ Parse a polygon element """ shape = Polygon() for point in poly.get('points', '').split(): if point: x, y = point.split(',') shape.add_point(make_x(x, self.svg_mult), make_y(y, self.svg_mult)) if shape.points: shape.add_point(shape.points[0].x, shape.points[0].y) return [shape] def parse_polyline(self, poly): """ Parse a polyline element """ shapes = [] last_point = None for point in poly.get('points', '').split(): if point: x, y = point.split(',') point = (make_x(x, self.svg_mult), make_y(y, self.svg_mult)) if last_point is not None: shapes.append(Line(last_point, point)) last_point = point return shapes def parse_circle(self, circle): """ Parse a circle element """ return [Circle(get_x(circle, 'cx', self.svg_mult), get_y(circle, 'cy', self.svg_mult), get_length(circle, 'r', self.svg_mult))]
class SBodyTests(unittest.TestCase): """ The tests of the core module body feature """ def setUp(self): """ Setup the test case. """ self.bod = SBody() def tearDown(self): """ Teardown the test case. """ self.bod = SBody() def test_create_new_body(self): """ Test the creation of a new empty body. """ assert len(self.bod.shapes) == 0 assert len(self.bod.pins) == 0 def test_empty_bounds(self): '''Test that an empty body only bounds the local origin''' top_left, bottom_right = self.bod.bounds() self.assertEqual(top_left.x, 0) self.assertEqual(top_left.y, 0) self.assertEqual(bottom_right.x, 0) self.assertEqual(bottom_right.y, 0) def test_bounds_pins(self): '''Test bounds() with just pins included''' pins = [Pin(str(i), Point(0, 0), Point(0, 0)) for i in range(4)] # checking body.bounds(), not the pins, so override their bounds() # methods for i, pin in enumerate(pins): bounds = [3, 3, 3, 3] bounds[i] = 2 * i mkbounds(pin, bounds[0], bounds[1], bounds[2], bounds[3]) self.bod.add_pin(pin) top_left, bottom_right = self.bod.bounds() self.assertEqual(top_left.x, 0) self.assertEqual(top_left.y, 2) self.assertEqual(bottom_right.x, 4) self.assertEqual(bottom_right.y, 6) def test_bounds_shapes(self): '''Test SBody.bounds() when the body only consists of shapes''' shapes = [Shape() for i in range(4)] for i, shape in enumerate(shapes): bounds = [3, 3, 3, 3] bounds[i] = 2 * i mkbounds(shape, bounds[0], bounds[1], bounds[2], bounds[3]) self.bod.add_shape(shape) top_left, bottom_right = self.bod.bounds() self.assertEqual(top_left.x, 0) self.assertEqual(top_left.y, 2) self.assertEqual(bottom_right.x, 4) self.assertEqual(bottom_right.y, 6) def test_bounds_pins_shapes(self): '''Test SBody.bounds() when some extremes are from pins, others shapes''' point = Point(0, 0) pin1 = Pin('foo', point, point) pin2 = Pin('bar', point, point) sh1 = Shape() sh2 = Shape() mkbounds(pin1, 3, 2, 3, 3) mkbounds(pin2, 3, 3, 5, 3) mkbounds(sh1, 3, 3, 3, 4) mkbounds(sh2, 1, 3, 3, 3) self.bod.add_pin(pin1) self.bod.add_pin(pin2) self.bod.add_shape(sh1) self.bod.add_shape(sh2) top_left, bottom_right = self.bod.bounds() self.assertEqual(top_left.x, 1) self.assertEqual(top_left.y, 2) self.assertEqual(bottom_right.x, 5) self.assertEqual(bottom_right.y, 4)
def make_body_from_symbol(self, lib, symbol_name, pin_number_lookup): """ Construct an openjson SBody from an eagle symbol in a library. """ body = SBody() symbol = [ s for s in get_subattr(lib, 'symbols.symbol') if s.name == symbol_name ][0] for wire in symbol.wire: body.add_shape(self.make_shape_for_wire(wire)) for rect in symbol.rectangle: rotation = make_angle('0' if rect.rot is None else rect.rot) x1, y1 = rotate_point( (self.make_length(rect.x1), self.make_length(rect.y1)), rotation) x2, y2 = rotate_point( (self.make_length(rect.x2), self.make_length(rect.y2)), rotation) ux, uy = min(x1, x2), max(y1, y2) lx, ly = max(x1, x2), min(y1, y2) body.add_shape(Rectangle(ux, uy, lx - ux, uy - ly)) for poly in symbol.polygon: map(body.add_shape, self.make_shapes_for_poly(poly)) for circ in symbol.circle: body.add_shape(self.make_shape_for_circle(circ)) pin_map = {} for pin in symbol.pin: connect_point = (self.make_length(pin.x), self.make_length(pin.y)) null_point = self.get_pin_null_point(connect_point, pin.length, pin.rot) label = self.get_pin_label(pin, null_point) pin_map[pin.name] = Pin(pin_number_lookup(pin.name), null_point, connect_point, label) if pin.direction: pin_map[pin.name].add_attribute('eaglexml_direction', pin.direction) if pin.visible: pin_map[pin.name].add_attribute('eaglexml_visible', pin.visible) body.add_pin(pin_map[pin.name]) ann_map = {} for text in symbol.text: x = self.make_length(text.x) y = self.make_length(text.y) content = '' if text.valueOf_ is None else text.valueOf_ rotation = make_angle('0' if text.rot is None else text.rot) align = 'right' if is_mirrored(text.rot) else 'left' if rotation == 0.5: rotation = 1.5 if content.lower() == '>name': ann_map['name'] = Annotation(content, x, y, rotation, 'true') elif content.lower() == '>value': ann_map['value'] = Annotation(content, x, y, rotation, 'true') else: body.add_shape( Label(x, y, content, align=align, rotation=rotation)) return body, pin_map, ann_map
def make_body_from_symbol(self, lib, symbol_name, pin_number_lookup): """ Construct an openjson SBody from an eagle symbol in a library. """ body = SBody() symbol = [s for s in get_subattr(lib, 'symbols.symbol') if s.name == symbol_name][0] for wire in symbol.wire: body.add_shape(self.make_shape_for_wire(wire)) for rect in symbol.rectangle: rotation = make_angle('0' if rect.rot is None else rect.rot) x1, y1 = rotate_point((self.make_length(rect.x1), self.make_length(rect.y1)), rotation) x2, y2 = rotate_point((self.make_length(rect.x2), self.make_length(rect.y2)), rotation) ux, uy = min(x1, x2), max(y1, y2) lx, ly = max(x1, x2), min(y1, y2) body.add_shape(Rectangle(ux, uy, lx - ux, uy - ly)) for poly in symbol.polygon: map(body.add_shape, self.make_shapes_for_poly(poly)) for circ in symbol.circle: body.add_shape(self.make_shape_for_circle(circ)) pin_map = {} for pin in symbol.pin: connect_point = (self.make_length(pin.x), self.make_length(pin.y)) null_point = self.get_pin_null_point(connect_point, pin.length, pin.rot) label = self.get_pin_label(pin, null_point) pin_map[pin.name] = Pin(pin_number_lookup(pin.name), null_point, connect_point, label) if pin.direction: pin_map[pin.name].add_attribute('eaglexml_direction', pin.direction) if pin.visible: pin_map[pin.name].add_attribute('eaglexml_visible', pin.visible) body.add_pin(pin_map[pin.name]) ann_map = {} for text in symbol.text: x = self.make_length(text.x) y = self.make_length(text.y) content = '' if text.valueOf_ is None else text.valueOf_ rotation = make_angle('0' if text.rot is None else text.rot) align = 'right' if is_mirrored(text.rot) else 'left' if rotation == 0.5: rotation = 1.5 if content.lower() == '>name': ann_map['name'] = Annotation(content, x, y, rotation, 'true') elif content.lower() == '>value': ann_map['value'] = Annotation(content, x, y, rotation, 'true') else: body.add_shape(Label(x, y, content, align=align, rotation=rotation)) return body, pin_map, ann_map