def __init__(self, pin_number, p1, p2, label=None):
     self.label = label # is a Label
     self.p1 = Point(p1) # null end
     self.p2 = Point(p2) # connect end
     self.pin_number = pin_number
     self.attributes = dict()
     self.styles = dict()
示例#2
0
 def __init__(self, pin_number, p1, p2, label=None):
     self.label = label  # is a Label
     self.p1 = Point(p1)  # null end
     self.p2 = Point(p2)  # connect end
     self.pin_number = pin_number
     self.attributes = dict()
     self.styles = dict()
    def _make_line(self, p1, p2, aperture):
        x1, y1 = float(p1[0]), float(p1[1])
        x2, y2 = float(p2[0]), float(p2[1])
        aperture = float(aperture)

        dx = x2 - x1
        dy = y2 - y1
        length = math.sqrt(dx * dx + dy * dy)

        if length == 0.0:
            return []
        result = []

        line1 = Line((x1 - aperture * (y2 - y1) / length, y1 - aperture *
                      (x1 - x2) / length),
                     (x2 - aperture * (y2 - y1) / length, y2 - aperture *
                      (x1 - x2) / length))
        line2 = Line((x1 + aperture * (y2 - y1) / length, y1 + aperture *
                      (x1 - x2) / length),
                     (x2 + aperture * (y2 - y1) / length, y2 + aperture *
                      (x1 - x2) / length))
        result.append(line1)
        result.append(line2)

        def make_arc(p1, p2, p0):
            start_angle = math.atan2(p1.y - p0.y, p1.x - p0.x) / math.pi
            end_angle = math.atan2(p2.y - p0.y, p2.x - p0.x) / math.pi
            return Arc(p0.x, p0.y, start_angle, end_angle, aperture)

        result.append(make_arc(line1.p1, line2.p1, Point(p1)))
        result.append(make_arc(line2.p2, line1.p2, Point(p2)))
        return result
示例#4
0
    def draw_schematic(self):
        """ Render the schematic into self.img """
        # start off with all the component instances
        for inst in self.design.component_instances:
            comp = self.design.components.components[inst.library_id]
            for body, attr in zip(comp.symbols[inst.symbol_index].bodies,
                                  inst.symbol_attributes):
                # draw the appropriate body, at the position in attr
                pos = Point(attr.x, attr.y)
                self.draw_symbol(body, pos, attr.rotation, attr.flip)
                # draw in any annotations
                for ann in attr.annotations:
                    if ann.visible:
                        pos = self.base_xform.chain(Point(ann.x, ann.y))
                        self.canvas.text((pos.x, pos.y),
                                         ann.value,
                                         fill=self.options.style['annot'])

        for shape in self.design.shapes:
            draw_method = getattr(self, 'draw_shape_%s' % shape.type)
            draw_method(shape, self.base_xform, self.options.style['annot'])

        for net in self.design.nets:
            self.draw_net(net)

        for ann in self.design.design_attributes.annotations:
            if ann.visible:
                pos = self.base_xform.chain(Point(ann.x, ann.y))
                self.canvas.text((pos.x, pos.y),
                                 ann.value,
                                 fill=self.options.style['annot'])
示例#5
0
 def bounds(self):
     """ Return the min and max point of a design """
     bounds = [net.bounds() for net in self.nets]
     bounds.extend([anno.bounds() for anno in
                    self.design_attributes.annotations])
     offset_bounds = lambda (p1, p2), (xo, yo): [Point(p1.x + xo, p1.y + yo),
                                                 Point(p2.x + xo, p2.y + yo)]
     for comp in self.component_instances:
         offsets = [(att.x, att.y) for att in comp.symbol_attributes]
         lib_comp = self.components.components[comp.library_id]
         bodybounds = [b.bounds() for b in
                       lib_comp.symbols[comp.symbol_index].bodies]
         # the offsets in symbol_attributes will align and apply to the
         # library components bodies
         bounds.extend([offset_bounds(b, o) for b, o in zip(bodybounds,
                                                            offsets)])
     # flatten out bounds to just a list of Points
     bounds = sum(bounds, [])
     x_values = [pt.x for pt in bounds]
     y_values = [pt.y for pt in bounds]
     # by convention, an empty design will bound just the origin
     if len(x_values) == 0:
         x_values = [0]
         y_values = [0]
     return [Point(min(x_values), min(y_values)),
             Point(max(x_values), max(y_values))]
