kopia lustrzana https://github.com/EmbroidePy/pyembroidery
Updating stitchification methods and visualizations.
-- Handle SVG files with any number of paths. -- Use a new method to compute intersections between line segments. It uses cross products, is robust to horizontal/vertical segments, and detects if there is no intersection. -- Remove consecutive duplicate points from the extracted SVG control points, which seems common and does not always start with the first point. -- Ignore any empty paths extracted from the SVG file. -- Add stitches in an order that preserves the original SVG path directionality. -- For each segment in an SVG path, compute intersections with each other path then add stitches in each inter-intersection section according to the desired pitch (avoiding the intersections themselves). -- Optionally animate the stitches to visualize the embroidery sequence. -- Allowing PyPlot to choose a good marker size when visualizing the stitch pattern. -- Testing with SVGs that have 1, 2, or 3 paths, and an SVG that has all 8 paths.pull/140/head
rodzic
2d5ea5305e
commit
239f503572
153
generateDST.py
153
generateDST.py
|
@ -1,68 +1,113 @@
|
|||
from generateDST_utils import *
|
||||
import argparse
|
||||
import time
|
||||
import os
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Specify the input SVG file.
|
||||
# design_dir = '.'
|
||||
design_dir = os.path.join(script_dir, 'SVG')
|
||||
|
||||
# svg_filename = 'embroidery_sensor_matrix.svg'
|
||||
# svg_filename = 'test_pattern_1path.svg'
|
||||
# svg_filename = 'test_pattern_2paths.svg'
|
||||
# svg_filename = 'test_pattern_2paths_skewed.svg'
|
||||
# svg_filename = 'test_pattern_3paths.svg'
|
||||
svg_filename = 'test_pattern_allPaths.svg'
|
||||
|
||||
svg_filepath = os.path.realpath(os.path.join(design_dir, svg_filename))
|
||||
|
||||
# Specify where to save the output DST file.
|
||||
output_dir = os.path.realpath(os.path.join(script_dir, 'DST'))
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
dst_filename = svg_filename.replace('.svg', '.dst')
|
||||
|
||||
# Parse command line arguments.
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--input_path', type=str, default='./SVG/Asset 2palm.svg', help='input file path')
|
||||
# parser.add_argument('--input_path', type=str, default='./Asset 1sensor1_exportNotResponsive.svg', help='input file path')
|
||||
parser.add_argument('--output_path', type=str, default='./output/', help='output path')
|
||||
parser.add_argument('--output_name', type=str, default='Asset 2palm.dst', help='output file name')
|
||||
parser.add_argument('--pitch', type=int, default=40, help='pitch')
|
||||
parser.add_argument('--viz', type=bool, default=True, help='viz')
|
||||
parser.add_argument('--scale', type=float, default=10/4.2067, help='design scale')
|
||||
parser.add_argument('--input_path', type=str, default=svg_filepath, help='input file path')
|
||||
parser.add_argument('--output_dir', type=str, default=output_dir, help='output directory')
|
||||
parser.add_argument('--output_filename', type=str, default=dst_filename, help='output file name')
|
||||
parser.add_argument('--pitch', type=int, default=30, help='pitch')
|
||||
parser.add_argument('--viz_svg', type=bool, default=False, help='viz')
|
||||
parser.add_argument('--viz_dst', type=bool, default=True, help='viz')
|
||||
parser.add_argument('--viz_dst_sim', type=bool, default=False, help='viz')
|
||||
# NOTE: the embroidery machine's units are 0.1 mm, so a scale factor of 10 is useful.
|
||||
parser.add_argument('--scale', type=float, default=10, help='design scale')
|
||||
args = parser.parse_args()
|
||||
dst_filepath = os.path.join(args.output_dir, args.output_filename)
|
||||
|
||||
pattern = pyembroidery.EmbPattern()
|
||||
|
||||
# Read control points from the SVG file.
|
||||
print ('Extracting control points from %s' % (args.input_path))
|
||||
start_time_s = time.time()
|
||||
x_all, y_all = extract_pt_from_svg(args.input_path, args.scale,
|
||||
viz=args.viz, target_units=None)
|
||||
print ('control points extracted from svg')
|
||||
viz=args.viz_svg, target_units='mm',
|
||||
remove_duplicates=True)
|
||||
num_paths = len(x_all)
|
||||
print('Extracted control points in %0.2fs' % (time.time() - start_time_s))
|
||||
print()
|
||||
|
||||
'''single path'''
|
||||
if len(x_all) == 1:
|
||||
x_pos_all = []
|
||||
y_pos_all = []
|
||||
for i in range(len(x_all[0])-1):
|
||||
if i%2 == 0:
|
||||
x, y = draw_line(pattern, [x_all[0][i], y_all[0][i]], [x_all[0][i+1], y_all[0][i+1]],
|
||||
args.pitch, endpoint=False)
|
||||
x_pos_all += x
|
||||
y_pos_all += y
|
||||
pyembroidery.write_dst(pattern, args.output_path + args.output_name)
|
||||
print ('single path, pitch=', args.pitch, ', ', args.output_path + args.output_name, 'saved')
|
||||
if args.viz:
|
||||
marker_size = (max(x_pos_all)-min(x_pos_all))
|
||||
plt.scatter(x_pos_all, y_pos_all, c='green', s=marker_size)
|
||||
for i in range(len(x_pos_all)-1):
|
||||
plt.plot([x_pos_all[i], x_pos_all[i+1]], [y_pos_all[i], y_pos_all[i+1]])
|
||||
plt.show()
|
||||
# Start an embroidery pattern.
|
||||
# x_pos_all and y_pos_all will represent the points that are added to the pattern.
|
||||
pattern = pyembroidery.EmbPattern()
|
||||
x_stitch_all = [[] for p in range(num_paths)]
|
||||
y_stitch_all = [[] for p in range(num_paths)]
|
||||
|
||||
# Add points to represent each path in the SVG file.
|
||||
for path_index in range(num_paths):
|
||||
print ('Stitching path %d/%d' % (path_index+1, num_paths))
|
||||
start_time_s = time.time()
|
||||
# Switch to a new thread for each new path.
|
||||
if path_index > 0:
|
||||
pattern.color_change()
|
||||
# Add stitch points for this path.
|
||||
x, y = stitch_path(pattern, x_all, y_all,
|
||||
path_index=path_index, pitch=args.pitch)
|
||||
x_stitch_all[path_index] += x
|
||||
y_stitch_all[path_index] += y
|
||||
print('Stitched path %d in %0.2fs' % (path_index+1, time.time() - start_time_s))
|
||||
print()
|
||||
|
||||
'''two crossing paths'''
|
||||
if len(x_all) == 2:
|
||||
x_pos_all = [[],[]]
|
||||
y_pos_all = [[],[]]
|
||||
print (len(x_all[0]), len(x_all[1]))
|
||||
x, y = drawline_halfway_1(pattern, x_all, y_all, pitch=args.pitch)
|
||||
x_pos_all[0] += x
|
||||
y_pos_all[0] += y
|
||||
pattern.color_change()
|
||||
x, y = drawline_halfway_2(pattern, x_all, y_all, pitch=args.pitch)
|
||||
x_pos_all[1] += x
|
||||
y_pos_all[1] += y
|
||||
# Write the path to a DST file.
|
||||
print('Writing the stitch pattern to %s' % dst_filepath)
|
||||
start_time_s = time.time()
|
||||
pyembroidery.write_dst(pattern, dst_filepath)
|
||||
print('Wrote the DST file in %0.2fs' % (time.time() - start_time_s))
|
||||
|
||||
pyembroidery.write_dst(pattern, args.output_path + args.output_name)
|
||||
print ('two paths, pitch=', args.pitch, ', ', args.output_path + args.output_name, 'saved')
|
||||
|
||||
if args.viz:
|
||||
# marker_size = (max(x_pos_all[0])-min(x_pos_all[0]))
|
||||
# print (x_pos_all[0])
|
||||
plt.scatter(x_pos_all[0], y_pos_all[0], c='green', s=10)
|
||||
for i in range(len(x_pos_all[0])-1):
|
||||
plt.plot([x_pos_all[0][i], x_pos_all[0][i+1]], [y_pos_all[0][i], y_pos_all[0][i+1]])
|
||||
plt.scatter(x_pos_all[1], y_pos_all[1], c='blue', s=10)
|
||||
for i in range(len(x_pos_all[1])-1):
|
||||
plt.plot([x_pos_all[1][i], x_pos_all[1][i+1]], [y_pos_all[1][i], y_pos_all[1][i+1]])
|
||||
plt.show()
|
||||
# Visualize the stitch pattern if desired.
|
||||
if args.viz_dst:
|
||||
# Step through the stitches to more easily visualize the order if desired.
|
||||
if args.viz_dst_sim:
|
||||
for path_index in range(num_paths):
|
||||
sim_handles = []
|
||||
# Plot the path without markers.
|
||||
sim_handles.append(plt.plot(x_stitch_all[path_index], y_stitch_all[path_index], '-'))
|
||||
# Show the real aspect ratio of the design.
|
||||
plt.gca().set_aspect('equal')
|
||||
# Show markers in stitch order.
|
||||
for i in range(len(x_stitch_all[path_index])):
|
||||
sim_handles.append(plt.plot(x_stitch_all[path_index][i],
|
||||
y_stitch_all[path_index][i], '.'))
|
||||
# Show the plot and wait for a specified timeout (or for the user to click the window).
|
||||
plt.waitforbuttonpress(0.01)
|
||||
# Remove the simulated stitches.
|
||||
for sim_handle in sim_handles:
|
||||
for h in sim_handle:
|
||||
h.remove()
|
||||
# Plot the final design with stitch markers, all in a single color.
|
||||
plt.plot(x_stitch_all[path_index], y_stitch_all[path_index], '.-')
|
||||
# Indicate the starting stitch.
|
||||
plt.plot(x_stitch_all[path_index][0], y_stitch_all[path_index][0], 'k.')
|
||||
# Plot the design, with a single color per path.
|
||||
plt.clf()
|
||||
for path_index in range(num_paths):
|
||||
# Plot the final design with stitch markers, all in a single color.
|
||||
plt.plot(x_stitch_all[path_index], y_stitch_all[path_index], '.-')
|
||||
# Indicate the starting stitch.
|
||||
plt.plot(x_stitch_all[path_index][0], y_stitch_all[path_index][0], 'k.')
|
||||
# Show the real aspect ratio of the design.
|
||||
plt.gca().set_aspect('equal')
|
||||
# Show the plot and wait for the user to close the window.
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,90 @@ import math
|
|||
import cv2
|
||||
import re
|
||||
|
||||
# Compute the intersection between two line segments.
|
||||
# @param endpoints_1 and endpoints_2 are each 2x2 numpy arrays of the form [[x1, y1], [x2, y2]]
|
||||
# @return The intersection point as a 2-element numpy array.
|
||||
# If the two segments are colinear and overlapping, will return the midpoint of the intersection area.
|
||||
# Returns None if the segments do not overlap.
|
||||
# If return_scenario_info is True, returns a tuple (intersection_point, info)
|
||||
# where info is a dict with keys 'parallel' and 'colinear'
|
||||
# The code is based on https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
|
||||
def get_segments_intersection(endpoints_1, endpoints_2, return_scenario_info=False, debug=False):
|
||||
if isinstance(endpoints_1, (list, tuple)):
|
||||
endpoints_1 = np.array(endpoints_1)
|
||||
if isinstance(endpoints_2, (list, tuple)):
|
||||
endpoints_2 = np.array(endpoints_2)
|
||||
p = endpoints_1[0,:]
|
||||
r = endpoints_1[1,:] - endpoints_1[0,:]
|
||||
q = endpoints_2[0,:]
|
||||
s = endpoints_2[1,:] - endpoints_2[0,:]
|
||||
if debug:
|
||||
print('p', p, 'r', r)
|
||||
print('q', q, 's', s)
|
||||
|
||||
scenario_info = {
|
||||
'colinear': False,
|
||||
'parallel': False
|
||||
}
|
||||
if np.cross(r, s) == 0 and np.cross((q - p), r) == 0:
|
||||
scenario_info['colinear'] = True
|
||||
# The segments are colinear, so check if they are overlapping or disjoint.
|
||||
# Frist, represent each endpoint in terms of the other segment's reference frame.
|
||||
q_in_pFrame_0 = np.dot((q - p), r)/np.dot(r, r)
|
||||
q_in_pFrame_1 = np.dot((q + s - p), r)/np.dot(r, r)
|
||||
p_in_qFrame_0 = np.dot((p - q), s)/np.dot(s, s)
|
||||
p_in_qFrame_1 = np.dot((p + r - q), s)/np.dot(s, s)
|
||||
# Take the ones that are on the other segment (the ratio along the segment is in [0,1]).
|
||||
intersection_points = []
|
||||
if q_in_pFrame_0 >= 0 and q_in_pFrame_0 <= 1:
|
||||
intersection_points.append(p + r*q_in_pFrame_0)
|
||||
if q_in_pFrame_1 >= 0 and q_in_pFrame_1 <= 1:
|
||||
intersection_points.append(p + r*q_in_pFrame_1)
|
||||
if p_in_qFrame_0 >= 0 and p_in_qFrame_0 <= 1:
|
||||
intersection_points.append(q + s*p_in_qFrame_0)
|
||||
if p_in_qFrame_1 >= 0 and p_in_qFrame_1 <= 1:
|
||||
intersection_points.append(q + s*p_in_qFrame_1)
|
||||
# There should be 2 endpoints that are on the other segment if the segments overlap, and 0 otherwise.
|
||||
if len(intersection_points) == 0:
|
||||
intersection_point = None
|
||||
else:
|
||||
# Use the midpoint between the two intersecting endpoints.
|
||||
intersection_points = np.array(intersection_points)
|
||||
intersection_point = np.mean(intersection_points, axis=0)
|
||||
elif np.cross(r, s) == 0 and np.cross((q-p), r) != 0:
|
||||
# The lines are parallel and non-intersecting.
|
||||
scenario_info['parallel'] = True
|
||||
intersection_point = None
|
||||
else:
|
||||
t = np.cross((q - p), s) / np.cross(r, s)
|
||||
u = np.cross((q - p), r) / np.cross(r, s)
|
||||
if np.cross(r, s) != 0 and t >= 0 and t <= 1 and u >= 0 and u <= 1:
|
||||
# There was an intersection.
|
||||
intersection_point = p + t*r
|
||||
else:
|
||||
# The segments do not intersect (and they are not parallel).
|
||||
intersection_point = None
|
||||
|
||||
# Print/plot debugging information if desired.
|
||||
if debug:
|
||||
print('scenario_info:', scenario_info)
|
||||
print('intersection_point:', intersection_point)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
plt.clf()
|
||||
plt.plot(endpoints_1[:,0], endpoints_1[:,1], '.-')
|
||||
plt.plot(endpoints_2[:,0], endpoints_2[:,1], '.-')
|
||||
if intersection_point is not None:
|
||||
plt.plot(intersection_point[0], intersection_point[1], '.', markersize=25)
|
||||
plt.grid(True, color='lightgray')
|
||||
plt.waitforbuttonpress(0)
|
||||
|
||||
# All done!
|
||||
if return_scenario_info:
|
||||
return (intersection_point, scenario_info)
|
||||
else:
|
||||
return intersection_point
|
||||
|
||||
# Parse a size string such as "10mm" into its value, and convert it to the target units.
|
||||
# Adapted from https://github.com/SebKuzminsky/svg2gcode/blob/94f28c1877c721c66cd90a38750f78d8031ac85a/gcoder.py#L238
|
||||
def _parse_svg_size_string(size_str, target_units='mm'):
|
||||
|
@ -52,13 +136,22 @@ def _parse_svg_size_string(size_str, target_units='mm'):
|
|||
|
||||
return val*scale
|
||||
|
||||
def extract_pt_from_svg(file, scale, viz, target_units):
|
||||
# Extract points stored in an SVG file.
|
||||
# @param target_units If provided, will look for scale informaion in the SVG metadata
|
||||
# and, if found, will scale the points to be in the target units.
|
||||
# @param scale A scale factor to apply to the design after converting to target units.
|
||||
# @param remove_duplicates Whether to remove successive duplicate points.
|
||||
# Otherwise, it seems the SVG can have back-to-back duplicates
|
||||
# (that may or may not start at the first point).
|
||||
# @return (x_all, y_all) where x_all and y_all are lists.
|
||||
# Each entry represents a path in the SVG, and contains a list of coordinates.
|
||||
# So point i of path p is at (x_all[p][i], y_all[p][i]).
|
||||
def extract_pt_from_svg(file, scale, viz, target_units=None, remove_duplicates=True):
|
||||
# Read paths from the SVG file.
|
||||
paths, attributes = svg2paths(file)
|
||||
# print (paths) # 0:path, 1:sec, 2:point
|
||||
|
||||
# Convert the paths into lists of coordinates.
|
||||
x_all = []
|
||||
y_all = []
|
||||
|
||||
for i in range(len(paths)):
|
||||
x = []
|
||||
y = []
|
||||
|
@ -67,17 +160,20 @@ def extract_pt_from_svg(file, scale, viz, target_units):
|
|||
# print (paths[i][j][k])
|
||||
x.append(paths[i][j][k].real)
|
||||
y.append(-paths[i][j][k].imag)
|
||||
|
||||
# Ignore empty paths.
|
||||
if len(x) == 0:
|
||||
continue
|
||||
# Append the new path.
|
||||
x_all.append(x)
|
||||
y_all.append(y)
|
||||
|
||||
# Visualize the control points if desired.
|
||||
if viz:
|
||||
plt.scatter(x,y, s=100, c='red')
|
||||
plt.scatter(x,y, s=10, c='red')
|
||||
plt.gca().set_aspect('equal')
|
||||
plt.show()
|
||||
|
||||
if target_units != None:
|
||||
|
||||
# Scale to get the desired units.
|
||||
# Scale to specified units if desired.
|
||||
if target_units is not None:
|
||||
doc = svgpathtools.Document(file)
|
||||
svg_attributes = doc.root.attrib
|
||||
if 'width' not in svg_attributes or 'height' not in svg_attributes:
|
||||
|
@ -106,195 +202,141 @@ def extract_pt_from_svg(file, scale, viz, target_units):
|
|||
# plt.figure()
|
||||
# plt.plot(x_all[0], y_all[0])
|
||||
# plt.show()
|
||||
|
||||
|
||||
# Apply any user-specified scaling.
|
||||
for (i, points) in enumerate(x_all):
|
||||
x_all[i] = [point*scale for point in points]
|
||||
for (i, points) in enumerate(y_all):
|
||||
y_all[i] = [point*scale for point in points]
|
||||
|
||||
# Remove successive duplicate points if desired.
|
||||
if remove_duplicates:
|
||||
x_all_unique = []
|
||||
y_all_unique = []
|
||||
for path_index in range(len(x_all)):
|
||||
x_all_unique.append([])
|
||||
y_all_unique.append([])
|
||||
# Add the first point.
|
||||
x_all_unique[path_index].append(x_all[path_index][0])
|
||||
y_all_unique[path_index].append(y_all[path_index][0])
|
||||
# Add points that do not duplicate their previous point.
|
||||
for point_index in range(1, len(x_all[path_index])):
|
||||
x = x_all[path_index][point_index]
|
||||
x_prev = x_all[path_index][point_index - 1]
|
||||
y = y_all[path_index][point_index]
|
||||
y_prev = y_all[path_index][point_index - 1]
|
||||
if not (x == x_prev and y == y_prev):
|
||||
x_all_unique[path_index].append(x)
|
||||
y_all_unique[path_index].append(y)
|
||||
x_all = x_all_unique
|
||||
y_all = y_all_unique
|
||||
|
||||
# Return the control points!
|
||||
return x_all, y_all
|
||||
|
||||
|
||||
def draw_line(pattern, st, ed, pitch, endpoint):
|
||||
x_pos = []
|
||||
y_pos = []
|
||||
x_dis = ed[0] - st[0]
|
||||
y_dis = ed[1] - st[1]
|
||||
dis = np.sqrt(x_dis**2 + y_dis**2)
|
||||
|
||||
sec = dis//pitch
|
||||
|
||||
if sec == 0:
|
||||
sec = 1
|
||||
|
||||
x = np.linspace(st[0], ed[0], int(sec)+1, endpoint=endpoint)
|
||||
y = np.linspace(st[1], ed[1], int(sec)+1, endpoint=endpoint)
|
||||
|
||||
# print (len(x))
|
||||
for i in range(len(x)):
|
||||
pattern.add_stitch_absolute(pyembroidery.STITCH, x[i], y[i])
|
||||
|
||||
x_pos.append(x[i])
|
||||
y_pos.append(y[i])
|
||||
|
||||
return x_pos, y_pos
|
||||
|
||||
def draw_line_mid(pattern, st, ed, pitch):
|
||||
x_pos = []
|
||||
y_pos = []
|
||||
x_dis = ed[0] - st[0]
|
||||
y_dis = ed[1] - st[1]
|
||||
dis = np.sqrt(x_dis**2 + y_dis**2)
|
||||
|
||||
|
||||
if dis <= 3 * pitch:
|
||||
x_mid = (st[0] + ed[0])/2
|
||||
y_mid = (st[1] + ed[1])/2
|
||||
pattern.add_stitch_absolute(pyembroidery.STITCH, x_mid, y_mid)
|
||||
return [x_mid], [y_mid]
|
||||
|
||||
else:
|
||||
sec = dis//pitch
|
||||
x = np.linspace(st[0], ed[0], int(sec), endpoint=True)
|
||||
y = np.linspace(st[1], ed[1], int(sec), endpoint=True)
|
||||
|
||||
x = x[1:-1]
|
||||
y = y[1:-1]
|
||||
|
||||
# print (len(x))
|
||||
for i in range(len(x)):
|
||||
pattern.add_stitch_absolute(pyembroidery.STITCH, x[i], y[i])
|
||||
|
||||
x_pos.append(x[i])
|
||||
y_pos.append(y[i])
|
||||
|
||||
return x_pos, y_pos
|
||||
|
||||
|
||||
def line_intersection(x1, y1, x2, y2, x3, y3, x4, y4):
|
||||
"""find the intersection of line segments A=(x1,y1)/(x2,y2) and
|
||||
B=(x3,y3)/(x4,y4). Returns a point or None"""
|
||||
denom = ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
|
||||
if denom==0: return None
|
||||
px = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
|
||||
py = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
|
||||
if (px - x1) * (px - x2) < 0 and (py - y1) * (py - y2) < 0 \
|
||||
and (px - x3) * (px - x4) < 0 and (py - y3) * (py - y4) < 0:
|
||||
return [px, py]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def drawline_halfway_1(pattern, x_all, y_all, pitch):
|
||||
pos_x = []
|
||||
pos_y = []
|
||||
for i in range(len(x_all[0])//2):
|
||||
pt_x = []
|
||||
pt_y = []
|
||||
pt_x.append(x_all[0][2*i])
|
||||
pt_y.append(y_all[0][2*i])
|
||||
|
||||
for j in range(len(x_all[1])//2):
|
||||
int_pt = line_intersection(x_all[0][2*i], y_all[0][2*i], x_all[0][2*i+1], y_all[0][2*i+1],
|
||||
x_all[1][2*j], y_all[1][2*j], x_all[1][2*j+1], y_all[1][2*j+1])
|
||||
|
||||
if int_pt:
|
||||
pt_x.append(int_pt[0])
|
||||
pt_y.append(int_pt[1])
|
||||
|
||||
pt_x.append(x_all[0][2*i+1])
|
||||
pt_y.append(y_all[0][2*i+1])
|
||||
|
||||
|
||||
# print ('compare', x_all[1][2*i], x_all[1][2*i+1], pt_x[-2], pt_x[1])
|
||||
if len(pt_x) > 3:
|
||||
if (x_all[0][2*i] > x_all[0][2*i+1] and pt_x[-2] > pt_x[1]) or (x_all[0][2*i] < x_all[0][2*i+1] and pt_x[-2] < pt_x[1]):
|
||||
temp_x = pt_x[1:-1]
|
||||
x = temp_x[::-1]
|
||||
temp_y = pt_y[1:-1]
|
||||
y = temp_y[::-1]
|
||||
pt_x[1:-1] = x
|
||||
pt_y[1:-1] = y
|
||||
|
||||
|
||||
# for l in range(len(pt_x)):
|
||||
# # print (pt_x[l], pt_y[l])
|
||||
# plt.scatter(pt_x[:l+1], pt_y[:l+1], c='red', s=20)
|
||||
# # plt.ylim(-400, 0)
|
||||
# # plt.xlim(-100, 600)
|
||||
# # plt.show()
|
||||
|
||||
|
||||
if len(pt_x) <= 1:
|
||||
raise Exception("error!")
|
||||
elif len(pt_x) == 2:
|
||||
for l in range(len(pt_x)-1):
|
||||
x, y = draw_line(pattern, [pt_x[l], pt_y[l]], [pt_x[l+1], pt_y[l+1]], pitch, True)
|
||||
pos_x += x
|
||||
pos_y += y
|
||||
# Create a sequence of stitches for the specified path of the provided SVG control points.
|
||||
# For each line segment, will compute the intersections with all other SVG paths.
|
||||
# Then for each inter-intersection section of the segment, will add stitches
|
||||
# according to the specified pitch (avoiding the intersections themselves).
|
||||
# Will place stitches for each segment according to the directionality of the original SVG path.
|
||||
# Will also ensure that stitches are placed at the SVG control points.
|
||||
def stitch_path(pattern, x_all, y_all, path_index, pitch, print_debug=False):
|
||||
x_stitch = []
|
||||
y_stitch = []
|
||||
num_paths = len(x_all)
|
||||
# Iterate through each of the SVG control points.
|
||||
num_points = len(x_all[path_index])
|
||||
for point_index in range(num_points-1):
|
||||
# Will create a list of notable points that define the line segment starting at the current point.
|
||||
segment_points_x = np.array([])
|
||||
segment_points_y = np.array([])
|
||||
|
||||
# Add the current control point as the starting point.
|
||||
segment_points_x = np.append(segment_points_x, x_all[path_index][point_index])
|
||||
segment_points_y = np.append(segment_points_y, y_all[path_index][point_index])
|
||||
# Add any intersection points between the segment from this control point to the next one,
|
||||
# and any segment of any other paths in the SVG.
|
||||
for other_path_index in range(num_paths):
|
||||
if other_path_index == path_index:
|
||||
continue
|
||||
num_other_points = len(x_all[other_path_index])
|
||||
for other_point_index in range(num_other_points-1):
|
||||
# if print_debug:
|
||||
# print('intersecting (%0.2f, %0.2f)-(%0.2f, %0.2f) with (%0.2f, %0.2f)-(%0.2f, %0.2f)' %
|
||||
# (x_all[path_index][point_index], y_all[path_index][point_index],
|
||||
# x_all[path_index][point_index+1], y_all[path_index][point_index+1],
|
||||
# x_all[other_path_index][other_point_index], y_all[other_path_index][other_point_index],
|
||||
# x_all[other_path_index][other_point_index+1], y_all[other_path_index][other_point_index+1]))
|
||||
intersection = get_segments_intersection(
|
||||
[[x_all[path_index][point_index], y_all[path_index][point_index]],
|
||||
[x_all[path_index][point_index+1], y_all[path_index][point_index+1]]],
|
||||
[[x_all[other_path_index][other_point_index], y_all[other_path_index][other_point_index]],
|
||||
[x_all[other_path_index][other_point_index+1], y_all[other_path_index][other_point_index+1]]],
|
||||
)
|
||||
if intersection is not None:
|
||||
segment_points_x = np.append(segment_points_x, intersection[0])
|
||||
segment_points_y = np.append(segment_points_y, intersection[1])
|
||||
# Add the next control point as the ending point.
|
||||
segment_points_x = np.append(segment_points_x, x_all[path_index][point_index+1])
|
||||
segment_points_y = np.append(segment_points_y, y_all[path_index][point_index+1])
|
||||
|
||||
# Sort the segment points by their x then y coordinate,
|
||||
# using the same directionality as the original SVG path.
|
||||
if x_all[path_index][point_index+1] > x_all[path_index][point_index]:
|
||||
x_sort_direction = 1
|
||||
else:
|
||||
# x, y = draw_line(pattern, [pt_x[0], pt_y[0]], [pt_x[1], pt_y[1]], pitch, False)
|
||||
# print ('x0',len(x))
|
||||
# pos_x += x
|
||||
# pos_y += y
|
||||
for l in range(len(pt_x)-1):
|
||||
x, y = draw_line_mid(pattern, [pt_x[l], pt_y[l]], [pt_x[l+1], pt_y[l+1]], pitch)
|
||||
pos_x += x
|
||||
pos_y += y
|
||||
|
||||
# x, y = draw_line(pattern, [pt_x[-2], pt_y[-2]], [pt_x[-1], pt_y[-1]], pitch, False)
|
||||
# # print ('x1', len(x))
|
||||
# pos_x += x
|
||||
# pos_y += y
|
||||
# print (len(pos_x))
|
||||
return pos_x, pos_y
|
||||
|
||||
def drawline_halfway_2(pattern, x_all, y_all, pitch):
|
||||
pos_x = []
|
||||
pos_y = []
|
||||
for i in range(len(x_all[1])//2):
|
||||
pt_x = []
|
||||
pt_y = []
|
||||
pt_x.append(x_all[1][2*i])
|
||||
pt_y.append(y_all[1][2*i])
|
||||
|
||||
for j in range(len(x_all[0])//2):
|
||||
int_pt = line_intersection(x_all[1][2*i], y_all[1][2*i], x_all[1][2*i+1], y_all[1][2*i+1],
|
||||
x_all[0][2*j], y_all[0][2*j], x_all[0][2*j+1], y_all[0][2*j+1])
|
||||
|
||||
if int_pt:
|
||||
pt_x.append(int_pt[0])
|
||||
pt_y.append(int_pt[1])
|
||||
|
||||
pt_x.append(x_all[1][2*i+1])
|
||||
pt_y.append(y_all[1][2*i+1])
|
||||
|
||||
if len(pt_x) > 3:
|
||||
if (y_all[1][2*i] > y_all[1][2*i+1] and pt_y[-2] > pt_y[1]) or (y_all[1][2*i] < y_all[1][2*i+1] and pt_y[-2] < pt_y[1]):
|
||||
temp_x = pt_x[1:-1]
|
||||
x = temp_x[::-1]
|
||||
temp_y = pt_y[1:-1]
|
||||
y = temp_y[::-1]
|
||||
pt_x[1:-1] = x
|
||||
pt_y[1:-1] = y
|
||||
|
||||
|
||||
if len(pt_x) <= 1:
|
||||
raise Exception("error!")
|
||||
elif len(pt_x) == 2:
|
||||
for l in range(len(pt_x)-1):
|
||||
x, y = draw_line(pattern, [pt_x[l], pt_y[l]], [pt_x[l+1], pt_y[l+1]], pitch, True)
|
||||
pos_x += x
|
||||
pos_y += y
|
||||
x_sort_direction = -1
|
||||
if y_all[path_index][point_index+1] > y_all[path_index][point_index]:
|
||||
y_sort_direction = 1
|
||||
else:
|
||||
# x, y = draw_line(pattern, [pt_x[0], pt_y[0]], [pt_x[1], pt_y[1]], pitch, False)
|
||||
# pos_x += x
|
||||
# pos_y += y
|
||||
for l in range(len(pt_x)-1):
|
||||
x, y = draw_line_mid(pattern, [pt_x[l], pt_y[l]], [pt_x[l+1], pt_y[l+1]], pitch)
|
||||
pos_x += x
|
||||
pos_y += y
|
||||
# x, y = draw_line(pattern, [pt_x[-2], pt_y[-2]], [pt_x[-1], pt_y[-1]], pitch, True)
|
||||
# pos_x += x
|
||||
# pos_y += y
|
||||
return pos_x, pos_y
|
||||
y_sort_direction = -1
|
||||
sorted_indexes = np.lexsort((y_sort_direction*np.array(segment_points_y),
|
||||
x_sort_direction*np.array(segment_points_x)))
|
||||
segment_points_x = segment_points_x[sorted_indexes]
|
||||
segment_points_y = segment_points_y[sorted_indexes]
|
||||
|
||||
# plt.figure()
|
||||
# plt.plot(pt_x, pt_y, '.-')
|
||||
# plt.title('path %d point %d' % (path_index, i))
|
||||
# plt.show()
|
||||
|
||||
# Add a stitch at the first point (the control point).
|
||||
x_stitch.append(x_all[path_index][point_index])
|
||||
y_stitch.append(y_all[path_index][point_index])
|
||||
# For each section of the line segment as divided by the intersection points,
|
||||
# add however many stitches will fit according to the specified pitch.
|
||||
for start_point_index in range(len(segment_points_x)-1):
|
||||
start_point = np.array([segment_points_x[start_point_index], segment_points_y[start_point_index]])
|
||||
end_point = np.array([segment_points_x[start_point_index+1], segment_points_y[start_point_index+1]])
|
||||
distance = np.linalg.norm(end_point - start_point)
|
||||
# Compute the number of stitches that can fit.
|
||||
# Will compute the number of inter-stitch gaps that can fit, which is one more
|
||||
# than the number of stitches since there will also be the start/end stitch.
|
||||
num_stitches = np.floor(distance/pitch).astype(int)-1
|
||||
# Put at least one stitch between each intersection pair.
|
||||
num_stitches = max(1, num_stitches)
|
||||
# Then compute the ratio along the segment that each stitch will be.
|
||||
stitch_position_ratios = np.arange(0, 1, 1/(num_stitches+1))[1:]
|
||||
# Finally, compute the actual stitch positions.
|
||||
for stitch_index in range(num_stitches):
|
||||
stitch_position = start_point + (end_point-start_point)*(stitch_position_ratios[stitch_index])
|
||||
x_stitch.append(stitch_position[0])
|
||||
y_stitch.append(stitch_position[1])
|
||||
if print_debug:
|
||||
print('path_index',path_index)
|
||||
print('point_index',point_index)
|
||||
print('start_point_index',start_point_index)
|
||||
print('start_point',start_point)
|
||||
print('end_point',end_point)
|
||||
print('distance', distance)
|
||||
print('num_stitches', num_stitches)
|
||||
print('stitch_position_ratios',stitch_position_ratios)
|
||||
print()
|
||||
# Add a stitch at the next point (the control point), if it's the last point in the path.
|
||||
# If it's not the last point, then the next iteration of the loop will add it as the starting point.
|
||||
if point_index == (num_points-1)-1:
|
||||
x_stitch.append(x_all[path_index][point_index+1])
|
||||
y_stitch.append(y_all[path_index][point_index+1])
|
||||
|
||||
return x_stitch, y_stitch
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue