Exemple #1
0
    def _get_cgal_elem(self):
        """
        Get a cgal geometry element representing this object, if any
        Width is only considered for Wire
        """
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect

        if isinstance(self, Swoop.Rectangle):
            rect = Rectangle(self.get_point(0), self.get_point(1), check=False)
            verts = list(rect.vertices_ccw())
            if self.get_rot() is not None:
                angle_obj = angle_match(self.get_rot())
                angle = math.radians(angle_obj['angle'])
                if angle_obj['mirrored']:
                    angle *= -1
                origin = rect.center()
                rmat = Rectangle.rotation_matrix(angle)
                for i, v in enumerate(verts):
                    verts[i] = np.dot(v - origin, rmat) + origin
            return Polygon_2(list(map(np2cgal, verts)))

        elif isinstance(self, Swoop.Wire):
            p1 = self.get_point(0)
            p2 = self.get_point(1)
            if self.get_width() is None or self.get_width() == 0:
                return Segment_2(np2cgal(p1), np2cgal(p2))
            else:
                # Wire has width
                # This is important to consider because wires can represent traces
                # When doing a query, it is important we can pick up the trace
                if p1[0] > p2[0]:
                    p1, p2 = p2, p1  # p1 is the left endpoint
                elif p1[0] == p2[0] and p1[1] > p2[1]:
                    p1, p2 = p2, p1  # or the bottom

                vec = (p2 - p1)
                vec /= np.linalg.norm(vec)  # Normalize
                radius = np.array(vec[1], -vec[0])
                radius *= self.get_width(
                ) / 2.0  # "Radius" of the wire, perpendicular to it

                vertices = []
                # Go around the vertices of the wire in CCW order
                # This should give you a rotated rectangle
                vertices.append(np2cgal(p1 + radius))
                vertices.append(np2cgal(p2 + radius))
                vertices.append(np2cgal(p2 - radius))
                vertices.append(np2cgal(p1 - radius))
                return Polygon_2(vertices)
        elif isinstance(self, Swoop.Polygon):
            return Polygon_2(
                [np2cgal(v.get_point()) for v in self.get_vertices()])
        else:
            return None
Exemple #2
0
    def _get_cgal_elem(self):
        """
        Get a cgal geometry element representing this object, if any
        Width is only considered for Wire
        """
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect

        if isinstance(self, Swoop.Rectangle):
            rect = Rectangle(self.get_point(0), self.get_point(1), check=False)
            verts = list(rect.vertices_ccw())
            if self.get_rot() is not None:
                angle_obj = angle_match(self.get_rot())
                angle = math.radians(angle_obj['angle'])
                if angle_obj['mirrored']:
                    angle *= -1
                origin = rect.center()
                rmat = Rectangle.rotation_matrix(angle)
                for i,v in enumerate(verts):
                    verts[i] = np.dot(v - origin, rmat) + origin
            return Polygon_2(map(np2cgal, verts))

        elif isinstance(self, Swoop.Wire):
            p1 = self.get_point(0)
            p2 = self.get_point(1)
            if self.get_width() is None or self.get_width() == 0:
                return Segment_2(np2cgal(p1), np2cgal(p2))
            else:
                # Wire has width
                # This is important to consider because wires can represent traces
                # When doing a query, it is important we can pick up the trace
                if p1[0] > p2[0]:
                    p1,p2 = p2,p1   # p1 is the left endpoint
                elif p1[0]==p2[0] and p1[1] > p2[1]:
                    p1,p2 = p2,p1   # or the bottom

                vec = (p2 - p1)
                vec /= np.linalg.norm(vec)          # Normalize
                radius = np.array(vec[1], -vec[0])
                radius *= self.get_width()/2.0     # "Radius" of the wire, perpendicular to it

                vertices = []
                # Go around the vertices of the wire in CCW order
                # This should give you a rotated rectangle
                vertices.append(np2cgal( p1 + radius ))
                vertices.append(np2cgal( p2 + radius ))
                vertices.append(np2cgal( p2 - radius ))
                vertices.append(np2cgal( p1 - radius ))
                return Polygon_2(vertices)
        elif isinstance(self, Swoop.Polygon):
            return Polygon_2([np2cgal(v.get_point()) for v in self.get_vertices()])
        else:
            return None
