Add object based min stitch length (#2792)

* add object based min stitch length (overwrites global)
* add object based minimum jump stitch (overwrites global)
* rename patches to stitch_groups
pull/2803/head dev-build-claudine-improve_fonts_7
Kaalleen 2024-03-26 07:10:40 +01:00 zatwierdzone przez GitHub
rodzic ea394f6d3b
commit 8e70f3d2fe
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
21 zmienionych plików z 229 dodań i 134 usunięć

Wyświetl plik

@ -20,8 +20,8 @@ def get_stitch_plan():
metadata = g.extension.get_inkstitch_metadata()
collapse_len = metadata['collapse_len_mm']
min_stitch_len = metadata['min_stitch_len_mm']
patches = g.extension.elements_to_stitch_groups(g.extension.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
stitch_groups = g.extension.elements_to_stitch_groups(g.extension.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
return jsonify(stitch_plan)
except InkstitchException as exc:
return jsonify({"error_message": str(exc)}), 500

Wyświetl plik

@ -218,6 +218,28 @@ class EmbroideryElement(object):
width = convert_length(width)
return width * self.stroke_scale
@property
@param('min_stitch_length_mm',
_('Minimum stitch length'),
tooltip=_('Overwrite global minimum stitch length setting. Shorter stitches than that will be removed.'),
type='float',
default=None,
sort_index=48)
@cache
def min_stitch_length(self):
return self.get_float_param("min_stitch_length_mm")
@property
@param('min_jump_stitch_length_mm',
_('Minimum jump stitch length'),
tooltip=_('Overwrite global minimum jump stitch length setting. Shorter distances to the next object will have no lock stitches.'),
type='float',
default=None,
sort_index=49)
@cache
def min_jump_stitch_length(self):
return self.get_float_param("min_jump_stitch_length_mm")
@property
@param('ties',
_('Allow lock stitches'),
@ -472,7 +494,7 @@ class EmbroideryElement(object):
return lock_start, lock_end
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_stitch_group):
raise NotImplementedError("%s must implement to_stitch_groups()" % self.__class__.__name__)
@debug.time
@ -576,6 +598,10 @@ class EmbroideryElement(object):
stitch_groups[-1].trim_after = self.has_command("trim") or self.trim_after
stitch_groups[-1].stop_after = self.has_command("stop") or self.stop_after
for stitch_group in stitch_groups:
stitch_group.min_jump_stitch_length = self.min_jump_stitch_length
stitch_group.set_minimum_stitch_length(self.min_stitch_length)
self._save_cached_stitch_groups(stitch_groups, previous_stitch)
debug.log(f"ending {self.node.get('id')} {self.node.get(INKSCAPE_LABEL)}")

Wyświetl plik

@ -27,5 +27,5 @@ class EmptyDObject(EmbroideryElement):
def shape(self):
return
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_stitch_group):
return []

Wyświetl plik

@ -714,7 +714,7 @@ class FillStitch(EmbroideryElement):
def get_starting_point(self, previous_stitch_group):
# If there is a "fill_start" Command, then use that; otherwise pick
# the point closest to the end of the last patch.
# the point closest to the end of the last stitch_group.
if self.get_command('fill_start'):
return self.get_command('fill_start').target_point
@ -792,18 +792,23 @@ class FillStitch(EmbroideryElement):
return stitch_groups
def do_legacy_fill(self):
stitch_lists = legacy_fill(self.shape,
self.angle,
self.row_spacing,
self.end_row_spacing,
self.max_stitch_length,
self.flip,
self.staggers,
self.skip_last)
return [StitchGroup(stitches=stitch_list,
color=self.color,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches) for stitch_list in stitch_lists]
stitch_lists = legacy_fill(
self.shape,
self.angle,
self.row_spacing,
self.end_row_spacing,
self.max_stitch_length,
self.flip,
self.staggers,
self.skip_last
)
return [StitchGroup(
stitches=stitch_list,
color=self.color,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches
) for stitch_list in stitch_lists]
def do_underlay(self, shape, starting_point):
color = self.color
@ -833,7 +838,7 @@ class FillStitch(EmbroideryElement):
starting_point = underlay.stitches[-1]
return [stitch_groups, starting_point]
def do_auto_fill(self, shape, last_patch, starting_point, ending_point):
def do_auto_fill(self, shape, last_stitch_group, starting_point, ending_point):
stitch_group = StitchGroup(
color=self.color,
tags=("auto_fill", "auto_fill_top"),
@ -851,10 +856,12 @@ class FillStitch(EmbroideryElement):
self.skip_last,
starting_point,
ending_point,
self.underpath))
self.underpath
)
)
return [stitch_group]
def do_contour_fill(self, polygon, last_patch, starting_point):
def do_contour_fill(self, polygon, last_stitch_group, starting_point):
if not starting_point:
starting_point = (0, 0)
starting_point = shgeo.Point(starting_point)
@ -894,17 +901,17 @@ class FillStitch(EmbroideryElement):
tags=("auto_fill", "auto_fill_top"),
stitches=stitches,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches,)
lock_stitches=self.lock_stitches)
stitch_groups.append(stitch_group)
return stitch_groups
def do_guided_fill(self, shape, last_patch, starting_point, ending_point):
def do_guided_fill(self, shape, last_stitch_group, starting_point, ending_point):
guide_line = self._get_guide_lines()
# No guide line: fallback to normal autofill
if not guide_line:
return self.do_auto_fill(shape, last_patch, starting_point, ending_point)
return self.do_auto_fill(shape, last_stitch_group, starting_point, ending_point)
stitch_group = StitchGroup(
color=self.color,
@ -947,11 +954,11 @@ class FillStitch(EmbroideryElement):
tags=("meander_fill", "meander_fill_top"),
stitches=meander_fill(self, shape, original_shape, i, starting_point, ending_point),
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches,
lock_stitches=self.lock_stitches
)
return [stitch_group]
def do_circular_fill(self, shape, last_patch, starting_point, ending_point):
def do_circular_fill(self, shape, last_stitch_group, starting_point, ending_point):
# get target position
command = self.get_command('ripple_target')
if command:
@ -983,8 +990,9 @@ class FillStitch(EmbroideryElement):
tags=("circular_fill", "auto_fill_top"),
stitches=stitches,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches,)
lock_stitches=self.lock_stitches
)
return [stitch_group]
def do_linear_gradient_fill(self, shape, last_patch, start, end):
def do_linear_gradient_fill(self, shape, last_stitch_group, start, end):
return linear_gradient_fill(self, shape, start, end)