示例#6
0
 def __init__(self, pin_number, p, shapes, label=None):
     self.label = label  # is a Label
     self.p = Point(p)
     self.pin_number = pin_number
     self.shapes = shapes
     self.attributes = dict()
     self.styles = dict()
示例#7
0
 def test_pin_bounds(self):
     '''Test bounds() for individual pins'''
     pin = Pin(0, Point(2, 5), Point(4, 3))
     top_left, bottom_right = pin.bounds()
     self.assertEqual(top_left.x, 2)
     self.assertEqual(top_left.y, 3)
     self.assertEqual(bottom_right.x, 4)
     self.assertEqual(bottom_right.y, 5)
示例#8
0
 def subtest_pin(self):
     """ Common tests for both pin test cases """
     k, v = self.sym.parse_pin('13 2 4 3 5 0 0 0')
     self.assertEqual(k, 'pin')
     self.assertEqual(v.p1, Point(3, 5))
     self.assertEqual(v.p2, Point(2, 4))
     self.assertEqual(v.pin_number, 13)
     return(v)
示例#9
0
 def test_line_one_seg(self):
     """One line segment, from (2,3) to (4,7)"""
     k, v = self.base.parse_line('2 1 3 4 7')
     self.assertEqual(k, 'lines')
     self.assertEqual(len(v), 1)
     self.assertEqual(v[0].type, 'line')
     p1, p2 = v[0].p1, v[0].p2
     self.assertEqual(p1, Point(1, 3))
     self.assertEqual(p2, Point(4, 7))
 def bounds(self):
     """ Return the min and max points of a pin """
     x_values = [self.p1.x, self.p2.x]
     y_values = [self.p1.y, self.p2.y]
     if self.label is not None:
         x_values.extend([pt.x for pt in self.label.bounds()])
         y_values.extend([pt.y for pt in self.label.bounds()])
     return [Point(min(x_values), min(y_values)),
             Point(max(x_values), max(y_values))]
示例#11
0
 def test_create_new_line(self):
     """ Test the creation of a new empty line. """
     p1 = Point(0, 1)
     p2 = Point(2, 3)
     line = Line(p1, p2)
     assert line.p1.x == p1.x
     assert line.p1.y == p1.y
     assert line.p2.x == p2.x
     assert line.p2.y == p2.y
示例#12
0
    def parse_trace_segments(self, segments_json):
        if segments_json is None:
            return None

        for segment_json in segments_json:
            p1 = Point(segment_json['p1']['x'], segment_json['p1']['y'])
            p2 = Point(segment_json['p2']['x'], segment_json['p2']['y'])
            segment = Segment(segment_json['layer'], p1, p2,
                              segment_json['width'])
            self.design.trace_segments.append(segment)
示例#13
0
 def test_point_equality(self):
     '''pt1 == pt2 iff (pt1.x == pt2.x and pt1.y == pt2.y)'''
     pta = Point(2, 3)
     ptb = Point(2, 3)
     ptc = Point(2, 4)
     ptd = Point(4, 3)
     pte = Point(4, 5)
     self.assertEqual(pta, ptb)
     for pt in (ptc, ptd, pte):
         self.assertNotEqual(pta, pt)
示例#14
0
 def bounds(self):
     """ Return the min and max points of the bounding box """
     x_values = [p.x for p in self.points.values()]
     y_values = [p.y for p in self.points.values()]
     # get a list of all the bounding points for annotations
     bounds = sum([ann.bounds() for ann in self.annotations], [])
     x_values.extend([pt.x for pt in bounds])
     y_values.extend([pt.y for pt in bounds])
     return [Point(min(x_values), min(y_values)),
             Point(max(x_values), max(y_values))]