Exemple #3
0
    def test_segments_in_rec(self):
        r = Rectangle((0,0), (19,16))
        s = LineSegment(np.array([4,4]), np.array([11,9]))
        self.assertTrue(r.overlaps(s))
        self.assertTrue(s.overlaps(r))

        s2 = LineSegment(np.array([-2,6]), np.array([11,9]))
        self.assertTrue(r.overlaps(s2))
        self.assertTrue(s2.overlaps(r))
Exemple #4
0
    def __init__(self, cgal_elem, swoop_elem):
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect
        self.cgal_elem = cgal_elem
        self.swoop_elem = swoop_elem
        bbox = cgal_elem.bbox()
        self.rect = Rectangle.from_cgal_bbox(bbox, check=False)

        #do_intersect only works with iso_rectangle, not bbox
        self.iso_rect = Iso_rectangle_2(bbox.xmin(), bbox.ymin(), bbox.xmax(), bbox.ymax())
Exemple #5
0
    def __init__(self, cgal_elem, swoop_elem):
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect
        self.cgal_elem = cgal_elem
        self.swoop_elem = swoop_elem
        bbox = cgal_elem.bbox()
        self.rect = Rectangle.from_cgal_bbox(bbox, check=False)

        #do_intersect only works with iso_rectangle, not bbox
        self.iso_rect = Iso_rectangle_2(bbox.xmin(), bbox.ymin(), bbox.xmax(),
                                        bbox.ymax())
Exemple #6
0
def arc_bounding_box(p1, p2, theta):
    """
    Bounding box of the semicircular arc sweeping an angle theta from point p1 to p2
    :param p1: numpy point, start of arc
    :param p2: numpy point, end of arc
    :param theta: angle (in radians), angle swept to the arc
    :return: Rectangle
    """
    assert -2 * pi <= theta <= 2 * pi, "Invalid radians angle"

    wire_vector = p2 - p1

    # Some magic using the law of sines
    arc_radius = abs(
        np.linalg.norm(wire_vector) * math.cos(theta / 2.0) / math.sin(theta))

    # Rotate the vector p1-c by theta and you get p2-c
    # This code exploits that and solves for c (center)
    # Used Maple code generation
    p1x, p1y = p1[0], p1[1]
    p2x, p2y = p2[0], p2[1]
    center = np.array([(p1x - math.cos(theta) * p1x - math.cos(theta) * p2x +
                        math.sin(theta) * p1y - p2y * math.sin(theta) + p2x) /
                       (0.2e1 - 0.2e1 * math.cos(theta)),
                       (p1y - math.cos(theta) * p1y - math.cos(theta) * p2y -
                        math.sin(theta) * p1x + math.sin(theta) * p2x + p2y) /
                       (0.2e1 - 0.2e1 * math.cos(theta))])

    # Convert all rotations to CCW
    # p1 sweeps positive theta to get to p2
    if theta < 0:
        p1, p2 = p2, p1
        theta = abs(theta)

    vertices = [p1, p2]
    v1 = p1 - center
    p1_angle = math.atan2(v1[1], v1[0])  # angle of vector (p1-c)
    for test_angle in [0, pi / 2.0, pi, 3 * pi / 2]:
        # 'rotate' (p1-c) to test angle
        # If rotation was less than theta, this is inside the arc sweep
        if 0 < angle_normalize(test_angle - p1_angle) < theta:
            vertices.append(
                np.array([
                    center[0] + arc_radius * math.cos(test_angle),
                    center[1] + arc_radius * math.sin(test_angle)
                ]))
    return Rectangle.from_vertices(vertices)
