def parse_body(self, body):
     """ Extract a body of a symbol. """
     bdy = Body()
     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
    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 = Body()
        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')
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 = Body()
        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))]
    def parse_library(self, filename, circuit):
        """
        Parse the library file and add the components to the given
        circuit.
        """

        f = open(filename)

        for line in f:
            parts = line.strip().split()
            prefix = parts[0]

            if prefix == 'DEF':
                component = Component(parts[1])
                component.add_attribute('_prefix', parts[2])
                symbol = Symbol()
                component.add_symbol(symbol)
                body = Body()
                symbol.add_body(body)
            elif prefix == 'A':  # Arc
                x, y, radius, start, end = [int(i) for i in parts[1:6]]
                # convert tenths of degrees to pi radians
                start = round(start / 1800.0, 1)
                end = round(end / 1800.0, 1)
                body.add_shape(shape.Arc(x, y, start, end, radius))
            elif prefix == 'C':  # Circle
                x, y, radius = [int(i) for i in parts[1:4]]
                body.add_shape(shape.Circle(x, y, radius))
            elif prefix == 'P':  # Polyline
                num_points = int(parts[1])
                poly = shape.Polygon()
                for i in xrange(num_points):
                    x, y = int(parts[5 + 2 * i]), int(parts[6 + 2 * i])
                    poly.addPoint(x, y)
                body.add_shape(poly)
            elif prefix == 'S':  # Rectangle
                x, y, x2, y2 = [int(i) for i in parts[1:5]]
                rec = shape.Rectangle(x, y, x2 - x, y2 - y)
                body.add_shape(rec)
            elif prefix == 'T':  # Text
                angle, x, y = [int(i) for i in parts[1:4]]
                angle = round(angle / 1800.0, 1)
                text = parts[8].replace('~', ' ')
                body.add_shape(shape.Label(x, y, text, 'left', angle))
            elif prefix == 'X':  # Pin
                num, direction = int(parts[2]), parts[6]
                p2x, p2y, pinlen = int(parts[3]), int(parts[4]), int(parts[5])
                if direction == 'U':  # up
                    p1x = p2x
                    p1y = p2y - pinlen
                elif direction == 'D':  # down
                    p1x = p2x
                    p1y = p2y + pinlen
                elif direction == 'L':  # left
                    p1x = p2x - pinlen
                    p1y = p2y
                elif direction == 'R':  # right
                    p1x = p2x + pinlen
                    p1y = p2y
                else:
                    raise ValueError('unexpected pin direction', direction)
                # TODO: label?
                body.add_pin(Pin(num, (p1x, p1y), (p2x, p2y)))
            elif prefix == 'ENDDEF':
                circuit.add_component(component.name, component)

        f.close()
    def parse_library(self, filename, circuit):
        """
        Parse the library file and add the components to the given
        circuit.
        """

        f = open(filename)

        for line in f:
            parts = line.strip().split()
            prefix = parts[0]

            if prefix == "DEF":
                component = Component(parts[1])
                component.add_attribute("_prefix", parts[2])
                symbol = Symbol()
                component.add_symbol(symbol)
                body = Body()
                symbol.add_body(body)
            elif prefix == "A":  # Arc
                x, y, radius, start, end = [int(i) for i in parts[1:6]]
                # convert tenths of degrees to pi radians
                start = round(start / 1800.0, 1)
                end = round(end / 1800.0, 1)
                body.add_shape(shape.Arc(x, y, start, end, radius))
            elif prefix == "C":  # Circle
                x, y, radius = [int(i) for i in parts[1:4]]
                body.add_shape(shape.Circle(x, y, radius))
            elif prefix == "P":  # Polyline
                num_points = int(parts[1])
                poly = shape.Polygon()
                for i in xrange(num_points):
                    x, y = int(parts[5 + 2 * i]), int(parts[6 + 2 * i])
                    poly.addPoint(x, y)
                body.add_shape(poly)
            elif prefix == "S":  # Rectangle
                x, y, x2, y2 = [int(i) for i in parts[1:5]]
                rec = shape.Rectangle(x, y, x2 - x, y2 - y)
                body.add_shape(rec)
            elif prefix == "T":  # Text
                angle, x, y = [int(i) for i in parts[1:4]]
                angle = round(angle / 1800.0, 1)
                text = parts[8].replace("~", " ")
                body.add_shape(shape.Label(x, y, text, "left", angle))
            elif prefix == "X":  # Pin
                num, direction = int(parts[2]), parts[6]
                p2x, p2y, pinlen = int(parts[3]), int(parts[4]), int(parts[5])
                if direction == "U":  # up
                    p1x = p2x
                    p1y = p2y - pinlen
                elif direction == "D":  # down
                    p1x = p2x
                    p1y = p2y + pinlen
                elif direction == "L":  # left
                    p1x = p2x - pinlen
                    p1y = p2y
                elif direction == "R":  # right
                    p1x = p2x + pinlen
                    p1y = p2y
                else:
                    raise ValueError("unexpected pin direction", direction)
                # TODO: label?
                body.add_pin(Pin(num, (p1x, p1y), (p2x, p2y)))
            elif prefix == "ENDDEF":
                circuit.add_component(component.name, component)

        f.close()
 def tearDown(self):
     """ Teardown the test case. """
     self.bod = Body()
 def setUp(self):
     """ Setup the test case. """
     self.bod = Body()
class BodyTests(unittest.TestCase):
    """ The tests of the core module body feature """

    def setUp(self):
        """ Setup the test case. """
        self.bod = Body()

    def tearDown(self):
        """ Teardown the test case. """
        self.bod = Body()

    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 Body.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 Body.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)