示例#15
0
 def test_pin_label_bounds(self):
     '''Test bounds() for a pin with a label'''
     lab = Label(0, 0, 'foo', align='left', rotation=0)
     mkbounds(lab, 1, 3, 2, 6)
     pin = Pin(0, Point(2, 2), Point(4, 3), lab)
     top_left, bottom_right = pin.bounds()
     self.assertEqual(top_left.x, 1)
     self.assertEqual(top_left.y, 2)
     self.assertEqual(bottom_right.x, 4)
     self.assertEqual(bottom_right.y, 6)
示例#16
0
    def draw_shape_arc(self, arc, xform, colour):
        """ draw an arc segment """
        x, y, r = arc.x, arc.y, arc.radius
        # if the arc segment were extended to draw a full circle, box would
        # enclose that circle
        minpt, maxpt = [
            xform.chain(Point(px, py))
            for (px, py) in [(x - r, y - r), (x + r, y + r)]
        ]
        xs, ys = [minpt.x, maxpt.x], [minpt.y, maxpt.y]
        box = (min(xs), min(ys), max(xs), max(ys))

        center = xform.chain(Point(x, y))

        def pt_to_deg(pt):
            # given a point, give the angle w.r.t. to the xform'd center of the
            # arc (ie. where it will be when drawn)
            # 3 o'clock is angle of 0, angles increase clockwise
            opp, adj = pt.y - center.y, pt.x - center.x
            if adj == 0:
                if opp > 0:
                    return 90
                return 270
            angle = 180 * atan(opp / float(adj)) / pi
            if pt.x < center.x:
                angle += 180
            return int(angle % 360)

        # create a point in the middle of the arc (used to detect that the xform
        # has flipped the arc around. In that case, drawing from start_angle to
        # end_angle will go in the wrong direction, and draw out exactly the
        # wrong part of the circle)
        mid_ang = (arc.start_angle + arc.end_angle) / 2
        if arc.start_angle > arc.end_angle:
            mid_ang = (mid_ang - 1) % 2
        mid_pt = xform.chain(
            Point(
                cos((2 - mid_ang) * pi) * arc.radius + x,
                sin((2 - mid_ang) * pi) * arc.radius + y))

        start, end = [xform.chain(pt) for pt in arc.ends()]
        if pt_to_deg(start) < pt_to_deg(end):
            if not (pt_to_deg(start) < pt_to_deg(mid_pt) < pt_to_deg(end)):
                # swap start and end so that the arc traces through the
                # transformed midpoint
                start, end = end, start
        elif (pt_to_deg(end) < pt_to_deg(mid_pt) < pt_to_deg(start)):
            # swap start and end so that the arc traces through the
            # transformed midpoint
            start, end = end, start

        # by using the arc.ends() points, any rotation in xform gets handled
        # properly.
        self.canvas.arc(box, pt_to_deg(start), pt_to_deg(end), fill=colour)
 def bounds(self):
     """ Return the min and max points of the bounding box around a body """
     points = sum([s.bounds() for s in self.shapes + self.pins], [])
     x_values = [pt.x for pt in points]
     y_values = [pt.y for pt in points]
     if len(x_values) == 0:
         # Empty body includes just the origin
         x_values = [0]
         y_values = [0]
     return [Point(min(x_values), min(y_values)),
             Point(max(x_values), max(y_values))]
示例#18
0
 def test_create_point_from_point(self):
     '''Test Point constructor when cloning another point'''
     oldpnt = Point(2, 3)
     newpnt = Point(oldpnt)
     self.assertFalse(oldpnt is newpnt)
     self.assertEqual(oldpnt.x, newpnt.x)
     self.assertEqual(oldpnt.y, newpnt.y)
     oldpnt.x = 4
     oldpnt.y = 5
     self.assertEqual(newpnt.x, 2)
     self.assertEqual(newpnt.y, 3)
示例#19
0
    def test_line_max_point(self):
        '''Test Line.max_point() in different situations'''
        line = Line(Point(2, 3), Point(4, 5))
        bottom_right = line.max_point()
        self.assertEqual(bottom_right.x, 4)
        self.assertEqual(bottom_right.y, 5)

        line = Line(Point(2, 3), Point(-1, 4))
        bottom_right = line.max_point()
        self.assertEqual(bottom_right.x, 2)
        self.assertEqual(bottom_right.y, 4)