Exemple #7
0
def arc_bounding_box(p1, p2, theta):
    """
    Bounding box of the semicircular arc sweeping an angle theta from point p1 to p2
    :param p1: numpy point, start of arc
    :param p2: numpy point, end of arc
    :param theta: angle (in radians), angle swept to the arc
    :return: Rectangle
    """
    assert -2*pi <= theta <= 2*pi, "Invalid radians angle"

    wire_vector = p2 - p1

    # Some magic using the law of sines
    arc_radius = abs(np.linalg.norm(wire_vector) * math.cos(theta/2.0)/math.sin(theta))

    # Rotate the vector p1-c by theta and you get p2-c
    # This code exploits that and solves for c (center)
    # Used Maple code generation
    p1x,p1y = p1[0],p1[1]
    p2x,p2y = p2[0],p2[1]
    center = np.array([
        (p1x - math.cos(theta) * p1x - math.cos(theta) * p2x +
         math.sin(theta) * p1y - p2y * math.sin(theta) + p2x) / (0.2e1 - 0.2e1 * math.cos(theta)),

        (p1y - math.cos(theta) * p1y - math.cos(theta) * p2y -
         math.sin(theta) * p1x + math.sin(theta) * p2x + p2y) / (0.2e1 - 0.2e1 * math.cos(theta))
    ])

    # Convert all rotations to CCW
    # p1 sweeps positive theta to get to p2
    if theta < 0:
        p1,p2 = p2,p1
        theta = abs(theta)

    vertices = [p1, p2]
    v1 = p1 - center
    p1_angle = math.atan2(v1[1],v1[0])     # angle of vector (p1-c)
    for test_angle in [0, pi/2.0, pi, 3*pi/2]:
        # 'rotate' (p1-c) to test angle
        # If rotation was less than theta, this is inside the arc sweep
        if 0 < angle_normalize(test_angle - p1_angle) < theta:
            vertices.append(np.array([center[0] + arc_radius*math.cos(test_angle),
                                      center[1] + arc_radius*math.sin(test_angle)]))
    return Rectangle.from_vertices(vertices)
Exemple #8
0
 def rotate(self, degrees):
     """
     Rotate everything around the origin (0,0)
     Positive is counter-clockwise
     """
     rot_mtx = Rectangle.rotation_matrix(math.radians(degrees))
     if isinstance(self, Swoop.Rectangle):
         # Special case: you can't just rotate each vertex :(
         origin = (self.get_point(0) + self.get_point(1)) / 2
         new_origin = rot_mtx.dot(origin)
         self.move(new_origin - origin)
     else:
         for i in xrange(self.num_points()):
             self.set_point( rot_mtx.dot(self.get_point(i)), i)
     if hasattr(self, "get_rot"):
         rot = self.get_rot() or "R0"
         angle = angle_match(rot)
         angle['angle'] = (angle['angle'] + degrees) % 360
         self.set_rot(angle_match_to_str(angle))
Exemple #9
0
 def rotate(self, degrees):
     """
     Rotate everything around the origin (0,0)
     Positive is counter-clockwise
     """
     rot_mtx = Rectangle.rotation_matrix(math.radians(degrees))
     if isinstance(self, Swoop.Rectangle):
         # Special case: you can't just rotate each vertex :(
         origin = (self.get_point(0) + self.get_point(1)) / 2
         new_origin = rot_mtx.dot(origin)
         self.move(new_origin - origin)
     else:
         for i in range(self.num_points()):
             self.set_point(rot_mtx.dot(self.get_point(i)), i)
     if hasattr(self, "get_rot"):
         rot = self.get_rot() or "R0"
         angle = angle_match(rot)
         angle['angle'] = (angle['angle'] + degrees) % 360
         self.set_rot(angle_match_to_str(angle))
