Exemplo n.º 1
0
    def fromShapelyLineString(cls, prefix: str,
                              shapely_path: shapely.geometry.LineString,
                              safeToClose: bool) -> 'SvgPath':
        '''
        '''
        pts = list(shapely_path.coords)

        discretized_svg_path: List[complex] = [
            complex(pt[0], pt[1]) for pt in pts
        ]

        svg_path = svgpathtools.Path()

        for i in range(len(discretized_svg_path) - 1):
            start = discretized_svg_path[i]
            end = discretized_svg_path[i + 1]

            svg_path.append(svgpathtools.Line(start, end))

        # last one : from end point to start point
        if safeToClose:
            start = discretized_svg_path[-1]
            end = discretized_svg_path[0]

        svg_path.append(svgpathtools.Line(start, end))

        return SvgPath(prefix, {'d': svg_path.d(), 'fill-rule': 'nonzero'})
Exemplo n.º 2
0
def rectangle(h, w) :
    rect = []
    rect.append(svg.Line(0 + 0j, w + 0j))
    rect.append(svg.Line(w + 0j, w + h * 1j))
    rect.append(svg.Line(w + h * 1j, 0 + h * 1j))
    rect.append(svg.Line(0 + h * 1j, 0 + 0j))
    return svg.Path(*rect)
 def make_rectangle(cls, left, top, bottom, right, width, color='black'):
     path = cls(
         svgpathtools.Path(
             svgpathtools.Line(left + top * 1j, right + top * 1j),
             svgpathtools.Line(right + top * 1j, right + bottom * 1j),
             svgpathtools.Line(right + bottom * 1j, left + bottom * 1j),
             svgpathtools.Line(left + bottom * 1j, left + top * 1j),
         ),
         {'stroke': color, 'stroke-width': f'{width}', 'fill': 'none'}
     )
     path.svg_attributes['stroke'] = color
     if isinstance(width, units.GraphicUnits):
         path.width = width
     return path
Exemplo n.º 4
0
def visualize_pen_transits(paths, svg_file):
    # We will construct a new image by storing (path, attribute)
    # pairs in this list.
    parts = list()

    last_end = None
    for i, path in enumerate(paths):
        start = path.start
        end = path.end

        # Generate a color based on how far along in the plot we are.
        frac = i / (len(paths) - 1)
        color = generate_color(frac, 1.0, 1.0)

        if last_end is not None:
            # If this isn't our first path, add a line between the end of
            # the last path and the start of this one.
            parts.append(
                (
                    svgpathtools.Line(last_end, start),
                    {
                        "stroke": "black",
                        "fill": "none",
                    },
                )
            )

        last_end = end

        # Also draw a faded, colorized version of this path.
        parts.append((path, {"stroke": color, "fill": "none", "opacity": "0.5"}))

    new_paths, new_attrs = zip(*parts)
    svgpathtools.wsvg(new_paths, attributes=new_attrs, filename=svg_file)
Exemplo n.º 5
0
 def add_quadratic(q):
     paths.append(q)
     q_ctrl = svgpathtools.Path(svgpathtools.Line(q.start, q.control),
                                svgpathtools.Line(q.control, q.end))
     paths.append(q_ctrl)
     colors.append((200, 50, 50))  # q_color
     colors.append((150, 150, 150))  # q_ctrl_color
     dots.append(q.start)
     dots.append(q.control)
     dots.append(q.end)
     ncols.append('purple')
     ncols.append('purple')
     ncols.append('purple')
     nradii.append(r)
     nradii.append(r)
     nradii.append(r)
     stroke_widths.append(3.0)
     stroke_widths.append(1.5)
Exemplo n.º 6
0
def get_path(points):
    acc = []
    start = complex(*points[0])
    points = points[1:] + points[:1]
    for p in points:
        end = complex(*p)
        line = svg.Line(start, end)
        acc.append(line)
        start = end
    return svg.Path(*acc)
 def make_line(cls, p1, p2, width, color='black'):
     path = cls(
         svgpathtools.Path(
             svgpathtools.Line(p1[0] + p1[1] * 1j, p2[0] + p2[1] * 1j),
         ),
         {'stroke': color, 'stroke-width': f'{width}', 'fill': 'none'}
     )
     path.svg_attributes['stroke'] = color
     if isinstance(width, units.GraphicUnits):
         path.width = width
     return path
Exemplo n.º 8
0
 def save_svgpathtools(self, filename):
     lines = []
     for x, y, _ in self.lines:
         for i in range(len(x) - 1):
             lines.append(
                 svg.Line(x[i] + y[i] * -1j, x[i + 1] + y[i + 1] * -1j))
     line_colors = [
         color for x, _, color in self.lines for _ in range(len(x))
     ]
     text_path = []
     for x, y, _, _, _ in self.texts:
         for i in range(len(x)):
             text_path.append(
                 svg.Line(x[i] + y[i] * -1j, x[i] + 1.0 + y[i] * -1j))
     text = [text for x, _, text, _, _ in self.texts for _ in range(len(x))]
     svg.wsvg(lines,
              line_colors,
              stroke_widths=[0.01] * len(lines),
              text=text,
              text_path=text_path,
              font_size=[0.1] * len(text),
              filename=filename)
