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'
Exemplo n.º 2
0
 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
    def test_bounds_all_elts(self):
        '''bounds() with all the elements competing'''
        net = Net('foo')
        mkbounds(net, 3, 3, -1, -2)
        self.des.add_net(net)

        annot = Annotation('foo', 3, 3, 0, True)
        mkbounds(annot, 3, 3, 3, 5)
        self.des.design_attributes.add_annotation(annot)

        libcomp = Component('bar')
        libcomp.add_symbol(Symbol())
        libcomp.symbols[0].add_body(SBody())
        mkbounds(libcomp.symbols[0].bodies[0], 0, 0, 3, 3)
        self.des.add_component('foo', libcomp)

        compinst = ComponentInstance('bar', libcomp, 'foo', 0)
        compinst.add_symbol_attribute(SymbolAttribute(3, 0, 0, False))
        self.des.add_component_instance(compinst)

        top_left, btm_right = self.des.bounds()
        self.assertEqual(top_left.x, -1)
        self.assertEqual(top_left.y, -2)
        self.assertEqual(btm_right.x, 6)
        self.assertEqual(btm_right.y, 5)
Exemplo n.º 4
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')
Exemplo n.º 5
0
    def build_symbols(self, has_convert):
        """ Build all Symbols and Bodies for this component. The
        has_convert argument should be True if there are DeMorgan
        convert bodies. """

        for _ in range(2 if has_convert else 1):
            symbol = Symbol()
            for _ in range(self.num_units):
                symbol.add_body(SBody())
            self.component.add_symbol(symbol)
Exemplo n.º 6
0
    def parse(self):
        """ Parses a component from the library, returns a Compenent. """
        part = Component(self.filename)
        part.add_symbol(Symbol())
        part.symbols[0].add_body(SBody())

        tree = ViewDrawBase.parse(self)
        for k, v in tree['attr']:
            part.add_attribute(k, v)
        for shape in tree['shape'] + sum(tree['lines'], []):
            part.symbols[0].bodies[0].add_shape(shape)
        for pin in tree['pin']:
            part.symbols[0].bodies[0].add_pin(pin)

        return part
Exemplo n.º 7
0
 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
    def test_bounds_parts(self):
        '''test bounds() with just components in the design'''
        libcomp = Component('bar')
        libcomp.add_symbol(Symbol())
        libcomp.symbols[0].add_body(SBody())
        mkbounds(libcomp.symbols[0].bodies[0], 0, 0, 10, 10)
        self.des.add_component('foo', libcomp)
        for (x, y) in ((1, 3), (3, 2), (5, 3), (3, 7)):
            compinst = ComponentInstance(str((x, y)), libcomp, 'foo', 0)
            compinst.add_symbol_attribute(SymbolAttribute(x, y, 0, False))
            self.des.add_component_instance(compinst)

        top_left, btm_right = self.des.bounds()
        self.assertEqual(top_left.x, 1)
        self.assertEqual(top_left.y, 2)
        self.assertEqual(btm_right.x, 15)
        self.assertEqual(btm_right.y, 17)
 def tearDown(self):
     """ Teardown the test case. """
     self.bod = SBody()
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
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))]
Exemplo n.º 12
0
 def tearDown(self):
     """ Teardown the test case. """
     self.bod = SBody()
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))]
Exemplo n.º 14
0
 def setUp(self):
     """ Setup the test case. """
     self.bod = SBody()
Exemplo n.º 15
0
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)
Exemplo n.º 16
0
    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
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 setUp(self):
     """ Setup the test case. """
     self.bod = SBody()