Wyświetl plik

@ -29,5 +29,5 @@ class ImageObject(EmbroideryElement):
def validation_warnings(self):
yield ImageTypeWarning(self.center())
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_stitch_group):
return []

Wyświetl plik

@ -28,5 +28,5 @@ class MarkerObject(EmbroideryElement):
repr_point = next(inkex.Path(self.parse_path()).end_points)
yield MarkerWarning(repr_point)
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_stitch_group):
return []

Wyświetl plik

@ -94,10 +94,10 @@ class Polyline(EmbroideryElement):
def validation_warnings(self):
yield PolylineWarning(self.path[0][0][0])
def to_stitch_groups(self, last_patch):
patch = StitchGroup(color=self.color, lock_stitches=(None, None))
def to_stitch_groups(self, last_stitch_group):
stitch_group = StitchGroup(color=self.color, lock_stitches=(None, None))
for stitch in self.stitches:
patch.add_stitch(Point(*stitch))
stitch_group.add_stitch(Point(*stitch))
return [patch]
return [stitch_group]

Wyświetl plik

@ -1145,7 +1145,8 @@ class SatinColumn(EmbroideryElement):
stitch_group = StitchGroup(
color=self.color,
tags=("satin_column", "satin_column_underlay", "satin_contour_underlay"),
stitches=first_side)
stitches=first_side
)
self.add_running_stitches(first_side[-1], second_side[0], stitch_group)
stitch_group.stitches += second_side
@ -1172,7 +1173,8 @@ class SatinColumn(EmbroideryElement):
return StitchGroup(
color=self.color,
tags=("satin_column", "satin_column_underlay", "satin_center_walk"),
stitches=stitches)
stitches=stitches
)
def do_zigzag_underlay(self):
# zigzag underlay, usually done at a much lower density than the
@ -1185,7 +1187,7 @@ class SatinColumn(EmbroideryElement):
# "German underlay" described here:
# http://www.mrxstitch.com/underlay-what-lies-beneath-machine-embroidery/
patch = StitchGroup(color=self.color)
stitch_group = StitchGroup(color=self.color)
pairs = self.plot_points_on_rails(self.zigzag_underlay_spacing / 2.0,
-self.zigzag_underlay_inset_px,
@ -1206,12 +1208,12 @@ class SatinColumn(EmbroideryElement):
if last_point.distance(point) > max_len:
split_points = running_stitch.split_segment_even_dist(last_point, point, max_len)
for p in split_points:
patch.add_stitch(p)
stitch_group.add_stitch(p)
last_point = point
patch.add_stitch(point)
stitch_group.add_stitch(point)
patch.add_tags(("satin_column", "satin_column_underlay", "satin_zigzag_underlay"))
return patch
stitch_group.add_tags(("satin_column", "satin_column_underlay", "satin_zigzag_underlay"))
return stitch_group
def do_satin(self):
# satin: do a zigzag pattern, alternating between the paths. The
@ -1222,7 +1224,7 @@ class SatinColumn(EmbroideryElement):
# print >> dbg, "satin", self.zigzag_spacing, self.pull_compensation
patch = StitchGroup(color=self.color)
stitch_group = StitchGroup(color=self.color)
# pull compensation is automatically converted from mm to pixels by get_float_param
pairs = self.plot_points_on_rails(
@ -1248,25 +1250,25 @@ class SatinColumn(EmbroideryElement):
split_points, _ = self.get_split_points(
last_point, a, last_short_point, a_short, max_stitch_length, last_count,
length_sigma, random_phase, min_split_length, prng.join_args(seed, 'satin-split', 2 * i), row_num=2 * i, from_end=True)
patch.add_stitches(split_points, ("satin_column", "satin_split_stitch"))
stitch_group.add_stitches(split_points, ("satin_column", "satin_split_stitch"))
patch.add_stitch(a_short)
patch.stitches[-1].add_tags(("satin_column", "satin_column_edge"))
stitch_group.add_stitch(a_short)
stitch_group.stitches[-1].add_tags(("satin_column", "satin_column_edge"))
split_points, last_count = self.get_split_points(
a, b, a_short, b_short, max_stitch_length, None,
length_sigma, random_phase, min_split_length, prng.join_args(seed, 'satin-split', 2 * i + 1), row_num=2 * i + 1)
patch.add_stitches(split_points, ("satin_column", "satin_split_stitch"))
stitch_group.add_stitches(split_points, ("satin_column", "satin_split_stitch"))
patch.add_stitch(b_short)
patch.stitches[-1].add_tags(("satin_column", "satin_column_edge"))
stitch_group.add_stitch(b_short)
stitch_group.stitches[-1].add_tags(("satin_column", "satin_column_edge"))
last_point = b
last_short_point = b_short
if self._center_walk_is_odd():
patch.stitches = list(reversed(patch.stitches))
stitch_group.stitches = list(reversed(stitch_group.stitches))
return patch
return stitch_group
def do_e_stitch(self):
# e stitch: do a pattern that looks like the letter "E". It looks like
@ -1274,7 +1276,7 @@ class SatinColumn(EmbroideryElement):
#
# _|_|_|_|_|_|_|_|_|_|_|_|
patch = StitchGroup(color=self.color)
stitch_group = StitchGroup(color=self.color)
pairs = self.plot_points_on_rails(
self.zigzag_spacing,
@ -1302,21 +1304,21 @@ class SatinColumn(EmbroideryElement):
# zigzag spacing is wider than stitch length, subdivide
if last_point is not None and max_stitch_length is not None and self.zigzag_spacing > max_stitch_length:
points, _ = self.get_split_points(last_point, left, last_point, left, max_stitch_length)
patch.add_stitches(points)
stitch_group.add_stitches(points)
patch.add_stitch(a_short, ("edge", "left"))
patch.add_stitches(split_points, ("split_stitch",))
patch.add_stitch(b_short, ("edge",))
patch.add_stitches(split_points[::-1], ("split_stitch",))
patch.add_stitch(a_short, ("edge",))
stitch_group.add_stitch(a_short, ("edge", "left"))
stitch_group.add_stitches(split_points, ("split_stitch",))
stitch_group.add_stitch(b_short, ("edge",))
stitch_group.add_stitches(split_points[::-1], ("split_stitch",))
stitch_group.add_stitch(a_short, ("edge",))
last_point = a_short
if self._center_walk_is_odd():
patch.stitches = list(reversed(patch.stitches))
stitch_group.stitches = list(reversed(stitch_group.stitches))
patch.add_tags(("satin_column", "e_stitch"))
return patch
stitch_group.add_tags(("satin_column", "e_stitch"))
return stitch_group
def do_s_stitch(self):
# S stitch: do a pattern that looks like the letter "S". It looks like
@ -1324,7 +1326,7 @@ class SatinColumn(EmbroideryElement):
# _ _ _ _ _ _
# _| |_| |_| |_| |_| |_| |
patch = StitchGroup(color=self.color)
stitch_group = StitchGroup(color=self.color)
pairs = self.plot_points_on_rails(
self.zigzag_spacing,
@ -1357,17 +1359,17 @@ class SatinColumn(EmbroideryElement):
if last_point is not None and max_stitch_length is not None and self.zigzag_spacing > max_stitch_length:
initial_points, _ = self.get_split_points(last_point, points[0], last_point, points[0], max_stitch_length)
patch.add_stitches(points)
stitch_group.add_stitches(points)
last_point = points[-1]
if self._center_walk_is_odd():
patch.stitches = list(reversed(patch.stitches))
stitch_group.stitches = list(reversed(stitch_group.stitches))
patch.add_tags(("satin", "s_stitch"))
return patch
stitch_group.add_tags(("satin", "s_stitch"))
return stitch_group
def do_zigzag(self):
patch = StitchGroup(color=self.color)
stitch_group = StitchGroup(color=self.color)
# calculate pairs at double the requested density
pairs = self.plot_points_on_rails(
@ -1401,24 +1403,24 @@ class SatinColumn(EmbroideryElement):
split_points, _ = self.get_split_points(
last_point, a, last_point_short, a_short, max_stitch_length, None,
length_sigma, random_phase, min_split_length, prng.join_args(seed, 'satin-split', 2 * i), row_num=2 * i, from_end=True)
patch.add_stitches(split_points, ("satin_column", "zigzag_split_stitch"))
stitch_group.add_stitches(split_points, ("satin_column", "zigzag_split_stitch"))
patch.add_stitch(a_short)
stitch_group.add_stitch(a_short)
split_points, _ = self.get_split_points(
a, b, a_short, b_short, max_stitch_length, None,
length_sigma, random_phase, min_split_length, prng.join_args(seed, 'satin-split', 2 * i + 1), row_num=2 * i + 1)
patch.add_stitches(split_points, ("satin_column", "zigzag_split_stitch"))
stitch_group.add_stitches(split_points, ("satin_column", "zigzag_split_stitch"))
patch.add_stitch(b_short)
stitch_group.add_stitch(b_short)
last_point = b
last_point_short = b_short
if self._center_walk_is_odd():
patch.stitches = list(reversed(patch.stitches))
stitch_group.stitches = list(reversed(stitch_group.stitches))
return patch
return stitch_group
def get_split_points(self, *args, **kwargs):
if self.split_method == "default":
@ -1526,15 +1528,17 @@ class SatinColumn(EmbroideryElement):
stitch_group += next_stitch_group
return stitch_group
def to_stitch_groups(self, last_patch=None):
def to_stitch_groups(self, last_stitch_group=None):
# Stitch a variable-width satin column, zig-zagging between two paths.
# The algorithm will draw zigzags between each consecutive pair of
# beziers. The boundary points between beziers serve as "checkpoints",
# allowing the user to control how the zigzags flow around corners.
stitch_group = StitchGroup(color=self.color,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches)
stitch_group = StitchGroup(
color=self.color,
force_lock_stitches=self.force_lock_stitches,
lock_stitches=self.lock_stitches
)
if self.center_walk_underlay:
stitch_group += self.do_center_walk()

Wyświetl plik

@ -443,11 +443,10 @@ class Stroke(EmbroideryElement):
# `self.zigzag_spacing` is the length for a zig and a zag
# together (a V shape). Start with running stitch at half
# that length:
patch = self.running_stitch(path, zigzag_spacing / 2.0, self.running_stitch_tolerance)
stitch_group = self.running_stitch(path, zigzag_spacing / 2.0, self.running_stitch_tolerance)
stitch_group.stitches = zigzag_stitch(stitch_group.stitches, zigzag_spacing, stroke_width, pull_compensation)
patch.stitches = zigzag_stitch(patch.stitches, zigzag_spacing, stroke_width, pull_compensation)
return patch
return stitch_group
def running_stitch(self, path, stitch_length, tolerance):
stitches = running_stitch(path, stitch_length, tolerance)
@ -463,7 +462,12 @@ class Stroke(EmbroideryElement):
repeated_stitches.extend(this_path)
return StitchGroup(self.color, repeated_stitches, lock_stitches=self.lock_stitches, force_lock_stitches=self.force_lock_stitches)
return StitchGroup(
self.color,
stitches=repeated_stitches,
lock_stitches=self.lock_stitches,
force_lock_stitches=self.force_lock_stitches
)
def apply_max_stitch_length(self, path):
# apply max distances
@ -491,16 +495,16 @@ class Stroke(EmbroideryElement):
def do_bean_repeats(self, stitches):
return bean_stitch(stitches, self.bean_stitch_repeats)
def to_stitch_groups(self, last_patch): # noqa: C901
patches = []
def to_stitch_groups(self, last_stitch_group): # noqa: C901
stitch_groups = []
# ripple stitch
if self.stroke_method == 'ripple_stitch':
patch = self.ripple_stitch()
if patch:
stitch_group = self.ripple_stitch()
if stitch_group:
if any(self.bean_stitch_repeats):
patch.stitches = self.do_bean_repeats(patch.stitches)
patches.append(patch)
stitch_group.stitches = self.do_bean_repeats(stitch_group.stitches)
stitch_groups.append(stitch_group)
else:
for path in self.paths:
path = [Point(x, y) for x, y in path]
@ -514,24 +518,26 @@ class Stroke(EmbroideryElement):
else:
# manual stitch disables lock stitches unless they force them
lock_stitches = (None, None)
patch = StitchGroup(color=self.color,
stitches=path,
lock_stitches=lock_stitches,
force_lock_stitches=self.force_lock_stitches)
stitch_group = StitchGroup(
color=self.color,
stitches=path,
lock_stitches=lock_stitches,
force_lock_stitches=self.force_lock_stitches
)
# simple satin
elif self.stroke_method == 'zigzag_stitch':
patch = self.simple_satin(path, self.zigzag_spacing, self.stroke_width, self.pull_compensation)
stitch_group = self.simple_satin(path, self.zigzag_spacing, self.stroke_width, self.pull_compensation)
# running stitch
else:
patch = self.running_stitch(path, self.running_stitch_length, self.running_stitch_tolerance)
stitch_group = self.running_stitch(path, self.running_stitch_length, self.running_stitch_tolerance)
# bean stitch
if any(self.bean_stitch_repeats):
patch.stitches = self.do_bean_repeats(patch.stitches)
stitch_group.stitches = self.do_bean_repeats(stitch_group.stitches)
if patch:
patches.append(patch)
if stitch_group:
stitch_groups.append(stitch_group)
return patches
return stitch_groups
@cache
def get_guide_line(self):

Wyświetl plik

@ -29,5 +29,5 @@ class TextObject(EmbroideryElement):
def validation_warnings(self):
yield TextTypeWarning(self.pointer())
def to_stitch_groups(self, last_patch):
def to_stitch_groups(self, last_stitch_group):
return []

Wyświetl plik

@ -124,16 +124,16 @@ class InkstitchExtension(inkex.EffectExtension):
return False
def elements_to_stitch_groups(self, elements):
patches = []
stitch_groups = []
for element in elements:
if patches:
last_patch = patches[-1]
if stitch_groups:
last_stitch_group = stitch_groups[-1]
else:
last_patch = None
last_stitch_group = None
patches.extend(element.embroider(last_patch))
stitch_groups.extend(element.embroider(last_stitch_group))
return patches
return stitch_groups
def get_inkstitch_metadata(self):
return InkStitchMetadata(self.svg)

Wyświetl plik

@ -39,8 +39,8 @@ class DensityMap(InkstitchExtension):
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']
min_stitch_len = self.metadata['min_stitch_len_mm']
patches = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
layer = svg.find(".//*[@id='__inkstitch_density_plan__']")
color_groups = create_color_groups(layer)

Wyświetl plik

@ -54,8 +54,8 @@ class Output(InkstitchExtension):
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']
min_stitch_len = self.metadata['min_stitch_len_mm']
patches = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, disable_ties=self.settings.get('laser_mode', False),
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, disable_ties=self.settings.get('laser_mode', False),
min_stitch_len=min_stitch_len)
ThreadCatalog().match_and_apply_palette(stitch_plan, self.metadata['thread-palette'])

Wyświetl plik

@ -310,8 +310,8 @@ class Print(InkstitchExtension):
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']
min_stitch_len = self.metadata['min_stitch_len_mm']
patches = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
palette = ThreadCatalog().match_and_apply_palette(stitch_plan, self.get_inkstitch_metadata()['thread-palette'])
overview_svg, color_block_svgs = self.render_svgs(stitch_plan, realistic=False)

Wyświetl plik

@ -38,8 +38,8 @@ class StitchPlanPreview(InkstitchExtension):
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']
min_stitch_len = self.metadata['min_stitch_len_mm']
patches = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
render_stitch_plan(svg, stitch_plan, realistic, visual_commands)
# apply options

Wyświetl plik

@ -54,8 +54,8 @@ class Zip(InkstitchExtension):
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']
min_stitch_len = self.metadata['min_stitch_len_mm']
patches = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
stitch_groups = self.elements_to_stitch_groups(self.elements)
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
ThreadCatalog().match_and_apply_palette(stitch_plan, self.get_inkstitch_metadata()['thread-palette'])
if self.options.x_repeats != 1 or self.options.y_repeats != 1:

Wyświetl plik

@ -112,6 +112,7 @@ class ColorBlock(object):
if min_stitch_len is None:
min_stitch_len = 0.1
min_stitch_len *= PIXELS_PER_MM
stitches = [self.stitches[0]]
for stitch in self.stitches[1:]:
@ -123,7 +124,8 @@ class ColorBlock(object):
pass
else:
length = (stitch - stitches[-1]).length()
if length <= min_stitch_len * PIXELS_PER_MM:
min_length = stitch.min_stitch_length or min_stitch_len
if length <= min_length:
# duplicate stitch, skip this one
continue

Wyświetl plik

@ -11,7 +11,17 @@ from ..utils.geometry import Point
class Stitch(Point):
"""A stitch is a Point with extra information telling how to sew it."""
def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, tags=None):
def __init__(
self,
x, y=None,
color=None,
jump=False,
stop=False,
trim=False,
color_change=False,
min_stitch_length=None,
tags=None
):
# DANGER: if you add new attributes, you MUST also set their default
# values in __new__() below. Otherwise, cached stitch plans can be
# loaded and create objects without those properties defined, because
@ -37,6 +47,7 @@ class Stitch(Point):
self._set('trim', trim, base_stitch)
self._set('stop', stop, base_stitch)
self._set('color_change', color_change, base_stitch)
self._set('min_stitch_length', min_stitch_length, base_stitch)
self.tags = set()
self.add_tags(tags or [])
@ -52,13 +63,16 @@ class Stitch(Point):
return instance
def __repr__(self):
return "Stitch(%s, %s, %s, %s, %s, %s, %s)" % (self.x,
self.y,
self.color,
"JUMP" if self.jump else " ",
"TRIM" if self.trim else " ",
"STOP" if self.stop else " ",
"COLOR CHANGE" if self.color_change else " ")
return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s)" % (
self.x,
self.y,
self.color,
self.min_stitch_length,
"JUMP" if self.jump else " ",
"TRIM" if self.trim else " ",
"STOP" if self.stop else " ",
"COLOR CHANGE" if self.color_change else " "
)
def _set(self, attribute, value, base_stitch):
# Set an attribute. If the caller passed a Stitch object, use its value, unless
@ -95,7 +109,17 @@ class Stitch(Point):
return tag in self.tags
def copy(self):
return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.tags)
return Stitch(
self.x,
self.y,
self.color,
self.jump,
self.stop,
self.trim,
self.color_change,
self.min_stitch_length,
self.tags
)
def offset(self, offset: Point):
out = self.copy()

