def separate_closed_paths(paths): """takes a list of path strings breaks non continuous paths and joins connecting paths together to return a list of closed paths """ discrete_paths = [] closed_paths = [] open_paths = [] dead_ends = [] for path in paths: discrete_paths += divide_pathstring_parts(path) for path in discrete_paths: parsed_path = SVGPT.parse_path(path) if parsed_path.isclosed(): closed_paths.append(path) else: open_paths.append(parsed_path) while open_paths: path = open_paths.pop() new_path = None for other_path in open_paths: if path.end == other_path.start: new_path = path.d() + " " + other_path.d().replace('M', 'L') open_paths.remove(other_path) break elif path.start == other_path.end: new_path = other_path.d() + " " + path.d().replace('M', 'L') open_paths.remove(other_path) break elif path.end == other_path.end: new_path = path.d() + " " + other_path.reversed().d().replace( 'M', 'L') open_paths.remove(other_path) break elif path.start == other_path.start: new_path = path.reversed().d() + " " + other_path.d().replace( 'M', 'L') open_paths.remove(other_path) break if new_path is not None: parsed_new_path = SVGPT.parse_path(new_path) if parsed_new_path.isclosed(): closed_paths.append(new_path) else: open_paths.append(parsed_new_path) else: dead_ends.append(path.d()) open_paths = dead_ends return closed_paths, open_paths
def path_to_segments(path_string): """breaks down a path into a list of segments""" segments = [] path = SVGPT.parse_path(path_string) for segment in path: if isinstance(segment, SVGPT.path.Line): # pylint: disable=maybe-no-member points = points_from_line(segment) new_path_string = f"M {points[0][0]} {points[0][1]} L {points[1][0]} {points[1][1]}" segments.append(new_path_string) return segments
def segments_overlap(first, second): """returns true if segments share more than a single point""" first_path_string = points_to_path(first, closed=False) second_path_string = points_to_path(second, closed=False) first_path = SVGPT.parse_path(first_path_string)[0] second_path = SVGPT.parse_path(second_path_string)[0] overlaps = [] for point in first: complex_point = xy_to_complex(point) place_on_path = second_path.point_to_t(complex_point) if place_on_path is not None: if point not in overlaps: overlaps.append(point) for point in second: complex_point = xy_to_complex(point) place_on_path = first_path.point_to_t(complex_point) if place_on_path is not None: if point not in overlaps: overlaps.append(point) overlap = len(overlaps) >= 2 return overlap
def rotate_path(path_string, angle_degrees, xy_point): """rotates a path string a given number of degrees (CCW) around point (x, y)""" path = SVGPT.parse_path(path_string) empty = SVGPT.Path() if path == empty: return "" complex_point = xy_to_complex(xy_point) rotated_path = path.rotated(angle_degrees, origin=complex_point) rotated_string = rotated_path.d() return rotated_string
def move_path(path_string, xy_translation): """Takes a path string and xy_translation (x, y), and moves it x units over, and y units down""" path = SVGPT.parse_path(path_string) empty = SVGPT.Path() if path == empty: return "" complex_translation = xy_to_complex(xy_translation) translated_path = path.translated(complex_translation) translated_string = translated_path.d() return translated_string
def path_string_to_points(path_string): """Convert path string into a list of points""" path = SVGPT.parse_path(path_string) empty = SVGPT.Path() if path == empty: return None points = [] for segment in path: segment_points = subpath_to_points(segment) for point in segment_points: if points == [] or point != points[-1]: points.append(point) return points
def get_start(path_string): """returns start point (x, y) of a path string""" path = SVGPT.parse_path(path_string) start_xy = complex_to_xy(path.start) return start_xy
def get_length(path_string): """returns the length of a path string""" path = SVGPT.parse_path(path_string) return path.length()
def get_angle(path_string): """measures the angle in degrees (CCW) from the path positive X axis (0,0), (0,1)""" path = SVGPT.parse_path(path_string) vector = path.point(1) - path.point(0) angle = np.angle(vector, deg=True) return angle
def scale_path(path_string, scale): """scales a path string by a scale factor (float)""" path = SVGPT.parse_path(path_string) scaled_path = path.scaled(scale) new_path_string = scaled_path.d() return new_path_string