示例#20
0
    def test_line_min_point(self):
        '''Test Line.min_point() in different situations'''
        line = Line(Point(2, 3), Point(4, 5))
        top_left = line.min_point()
        self.assertEqual(top_left.x, 2)
        self.assertEqual(top_left.y, 3)

        line = Line(Point(2, 3), Point(-1, 4))
        top_left = line.min_point()
        self.assertEqual(top_left.x, -1)
        self.assertEqual(top_left.y, 3)
示例#21
0
 def test_create_new_pin(self):
     """ Test the creation of a new empty pin. """
     p1 = Point(0, 1)
     p2 = Point(2, 3)
     pin = Pin(0, p1, p2, 'abc')
     assert pin.label == 'abc'
     assert pin.p1.x == p1.x
     assert pin.p1.y == p1.y
     assert pin.p2.x == p2.x
     assert pin.p2.y == p2.y
     assert pin.pin_number == 0
示例#22
0
 def test_line_multi_seg(self):
     """A multi-segment line, should become many Line objects"""
     pts = [(2, 1), (4, 5), (8, 7), (9, 2)]
     pts_text = ' '.join([str(p1) + ' ' + str(p2) for p1, p2 in pts])
     k, v = self.base.parse_line(str(len(pts)) + ' ' + pts_text)
     self.assertEqual(k, 'lines')
     self.assertEqual(len(v), len(pts) - 1)
     # line segments returned out of order, or with p1/p2 swapped, would be
     # acceptable. The test would need to change for that.
     for i, line in enumerate(v):
         self.assertEqual(line.type, 'line')
         self.assertEqual(Point(pts[i]), line.p1)
         self.assertEqual(Point(pts[i + 1]), line.p2)
示例#23
0
 def test_create_new_bezier_curve(self):
     """ Test the creation of a new empty bezier. """
     control1 = Point(2, 9)
     control2 = Point(9, 8)
     p1 = Point(1, 1)
     p2 = Point(7, 2)
     assert self.curve.control1.x == control1.x
     assert self.curve.control1.y == control1.y
     assert self.curve.control2.x == control2.x
     assert self.curve.control2.y == control2.y
     assert self.curve.p1.x == p1.x
     assert self.curve.p1.y == p1.y
     assert self.curve.p2.x == p2.x
     assert self.curve.p2.y == p2.y
示例#24
0
    def _do_funct(self, block):
        """ Set drawing modes, fill terminators. """
        code = int(block.code)
        if 'D' in block.type_:
            if code < 10:
                effect = {'draw': D_MAP[code]}

                # flash current pos/aperture
                if 'X' in block.type_:
                    apertures = self.layer_buff.apertures
                    aperture = apertures[self.status['aperture']]
                    pos = Point(self.status['x'], self.status['y'])
                    shape_inst = ShapeInstance(pos, aperture)
                    self.img_buff.shape_instances.append(shape_inst)

                # terminate fill mid mode
                if (self.status['outline_fill'] and code == 2
                        and self.fill_buff):
                    self.img_buff.fills.append(self._check_fill())

            else:
                effect = {'aperture': block.code}
        else:
            effect = G_MAP[code]
            if code == 37:

                # terminate fill if D02 was not specified
                if self.fill_buff:
                    self.img_buff.fills.append(self._check_fill())

        return effect
示例#25
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)
示例#26
0
    def __init__(self, symbol_dirs=None):
        """ Constructs a new GEDA object and initialises it. *symbol_dirs*
            expects a list of directories. It will search for .sym files
            in all the specified directories.
        """

        if symbol_dirs is None:
            symbol_dirs = []

        symbol_dirs = symbol_dirs + \
            [os.path.join(os.path.dirname(__file__), '..',
                          'library', 'geda')]

        self.known_symbols = find_symbols(symbol_dirs)

        ## offset used as bottom left origin as default
        ## in gEDA when starting new file
        self.offset = Point(0, 0)
        self.component_library = None

        ##NOTE: special attributes that are processed
        ## separately and will not be used as regular attributes
        self.ignored_attributes = [
            '_prefix',
            '_suffix',
            '_refdes',
            '_name',
            '_geda_imported',
        ]

        self.project_dirs = {
            'symbol': None,
            'project': None,
        }