Wyświetl plik

@ -17,8 +17,17 @@ class StitchGroup:
between them by the stitch plan generation code.
"""
def __init__(self, color=None, stitches=None, trim_after=False, stop_after=False,
lock_stitches=(None, None), force_lock_stitches=False, tags=None):
def __init__(
self,
color=None,
stitches=None,
min_jump_stitch_length=False,
trim_after=False,
stop_after=False,
lock_stitches=(None, None),
force_lock_stitches=False,
tags=None
):
# DANGER: if you add new attributes, you MUST also set their default
# values in __new__() below. Otherwise, cached stitch plans can be
# loaded and create objects without those properties defined, because
@ -29,6 +38,7 @@ class StitchGroup:
self.stop_after = stop_after
self.lock_stitches = lock_stitches
self.force_lock_stitches = force_lock_stitches
self.min_jump_stitch_length = min_jump_stitch_length
self.stitches = []
if stitches:
@ -55,9 +65,13 @@ class StitchGroup:
raise TypeError("StitchGroup can only be added to another StitchGroup")
def __len__(self):
# This method allows `len(patch)` and `if patch:
# This method allows `len(stitch_group)` and `if stitch_group:
return len(self.stitches)
def set_minimum_stitch_length(self, min_stitch_length):
for stitch in self.stitches:
stitch.min_stitch_length = min_stitch_length
def add_stitches(self, stitches, tags=None):
for stitch in stitches:
self.add_stitch(stitch, tags=tags)
@ -66,7 +80,6 @@ class StitchGroup:
if not isinstance(stitch, Stitch):
# probably a Point
stitch = Stitch(stitch, tags=tags)
self.stitches.append(stitch)
def reverse(self):

Wyświetl plik

@ -59,13 +59,23 @@ def stitch_groups_to_stitch_plan(stitch_groups, collapse_len=None, min_stitch_le
# make a new block of our color
color_block = stitch_plan.new_color_block(color=stitch_group.color)
else:
if (len(color_block) and not need_tie_in and
((stitch_group.stitches[0] - color_block.stitches[-1]).length() > collapse_len or
previous_stitch_group.force_lock_stitches)):
lock_stitches = previous_stitch_group.get_lock_stitches("end", disable_ties)
if lock_stitches:
color_block.add_stitches(stitches=lock_stitches)
need_tie_in = True
add_lock = False
if len(color_block) and not need_tie_in:
distance_to_previous_stitch = (stitch_group.stitches[0] - color_block.stitches[-1]).length()
if previous_stitch_group.force_lock_stitches:
add_lock = True
elif previous_stitch_group.min_jump_stitch_length:
# object based minimum jump stitch length overrides the global collapse_len setting
if distance_to_previous_stitch > previous_stitch_group.min_jump_stitch_length:
add_lock = True
elif distance_to_previous_stitch > collapse_len:
add_lock = True
if add_lock:
lock_stitches = previous_stitch_group.get_lock_stitches("end", disable_ties)
need_tie_in = True
if lock_stitches:
color_block.add_stitches(stitches=lock_stitches)
if need_tie_in is True:
lock_stitches = stitch_group.get_lock_stitches("start", disable_ties)

Wyświetl plik

@ -54,6 +54,8 @@ SVG_OBJECT_TAGS = (SVG_ELLIPSE_TAG, SVG_CIRCLE_TAG, SVG_RECT_TAG)
INKSTITCH_ATTRIBS = {}
inkstitch_attribs = [
'min_stitch_length_mm',
'min_jump_stitch_length_mm',
'ties',
'force_lock_stitches',
'lock_start',