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 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 #4
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 #5
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 #6
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 #7
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 #8
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