示例#27
0
    def draw_net(self, net):
        """ draw out a net """
        # need a second dict so that removing nets as they are drawn does
        # not affect the actual design object.
        connects = dict([(pt.point_id, list(pt.connected_points))
                         for pt in net.points.values()])
        for pid, connlist in connects.items():
            pidpt = self.base_xform.chain(net.points[pid])
            for junc in connlist:
                juncpt = self.base_xform.chain(net.points[junc])
                # draw a line to each connected point from this junction
                self.canvas.line([(pidpt.x, pidpt.y), (juncpt.x, juncpt.y)],
                                 fill=self.options.style['net'])
                # don't need the connected point to draw a line back
                connects[junc].remove(pid)
                # TODO draw the connection to the component pin
                #      (may actually be done)

        for pt in net.points.values():
            if self.dot_at(pt, net):
                drawpt = self.base_xform.chain(pt)
                # arbitrarily, drawing the dot 4x the minimum dimension in the
                # design + 1 pixel.
                scale = self.options.scale * 2
                # draw the actual solder dot
                self.canvas.ellipse((drawpt.x - scale, drawpt.y - scale,
                                     drawpt.x + scale, drawpt.y + scale),
                                    outline=self.options.style['net'],
                                    fill=self.options.style['net'])

        for ann in net.annotations:
            pos = self.base_xform.chain(Point(ann.x, ann.y))
            self.canvas.text((pos.x, pos.y),
                             ann.value,
                             fill=self.options.style['annot'])
 def __init__(self, pin_number, p, shapes, label=None):
     self.label = label # is a Label
     self.p = Point(p)
     self.pin_number = pin_number
     self.shapes = shapes
     self.attributes = dict()
     self.styles = dict()
示例#29
0
    def parse_pours(self, pours_json):
        if pours_json is None:
            return None

        for pour_json in pours_json:
            points = [
                Point(point_json['x'], point_json['y'])
                for point_json in pour_json['points']
            ]

            layer = pour_json['layer']
            subtractive_shapes = []
            if 'subtractive_shapes' in pour_json:
                subtractive_shapes = [
                    self.parse_shape(shape_json)
                    for shape_json in pour_json['subtractive_shapes']
                ]
            if 'readded_shapes' in pour_json:
                readded_shapes = [
                    self.parse_shape(shape_json)
                    for shape_json in pour_json['readded_shapes']
                ]

            pour = Pour(layer, points, subtractive_shapes, readded_shapes)
            self.design.pours.append(pour)
示例#30
0
    def _convert_shapes(self, shapes, center=(0, 0), absolute=False):
        """ Convert shapes """
        result = []

        def fix_point(point):
            x, y = (point[0] + center[0], point[1] + center[1])
            if absolute:
                # freerouter often creates points outside boundary, fix it
                if x > self.max_x:
                    x = self.min_x + x - self.max_x
                elif x < self.min_x:
                    x = self.max_x - x - self.min_x
                if y > self.max_y:
                    y = self.min_y + y - self.max_y
                elif y < self.min_y:
                    y = self.max_y - y - self.min_y

            return (x, y)

        for shape in shapes:
            if isinstance(shape, specctraobj.PolylinePath):
                points = [
                    fix_point(self.to_pixels(point)) for point in shape.vertex
                ]
                result.extend(
                    self._convert_path(self.to_pixels(shape.aperture_width),
                                       points))

            elif isinstance(shape, specctraobj.Path):
                points = [
                    fix_point(self.to_pixels(point)) for point in shape.vertex
                ]
                # Path has connected start and end points
                if points[0] != points[-1] and len(points) != 2:
                    points.append(points[0])
                result.extend(
                    self._convert_path(self.to_pixels(shape.aperture_width),
                                       points))

            elif isinstance(shape, specctraobj.Polygon):
                points = [
                    fix_point(self.to_pixels(point)) for point in shape.vertex
                ]
                points = [Point(point[0], point[1]) for point in points]
                result.append(Polygon(points))

            elif isinstance(shape, specctraobj.Rectangle):
                x1, y1 = self.to_pixels(shape.vertex1)
                x2, y2 = self.to_pixels(shape.vertex2)
                width, height = abs(x1 - x2), abs(y1 - y2)
                x1, y1 = fix_point((min(x1, x2), max(y1, y2)))

                result.append(Rectangle(x1, y1, width, height))
            elif isinstance(shape, specctraobj.Circle):
                point = fix_point(self.to_pixels(shape.vertex))
                result.append(
                    Circle(point[0], point[1],
                           self.to_pixels(shape.diameter / 2.0)))
        return result
 def max_point(self):
     """ Return the max point of the shape """
     if len(self.points) < 1:
         # by convention
         x, y = 0, 0
     else:
         x = max([pt.x for pt in self.points])
         y = max([pt.y for pt in self.points])
     return Point(x, y)
