Stitching of arbitrary curves and line segments

Next TODO: stitching of composite grids as well as rework of combine grids algorithm
main
Howard DaCosta 2022-03-08 16:37:17 -05:00
rodzic 72a932b325
commit 0a08c8d4a4
7 zmienionych plików z 234 dodań i 95 usunięć

BIN
.DS_Store vendored

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,27 +1,8 @@
from operator import is_
from inkex.deprecated import INKEX_DIR
from networkx.algorithms.graphical import is_graphical
from networkx.algorithms.operators.binary import union
import sys
from base64 import b64decode
from argparse import ArgumentParser, REMAINDER
import appdirs
from argparse import ArgumentParser
import inkex
from inkex import Line, Rectangle, Path, Polyline, PathElement
import wx
import wx.adv
from inkex import Polyline, PathElement
from lxml import etree
class CombineGridsFrame(wx.Frame):
DEFAULT_FONT = "small_font"
def __init__(self, shape1, shape2, svg, *args, **kwargs):
if sys.platform.startswith('win32'):
import locale
locale.setlocale(locale.LC_ALL, "C")
lc = wx.Locale()
lc.Init(wx.LANGUAGE_DEFAULT)
pass
class Connector():
'''
@ -54,19 +35,22 @@ class Connector():
class CombineGridsEffect(inkex.Effect):
def add_arguments(self, pars):
pars.add_argument("--alignment", type=bool, help="The type of connection to make")
pars.add_argument("--alignment", type=int, help="The type of connection to make")
def effect(self):
arg_parser = ArgumentParser()
self.add_arguments(arg_parser)
args,_ = arg_parser.parse_known_args()
combine_grids_worker = CombineGridsWorker(self.svg, args.alignment)
inkex.errormsg("what is alignment:{}".format(args.alignment))
is_horizontal_connection = True if args.alignment == 1 else False
combine_grids_worker = CombineGridsWorker(self.svg, is_horizontal_connection)
combine_grids_worker.run()
class CombineGridsWorker():
COMMANDS = ["combine_grids"]
def __init__(self, svg, is_horizontal_connection):
print("WORKER INIT")
self.svg = svg
self.is_horizontal_connection = is_horizontal_connection
self.wires = []
@ -306,6 +290,7 @@ class CombineGridsWorker():
etree.SubElement(self.svg.get_current_layer(), inkex.addNS('path','svg'), line_attribs)
def run(self):
connector_pins = []
connector_bbox = None
for elem in self.svg.get_selected():
@ -343,7 +328,6 @@ class Wire():
def get_num_wire_joins(self, is_horizontal):
'''
Determines how many wires were horizontally joined together to create the current wire object
The default is 1
'''
point_counter = 1

Wyświetl plik

@ -12,14 +12,12 @@ MIN_GRID_SPACING = inkex.units.convert_unit(2.5, "mm")
class CreateCustomGridEffect(inkex.Effect):
def add_arguments(self, pars):
inkex.errormsg("HELOOOOOOOOOOO")
pars.add_argument("--horizontal_wires", type=str,\
help="The number of desired horizontal wires")
pars.add_argument("--vertical_wires", type=str,\
help="The number of desired vertical wires")
def effect(self):
inkex.errormsg("HELOOOOOOOOOOO")
arg_parser = ArgumentParser()
self.add_arguments(arg_parser)
args, _ = arg_parser.parse_known_args()
@ -86,8 +84,6 @@ class CreateCustomGridWorker():
points = ['{}, {}'.format(p.x, p.y) for p in [self.lower_left, self.lower_right]]
self.create_path(points, "orange")
def run(self):
# self.draw_corners()

Wyświetl plik

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Create Grid</name>
<name>Create BBox Grid</name>
<id>org.inkscape.effect.create_grid</id>
<effect>
<effects-menu>

Wyświetl plik

@ -1,14 +1,6 @@
import json
import os
import sys
from base64 import b64decode
from argparse import ArgumentParser, REMAINDER
import appdirs
from argparse import ArgumentParser
import inkex
from inkex import Line, Rectangle, Path, Polyline
import wx
import wx.adv
from inkex import Rectangle
from lxml import etree
import pyembroidery
import matplotlib.pyplot as plt
@ -63,59 +55,29 @@ class CreateGridEffect(inkex.Effect):
shape_points = None
rectangle = None
for elem in self.svg.get_selected(): # PATH ELEMENT
units = "mm" if type(elem) == Rectangle else "px"
shape_points = [p for p in elem.path.end_points]
bbox = elem.bounding_box()
rectangle = BoundingBoxMetadata(inkex.units.convert_unit(bbox.width, 'mm'),
inkex.units.convert_unit(bbox.height, 'mm'),
inkex.units.convert_unit(bbox.top, 'mm'),
inkex.units.convert_unit(bbox.bottom, 'mm'),
inkex.units.convert_unit(bbox.left, 'mm'),
inkex.units.convert_unit(bbox.right, 'mm'))
inkex.errormsg("shape points, bbox:{} , {}".format(shape_points, bbox))
rectangle = BoundingBoxMetadata(inkex.units.convert_unit(bbox.width, units),
inkex.units.convert_unit(bbox.height, units),
inkex.units.convert_unit(bbox.top, units),
inkex.units.convert_unit(bbox.bottom, units),
inkex.units.convert_unit(bbox.left, units),
inkex.units.convert_unit(bbox.right, units))
create_grid_worker = CreateGridWorker(shape_points, rectangle, int(args.horizontal_wires), int(args.vertical_wires), self.svg, self.document)
create_grid_worker = CreateGridWorker(shape_points, rectangle, int(args.horizontal_wires), int(args.vertical_wires), self.svg)
create_grid_worker.run()
# parent = self.svg.get_current_layer()
# self.draw_SVG_line(10,10,0,0, parent)
def draw_SVG_line(self, x1, y1, x2, y2, parent):
color = "red"
line_attribs = {
'style' : "stroke: %s; stroke-width: 0.4; fill: none; stroke-dasharray:0.4,0.4" % color,
# 'd' : 'M 55.3977 90.881 H 114.748 V 80.9478 H 46.3977 V 78.0146 H 114.748 V 75.0815 H 46.3977 V 72.1483 H 114.748 V 69.2151 H 46.3977 V 66.2819 H 114.748 V 63.3488 H 46.3977 V 60.4156 H 114.748 V 57.4824 H 46.3977',
'points': 'M 0,0 9,9 5,5'
}
line = etree.SubElement(parent, inkex.addNS('path','svg'), line_attribs)
points = ['0,0', '10,10']
color = "red"
path_str = ' '.join(points)
poly = inkex.Polyline(attrib={
# 'id': "wire_segment",
'style':"stroke: %s; stroke-width: 0.4; fill: none; stroke-dasharray:0.4,0.4" % color,
'points': path_str,
})
inkex.errormsg(str(poly.get_path()))
line_attribs = {
'style' : "stroke: %s; stroke-width: 0.4; fill: none; stroke-dasharray:0.4,0.4" % color,
'd': str(poly.get_path())
}
etree.SubElement(self.svg.get_current_layer(), inkex.addNS('path','svg'), line_attribs)
# polyline(points,style=style)
class CreateGridWorker():
def __init__(self, shape_points, rectangle, num_horizontal_wires, num_vertical_wires, svg, document):
def __init__(self, shape_points, rectangle, num_horizontal_wires, num_vertical_wires, svg):
self.shape_points = shape_points
self.rectangle = rectangle
self.num_horizontal_wires = num_horizontal_wires
self.num_vertical_wires = num_vertical_wires
self.svg = svg
self.upper_left, self.upper_right,self.lower_left,self.lower_right = self.rectangle.get_rectangle_points()
self.document = document
def run(self):
@ -157,12 +119,12 @@ class CreateGridWorker():
if wire_count % 2 == 0:
points.append('{},{}'.format(self.rectangle.left - BBOX_SPACING, curr_point[1]))
points.append('{},{}'.format(self.rectangle.right, curr_point[1]))
# for p in connections:
# points.append('{},{}'.format(p.x, p.y))
else:
points.append('{},{}'.format(self.rectangle.right, curr_point[1]))
points.append('{},{}'.format(self.rectangle.left - BBOX_SPACING, curr_point[1]))
wire_count += 1
inkex.errormsg("coming to lay horsz points:{}".format(points))
return self.create_path(points, is_horizontal=True)
def lay_vertical_wires(self, vertical_wire_spacing):
@ -239,20 +201,12 @@ class MakeStitchesWorker():
intersection_points = sorted(intersection_points, key = lambda p: p[0])
point_idx = 0
# inkex.errormsg("num intersection points:{}".format(len(intersection_points)))
# inkex.errormsg("what are intersection points:{},{}".format(intersection_points, len(intersection_points)))
if p0.x < p1.x: #p0 is on the right
row_stitch_array.append([(p0.x + intersection_points[point_idx][0]) // 2, p0.y])
row_stitch_array.append([(p1.x + intersection_points[-1][0]) // 2, p1.y])
# pattern.add_stitch_absolute(pyembroidery.STITCH, (p0.x + intersection_points[point_idx][0]) // 2, p0.y)
# pattern.add_stitch_absolute(pyembroidery.STITCH, (p1.x + intersection_points[-1][0]) // 2, p1.y)
else:
row_stitch_array.append([(p0.x + intersection_points[-1][0]) // 2, p0.y])
row_stitch_array.append([(p1.x + intersection_points[0][0]) // 2, p1.y])
# pattern.add_stitch_absolute(pyembroidery.STITCH, (p0.x + intersection_points[-1][0]) // 2, p0.y)
# pattern.add_stitch_absolute(pyembroidery.STITCH, (p1.x + intersection_points[0][0]) // 2, p1.y)
inkex.errormsg("first and last endpoint x values: {} and {}".format(p0.x, p1.x))
inkex.errormsg("first and last x values:{} and {}".format(intersection_points[point_idx][0], intersection_points[-1][0]))
@ -261,7 +215,6 @@ class MakeStitchesWorker():
mid_x = (intersection_points[point_idx][0] + intersection_points[point_idx+1][0]) // 2
point_idx += 1
row_stitch_array.append([mid_x, p0.y])
# pattern.add_stitch_absolute(pyembroidery.STITCH, mid_x, p0.y)
# need to stitch wire continously from bottom left to top right, so row_stitch array is reversed
# depending on what side of the wire we started on for this iteration
@ -277,22 +230,18 @@ class MakeStitchesWorker():
for x, y in stitch_array:
pattern.add_stitch_absolute(pyembroidery.STITCH, x, y)
inkex.errormsg("where are my stitches:{}, num_stitches = {}".format(pattern.stitches, len(pattern.stitches)))
pyembroidery.write_pes(pattern, '/Users/hdacosta/Desktop/UROP/output/pattern.dst')
# sanity_check
# inkex.errormsg("num intersections:{}".format(len(intersection_points)))
self.visualize_stitches(pattern)
# self.visualize_stitches(pattern)
def visualize_stitches(self, pattern):
#visualize stitches
stitch_info = np.asarray(pattern.stitches)
#Extract info from np.array and convert to mm
x_coord = stitch_info[:,0]/10
inkex.errormsg("vis x coords:{}".format(x_coord))
y_coord = stitch_info[:,1]/10
num_of_stitches = len(x_coord)
inkex.errormsg("NUM OF STITCHES:{}".format(num_of_stitches))
#Plot the stitches
stitch_loc = plt.scatter(x_coord, y_coord, s = 1, c = 'black')
@ -309,6 +258,7 @@ class MakeStitchesWorker():
#show the plot
plt.show()
def make_vertical_stitches(self):
pass

Wyświetl plik

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Make Stitches</name>
<id>org.inkscape.effect.make_stitches</id>
<effect>
<effects-menu>
<submenu name="Sensor Grid Tools" />
</effects-menu>
</effect>
<param name="wire_type" type="optiongroup" appearance="radio" gui-text="Method">
<option value="0">Straight</option>
<option value="1">Curved</option>
</param> <script>
<command location="inx" interpreter="python">make_stitches.py</command>
</script>
</inkscape-extension>

Wyświetl plik

@ -0,0 +1,193 @@
import inkex
from inkex.paths import Path
import pyembroidery
from argparse import ArgumentParser
import numpy as np
from matplotlib import pyplot as plt
from lxml import etree
import math
from bezier import Curve
from scipy import interpolate
import simplepath
class MakeStitchesEffect(inkex.Effect):
def add_arguments(self, pars):
pars.add_argument("--wire_type", type=int)
def effect(self):
arg_parser = ArgumentParser()
self.add_arguments(arg_parser)
args,_ = arg_parser.parse_known_args()
wire = None
# add a case for multiple selections (grid stitching)
for elem in self.svg.get_selected():
wire = elem
# debugging for mapping out control and end points of a path
# poi = [p for p in wire.path.end_points]
# points = ['{},{}'.format(p.x,p.y) for p in poi]
# self.create_path(points, True)
# poi = [p for p in wire.path.control_points]
# points = ['{},{}'.format(p.x,p.y) for p in poi]
# self.create_path(points, False)
is_curve = True if args.wire_type == 1 else False
make_stitches_worker = MakeStitchesWorker(wire, is_curve)
make_stitches_worker.run()
def create_path(self, points, is_horizontal):
'''
Creates a wire segment path given all of the points sequentially
'''
color = "red" if is_horizontal else "blue"
path_str = ' '.join(points)
path = inkex.Polyline(attrib={
'id': "wire_segment",
'points': path_str,
})
line_attribs = {
'style' : "stroke: %s; stroke-width: 0.4; fill: none; stroke-dasharray:0.4,0.4" % color,
'd': str(path.get_path())
# 'points': 'M 0,0 9,9 5,5'
}
etree.SubElement(self.svg.get_current_layer(), inkex.addNS('path','svg'), line_attribs)
return path
class MakeStitchesWorker(inkex.Effect):
def __init__(self, wire, is_curve):
self.wire = wire
self.is_curve = is_curve
self.control_points = []
if self.is_curve:
self.control_points = [p for p in wire.path.control_points]
self.end_points = [p for p in wire.path.end_points]
self.wire_points = []
if self.is_curve:
points = []
path = simplepath.parsePath(wire.path)
for t, ele in path:
for i in range(0,len(ele)-1,2):
x = ele[i]
y = ele[i+1]
points.append([x,y])
self.wire_points = points
else:
self.wire_points = self.end_points
def stitch_curve(self):
'''
Bezier curves represented with 4 points
We can use them and a general parametric equation to generate stitch points
'''
stitch_points = []
nodes = np.asfortranarray([
# [p.x for p in self.wire_points],
# [p.y for p in self.wire_points]
[p[0] for p in self.wire_points],
[p[1] for p in self.wire_points]
]).astype('double')
curve = Curve.from_nodes(nodes, copy=True)
t = 0.01
point = curve.evaluate(t)
x = point[0][0]
y = point[1][0]
stitch_points.append([x,y])
for _ in range(99): # change this number to change pitch of stitches, may also convert to user input in the future
point = curve.evaluate(t)
x = point[0][0]
y = point[1][0]
stitch_points.append([x,y])
t += .01
return stitch_points
def compute_euclidean_distance(self, x1, y1, x2, y2):
return math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2)
def segment_line(self, line, num_points):
'''
Breaks line into num_points equal parts
returns array of points
line: A shapely.LineString object (interpolation along line can be done manually but this is easier)
'''
points = []
def parameterize_line(t):
x_t = line[0][0] + (line[1][0] - line[0][0]) * t
y_t = line[0][1] + (line[1][1] - line[0][1]) * t
return x_t, y_t
segment_length = 1 / (num_points + 1)
for i in range(1 ,num_points+2): # adjust from 0 to n+1 bc we cant put in 0 to the parameterized line equation
x, y = parameterize_line(i * segment_length)
points.append([x,y])
return points
def stitch_segment(self):
stitch_points = []
for i in range(len(self.wire_points) - 1):
p1 = self.wire_points[i]
p2 = self.wire_points[i+1]
stitch_points.append([p1.x, p1.y])
line = [[p1.x, p1.y], [p2.x, p2.y]]
num_points = 3 # could make this a user input somehow?
stitch_points.extend(self.segment_line(line, num_points))
return stitch_points
def make_stitches(self, stitch_points):
pattern = pyembroidery.EmbPattern()
for x, y in stitch_points:
pattern.add_stitch_absolute(pyembroidery.STITCH, x, y)
pyembroidery.write_pes(pattern, '/Users/hdacosta/Desktop/UROP/output/pattern1.dst')
self.visualize_stitches(pattern)
def visualize_stitches(self, pattern):
#visualize stitches
stitch_info = np.asarray(pattern.stitches)
#Extract info from np.array and convert to mm
x_coord = stitch_info[:,0]/10
y_coord = stitch_info[:,1]/10
num_of_stitches = len(x_coord)
#Plot the stitches
stitch_loc = plt.scatter(x_coord, y_coord, s = 1, c = 'black')
#Add label to every ith stitch
i = 0
while i <= num_of_stitches - 1:
plt.annotate(i, (x_coord[i], y_coord[i]))
i += 5
#label axis
plt.title("Stitch Vis")
plt.xlabel('X Coordinates (mm)')
plt.ylabel('Y Coordinates (mm)')
#show the plot
plt.show()
def run(self):
stitch_points = None
if self.is_curve:
stitch_points = self.stitch_curve()
else:
stitch_points = self.stitch_segment()
self.make_stitches(stitch_points)
if __name__ == '__main__':
MakeStitchesEffect().run()