Exemplo n.º 9
0
def approximate_with_line_segs(seg, tol, min_length):
  # Potential line approximation.
  line = svgpathtools.Line(seg.start, seg.end)

  # Check if the line segment is already super short.
  if len(line) > min_length:
    # Check if the line segment is within the tolerance at the quartiles.
    for p in [.25, .5, .75]:
      if np.abs(seg.point(p) - line.point(p)) > tol:
        left_seg, right_seg = seg.split(.5)
        return (approximate_with_line_segs(left_seg, tol, min_length) + 
                approximate_with_line_segs(right_seg, tol, min_length))
  # Return the line if it's already short or sufficiently close to the curve.
  return [line]
Exemplo n.º 10
0
 def save_svg(self, filename):
     lines = [
         svg.Line(start[0] + start[1] * 1j, end[0] + end[1] * 1j)
         for [start, end], _ in self.lines
     ]
     line_colors = [color for [_, _], color in self.lines]
     nodes = [point[0] + point[1] * 1j for point, _ in self.nodes]
     node_colors = [color for _, color in self.nodes]
     text_path = [
         svg.Line(coordinate[0] + coordinate[1] * 1j,
                  coordinate[0] + 100.0 + coordinate[1] * 1j)
         for coordinate, _, _, _ in self.texts
     ]
     text = [text for _, text, _, _ in self.texts]
     svg.wsvg(lines,
              line_colors,
              stroke_widths=[1.0] * len(lines),
              nodes=nodes,
              node_colors=node_colors,
              node_radii=[2.5] * len(nodes),
              text=text,
              text_path=text_path,
              font_size=[5] * len(text),
              filename=filename)
Exemplo n.º 11
0
def segment_svg(svg_file, output_file, segment_size=8):
    paths, _ = svgpathtools.svg2paths(svg_file)

    lines = []

    for path in paths:
        for cp in path.continuous_subpaths():
            last_pos = None
            segments = int(cp.length() / segment_size)
            for frac in np.linspace(0., 1., segments):
                pos = cp.point(frac)
                if last_pos:
                    lines.append(svgpathtools.Line(last_pos, pos))
                last_pos = pos

    svgpathtools.wsvg(lines, filename=output_file)
 def from_primitive(cls, primitive_type, primitive):
     if primitive_type == PT_LINE:
         start = primitive[0] + primitive[1] * 1j
         end = primitive[2] + primitive[3] * 1j
         width = primitive[4]
         primitive = svgpathtools.Line(start, end)
     elif primitive_type == PT_QBEZIER:
         p1 = primitive[0] + primitive[1] * 1j
         p2 = primitive[2] + primitive[3] * 1j
         p3 = primitive[4] + primitive[5] * 1j
         width = primitive[6]
         primitive = svgpathtools.QuadraticBezier(p1, p2, p3)
     else:
         raise NotImplementedError
     path = svgpathtools.Path(primitive)
     attributes = {'stroke': 'black', 'stroke-width': f'{width}', 'fill': 'none'}
     return cls(path, attributes)
Exemplo n.º 13
0
def point_inside_curve(c, p, s=0):
    crv = np.array(c)
    center = np.array([np.average(crv[:, 0]), np.average(crv[:, 1])])
    new_crv = []
    for pt in crv:
        if s == 0:
            new_crv.append(pt)
        else:
            new_crv.append(explode(pt, center, (s / 2)))
    new_crv = np.array(new_crv)
    pth = svgpathtools.parse_path(get_path_for_curve(new_crv))
    bbox = pth.bbox()

    end = p[0] + 1j * bbox[3]
    p = p[0] + 1j * p[1]
    curve = svgpathtools.Path(svgpathtools.Line(p, end))

    intersection_count = len(curve.intersect(pth))

    return intersection_count % 2 == 1
Exemplo n.º 14
0
def _bezier_to_svg_arc(segment, max_dist, min_radius):
    p1 = segment.start
    p2 = segment.point(0.5)
    p3 = segment.end

    p = []
    # if p1, p2, p3 are collinear, then
    # (p1-p3)/(p3-p2) is real
    tol = 1.e-5
    _ratio = (p1-p3)/(p3-p2)
    if np.abs(np.imag(_ratio)) < tol:
        a = spt.Line(start=p1, end=p3)
        p.append(a)
    else:
        t_start = 0
        t_end = 1
        while t_start < t_end:
            a, t_arc = fit_arc(segment, t_start, t_end, max_dist, min_radius)
            p.append(a)
            t_start += t_arc
    return p
