Пример #1
0
 def fillet(self, point, radius):
     """
     Add a fillet to the substrate at given point. If the point does not lie
     on an inner corner or the surrounding geometry does not allow for fillet,
     does nothing. Return true if the fillet was created, else otherwise
     """
     if radius == 0:
         return
     LEN_LIMIT = 40  # Should be roughly equal to half the circular segments,
     # may prevent from finding a solution
     cut1, cut2 = cutOutline(point,
                             self.substrates.boundary,
                             LEN_LIMIT,
                             tolerance=pcbnew.FromMM(0.02))
     if not cut1 or not cut2:
         # The point does not intersect any outline
         return False
     offset1 = cut1.parallel_offset(radius, 'right', join_style=2)
     offset2 = cut2.parallel_offset(radius, 'right', join_style=2)
     filletCenter = extractPoint(offset1.intersection(offset2))
     if not filletCenter:
         return False
     a, _ = shapely.ops.nearest_points(cut1, filletCenter)
     b, _ = shapely.ops.nearest_points(cut2, filletCenter)
     if not a or not b:
         return False
     patch = Polygon([a, b, point]).buffer(pcbnew.FromMM(0.001)).difference(
         filletCenter.buffer(radius))
     self.union(patch)
     return True
Пример #2
0
def splitLine(linestring, point, tolerance=pcbnew.FromMM(0.01)):
    splitted = split(linestring, point.buffer(tolerance, resolution=1))
    if len(splitted) != 3:
        print(splitted)
        raise RuntimeError("Expected 3 segments in line spitting")
    p1 = LineString(list(splitted[0].coords) + [point])
    p2 = LineString([point] + list(splitted[2].coords))
    return shapely.geometry.collection.GeometryCollection([p1, p2])
Пример #3
0
    def tab(self, origin, direction, width, partitionLine=None,
               maxHeight=pcbnew.FromMM(50)):
        """
        Create a tab for the substrate. The tab starts at the specified origin
        (2D point) and tries to penetrate existing substrate in direction (a 2D
        vector). The tab is constructed with given width. If the substrate is
        not penetrated within maxHeight, exception is raised.

        Returns a pair tab and cut outline. Add the tab it via
        uion - batch adding of geometry is more efficient.
        """
        origin = np.array(origin)
        direction = normalize(direction)
        sideOriginA = origin + makePerpendicular(direction) * width / 2
        sideOriginB = origin - makePerpendicular(direction) * width / 2
        boundary = self.substrates.boundary
        splitPointA = closestIntersectionPoint(sideOriginA, direction,
            boundary, maxHeight)
        splitPointB = closestIntersectionPoint(sideOriginB, direction,
            boundary, maxHeight)
        if isinstance(boundary, MultiLineString):
            shiftedOutline = cutOutline(splitPointB, boundary)
            tabFace = splitLine(shiftedOutline, splitPointA)[0]
            tab = Polygon(list(tabFace.coords) + [sideOriginA, sideOriginB])
            return tab, tabFace
        else:
            tabFace = biteBoundary(boundary, splitPointB, splitPointA)
            if partitionLine is None:
                # There is nothing else to do, return the tab
                tab = Polygon(list(tabFace.coords) + [sideOriginA, sideOriginB])
                return tab, tabFace
            # Span the tab towwards the partition line
            # There might be multiple geometries in the partition line, so try them
            # individually.
            direction = -direction
            for p in listGeometries(partitionLine):
                try:
                    partitionSplitPointA = closestIntersectionPoint(splitPointA.coords[0],
                            direction, p, maxHeight)
                    partitionSplitPointB = closestIntersectionPoint(splitPointB.coords[0],
                        direction, p, maxHeight)
                except NoIntersectionError: # We cannot span towards the partition line
                    continue
                if isLinestringCyclic(p):
                    candidates = [(partitionSplitPointA, partitionSplitPointB)]
                else:
                    candidates = [(partitionSplitPointA, partitionSplitPointB),
                        (partitionSplitPointB, partitionSplitPointA)]
                for i, (spa, spb) in enumerate(candidates):
                    partitionFace = biteBoundary(p, spa, spb)
                    if partitionFace is None:
                        continue
                    partitionFaceCoord = list(partitionFace.coords)
                    if i == 1:
                        partitionFaceCoord = partitionFaceCoord[::-1]
                    tab = Polygon(list(tabFace.coords) + partitionFaceCoord)
                    return tab, tabFace
            return None, None