Exemple #10
0
    def test_recs(self):
        r1 = Rectangle( (-1,-1), (8,7) )
        r2 = Rectangle((8.79, 2.144), (17.79, 11.144)).rotate(-10, around_origin=False)

        self.assertFalse(r1.overlaps(r2))
        self.assertFalse(r2.overlaps(r1))

        r2 = Rectangle((8.5, 2.144), (17.5, 11.144)).rotate(-10, around_origin=False)
        # edges=list(r2.edges())
        # self.assertEqual(len(edges), 4)
        # for e in edges:
        #     print e.p1, e.p2

        self.assertTrue(r1.overlaps(r2))
        self.assertTrue(r2.overlaps(r1))

        r = Rectangle((0.498, -12.082), (13.29, -0.124))
        npt.assert_allclose(r.center(), np.array([ 6.894, -6.103]))
        r1 = r.rotate(10, False)
        npt.assert_allclose(r1.center(), np.array([ 6.894, -6.103]))
        rcopy = r1.copy()
        npt.assert_allclose(rcopy.center(), np.array([ 6.894, -6.103]))
        rcopy = rcopy.rotate(-10, False)
        npt.assert_allclose(rcopy.center(), np.array([ 6.894, -6.103]))

        npt.assert_allclose(r1.center(), np.array([ 6.894, -6.103]))
        r2 = Rectangle((0,-12), (13,0)).rotate(20, False)
        self.assertTrue(r1.overlaps(r2))
        npt.assert_allclose(r1.center(), np.array([ 6.894, -6.103]))
        r2 = Rectangle((3,-9), (9,-5)).rotate(169, False)
        self.assertTrue(r1.overlaps(r2))
        r2 = Rectangle((13,-5), (19,-1)).rotate(169, False)
        self.assertTrue(r1.overlaps(r2))
        r2.move(np.array([1,0]))
        self.assertFalse(r1.overlaps(r2))

        r1 = Rectangle((3,-8), (18,-2)).rotate(0)
        r2 = r1.copy().rotate(90, around_origin=False)
        self.assertTrue(r1.overlaps(r2))
        r2.move(np.array([11,0]))
        self.assertFalse(r1.overlaps(r2))

        r1 = Rectangle((3,-8), (18,-2)).rotate(10,False)
        r2 = Rectangle((-5,-8), (10,-2)).rotate(280,False)
        self.assertTrue(r1.overlaps(r2))
        self.assertTrue(r2.overlaps(r1))

        r2.move(np.array([-3,0]))
        self.assertFalse(r1.overlaps(r2))
        self.assertFalse(r2.overlaps(r1))
Exemple #11
0
    def __init__(self, filename):

        """
        Completely overrides the parent constructor

        Need a few things to init from:
        -All <elements> (parts on the board)
        -Random other stuff from <signals> or <plain>

        :param filename: .brd file to create self from
        :return:
        """
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect

        # From needs this in order to work
        # Call from_file in Swoop and get a Swoop.BoardFile
        super(BoardFile, self).__init__(WithMixin.from_file(filename))

        # Tuples of (geometry element, swoop element)
        # Everything that you can see on the board
        self._elements = []

        #Add all the stuff in <signals>
        #First wires
        for wire in self.get_signals().get_wires():
            self._elements.append(GeoElem(wire._get_cgal_elem(), wire))

        #Then vias
        for via in self.get_signals().get_vias():
            #Circular kernel does not have python bindings yet...so just use bounding box
            rect = via.get_bounding_box()
            self._elements.append(GeoElem(Iso_rectangle_2(*rect.bounds_tuple), via))


        #Stuff in <plain>
        for elem in self.get_plain_elements():
            cgal_elem = elem._get_cgal_elem()
            if cgal_elem is not None:
                self._elements.append(GeoElem(cgal_elem, elem) )

        # The actual elements in <elements>
        # Each element will be represented as a rotated rectangle
        # The element's children, after translation and rotating, will be in the package_moved attribute
        for elem in self.get_elements():
            package = self.get_libraries().get_package(elem.get_package())
            rect = package.get_children().get_bounding_box().reduce(Rectangle.union)

            if elem.get_rot() is not None:
                angle = angle_match(elem.get_rot())
            else:
                angle = {'angle': 0, 'mirrored': False}
            rotmatx = Rectangle.rotation_matrix(math.radians(angle['angle']))
            origin = elem.get_point()
            # Convert rotated rectangle to polygon
            poly = []
            for v in rect.vertices_ccw():
                vr = rotmatx.dot(v)         # rotate before mirroring
                if angle['mirrored']:
                    vr[0] *= -1
                poly.append(np2cgal(vr + origin))

            # Create a copy of the package with all the children rotated/moved
            elem.package_moved = package[0].clone()
            for package_elem in elem.package_moved.get_children():
                package_elem.rotate(angle['angle'])
                if angle['mirrored']:
                    package_elem.mirror()
                package_elem.move(origin)

            geom = GeoElem(Polygon_2(poly), elem)
            self._elements.append(geom)
            elem._extension_geo_elem = geom

        #Finally, the board outline
        outline = self.get_plain_elements().\
            filtered_by(lambda e: hasattr(e, "get_layer")).\
            with_layer("Dimension")

        if len(outline) > 0:
            self.bbox = outline.get_bounding_box().reduce(Rectangle.union)
