kopia lustrzana https://github.com/EmbroidePy/pyembroidery
698 wiersze
28 KiB
Python
698 wiersze
28 KiB
Python
import math
|
|
|
|
from .EmbFunctions import *
|
|
from .EmbMatrix import EmbMatrix
|
|
|
|
|
|
class Transcoder:
|
|
def __init__(self, settings=None):
|
|
if settings is None:
|
|
settings = {}
|
|
self.max_stitch = settings.get("max_stitch", float("inf"))
|
|
self.max_jump = settings.get("max_jump", float("inf"))
|
|
self.full_jump = settings.get("full_jump", False)
|
|
self.round = settings.get("round", False)
|
|
self.needle_count = settings.get("needle_count", 5)
|
|
self.thread_change_command = settings.get("thread_change_command", COLOR_CHANGE)
|
|
if self.needle_count <= 1 and self.thread_change_command == NEEDLE_SET:
|
|
self.thread_change_command = STOP
|
|
strip_sequins = settings.get("strip_sequins", True)
|
|
# deprecated, use sequin_contingency.
|
|
if strip_sequins:
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_JUMP
|
|
else:
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_UTILIZE
|
|
self.sequin_contingency = settings.get(
|
|
"sequin_contingency", self.sequin_contingency
|
|
)
|
|
|
|
self.writes_speeds = settings.get("writes_speeds", True)
|
|
self.explicit_trim = settings.get("explicit_trim", False)
|
|
|
|
self.tie_on_contingency = settings.get("tie_on", CONTINGENCY_TIE_ON_NONE)
|
|
if self.tie_on_contingency is True:
|
|
self.tie_on_contingency = CONTINGENCY_TIE_ON_THREE_SMALL
|
|
if self.tie_on_contingency is False:
|
|
self.tie_on_contingency = CONTINGENCY_TIE_ON_NONE
|
|
|
|
self.tie_off_contingency = settings.get("tie_off", CONTINGENCY_TIE_OFF_NONE)
|
|
if self.tie_off_contingency is True:
|
|
self.tie_off_contingency = CONTINGENCY_TIE_OFF_THREE_SMALL
|
|
if self.tie_off_contingency is False:
|
|
self.tie_off_contingency = CONTINGENCY_TIE_OFF_NONE
|
|
|
|
self.long_stitch_contingency = settings.get(
|
|
"long_stitch_contingency", CONTINGENCY_LONG_STITCH_JUMP_NEEDLE
|
|
)
|
|
|
|
self.matrix = EmbMatrix()
|
|
translate = settings.get("translate", None)
|
|
if translate is not None:
|
|
try:
|
|
self.matrix.post_translate(translate[0], translate[1])
|
|
except IndexError:
|
|
try:
|
|
self.matrix.post_translate(translate.x, translate.y)
|
|
except AttributeError:
|
|
pass
|
|
scale = settings.get("scale", None)
|
|
if scale is not None:
|
|
try:
|
|
self.matrix.post_scale(scale[0], scale[1])
|
|
except (IndexError, TypeError):
|
|
try:
|
|
self.matrix.post_scale(scale.x, scale.y)
|
|
except AttributeError:
|
|
self.matrix.post_scale(scale, scale)
|
|
rotate = settings.get("rotate", None)
|
|
if rotate is not None:
|
|
self.matrix.post_rotate(rotate)
|
|
self.source_pattern = None
|
|
self.destination_pattern = None
|
|
self.position = 0
|
|
self.order_index = -1
|
|
self.change_sequence = {}
|
|
self.stitch = None
|
|
self.state_trimmed = True
|
|
self.state_sequin_mode = False
|
|
self.state_jumping = False
|
|
self.needle_x = 0
|
|
self.needle_y = 0
|
|
self.high_flags = 0
|
|
|
|
def transcode(self, source_pattern, destination_pattern):
|
|
if source_pattern is destination_pattern:
|
|
# both objects are same. Source is copy, destination is cleared.
|
|
source_pattern = destination_pattern.copy()
|
|
destination_pattern.clear()
|
|
self.source_pattern = source_pattern
|
|
self.destination_pattern = destination_pattern
|
|
self.transcode_metadata()
|
|
self.transcode_main()
|
|
return destination_pattern
|
|
|
|
def transcode_metadata(self):
|
|
"""Transcodes metadata, (just moves)"""
|
|
source = self.source_pattern.extras
|
|
dest = self.destination_pattern.extras
|
|
dest.update(source)
|
|
|
|
def get_as_thread_change_sequence_events(self):
|
|
"""Generates the sequence change events with their relevant indexes.
|
|
If there is a sewing event prior to the thread sequence event, the first event
|
|
is indexed as 1. If the first event is a discrete event, occurring before
|
|
the sewing starts it's indexed as zero."""
|
|
source = self.source_pattern.stitches
|
|
current_index = 0
|
|
for stitch in source:
|
|
change = decode_embroidery_command(stitch[2])
|
|
command = change[0]
|
|
flags = command & COMMAND_MASK
|
|
if current_index == 0:
|
|
if (
|
|
flags == STITCH
|
|
or flags == SEW_TO
|
|
or flags == NEEDLE_AT
|
|
or flags == SEQUIN_EJECT
|
|
):
|
|
current_index = 1
|
|
if flags == SET_CHANGE_SEQUENCE:
|
|
thread = change[1]
|
|
needle = change[2]
|
|
order = change[3]
|
|
yield flags, thread, needle, order, None
|
|
elif flags == NEEDLE_SET or flags == COLOR_CHANGE or flags == COLOR_BREAK:
|
|
change = decode_embroidery_command(command)
|
|
thread = change[1]
|
|
needle = change[2]
|
|
order = change[3]
|
|
yield flags, thread, needle, order, current_index
|
|
current_index += 1
|
|
|
|
def build_thread_change_sequence(self):
|
|
"""Builds a change sequence to plan out all the color changes for the file."""
|
|
change_sequence = {}
|
|
lookahead_index = 0
|
|
change_sequence[0] = [None, None, None, None]
|
|
for (
|
|
flags,
|
|
thread,
|
|
needle,
|
|
order,
|
|
current_index,
|
|
) in self.get_as_thread_change_sequence_events():
|
|
if flags == SET_CHANGE_SEQUENCE:
|
|
if order is None:
|
|
try:
|
|
current = change_sequence[lookahead_index]
|
|
except KeyError:
|
|
current = [None, None, None, None]
|
|
change_sequence[lookahead_index] = current
|
|
lookahead_index += 1
|
|
else:
|
|
try:
|
|
current = change_sequence[order]
|
|
except KeyError:
|
|
current = [None, None, None, None]
|
|
change_sequence[order] = current
|
|
else:
|
|
try:
|
|
current = change_sequence[current_index]
|
|
except KeyError:
|
|
current = [None, None, None, None]
|
|
change_sequence[current_index] = current
|
|
if current_index >= lookahead_index:
|
|
lookahead_index = current_index + 1
|
|
if flags == COLOR_CHANGE or flags == NEEDLE_SET:
|
|
current[0] = flags
|
|
if thread is not None:
|
|
current[1] = thread
|
|
current[3] = self.source_pattern.get_thread_or_filler(thread)
|
|
if needle is not None:
|
|
current[2] = needle
|
|
# TODO: account for contingency where threadset repeats threads without explicit values set within the commands.
|
|
|
|
needle_limit = self.needle_count
|
|
thread_index = 0
|
|
needle_index = 1
|
|
for order, s in change_sequence.items():
|
|
if s[0] is None:
|
|
s[0] = self.thread_change_command
|
|
if s[1] is None:
|
|
s[1] = thread_index
|
|
thread_index += 1
|
|
if s[2] is None:
|
|
s[2] = needle_index
|
|
if s[2] > needle_limit:
|
|
s[2] = (s[2] - 1) % needle_limit
|
|
s[2] += 1
|
|
needle_index += 1
|
|
if s[3] is None:
|
|
s[3] = self.source_pattern.get_thread_or_filler(s[1])
|
|
return change_sequence
|
|
|
|
def transcode_main(self):
|
|
"""Transcodes stitches.
|
|
Converts middle-level commands and potentially incompatible
|
|
commands into a format friendly low level commands."""
|
|
|
|
source = self.source_pattern.stitches
|
|
self.state_trimmed = True
|
|
self.needle_x = 0
|
|
self.needle_y = 0
|
|
self.position = 0
|
|
self.order_index = -1
|
|
self.change_sequence = self.build_thread_change_sequence()
|
|
|
|
flags = NO_COMMAND
|
|
for self.position, self.stitch in enumerate(source):
|
|
p = self.matrix.point_in_matrix_space(self.stitch)
|
|
x = p[0]
|
|
y = p[1]
|
|
if self.round:
|
|
x = round(x)
|
|
y = round(y)
|
|
flags = self.stitch[2] & COMMAND_MASK
|
|
self.high_flags = self.stitch[2] & FLAGS_MASK
|
|
|
|
if flags == NO_COMMAND:
|
|
continue
|
|
elif flags == STITCH:
|
|
if self.state_trimmed:
|
|
self.declare_not_trimmed()
|
|
self.jump_to_within_stitchrange(x, y)
|
|
self.stitch_at(x, y)
|
|
self.tie_on()
|
|
elif self.state_jumping:
|
|
self.needle_to(x, y)
|
|
self.state_jumping = False
|
|
else:
|
|
self.stitch_with_contingency(x, y)
|
|
elif flags == NEEDLE_AT:
|
|
if self.state_trimmed:
|
|
self.declare_not_trimmed()
|
|
self.jump_to_within_stitchrange(x, y)
|
|
self.stitch_at(x, y)
|
|
self.tie_on()
|
|
elif self.state_jumping:
|
|
self.needle_to(x, y)
|
|
self.state_jumping = False
|
|
else:
|
|
self.needle_to(x, y)
|
|
elif flags == SEW_TO:
|
|
if self.state_trimmed:
|
|
self.declare_not_trimmed()
|
|
self.jump_to_within_stitchrange(x, y)
|
|
self.stitch_at(x, y)
|
|
self.tie_on()
|
|
elif self.state_jumping:
|
|
self.needle_to(x, y)
|
|
self.state_jumping = False
|
|
else:
|
|
self.sew_to(x, y)
|
|
|
|
# Middle Level Commands.
|
|
elif flags == STITCH_BREAK:
|
|
self.state_jumping = True
|
|
elif flags == FRAME_EJECT:
|
|
self.tie_off_and_trim_if_needed()
|
|
self.jump_to(x, y)
|
|
self.stop_here()
|
|
elif flags == SEQUENCE_BREAK:
|
|
self.tie_off_and_trim_if_needed()
|
|
elif flags == COLOR_BREAK:
|
|
self.color_break()
|
|
elif flags == TIE_OFF:
|
|
self.tie_off()
|
|
elif flags == TIE_ON:
|
|
self.tie_on()
|
|
|
|
# Core Commands.
|
|
elif flags == TRIM:
|
|
self.tie_off_and_trim_if_needed()
|
|
elif flags == JUMP:
|
|
if not self.state_jumping:
|
|
self.jump_to(x, y)
|
|
elif flags == SEQUIN_MODE:
|
|
self.toggle_sequins()
|
|
elif flags == SEQUIN_EJECT:
|
|
if self.state_trimmed:
|
|
self.declare_not_trimmed()
|
|
self.jump_to_within_stitchrange(x, y)
|
|
self.stitch_at(x, y)
|
|
self.tie_on()
|
|
if not self.state_sequin_mode:
|
|
self.toggle_sequins()
|
|
self.sequin_at(x, y)
|
|
elif flags == COLOR_CHANGE:
|
|
self.tie_off_trim_color_change()
|
|
elif flags == NEEDLE_SET:
|
|
self.tie_off_trim_color_change()
|
|
elif flags == STOP:
|
|
self.stop_here()
|
|
elif flags == SLOW:
|
|
self.slow_command_here()
|
|
elif flags == FAST:
|
|
self.fast_command_here()
|
|
elif flags == END:
|
|
self.end_here()
|
|
break
|
|
# On-the-fly Settings Commands.
|
|
elif flags == CONTINGENCY_TIE_ON_THREE_SMALL:
|
|
self.tie_on_contingency = CONTINGENCY_TIE_ON_THREE_SMALL
|
|
elif flags == CONTINGENCY_TIE_OFF_THREE_SMALL:
|
|
self.tie_off_contingency = CONTINGENCY_TIE_OFF_THREE_SMALL
|
|
elif flags == CONTINGENCY_TIE_ON_NONE:
|
|
self.tie_on_contingency = CONTINGENCY_TIE_ON_NONE
|
|
elif flags == CONTINGENCY_TIE_OFF_NONE:
|
|
self.tie_off_contingency = CONTINGENCY_TIE_OFF_NONE
|
|
elif flags == OPTION_MAX_JUMP_LENGTH:
|
|
x = self.stitch[0]
|
|
self.max_jump = x
|
|
elif flags == OPTION_MAX_STITCH_LENGTH:
|
|
x = self.stitch[0]
|
|
self.max_stitch = x
|
|
elif flags == OPTION_EXPLICIT_TRIM:
|
|
self.explicit_trim = True
|
|
elif flags == OPTION_IMPLICIT_TRIM:
|
|
self.explicit_trim = False
|
|
elif flags == CONTINGENCY_LONG_STITCH_NONE:
|
|
self.long_stitch_contingency = CONTINGENCY_LONG_STITCH_NONE
|
|
elif flags == CONTINGENCY_LONG_STITCH_JUMP_NEEDLE:
|
|
self.long_stitch_contingency = CONTINGENCY_LONG_STITCH_JUMP_NEEDLE
|
|
elif flags == CONTINGENCY_LONG_STITCH_SEW_TO:
|
|
self.long_stitch_contingency = CONTINGENCY_LONG_STITCH_SEW_TO
|
|
elif flags == CONTINGENCY_SEQUIN_REMOVE:
|
|
if self.state_sequin_mode: # if sequin_mode, turn it off.
|
|
self.toggle_sequins()
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_REMOVE
|
|
elif flags == CONTINGENCY_SEQUIN_STITCH:
|
|
if self.state_sequin_mode: # if sequin_mode, turn it off.
|
|
self.toggle_sequins()
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_STITCH
|
|
elif flags == CONTINGENCY_SEQUIN_JUMP:
|
|
if self.state_sequin_mode: # if sequin_mode, turn it off.
|
|
self.toggle_sequins()
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_JUMP
|
|
elif flags == CONTINGENCY_SEQUIN_UTILIZE:
|
|
self.sequin_contingency = CONTINGENCY_SEQUIN_UTILIZE
|
|
elif flags == MATRIX_TRANSLATE:
|
|
self.matrix.post_translate(self.stitch[0], self.stitch[1])
|
|
elif flags == MATRIX_SCALE_ORIGIN:
|
|
self.matrix.post_scale(self.stitch[0], self.stitch[1])
|
|
elif flags == MATRIX_ROTATE_ORIGIN:
|
|
self.matrix.post_rotate(self.stitch[0])
|
|
elif flags == MATRIX_SCALE:
|
|
self.matrix.inverse()
|
|
q = self.matrix.point_in_matrix_space(self.needle_x, self.needle_y)
|
|
self.matrix.inverse()
|
|
self.matrix.post_scale(self.stitch[0], self.stitch[1], q[0], q[1])
|
|
elif flags == MATRIX_ROTATE:
|
|
self.matrix.inverse()
|
|
q = self.matrix.point_in_matrix_space(self.needle_x, self.needle_y)
|
|
self.matrix.inverse()
|
|
self.matrix.post_rotate(self.stitch[0], q[0], q[1])
|
|
elif flags == MATRIX_RESET:
|
|
self.matrix.reset()
|
|
if flags != END:
|
|
self.end_here()
|
|
|
|
def update_needle_position(self, x, y):
|
|
self.needle_x = x
|
|
self.needle_y = y
|
|
|
|
def declare_not_trimmed(self):
|
|
if self.order_index == -1:
|
|
self.next_change_sequence()
|
|
self.state_trimmed = False
|
|
|
|
def add_thread_change(self, command, thread=None, needle=None, order=None):
|
|
x = self.needle_x
|
|
y = self.needle_y
|
|
cmd = encode_thread_change(command, thread, needle, order)
|
|
self.destination_pattern.stitches.append([x, y, cmd])
|
|
|
|
def add(self, flags, x=None, y=None):
|
|
if x is None:
|
|
x = self.needle_x
|
|
if y is None:
|
|
y = self.needle_y
|
|
flags |= self.high_flags
|
|
self.destination_pattern.stitches.append([x, y, flags])
|
|
|
|
def lookahead_stitch(self):
|
|
"""Looks forward from current position and
|
|
determines if anymore stitching will occur."""
|
|
source = self.source_pattern.stitches
|
|
for pos in range(self.position, len(source)):
|
|
stitch = source[pos]
|
|
flags = stitch[2]
|
|
if flags == STITCH:
|
|
return True
|
|
elif flags == NEEDLE_AT:
|
|
return True
|
|
elif flags == SEW_TO:
|
|
return True
|
|
elif flags == TIE_ON:
|
|
return True
|
|
elif flags == SEQUIN_EJECT:
|
|
return True
|
|
elif flags == END:
|
|
return False
|
|
return False
|
|
|
|
def color_break(self):
|
|
"""Implements color break. Should add color changes add needed only."""
|
|
if self.order_index < 0:
|
|
return # We haven't stitched anything, colorbreak happens, before start. Ignore.
|
|
if not self.state_trimmed:
|
|
self.tie_off()
|
|
if self.explicit_trim:
|
|
self.trim_here()
|
|
if not self.lookahead_stitch():
|
|
return # No more stitching will happen, colorchange unneeded.
|
|
self.next_change_sequence()
|
|
self.state_trimmed = True
|
|
|
|
def tie_off_trim_color_change(self):
|
|
if not self.state_trimmed:
|
|
self.tie_off()
|
|
if self.explicit_trim:
|
|
self.trim_here()
|
|
self.next_change_sequence()
|
|
self.state_trimmed = True
|
|
|
|
def tie_off_and_trim_if_needed(self):
|
|
if not self.state_trimmed:
|
|
self.tie_off_and_trim()
|
|
|
|
def tie_off_and_trim(self):
|
|
self.tie_off()
|
|
self.trim_here()
|
|
|
|
def tie_off(self):
|
|
if self.tie_off_contingency == CONTINGENCY_TIE_OFF_THREE_SMALL:
|
|
try:
|
|
b = self.matrix.point_in_matrix_space(
|
|
self.source_pattern.stitches[self.position - 1]
|
|
)
|
|
flags = b[2]
|
|
if (
|
|
flags == STITCH
|
|
or flags == NEEDLE_AT
|
|
or flags == SEW_TO
|
|
or flags == SEQUIN_EJECT
|
|
):
|
|
self.lock_stitch(
|
|
self.needle_x, self.needle_y, b[0], b[1], self.max_stitch
|
|
)
|
|
except IndexError:
|
|
pass # must be an island stitch. jump-stitch-jump
|
|
|
|
def tie_on(self):
|
|
if self.tie_on_contingency == CONTINGENCY_TIE_ON_THREE_SMALL:
|
|
try:
|
|
b = self.matrix.point_in_matrix_space(
|
|
self.source_pattern.stitches[self.position + 1]
|
|
)
|
|
flags = b[2]
|
|
if (
|
|
flags == STITCH
|
|
or flags == NEEDLE_AT
|
|
or flags == SEW_TO
|
|
or flags == SEQUIN_EJECT
|
|
):
|
|
self.lock_stitch(
|
|
self.needle_x, self.needle_y, b[0], b[1], self.max_stitch
|
|
)
|
|
except IndexError:
|
|
pass # must be an island stitch. jump-stitch-jump
|
|
|
|
def trim_here(self):
|
|
if self.state_sequin_mode:
|
|
# Can't trim in sequin mode. DST uses jumps to trigger sequin eject and to trim.
|
|
self.toggle_sequins()
|
|
self.add(TRIM)
|
|
self.state_trimmed = True
|
|
|
|
def toggle_sequins(self):
|
|
"""Sequin mode toggle can be called whenever but will only actually turn on if set
|
|
to utilize mode for the sequin contingency."""
|
|
contingency = self.sequin_contingency
|
|
if contingency == CONTINGENCY_SEQUIN_UTILIZE:
|
|
self.add(SEQUIN_MODE)
|
|
self.state_sequin_mode = not self.state_sequin_mode
|
|
|
|
def jump_to_within_stitchrange(self, new_x, new_y):
|
|
"""Jumps close enough to stitch a position in x,y
|
|
without violating the length constraints."""
|
|
x0 = self.needle_x
|
|
y0 = self.needle_y
|
|
max_length = self.max_jump
|
|
self.interpolate_gap_stitches(x0, y0, new_x, new_y, max_length, JUMP)
|
|
if self.full_jump:
|
|
if self.needle_x != new_x or self.needle_y != new_y:
|
|
self.jump_at(new_x, new_y)
|
|
# We are currently assuming that max_jump is also max_stitch.
|
|
# Properly it might be the case that some format could require
|
|
# a split constraint here where we would need to jump further
|
|
# so that we could then stitch closer.
|
|
|
|
def jump_to(self, new_x, new_y):
|
|
x0 = self.needle_x
|
|
y0 = self.needle_y
|
|
max_length = self.max_jump
|
|
self.interpolate_gap_stitches(x0, y0, new_x, new_y, max_length, JUMP)
|
|
self.jump_at(new_x, new_y)
|
|
|
|
def jump_at(self, new_x, new_y):
|
|
if self.state_sequin_mode:
|
|
self.toggle_sequins() # can't jump with sequin mode on.
|
|
self.add(JUMP, new_x, new_y)
|
|
self.update_needle_position(new_x, new_y)
|
|
|
|
def stitch_with_contingency(self, new_x, new_y):
|
|
if self.long_stitch_contingency == CONTINGENCY_LONG_STITCH_SEW_TO:
|
|
self.sew_to(new_x, new_y)
|
|
elif self.long_stitch_contingency == CONTINGENCY_LONG_STITCH_JUMP_NEEDLE:
|
|
self.needle_to(new_x, new_y)
|
|
else:
|
|
self.stitch_at(new_x, new_y)
|
|
|
|
def sew_to(self, new_x, new_y):
|
|
"""Stitches to a specific location, with the emphasis on sewing.
|
|
Subdivides long stitches into additional stitches.
|
|
"""
|
|
x0 = self.needle_x
|
|
y0 = self.needle_y
|
|
max_length = self.max_stitch
|
|
self.interpolate_gap_stitches(x0, y0, new_x, new_y, max_length, STITCH)
|
|
self.stitch_at(new_x, new_y)
|
|
|
|
def needle_to(self, new_x, new_y):
|
|
"""Insert needle at specific location, emphasis on the needle.
|
|
Uses jumps to avoid needle penetrations where possible.
|
|
|
|
The limit here is the max stitch limit or jump threshold.
|
|
If jump threshold is set low, it will insert jumps even
|
|
between stitches it could have technically encoded values for.
|
|
|
|
Stitches to the new location, adding jumps if needed.
|
|
"""
|
|
x0 = self.needle_x
|
|
y0 = self.needle_y
|
|
max_length = self.max_stitch
|
|
self.interpolate_gap_stitches(x0, y0, new_x, new_y, max_length, JUMP)
|
|
self.stitch_at(new_x, new_y)
|
|
|
|
def stitch_at(self, new_x, new_y):
|
|
"""Inserts a stitch at the specific location.
|
|
Should have already been checked for constraints."""
|
|
self.add(STITCH, new_x, new_y)
|
|
self.update_needle_position(new_x, new_y)
|
|
|
|
def sequin_at(self, new_x, new_y):
|
|
"""Sequin Ejects at position with the proper Sequin Contingency
|
|
|
|
If long stitch contingency is required and since JUMP is often
|
|
sequin eject, the contingency for long stitch is always sew to.
|
|
This shouldn't be exceptionally rare."""
|
|
contingency = self.sequin_contingency
|
|
if contingency == CONTINGENCY_SEQUIN_REMOVE:
|
|
# Do not update the needle position or declare untrimmed.
|
|
return
|
|
x0 = self.needle_x
|
|
y0 = self.needle_y
|
|
max_length = self.max_stitch
|
|
self.interpolate_gap_stitches(x0, y0, new_x, new_y, max_length, STITCH)
|
|
if contingency == CONTINGENCY_SEQUIN_UTILIZE:
|
|
self.add(SEQUIN_EJECT, new_x, new_y)
|
|
elif contingency == CONTINGENCY_SEQUIN_JUMP:
|
|
self.add(JUMP, new_x, new_y)
|
|
elif contingency == CONTINGENCY_SEQUIN_STITCH:
|
|
self.add(STITCH, new_x, new_y)
|
|
self.update_needle_position(new_x, new_y)
|
|
|
|
def slow_command_here(self):
|
|
if self.writes_speeds:
|
|
self.add(SLOW)
|
|
|
|
def fast_command_here(self):
|
|
if self.writes_speeds:
|
|
self.add(FAST)
|
|
|
|
def stop_here(self):
|
|
self.add(STOP)
|
|
self.state_trimmed = True
|
|
|
|
def end_here(self):
|
|
self.add(END)
|
|
self.state_trimmed = True
|
|
|
|
def next_change_sequence(self):
|
|
self.order_index += 1
|
|
change = self.change_sequence[self.order_index]
|
|
threadlist = self.destination_pattern.threadlist
|
|
threadlist.append(change[3])
|
|
if self.thread_change_command == COLOR_CHANGE:
|
|
if self.order_index != 0:
|
|
self.add_thread_change(COLOR_CHANGE, change[1], change[2])
|
|
elif self.thread_change_command == NEEDLE_SET:
|
|
self.add_thread_change(NEEDLE_SET, change[1], change[2])
|
|
elif self.thread_change_command == STOP:
|
|
self.add_thread_change(STOP, change[1], change[2])
|
|
self.state_trimmed = True
|
|
|
|
def position_will_exceed_constraint(self, length=None, new_x=None, new_y=None):
|
|
"""Check if the stitch is too long before trying to deal with it."""
|
|
if length is None:
|
|
length = self.max_stitch
|
|
if new_x is None or new_y is None:
|
|
p = self.matrix.point_in_matrix_space(self.stitch[0], self.stitch[1])
|
|
new_x = p[0]
|
|
new_y = p[1]
|
|
distance_x = new_x - self.needle_x
|
|
distance_y = new_y - self.needle_y
|
|
return abs(distance_x) > length or abs(distance_y) > length
|
|
|
|
def interpolate_gap_stitches(self, x0, y0, x1, y1, max_length, data):
|
|
"""Command sequence line to x, y, respecting length as maximum.
|
|
This does not arrive_at, it steps to within striking distance.
|
|
The next step can arrive at (x, y) without violating constraint.
|
|
If these are already in range, this command will do nothing.
|
|
|
|
returns the last stitch interpolated by the code.
|
|
"""
|
|
transcode = self.destination_pattern.stitches
|
|
distance_x = x1 - x0
|
|
distance_y = y1 - y0
|
|
if abs(distance_x) > max_length or abs(distance_y) > max_length:
|
|
if data == JUMP and self.state_sequin_mode:
|
|
self.toggle_sequins() # can't jump with sequin mode on.
|
|
|
|
# python 2,3 patch of division that could be integer.
|
|
steps_x = math.ceil(abs(distance_x / (max_length * 1.0)))
|
|
steps_y = math.ceil(abs(distance_y / (max_length * 1.0)))
|
|
if steps_x > steps_y:
|
|
steps = steps_x
|
|
else:
|
|
steps = steps_y
|
|
step_size_x = distance_x / steps
|
|
step_size_y = distance_y / steps
|
|
qx = x0
|
|
qy = y0
|
|
for q in range(1, int(steps)):
|
|
# we need the gap stitches only, not start or end stitch.
|
|
qx += step_size_x
|
|
qy += step_size_y
|
|
stitch = [qx, qy, data | self.high_flags]
|
|
transcode.append(stitch)
|
|
self.update_needle_position(stitch[0], stitch[1])
|
|
|
|
def lock_stitch(self, x, y, anchor_x, anchor_y, max_length=None):
|
|
"""Tie-on, Tie-off. Lock stitch from current location towards
|
|
anchor location.Ends again at lock location. May not exceed
|
|
max_length in the process."""
|
|
if max_length is None:
|
|
max_length = self.max_stitch
|
|
transcode = self.destination_pattern.stitches
|
|
length = distance(x, y, anchor_x, anchor_y)
|
|
if length > max_length:
|
|
p = oriented(x, y, anchor_x, anchor_y, max_length)
|
|
anchor_x = p[0]
|
|
anchor_y = p[1]
|
|
for amount in (0.33, 0.66, 0.33, 0):
|
|
transcode.append(
|
|
[towards(x, anchor_x, amount), towards(y, anchor_y, amount), STITCH]
|
|
)
|
|
|
|
|
|
def distance_squared(x0, y0, x1, y1):
|
|
"""squared of distance between x0,y0 and x1,y1"""
|
|
dx = x1 - x0
|
|
dy = y1 - y0
|
|
dx *= dx
|
|
dy *= dy
|
|
return dx + dy
|
|
|
|
|
|
def distance(x0, y0, x1, y1):
|
|
"""distance between x0,y0 and x1,y1"""
|
|
return math.sqrt(distance_squared(x0, y0, x1, y1))
|
|
|
|
|
|
def towards(a, b, amount):
|
|
"""amount between [0,1] -> [a,b]"""
|
|
return (amount * (b - a)) + a
|
|
|
|
|
|
def angle_radians(x0, y0, x1, y1):
|
|
"""Angle in radians between x0,y0 and x1,y1"""
|
|
return math.atan2(y1 - y0, x1 - x0)
|
|
|
|
|
|
def oriented(x0, y0, x1, y1, r):
|
|
"""from x0,y0 in the direction of x1,y1 in the distance of r"""
|
|
radians = angle_radians(x0, y0, x1, y1)
|
|
return x0 + (r * math.cos(radians)), y0 + (r * math.sin(radians))
|