class Pin:
    """ Pins are the parts of Bodies (/symbols/components) that connect
    to nets. Basically a line segment, with a null end and a connect end
    """

    def __init__(self, pin_number, p1, p2, label=None):
        self.label = label # is a Label
        self.p1 = Point(p1) # null end
        self.p2 = Point(p2) # connect end
        self.pin_number = pin_number
        self.attributes = dict()
        self.styles = dict()


    def add_attribute(self, key, value):
        """ Add attribute to a pin """
        self.attributes[key] = value


    def bounds(self):
        """ Return the min and max points of a pin """
        x_values = [self.p1.x, self.p2.x]
        y_values = [self.p1.y, self.p2.y]
        if self.label is not None:
            x_values.extend([pt.x for pt in self.label.bounds()])
            y_values.extend([pt.y for pt in self.label.bounds()])
        return [Point(min(x_values), min(y_values)),
                Point(max(x_values), max(y_values))]


    def json(self):
        """ Return a pin as JSON """
        ret = {
            "pin_number":self.pin_number,
            "p1":self.p1.json(),
            "p2":self.p2.json(),
            "attributes" : self.attributes,
            "styles": self.styles,
            }
        if self.label is not None:
            ret["label"] = self.label.json()
        return ret
示例#33
0
    def draw_layout(self):
        """ Render the layout into self.img """
        # start off with all the component instances
        for inst in self.design.component_instances:
            comp = self.design.components.components[inst.library_id]
            for body, attr in zip(comp.footprints[inst.footprint_index].bodies,
                                  inst.footprint_attributes):
                # draw the appropriate body, at the position in attr
                pos = Point(attr.x, attr.y)
                self.draw_footprint(body, pos, attr.rotation, attr.flip)
                # draw in any annotations
                for ann in attr.annotations:
                    if ann.visible:
                        pos = self.base_xform.chain(Point(ann.x, ann.y))
                        self.canvas.text((pos.x, pos.y),
                                         ann.value,
                                         fill=self.options.style['annot'])

        for trace in self.design.traces:
            self.draw_trace(trace)
class Pad:
    """ Pads are the parts of FBodies (/footprints/components) that connect
    to traces. Basically a set of shapes.
    """

    def __init__(self, pin_number, p, shapes, label=None):
        self.label = label # is a Label
        self.p = Point(p)
        self.pin_number = pin_number
        self.shapes = shapes
        self.attributes = dict()
        self.styles = dict()


    def add_attribute(self, key, value):
        """ Add attribute to a pin """
        self.attributes[key] = value


    def bounds(self):
        """ Return the min and max points of a pin """
        pass


    def scale(self, factor):
        """ Scale the x & y coordinates in the pin. """
        pass


    def shift(self, dx, dy):
        """ Shift the x & y coordinates in the pin. """
        pass


    def rebase_y_axis(self, height):
        """ Rebase the y coordinate in the pin. """
        pass


    def json(self):
        """ Return a pin as JSON """
        ret = {
            "pin_number": self.pin_number,
            "p": self.p.json(),
            "shapes": [s.json() for s in self.shapes],
            "attributes": stringify_attributes(self.attributes),
            "styles": self.styles,
            }
        if self.label is not None:
            ret["label"] = self.label.json()
        return ret