Exemple #12
0
    def get_bounding_box(self, layer=None, type=None):
        """
        Get the minimum bounding box enclosing this list of primitive elements
        More accurate than self._get_cgal_elem().bbox(), because it accounts for segment width

        :param layer: Swoop layer to filter on
        """
        def max_min(vertex_list, width=None):
            max = np.maximum.reduce(vertex_list)
            min = np.minimum.reduce(vertex_list)
            if width is not None:
                radius = np.array([width, width]) / 2.0
                max += radius
                min -= radius
            return max,min

        if isinstance(self, Swoop.Polygon):
            vertices = [v.get_point() for v in self.get_vertices()]
            return Rectangle(*max_min(vertices, self.get_width()))
        elif isinstance(self, Swoop.Wire):
            if self.get_curve() is not None:
                theta = math.radians(self.get_curve()) # angle swept by arc
                theta = math.fmod(theta, 2*math.pi)
                p1 = self.get_point(0)  # 2 points on the circle
                p2 = self.get_point(1)
                return arc_bounding_box(p1, p2, theta).pad(self.get_width() / 2.0)
            else:
                vertices = [self.get_point(0), self.get_point(1)]
                return Rectangle(*max_min(vertices, self.get_width()), check=False)
        elif isinstance(self, Swoop.Rectangle):
            #get_cgal_elem already handles rotation
            bbox = self._get_cgal_elem().bbox()
            return Rectangle.from_cgal_bbox(bbox, check=False)
        elif isinstance(self, Swoop.Via) or isinstance(self, Swoop.Pad):
            #These assume default settings
            #Unfortunately, restring can change the sizes of things after import

            center = self.get_point()
            if self.get_diameter() is None:
                # Gotta figure it out from drill
                if self.get_drill() < 1.0:
                    diameter = self.get_drill() + 0.5
                else:
                    diameter = self.get_drill()*1.5
            else:
                diameter = self.get_diameter()
            radius = diameter/2.0 * np.ones(2)

            angle = 0.0
            if isinstance(self, Swoop.Pad) and self.get_rot() is not None:
                angle_m = angle_match(self.get_rot())
                angle = angle_m['angle']
                if angle_m['mirrored']:
                    angle = 180.0 - angle
            rotate_matrix = Rectangle.rotation_matrix(math.radians(angle))

            if self.get_shape() in ['long', 'offset']:
                # This is basically a square pad with 180º arc endcaps
                if self.get_shape()=='offset':
                    center[0] += diameter/2.0   # Center is offset to the left, correct it
                rect = Rectangle(center - radius, center + radius)
                verts = list(rect.vertices())
                verts = map(lambda v: rotate_matrix.dot(v - center) + center, verts)
                left_cap = arc_bounding_box(verts[0], verts[3], pi)
                right_cap = arc_bounding_box(verts[2], verts[1], pi)

                rect = Rectangle.union(rect, left_cap)
                rect = Rectangle.union(rect, right_cap)
                return rect
            elif self.get_shape()=='octagon':
                vertex = np.array([diameter/2.0,
                                   diameter/2.0 * math.tan(2*pi / 16 )])
                vertex = rotate_matrix.dot(vertex)
                vertices = []
                rot = Rectangle.rotation_matrix(2*pi/8)
                for i in xrange(8):
                    vertices.append(vertex.copy() + center)
                    vertex = rot.dot(vertex)
                return Rectangle.from_vertices(vertices, check=True)
            else:
                rect = Rectangle(center - radius, center + radius)
                if self.get_shape()=='square':
                    rect.rotate_resize(angle,origin=center)
                return rect
        elif isinstance(self, Swoop.Smd):
            center = self.get_point()
            radius = np.array([self.get_dx(), self.get_dy()]) / 2.0
            if self.get_rot() is not None:
                angle = angle_match(self.get_rot())
                radius = abs(Rectangle.rotation_matrix(math.radians(angle['angle'])).dot(radius))
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Circle):
            center = self.get_point()
            radius = np.ones(2) * (self.get_radius() + self.get_width()/2.0)
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Hole):
            center = self.get_point()
            radius = np.ones(2) * self.get_drill()/2.0
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Element):
            # Element objects do not have enough information for the bounding box
            # That gets set in the constructor
            if hasattr(self,"_extension_geo_elem"):
                return self._extension_geo_elem.rect
            else:
                tform = self.get_transform()
                bbox = self.find_package().get_bounding_box(layer=layer, type=type)
                if bbox is None:
                    return None
                return tform.apply(bbox)
        elif isinstance(self, Swoop.Package):
            rect = None
            for c in self.get_children():
                if ((layer is None) or (hasattr(c,"get_layer") and c.get_layer()==layer))\
                        and ((type is None) or isinstance(c, type)):
                    r = c.get_bounding_box()
                    if r is not None:
                        if rect is None:
                            rect = r
                        rect = Rectangle.union(rect, r)
            return rect
        else:
            return None