Пример #4
0
def approximateArc(arc, endWith):
    """
    Take DRAWINGITEM and approximate it using lines
    """
    SEGMENTS_PER_FULL = 4 * 16  # To Be consistent with default shapely settings
    startAngle = arc.GetArcAngleStart() / 10
    if arc.GetShape() == STROKE_T.S_CIRCLE:
        endAngle = startAngle + 360
        segments = SEGMENTS_PER_FULL
    else:
        endAngle = startAngle + arc.GetAngle() / 10
        segments = abs(int((endAngle - startAngle) * SEGMENTS_PER_FULL // 360))
    theta = np.radians(np.linspace(startAngle, endAngle, segments))
    x = arc.GetCenter()[0] + arc.GetRadius() * np.cos(theta)
    y = arc.GetCenter()[1] + arc.GetRadius() * np.sin(theta)
    outline = list(np.column_stack([x, y]))
    last = outline[-1][0], outline[-1][1]
    if (not np.isclose(last[0], endWith[0], atol=pcbnew.FromMM(0.001))
            or not np.isclose(last[1], endWith[1], atol=pcbnew.FromMM(0.001))):
        outline.reverse()
    return outline
Пример #5
0
 def _serializeRing(self, ring):
     coords = list(ring.simplify(pcbnew.FromMM(0.001)).coords)
     segments = []
     # ToDo: Reconstruct arcs
     if coords[0] != coords[-1]:
         raise RuntimeError("Ring is incomplete")
     for a, b in zip(coords, coords[1:]):
         segment = pcbnew.PCB_SHAPE()
         segment.SetShape(STROKE_T.S_SEGMENT)
         segment.SetLayer(Layer.Edge_Cuts)
         segment.SetStart(roundPoint(a))
         segment.SetEnd(roundPoint(b))
         segments.append(segment)
     return segments
Пример #6
0
    def tab(self, origin, direction, width, maxHeight=pcbnew.FromMM(50)):
        """
        Create a tab for the substrate. The tab starts at the specified origin
        (2D point) and tries to penetrate existing substrate in direction (a 2D
        vector). The tab is constructed with given width. If the substrate is
        not penetrated within maxHeight, exception is raised.

        Returns a pair tab and cut outline. Add the tab it via
        uion - batch adding of geometry is more efficient.
        origin = np.array(origin)
        direction = normalize(direction)
        sideOriginA = origin + makePerpendicular(direction) * width / 2
        sideOriginB = origin - makePerpendicular(direction) * width / 2
        boundary = self.substrates.boundary
        splitPointA = closestIntersectionPoint(sideOriginA, direction,
            boundary, maxHeight)
        splitPointB = closestIntersectionPoint(sideOriginB, direction,
            boundary, maxHeight)
        shiftedOutline = cutOutline(splitPointB, boundary)
        tabFace = splitLine(shiftedOutline, splitPointA)[0]
        tab = Polygon(list(tabFace.coords) + [sideOriginA, sideOriginB])
        return tab, tabFace
        """
        origin = np.array(origin)
        direction = normalize(direction)
        sideOriginA = origin + makePerpendicular(direction) * width / 2
        sideOriginB = origin - makePerpendicular(direction) * width / 2
        boundary = self.substrates.boundary
        splitPointA1 = closestIntersectionPoint(np.array(sideOriginA),
                                                direction, boundary, maxHeight)
        splitPointB1 = closestIntersectionPoint(np.array(sideOriginB),
                                                direction, boundary, maxHeight)
        splitPointA2 = closestIntersectionPoint(np.array(splitPointA1),
                                                direction, boundary, maxHeight)
        splitPointB2 = closestIntersectionPoint(np.array(splitPointB1),
                                                direction, boundary, maxHeight)
        shiftedOutline = cutOutline(splitPointB1, boundary)
        tabFace1 = splitLine(shiftedOutline, splitPointA1)[0]
        shiftedOutline = cutOutline(splitPointB2, boundary)
        tabFace2 = splitLine(shiftedOutline, splitPointA2)[0]
        #tabFace1 = shapely.geometry.collection.GeometryCollection([splitPointA1, splitPointB1])
        #tabFace2 = shapely.geometry.collection.GeometryCollection([splitPointA2, splitPointB2])
        tab = Polygon([splitPointA1, splitPointB1, splitPointB2, splitPointA2])
        #print("Found a tab: "+tab)
        return tab, tabFace1, tabFace2
Пример #7
0
def cutOutline(point,
               linestring,
               segmentLimit=None,
               tolerance=pcbnew.FromMM(0.001)):
    """
    Given a point finds an entity in (multi)linestring which goes through the
    point.

    It is possible to restrict the number of segments used by specifying
    segmentLimit.

    When segment limit is passed, return a tuple of the string starting and
    ending in that point. When no limit is passed returns a single string.
    """
    if not isinstance(point, Point):
        point = Point(point[0], point[1])
    if isinstance(linestring, LineString):
        geom = [linestring]
    elif isinstance(linestring, MultiLineString):
        geom = linestring.geoms
    else:
        raise RuntimeError("Unknown geometry '{}' passed".format(
            type(linestring)))
    for string in geom:
        bufferedPoint = point.buffer(tolerance, resolution=1)
        splitted = split(string, bufferedPoint)
        if len(splitted) == 1 and not Point(
                splitted[0].coords[0]).intersects(bufferedPoint):
            continue
        if len(splitted) == 3:
            string = LineString([point] + list(splitted[2].coords) +
                                splitted[0].coords[1:])
        elif len(splitted) == 2:
            string = LineString(
                list(splitted[1].coords) + splitted[0].coords[1:])
        else:
            string = splitted[0]
        if segmentLimit is None:
            return string
        limit1 = max(1, len(string.coords) - segmentLimit)
        limit2 = min(segmentLimit, len(string.coords) - 1)
        return LineString(string.coords[limit1:]), LineString(
            string.coords[:limit2])
    return None, None
Пример #8
0
def fromMm(mm):
    """Convert millimeters to KiCAD internal units"""
    return pcbnew.FromMM(mm)
Пример #9
0
from kikit.pcbnew_compatibility import pcbnew
from kikit.intervals import Interval, AxialLine
from pcbnew import wxPoint, wxRect
import os
from itertools import product, chain, islice
import numpy as np
from shapely.geometry import LinearRing

PKG_BASE = os.path.dirname(__file__)
KIKIT_LIB = os.path.join(PKG_BASE, "resources/kikit.pretty")
SHP_EPSILON = pcbnew.FromMM(0.01)  # Common factor of enlarging substrates to
# cover up numerical imprecisions of Shapely


def fromDegrees(angle):
    return angle * 10


def fromKicadAngle(angle):
    return angle / 10


def fromMm(mm):
    """Convert millimeters to KiCAD internal units"""
    return pcbnew.FromMM(mm)


def toMm(kiUnits):
    """Convert KiCAD internal units to millimeters"""
    return pcbnew.ToMM(kiUnits)