示例#35
0
    def _get_ctr_and_radius(self, end_pts, offset):
        """ Apply gerber circular interpolation logic. """
        start, end = end_pts
        radius = sqrt(offset['i']**2 + offset['j']**2)
        center = Point(x=start.x + offset['i'],
                       y=start.y + offset['j'])

        # In single-quadrant mode, gerber requires implicit
        # determination of offset direction, so we find the
        # center through trial and error.
        if not self.status['multi_quadrant']:
            if not snap(center.dist(end), radius):
                center = Point(x=start.x - offset['i'],
                               y=start.y - offset['j'])
                if not snap(center.dist(end), radius):
                    center = Point(x=start.x + offset['i'],
                                   y=start.y - offset['j'])
                    if not snap(center.dist(end), radius):
                        center = Point(x=start.x - offset['i'],
                                       y=start.y + offset['j'])
                        if not snap(center.dist(end), radius):
                            raise ImpossibleGeometry
        return (center, radius)
class Pin:
    """ Pins are the parts of SBodies (/symbols/components) that connect
    to nets. Basically a line segment, with a null end and a connect end
    """

    def __init__(self, pin_number, p1, p2, label=None):
        self.label = label # is a Label
        self.p1 = Point(p1) # null end
        self.p2 = Point(p2) # connect end
        self.pin_number = pin_number
        self.attributes = dict()
        self.styles = dict()


    def add_attribute(self, key, value):
        """ Add attribute to a pin """
        self.attributes[key] = value


    def bounds(self):
        """ Return the min and max points of a pin """
        x_values = [self.p1.x, self.p2.x]
        y_values = [self.p1.y, self.p2.y]
        if self.label is not None:
            x_values.extend([pt.x for pt in self.label.bounds()])
            y_values.extend([pt.y for pt in self.label.bounds()])
        return [Point(min(x_values), min(y_values)),
                Point(max(x_values), max(y_values))]


    def scale(self, factor):
        """ Scale the x & y coordinates in the pin. """
        if self.label is not None:
            self.label.scale(factor)
        self.p1.scale(factor)
        self.p2.scale(factor)


    def shift(self, dx, dy):
        """ Shift the x & y coordinates in the pin. """
        if self.label is not None:
            self.label.shift(dx, dy)
        self.p1.shift(dx, dy)
        self.p2.shift(dx, dy)


    def rebase_y_axis(self, height):
        """ Rebase the y coordinate in the pin. """
        if self.label is not None:
            self.label.rebase_y_axis(height)
        self.p1.rebase_y_axis(height)
        self.p2.rebase_y_axis(height)


    def json(self):
        """ Return a pin as JSON """
        ret = {
            "pin_number": self.pin_number,
            "p1": self.p1.json(),
            "p2": self.p2.json(),
            "attributes": stringify_attributes(self.attributes),
            "styles": self.styles,
            }
        if self.label is not None:
            ret["label"] = self.label.json()
        return ret
示例#37
0
    def _add_hole(self, parent_attr, body_attr, shape):
        if not isinstance(shape, Circle):
            log.error('holes must be circular, found %s', shape)
            return
            #raise Unwritable('all holes must be circular')

        pos = Point(shape.x, shape.y)
        pos.shift(body_attr.x, body_attr.y)
        if parent_attr:
            if parent_attr.rotation != 0:
                pos.rotate(parent_attr.rotation)
            if parent_attr.flip_horizontal:
                pos.flip(parent_attr.flip_horizontal)
            pos.shift(parent_attr.x, parent_attr.y)

        if body_attr.rotation != 0:
            if parent_attr and parent_attr.flip_horizontal:
                pos.rotate(-body_attr.rotation, in_place=True)
            else:
                pos.rotate(body_attr.rotation, in_place=True)
        if body_attr.flip_horizontal:
            shape.flip(body_attr.flip_horizontal)

        log.debug('adding %d hole at %d, %d', shape.radius * 2, pos.x, pos.y)
        if shape.radius not in self._holes:
            self._holes[shape.radius] = Hole(shape)
        self._holes[shape.radius].locations.append(pos)