Exemple #13
0
    def __init__(self, filename):
        """
        Completely overrides the parent constructor

        Need a few things to init from:
        -All <elements> (parts on the board)
        -Random other stuff from <signals> or <plain>

        :param filename: .brd file to create self from
        :return:
        """
        from CGAL.CGAL_Kernel import \
            Segment_2,\
            Polygon_2,\
            Point_2,\
            Bbox_2,\
            Iso_rectangle_2,\
            do_intersect

        # From needs this in order to work
        # Call from_file in Swoop and get a Swoop.BoardFile
        super(BoardFile, self).__init__(WithMixin.from_file(filename))

        # Tuples of (geometry element, swoop element)
        # Everything that you can see on the board
        self._elements = []

        #Add all the stuff in <signals>
        #First wires
        for wire in self.get_signals().get_wires():
            self._elements.append(GeoElem(wire._get_cgal_elem(), wire))

        #Then vias
        for via in self.get_signals().get_vias():
            #Circular kernel does not have python bindings yet...so just use bounding box
            rect = via.get_bounding_box()
            self._elements.append(
                GeoElem(Iso_rectangle_2(*rect.bounds_tuple), via))

        #Stuff in <plain>
        for elem in self.get_plain_elements():
            cgal_elem = elem._get_cgal_elem()
            if cgal_elem is not None:
                self._elements.append(GeoElem(cgal_elem, elem))

        # The actual elements in <elements>
        # Each element will be represented as a rotated rectangle
        # The element's children, after translation and rotating, will be in the package_moved attribute
        for elem in self.get_elements():
            package = self.get_libraries().get_package(elem.get_package())
            rect = package.get_children().get_bounding_box().reduce(
                Rectangle.union)

            if elem.get_rot() is not None:
                angle = angle_match(elem.get_rot())
            else:
                angle = {'angle': 0, 'mirrored': False}
            rotmatx = Rectangle.rotation_matrix(math.radians(angle['angle']))
            origin = elem.get_point()
            # Convert rotated rectangle to polygon
            poly = []
            for v in rect.vertices_ccw():
                vr = rotmatx.dot(v)  # rotate before mirroring
                if angle['mirrored']:
                    vr[0] *= -1
                poly.append(np2cgal(vr + origin))

            # Create a copy of the package with all the children rotated/moved
            elem.package_moved = package[0].clone()
            for package_elem in elem.package_moved.get_children():
                package_elem.rotate(angle['angle'])
                if angle['mirrored']:
                    package_elem.mirror()
                package_elem.move(origin)

            geom = GeoElem(Polygon_2(poly), elem)
            self._elements.append(geom)
            elem._extension_geo_elem = geom

        #Finally, the board outline
        outline = self.get_plain_elements().\
            filtered_by(lambda e: hasattr(e, "get_layer")).\
            with_layer("Dimension")

        if len(outline) > 0:
            self.bbox = outline.get_bounding_box().reduce(Rectangle.union)