Exemplo n.º 15
0
def join_close_paths(paths, threshold):
    new_paths = []
    current_path = []

    for path in paths:
        start = path[0].start
        end = path[-1].end

        if current_path:
            last_point = current_path[-1].end
            start_point = path[0].start
            if dist(last_point, start_point) < threshold:
                current_path.append(svgpathtools.Line(last_point, start_point))
                current_path.extend(path)
                continue
            else:
                new_paths.append(svgpathtools.Path(*current_path))
        current_path = list(path)

    if current_path:
        new_paths.append(svgpathtools.Path(*current_path))
    return new_paths
Exemplo n.º 16
0
    def fromCircleDef(cls, center: float, radius: float) -> 'SvgPath':
        '''
        PyCut Tab import in svg viewer
        '''
        NB_SEGMENTS = 12
        angles = [
            float(k * M_PI) / (NB_SEGMENTS) for k in range(NB_SEGMENTS * 2 + 1)
        ]

        discretized_svg_path : List[complex] = [ complex( \
                    center[0] + radius*math.cos(angle),
                    center[1] + radius*math.sin(angle) ) for angle in angles]

        svg_path = svgpathtools.Path()

        for i in range(len(discretized_svg_path) - 1):
            start = discretized_svg_path[i]
            end = discretized_svg_path[i + 1]

            svg_path.append(svgpathtools.Line(start, end))

        return SvgPath("pycut_tab", {'d': svg_path.d()})
Exemplo n.º 17
0
def approximate_with_line_segs(seg, tol, min_length, depth, max_depth):
    # Potential line approximation.
    line = svgpathtools.Line(seg.start, seg.end)
    done = True
    # Check if the line segment is already super short
    if line.length() > min_length:
        # Check if the line segment is within the tolerance at the quartiles
        for p in [.25, .5, .75]:
            if np.abs(seg.point(p) - line.point(p)) > tol:
                done = False
                break
    if depth < max_depth and not done:
        left_seg, right_seg = seg.split(.5)
        path_left, done_left = approximate_with_line_segs(
            left_seg, tol, min_length, depth + 1, max_depth)
        path_right, done_right = approximate_with_line_segs(
            right_seg, tol, min_length, depth + 1, max_depth)

        return (path_left + path_right), (done_left and done_right)

    # Return the line if it's already short or sufficiently close to the curve
    return [line], done
Exemplo n.º 18
0
def find_path_between_points(p1, p2):
    # temp
    if ruv.distance(p1, p2) < 100:
        edist = 100 - ruv.distance(p1, p2)
        mid = np.array([
            (p1[0] + p1[1]) / 2,
            (p2[0] + p2[1]) / 2
        ])
        dir = p1[0] % 2 == 0
        vec = (p2 - p1) / ruv.distance(p1, p2)
        a = vec[0]
        b = vec[1]
        if dir:
            a = -a
        else:
            b = -b
        vec[0] = b
        vec[1] = a
        p0 = mid + (vec * edist)
        pth = svgpathtools.Path(svgpathtools.QuadraticBezier(complex_as_point(p1), complex_as_point(ruv.cp_for(p1, p0, p2)), complex_as_point(p2)))
    else:
        pth = svgpathtools.Path(svgpathtools.Line(complex_as_point(p1), complex_as_point(p2)))

    return pth.d()
Exemplo n.º 19
0
def from_svg_path(path_str, shape_to_canvas=torch.eye(3), force_close=False):
    path = svgpathtools.parse_path(path_str)
    if len(path) == 0:
        return []
    ret_paths = []
    subpaths = path.continuous_subpaths()
    for subpath in subpaths:
        if subpath.isclosed():
            if len(subpath) > 1 and isinstance(
                    subpath[-1],
                    svgpathtools.Line) and subpath[-1].length() < 1e-5:
                subpath.remove(subpath[-1])
                subpath[-1].end = subpath[0].start  # Force closing the path
                subpath.end = subpath[-1].end
                assert (subpath.isclosed())
        else:
            beg = subpath[0].start
            end = subpath[-1].end
            if abs(end - beg) < 1e-5:
                subpath[-1].end = beg  # Force closing the path
                subpath.end = subpath[-1].end
                assert (subpath.isclosed())
            elif force_close:
                subpath.append(svgpathtools.Line(end, beg))
                subpath.end = subpath[-1].end
                assert (subpath.isclosed())

        num_control_points = []
        points = []

        for i, e in enumerate(subpath):
            if i == 0:
                points.append((e.start.real, e.start.imag))
            else:
                # Must begin from the end of previous segment
                assert (e.start.real == points[-1][0])
                assert (e.start.imag == points[-1][1])
            if isinstance(e, svgpathtools.Line):
                num_control_points.append(0)
            elif isinstance(e, svgpathtools.QuadraticBezier):
                num_control_points.append(1)
                points.append((e.control.real, e.control.imag))
            elif isinstance(e, svgpathtools.CubicBezier):
                num_control_points.append(2)
                points.append((e.control1.real, e.control1.imag))
                points.append((e.control2.real, e.control2.imag))
            elif isinstance(e, svgpathtools.Arc):
                # Convert to Cubic curves
                # https://www.joecridge.me/content/pdf/bezier-arcs.pdf
                start = e.theta * math.pi / 180.0
                stop = (e.theta + e.delta) * math.pi / 180.0

                sign = 1.0
                if stop < start:
                    sign = -1.0

                epsilon = 0.00001
                # debug = abs(e.delta) >= 90.0
                while (sign * (stop - start) > epsilon):
                    arc_to_draw = stop - start
                    if arc_to_draw > 0.0:
                        arc_to_draw = min(arc_to_draw, 0.5 * math.pi)
                    else:
                        arc_to_draw = max(arc_to_draw, -0.5 * math.pi)
                    alpha = arc_to_draw / 2.0
                    cos_alpha = math.cos(alpha)
                    sin_alpha = math.sin(alpha)
                    cot_alpha = 1.0 / math.tan(alpha)
                    phi = start + alpha
                    cos_phi = math.cos(phi)
                    sin_phi = math.sin(phi)
                    lambda_ = (4.0 - cos_alpha) / 3.0
                    mu = sin_alpha + (cos_alpha - lambda_) * cot_alpha
                    last = sign * (stop - (start + arc_to_draw)) <= epsilon
                    num_control_points.append(2)
                    rx = e.radius.real
                    ry = e.radius.imag
                    cx = e.center.real
                    cy = e.center.imag
                    rot = e.phi * math.pi / 180.0
                    cos_rot = math.cos(rot)
                    sin_rot = math.sin(rot)
                    x = lambda_ * cos_phi + mu * sin_phi
                    y = lambda_ * sin_phi - mu * cos_phi
                    xx = x * cos_rot - y * sin_rot
                    yy = x * sin_rot + y * cos_rot
                    points.append((cx + rx * xx, cy + ry * yy))
                    x = lambda_ * cos_phi - mu * sin_phi
                    y = lambda_ * sin_phi + mu * cos_phi
                    xx = x * cos_rot - y * sin_rot
                    yy = x * sin_rot + y * cos_rot
                    points.append((cx + rx * xx, cy + ry * yy))
                    if not last:
                        points.append(
                            (cx + rx * math.cos(rot + start + arc_to_draw),
                             cy + ry * math.sin(rot + start + arc_to_draw)))
                    start += arc_to_draw
                    # first = False
            if i != len(subpath) - 1:
                points.append((e.end.real, e.end.imag))
            else:
                if subpath.isclosed():
                    # Must end at the beginning of first segment
                    assert (e.end.real == points[0][0])
                    assert (e.end.imag == points[0][1])
                else:
                    points.append((e.end.real, e.end.imag))
        points = torch.tensor(points)
        points = torch.cat((points, torch.ones([points.shape[0], 1])),
                           dim=1) @ torch.transpose(shape_to_canvas, 0, 1)
        points = points / points[:, 2:3]
        points = points[:, :2].contiguous()
        ret_paths.append(
            Path(torch.tensor(num_control_points), points, subpath.isclosed()))
    return ret_paths
Exemplo n.º 20
0
def convert_and_write_svg(cubic, filename):
    cubic_path = svgpathtools.Path(cubic)
    cubic_ctrl = svgpathtools.Path(
        svgpathtools.Line(cubic.start, cubic.control1),
        svgpathtools.Line(cubic.control1, cubic.control2),
        svgpathtools.Line(cubic.control2, cubic.end))
    cubic_color = (50, 50, 200)
    cubic_ctrl_color = (150, 150, 150)

    r = 4.0

    paths = [cubic_path, cubic_ctrl]
    colors = [cubic_color, cubic_ctrl_color]
    dots = [
        cubic_path[0].start, cubic_path[0].control1, cubic_path[0].control2,
        cubic_path[0].end
    ]
    ncols = ['green', 'green', 'green', 'green']
    nradii = [r, r, r, r]
    stroke_widths = [3.0, 1.5]

    def add_quadratic(q):
        paths.append(q)
        q_ctrl = svgpathtools.Path(svgpathtools.Line(q.start, q.control),
                                   svgpathtools.Line(q.control, q.end))
        paths.append(q_ctrl)
        colors.append((200, 50, 50))  # q_color
        colors.append((150, 150, 150))  # q_ctrl_color
        dots.append(q.start)
        dots.append(q.control)
        dots.append(q.end)
        ncols.append('purple')
        ncols.append('purple')
        ncols.append('purple')
        nradii.append(r)
        nradii.append(r)
        nradii.append(r)
        stroke_widths.append(3.0)
        stroke_widths.append(1.5)

    prec = 1.0
    queue = [cubic]
    num_quadratics = 0
    while len(queue) > 0:
        c = queue[-1]
        queue = queue[:-1]

        # Criteria for conversion
        # http://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
        p = c.end - 3 * c.control2 + 3 * c.control1 - c.start
        d = math.sqrt(p.real * p.real + p.imag * p.imag) * math.sqrt(3.0) / 36
        t = math.pow(1.0 / d, 1.0 / 3.0)

        if t < 1.0:
            c0, c1 = split_cubic(c, 0.5)
            queue.append(c0)
            queue.append(c1)
        else:
            quadratic = cubic_to_quadratic(c)
            print(quadratic)
            add_quadratic(quadratic)
            num_quadratics += 1
    print('num_quadratics:', num_quadratics)

    svgpathtools.wsvg(paths,
                      colors=colors,
                      stroke_widths=stroke_widths,
                      nodes=dots,
                      node_colors=ncols,
                      node_radii=nradii,
                      filename=filename)