Exemple #14
0
    def get_bounding_box(self, layer=None, type=None):
        """
        Get the minimum bounding box enclosing this list of primitive elements
        More accurate than self._get_cgal_elem().bbox(), because it accounts for segment width

        :param layer: Swoop layer to filter on
        """
        def max_min(vertex_list, width=None):
            max = np.maximum.reduce(vertex_list)
            min = np.minimum.reduce(vertex_list)
            if width is not None:
                radius = np.array([width, width]) / 2.0
                max += radius
                min -= radius
            return max, min

        if isinstance(self, Swoop.Polygon):
            vertices = [v.get_point() for v in self.get_vertices()]
            return Rectangle(*max_min(vertices, self.get_width()))
        elif isinstance(self, Swoop.Wire):
            if self.get_curve() is not None:
                theta = math.radians(self.get_curve())  # angle swept by arc
                theta = math.fmod(theta, 2 * math.pi)
                p1 = self.get_point(0)  # 2 points on the circle
                p2 = self.get_point(1)
                return arc_bounding_box(p1, p2,
                                        theta).pad(self.get_width() / 2.0)
            else:
                vertices = [self.get_point(0), self.get_point(1)]
                return Rectangle(*max_min(vertices, self.get_width()),
                                 check=False)
        elif isinstance(self, Swoop.Rectangle):
            #get_cgal_elem already handles rotation
            bbox = self._get_cgal_elem().bbox()
            return Rectangle.from_cgal_bbox(bbox, check=False)
        elif isinstance(self, Swoop.Via) or isinstance(self, Swoop.Pad):
            #These assume default settings
            #Unfortunately, restring can change the sizes of things after import

            center = self.get_point()
            if self.get_diameter() is None:
                # Gotta figure it out from drill
                if self.get_drill() < 1.0:
                    diameter = self.get_drill() + 0.5
                else:
                    diameter = self.get_drill() * 1.5
            else:
                diameter = self.get_diameter()
            radius = diameter / 2.0 * np.ones(2)

            angle = 0.0
            if isinstance(self, Swoop.Pad) and self.get_rot() is not None:
                angle_m = angle_match(self.get_rot())
                angle = angle_m['angle']
                if angle_m['mirrored']:
                    angle = 180.0 - angle
            rotate_matrix = Rectangle.rotation_matrix(math.radians(angle))

            if self.get_shape() in ['long', 'offset']:
                # This is basically a square pad with 180º arc endcaps
                if self.get_shape() == 'offset':
                    center[
                        0] += diameter / 2.0  # Center is offset to the left, correct it
                rect = Rectangle(center - radius, center + radius)
                verts = list(rect.vertices())
                verts = [rotate_matrix.dot(v - center) + center for v in verts]
                left_cap = arc_bounding_box(verts[0], verts[3], pi)
                right_cap = arc_bounding_box(verts[2], verts[1], pi)

                rect = Rectangle.union(rect, left_cap)
                rect = Rectangle.union(rect, right_cap)
                return rect
            elif self.get_shape() == 'octagon':
                vertex = np.array(
                    [diameter / 2.0, diameter / 2.0 * math.tan(2 * pi / 16)])
                vertex = rotate_matrix.dot(vertex)
                vertices = []
                rot = Rectangle.rotation_matrix(2 * pi / 8)
                for i in range(8):
                    vertices.append(vertex.copy() + center)
                    vertex = rot.dot(vertex)
                return Rectangle.from_vertices(vertices, check=True)
            else:
                rect = Rectangle(center - radius, center + radius)
                if self.get_shape() == 'square':
                    rect.rotate_resize(angle, origin=center)
                return rect
        elif isinstance(self, Swoop.Smd):
            center = self.get_point()
            radius = np.array([self.get_dx(), self.get_dy()]) / 2.0
            if self.get_rot() is not None:
                angle = angle_match(self.get_rot())
                radius = abs(
                    Rectangle.rotation_matrix(math.radians(
                        angle['angle'])).dot(radius))
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Circle):
            center = self.get_point()
            radius = np.ones(2) * (self.get_radius() + self.get_width() / 2.0)
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Hole):
            center = self.get_point()
            radius = np.ones(2) * self.get_drill() / 2.0
            return Rectangle(center - radius, center + radius)
        elif isinstance(self, Swoop.Element):
            # Element objects do not have enough information for the bounding box
            # That gets set in the constructor
            if hasattr(self, "_extension_geo_elem"):
                return self._extension_geo_elem.rect
            else:
                tform = self.get_transform()
                bbox = self.find_package().get_bounding_box(layer=layer,
                                                            type=type)
                if bbox is None:
                    return None
                return tform.apply(bbox)
        elif isinstance(self, Swoop.Package):
            rect = None
            for c in self.get_children():
                if ((layer is None) or (hasattr(c,"get_layer") and c.get_layer()==layer))\
                        and ((type is None) or isinstance(c, type)):
                    r = c.get_bounding_box()
                    if r is not None:
                        if rect is None:
                            rect = r
                        rect = Rectangle.union(rect, r)
            return rect
        else:
            return None