Exemplo n.º 21
0
def _line_from_points(points):
    return svgpathtools.Line(*(complex(*p) for p in points))
Exemplo n.º 22
0
    segments = numpy.linspace(0, 1, args.approximate + 1)

    for path in paths:
        for element in path:
            if args.verbose:
                print('Plotting', str(type(element)).split('.')[-1], end='')
            if type(element) == svgpathtools.path.Line or element.length(
            ) * args.scale < args.min_length:
                line_list = [element]
                if args.verbose:
                    print(' as line: (', element.start, ',', element.end, ')')
            else:  # adapted from svgpathtools issue #61
                pts = [element.point(t) for t in segments]
                line_list = [
                    svgpathtools.Line(pts[i - 1], pts[i])
                    for i in range(1, len(pts))
                ]
                if args.verbose:
                    print(' as lines:')
                    for line in line_list:
                        print('          (', element.start, ',', element.end,
                              ')')

            for line in line_list:
                if robot.stop_project_flag.is_set():
                    raise RuntimeError('Robot requested stop.')
                start = invert_y(line.start) * args.scale
                end = invert_y(line.end) * args.scale
                # if the robot's last known position isn't close enough to the start, lift the pen
                if numpy.linalg.norm(robot._last_coord - start) > args.epsilon:
Exemplo n.º 23
0
def offset_paths(path, offset_distance, steps=100, debug=False):
    """Takes an svgpathtools.path.Path object, `path`, and a float
    distance, `offset_distance`, and returns the parallel offset curves
    (in the form of a list of svgpathtools.path.Path objects)."""


    def is_enclosed(path, check_paths):

        """`path` is an svgpathtools.path.Path object, `check_paths`
        is a list of svgpath.path.Path objects.  This function returns
        True if `path` lies inside any of the paths in `check_paths`,
        and returns False if it lies outside all of them."""

        seg = path[0]
        point = seg.point(0.5)

        for i in range(len(check_paths)):
            test_path = check_paths[i]
            if path == test_path:
                continue
            # find outside_point, which lies outside other_path
            (xmin, xmax, ymin, ymax) = test_path.bbox()
            outside_point = complex(xmax+100, ymax+100)
            if svgpathtools.path_encloses_pt(point, outside_point, test_path):
                if debug: print("point is within path", i, file=sys.stderr)
                return True
        return False


    # This only works on closed paths.
    if debug: print("input path:", file=sys.stderr)
    if debug: print(path, file=sys.stderr)
    if debug: print("offset:", offset_distance, file=sys.stderr)
    assert(path.isclosed())


    #
    # First generate a list of Path elements (Lines and Arcs),
    # corresponding to the offset versions of the Path elements in the
    # input path.
    #

    if debug: print("generating offset segments...", file=sys.stderr)

    offset_path_list = []
    for seg in path:
        if type(seg) == svgpathtools.path.Line:
            start = seg.point(0) + (offset_distance * seg.normal(0))
            end = seg.point(1) + (offset_distance * seg.normal(1))
            offset_path_list.append(svgpathtools.Line(start, end))
            if debug: print("    ", offset_path_list[-1], file=sys.stderr)

        elif type(seg) == svgpathtools.path.Arc and (seg.radius.real == seg.radius.imag):
            # Circular arcs remain arcs, elliptical arcs become linear
            # approximations below.
            #
            # Polygons (input paths) are counter-clockwise.
            #
            # Positive offsets are to the inside of the polygon, negative
            # offsets are to the outside.
            #
            # If this arc is counter-clockwise (sweep == False),
            # *subtract* the `offset_distance` from its radius, so
            # insetting makes the arc smaller and outsetting makes
            # it larger.
            #
            # If this arc is clockwise (sweep == True), *add* the
            # `offset_distance` from its radius, so insetting makes the
            # arc larger and outsetting makes it smaller.
            #
            # If the radius of the offset arc is negative, use its
            # absolute value and invert the sweep.

            if seg.sweep == False:
                new_radius = seg.radius.real - offset_distance
            else:
                new_radius = seg.radius.real + offset_distance

            start = seg.point(0) + (offset_distance * seg.normal(0))
            end = seg.point(1) + (offset_distance * seg.normal(1))
            sweep = seg.sweep

            flipped = False
            if new_radius < 0.0:
                if debug: print("    inverting Arc!", file=sys.stderr)
                flipped = True
                new_radius = abs(new_radius)
                sweep = not sweep

            if new_radius > 0.002:
                radius = complex(new_radius, new_radius)
                offset_arc = svgpathtools.path.Arc(
                    start = start,
                    end = end,
                    radius = radius,
                    rotation = seg.rotation,
                    large_arc = seg.large_arc,
                    sweep = sweep
                )
                offset_path_list.append(offset_arc)
            elif new_radius > epsilon:
                # Offset Arc radius is smaller than the minimum that
                # LinuxCNC accepts, replace with a Line.
                if debug: print("    arc too small, replacing with a line", file=sys.stderr)
                if flipped:
                    old_start = start
                    start = end
                    end = old_start
                offset_arc = svgpathtools.path.Line(start = start, end = end)
                offset_path_list.append(offset_arc)
            else:
                # Zero-radius Arc, it disappeared.
                if debug: print("    arc way too small, removing", file=sys.stderr)
                continue
            if debug: print("    ", offset_path_list[-1], file=sys.stderr)

        else:
            # Deal with any segment that's not a line or a circular arc.
            # This includes elliptic arcs and bezier curves.  Use linear
            # approximation.
            #
            # FIXME: Steps should probably be computed dynamically to make
            #     the length of the *offset* line segments manageable.
            points = []
            for k in range(steps+1):
                t = k / float(steps)
                normal = seg.normal(t)
                offset_vector = offset_distance * normal
                points.append(seg.point(t) + offset_vector)
            for k in range(len(points)-1):
                start = points[k]
                end = points[k+1]
                offset_path_list.append(svgpathtools.Line(start, end))
            if debug: print("    (long list of short lines)", file=sys.stderr)


    #
    # Find all the places where one segment intersects the next, and
    # trim to the intersection.
    #

    if debug: print("trimming intersecting segments...", file=sys.stderr)

    for i in range(len(offset_path_list)):
        this_seg = offset_path_list[i]
        if (i+1) < len(offset_path_list):
            next_seg = offset_path_list[i+1]
        else:
            next_seg = offset_path_list[0]

        # FIXME: I'm not sure about this part.
        if debug: print("intersecting", file=sys.stderr)
        if debug: print("    this", this_seg, file=sys.stderr)
        if debug: print("    next", next_seg, file=sys.stderr)
        intersections = this_seg.intersect(next_seg)
        if debug: print("    intersections:", intersections, file=sys.stderr)
        if len(intersections) > 0:
            intersection = intersections[0]
            point = this_seg.point(intersection[0])
            if debug: print("    intersection point:", point, file=sys.stderr)
            if not complex_close_enough(point, this_seg.end):
                this_seg.end = this_seg.point(intersection[0])
                next_seg.start = this_seg.end


    #
    # Find all the places where adjacent segments do not end/start close
    # to each other, and join them with Arcs.
    #

    if debug: print("joining non-connecting segments with arcs...", file=sys.stderr)

    joined_offset_path_list = []
    for i in range(len(offset_path_list)):
        this_seg = offset_path_list[i]
        if (i+1) < len(offset_path_list):
            next_seg = offset_path_list[i+1]
        else:
            next_seg = offset_path_list[0]

        if complex_close_enough(this_seg.end, next_seg.start):
            joined_offset_path_list.append(this_seg)
            continue

        if debug: print("these segments don't touch end to end:", file=sys.stderr)
        if debug: print(this_seg, file=sys.stderr)
        if debug: print(next_seg, file=sys.stderr)
        if debug: print("    error:", this_seg.end-next_seg.start, file=sys.stderr)

        # FIXME: Choose values for `large_arc` and `sweep` correctly here.
        # I think the goal is to make the joining arc tangent to the segments it joins.
        # large_arc should always be False
        # sweep means "clockwise" (but +Y is down)
        if debug: print("determining joining arc:", file=sys.stderr)
        if debug: print("    this_seg ending normal:", this_seg.normal(1), file=sys.stderr)
        if debug: print("    next_seg starting normal:", next_seg.normal(0), file=sys.stderr)

        sweep_arc = svgpathtools.path.Arc(
            start = this_seg.end,
            end = next_seg.start,
            radius = complex(offset_distance, offset_distance),
            rotation = 0,
            large_arc = False,
            sweep = True
        )
        sweep_start_error = this_seg.normal(1) - sweep_arc.normal(0)
        sweep_end_error = next_seg.normal(0) - sweep_arc.normal(1)
        sweep_error = pow(abs(sweep_start_error), 2) + pow(abs(sweep_end_error), 2)
        if debug: print("    sweep arc starting normal:", sweep_arc.normal(0), file=sys.stderr)
        if debug: print("    sweep arc ending normal:", sweep_arc.normal(1), file=sys.stderr)
        if debug: print("    sweep starting error:", sweep_start_error, file=sys.stderr)
        if debug: print("    sweep end error:", sweep_end_error, file=sys.stderr)
        if debug: print("    sweep error:", sweep_error, file=sys.stderr)

        antisweep_arc = svgpathtools.path.Arc(
            start = this_seg.end,
            end = next_seg.start,
            radius = complex(offset_distance, offset_distance),
            rotation = 0,
            large_arc = False,
            sweep = False
        )
        antisweep_start_error = this_seg.normal(1) - antisweep_arc.normal(0)
        antisweep_end_error = next_seg.normal(0) - antisweep_arc.normal(1)
        antisweep_error = pow(abs(antisweep_start_error), 2) + pow(abs(antisweep_end_error), 2)
        if debug: print("    antisweep arc starting normal:", antisweep_arc.normal(0), file=sys.stderr)
        if debug: print("    antisweep arc ending normal:", antisweep_arc.normal(1), file=sys.stderr)
        if debug: print("    antisweep starting error:", antisweep_start_error, file=sys.stderr)
        if debug: print("    antisweep end error:", antisweep_end_error, file=sys.stderr)
        if debug: print("    antisweep error:", antisweep_error, file=sys.stderr)

        joining_arc = None
        if sweep_error < antisweep_error:
            if debug: print("joining arc is sweep", file=sys.stderr)
            joining_arc = sweep_arc
        else:
            if debug: print("joining arc is antisweep", file=sys.stderr)
            joining_arc = antisweep_arc

        if debug: print("joining arc:", file=sys.stderr)
        if debug: print(joining_arc, file=sys.stderr)
        if debug: print("    length:", joining_arc.length(), file=sys.stderr)
        if debug: print("    start-end distance:", joining_arc.start-joining_arc.end, file=sys.stderr)

        # FIXME: this is kind of arbitrary
        joining_seg = joining_arc
        if joining_arc.length() < 1e-4:
            joining_seg = svgpathtools.path.Line(joining_arc.start, joining_arc.end)
            if debug: print("    too short!  replacing with a line:", joining_seg, file=sys.stderr)

        joined_offset_path_list.append(this_seg)
        joined_offset_path_list.append(joining_seg)

    offset_path_list = joined_offset_path_list


    #
    # Find the places where the path intersects itself, split into
    # multiple separate paths in those places.
    #

    if debug: print("splitting path at intersections...", file=sys.stderr)

    offset_paths_list = split_path_at_intersections(offset_path_list)


    #
    # Smooth the path: adjacent segments whose start/end points are
    # "close enough" to each other are adjusted so they actually touch.
    #
    # FIXME: is this still needed?
    #

    if debug: print("smoothing paths...", file=sys.stderr)

    for path_list in offset_paths_list:
        for i in range(len(path_list)):
            this_seg = path_list[i]
            if (i+1) < len(path_list):
                next_seg = path_list[i+1]
            else:
                next_seg = path_list[0]
            if complex_close_enough(this_seg.end, next_seg.start):
                next_seg.start = this_seg.end
            else:
                if debug: print("gap in the path (seg %d and following):" % i, file=sys.stderr)
                if debug: print("    this_seg.end:", this_seg.end, file=sys.stderr)
                if debug: print("    next_seg.start:", next_seg.start, file=sys.stderr)


    #
    # Convert each path list to a Path object and sanity check.
    #

    if debug: print("converting path lists to paths...", file=sys.stderr)

    offset_paths = []
    for path_list in offset_paths_list:
        offset_path = svgpathtools.Path(*path_list)
        if debug: print("offset path:", file=sys.stderr)
        if debug: print(offset_path, file=sys.stderr)
        assert(offset_path.isclosed())
        offset_paths.append(offset_path)


    #
    # The set of paths we got from split_path_at_intersections() has
    # zero or more 'true paths' that we actually want to return, plus
    # zero or more 'false paths' that should be discarded.
    #
    # When offsetting a path to the inside, the false paths will be
    # outside the true path and will wind in the opposite direction of
    # the input path.
    #
    # When offsetting a path to the outside, the false paths will be
    # inside the true paths, and will wind in the same direction as the
    # input path.
    #
    # [citation needed]
    #

    if debug: print("pruning false paths...", file=sys.stderr)

    path_area = approximate_path_area(path)
    if debug: print("input path area:", path_area, file=sys.stderr)

    keepers = []

    if offset_distance > 0:
        # The offset is positive (inwards), discard paths with opposite
        # direction from input path.
        for offset_path in offset_paths:
            if debug: print("checking path:", offset_path, file=sys.stderr)
            offset_path_area = approximate_path_area(offset_path)
            if debug: print("offset path area:", offset_path_area, file=sys.stderr)
            if path_area * offset_path_area < 0.0:
                # Input path and offset path go in the opposite directions,
                # drop offset path.
                if debug: print("wrong direction, dropping", file=sys.stderr)
                continue
            keepers.append(offset_path)

    else:
        # The offset is negative (outwards), discard paths that lie
        # inside any other path and have the same winding direction as
        # the input path.
        for offset_path in offset_paths:
            if debug: print("checking path:", offset_path, file=sys.stderr)
            if is_enclosed(offset_path, offset_paths):
                if debug: print("    enclosed", file=sys.stderr)
                # This path is enclosed, check the winding direction.
                offset_path_area = approximate_path_area(offset_path)
                if debug: print("offset path area:", offset_path_area, file=sys.stderr)
                if path_area * offset_path_area > 0.0:
                    if debug: print("    winding is the same as input, dropping", file=sys.stderr)
                    continue
                else:
                    if debug: print("    winding is opposite input", file=sys.stderr)
            else:
                if debug: print("    not enclosed", file=sys.stderr)
            if debug: print("    keeping", file=sys.stderr)
            keepers.append(offset_path)

    offset_paths = keepers

    return offset_paths
Exemplo n.º 24
0
 def __init__ (self, h1, w1, h2, w2) :
     self.top = rectangle(h1, w1)
     self.joint = (h1/2) * 1j
     self.joint2 = w1 + (h1/2) * 1j
     self.bottom = rectangle(h2, w2).translated(self.joint2 - (h2/2) * 1j)
     self.pseudoLine = svg.Line(start=self.joint, end=self.joint2)
Exemplo n.º 25
0
 def from_points(cls, start, end):
     return cls(svgpathtools.Line(complex(*start), complex(*end)))
Exemplo n.º 26
0
 def __init__(self, segment):
     self.segment = svgpathtools.Line(*(np.round(p, INTERNAL_PRECISION) for p in segment))
Exemplo n.º 27
0
    def _calc_fillet_for_joint(self, p, i):
        seg1 = p[(i) % len(p)]
        seg2 = p[(i + 1) % len(p)]

        ori_p = svgpathtools.Path(seg1, seg2)
        new_p = svgpathtools.Path()

        # ignore the node if G1 continuity
        tg1 = seg1.unit_tangent(1.0)
        tg2 = seg2.unit_tangent(0.0)
        cosA = abs(tg1.real * tg2.real + tg1.imag * tg2.imag)
        if abs(cosA - 1.0) < 1e-6:
            new_p.append(seg1.cropped(self._prev_t, 1.0))
            self._prev_t = 0.0
            if self._very_first_t is None:
                self._very_first_t = 1.0
            if not isclosedac(p) and i == len(p) - 2:
                new_p.append(seg2.cropped(
                    0.0, 1.0))  # add last segment if not closed
        else:
            cir = self.circle(seg1.end, self.options.radius)
            #        new_p.extend(cir)

            intersects = ori_p.intersect(cir)
            if len(intersects) != 2:
                inkex.errormsg(
                    "Some fillet or chamfer may not be drawn: %d intersections!"
                    % len(intersects))
                new_p.append(seg1.cropped(self._prev_t, 1.0))
                self._prev_t = 0.0
                if self._very_first_t is None:
                    self._very_first_t = 1.0
                if not isclosedac(p) and i == len(p) - 2:
                    new_p.append(seg2.cropped(
                        0.0, 1.0))  # add last segment if not closed
            else:
                cb = []
                segs = []
                ts = []
                for (T1, seg1, t1), (T2, seg2, t2) in intersects:
                    c1 = seg1.point(t1)
                    tg1 = seg1.unit_tangent(t1) * (self.options.radius * KAPPA)
                    cb.extend([c1, tg1])
                    segs.append(seg1)
                    ts.append(t1)

    #                cir1 = self.circle(c1, self.options.radius * KAPPA)
    #                new_p.extend(cir1)
    #                new_p.append(svgpathtools.Line(c1, c1+tg1))

                assert len(cb) == 4
                new_p.append(segs[0].cropped(self._prev_t, ts[0]))
                if self.options.fillet_type == 'fillet':
                    fillet = svgpathtools.CubicBezier(cb[0], cb[0] + cb[1],
                                                      cb[2] - cb[3], cb[2])
                else:
                    fillet = svgpathtools.Line(cb[0], cb[2])
                new_p.append(fillet)
                self._prev_t = ts[1]
                if self._very_first_t is None:
                    self._very_first_t = ts[0]

                if isclosedac(p) and i == len(p) - 1:
                    new_p.append(segs[1].cropped(
                        ts[1],
                        self._very_first_t))  # update first segment if closed
                elif not isclosedac(p) and i == len(p) - 2:
                    new_p.append(segs[1].cropped(
                        ts[1], 1.0))  # add last segment if not closed


#            # fix for the first segment
#            if p.isclosed():
#                new_p[0] = p[0].cropped(ts[1], self._very_first_t)

#            new_p.append(segs[0].cropped(ts[0], 1.0))
#            new_p.append(segs[1].cropped(0.0, ts[1]))
#            if self.options.fillet_type == 'fillet':
#                fillet = svgpathtools.CubicBezier(cb[0], cb[0]+cb[1], cb[2]-cb[3], cb[2])
#            else:
#                fillet = svgpathtools.Line(cb[0], cb[2])
#            new_p.append(fillet.reversed())

        return new_p