diff options
| author | Kaalleen <36401965+kaalleen@users.noreply.github.com> | 2024-12-26 16:19:35 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-26 16:19:35 +0100 |
| commit | ef7d056173cc6d7782d6120c031dae9276725a3d (patch) | |
| tree | 75d2ef67976336a93424b504e42bbf1a394b9a49 /lib/elements/element.py | |
| parent | e20161a4ec9a69cb0f1bdfdd16bcd27a8601fde7 (diff) | |
End points (#3370)
* end at nearest point to next element (if requested and possible)
Diffstat (limited to 'lib/elements/element.py')
| -rw-r--r-- | lib/elements/element.py | 71 |
1 files changed, 53 insertions, 18 deletions
diff --git a/lib/elements/element.py b/lib/elements/element.py index ac5cf51f..0846b7ab 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -5,26 +5,29 @@ import sys from contextlib import contextmanager from copy import deepcopy +from typing import List, Optional import inkex import numpy as np -from inkex import bezier, BaseElement -from typing import List, Optional +from inkex import BaseElement, bezier +from shapely import Point as ShapelyPoint +from shapely.ops import nearest_points from ..commands import Command, find_commands -from ..stitch_plan import StitchGroup from ..debug.debug import debug from ..exceptions import InkstitchException, format_uncaught_exception from ..i18n import _ from ..marker import get_marker_elements_cache_key_data from ..patterns import apply_patterns, get_patterns_cache_key_data +from ..stitch_plan import StitchGroup from ..stitch_plan.lock_stitch import (LOCK_DEFAULTS, AbsoluteLock, CustomLock, LockStitch, SVGLock) from ..svg import (PIXELS_PER_MM, apply_transforms, convert_length, get_node_transform) from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_ATTRIBS from ..utils import Point, cache -from ..utils.cache import get_stitch_plan_cache, is_cache_disabled, CacheKeyGenerator +from ..utils.cache import (CacheKeyGenerator, get_stitch_plan_cache, + is_cache_disabled) class Param(object): @@ -437,6 +440,12 @@ class EmbroideryElement(object): raise NotImplementedError("INTERNAL ERROR: %s must implement shape()", self.__class__) @property + def first_stitch(self): + # first stitch is an approximation to where the first stitch may possibly be + # if not defined through commands or repositioned by the previous element + raise NotImplementedError("INTERNAL ERROR: %s must implement first_stitch()", self.__class__) + + @property @cache def commands(self) -> List[Command]: return find_commands(self.node) @@ -497,11 +506,11 @@ class EmbroideryElement(object): return lock_start, lock_end - def to_stitch_groups(self, last_stitch_group: Optional[StitchGroup]) -> List[StitchGroup]: + def to_stitch_groups(self, last_stitch_group: Optional[StitchGroup], next_element: Optional[ShapelyPoint] = None) -> List[StitchGroup]: raise NotImplementedError("%s must implement to_stitch_groups()" % self.__class__.__name__) @debug.time - def _load_cached_stitch_groups(self, previous_stitch): + def _load_cached_stitch_groups(self, previous_stitch, next_element): if is_cache_disabled(): return None @@ -509,7 +518,7 @@ class EmbroideryElement(object): # we don't care about the previous stitch previous_stitch = None - cache_key = self.get_cache_key(previous_stitch) + cache_key = self.get_cache_key(previous_stitch, next_element) stitch_groups = get_stitch_plan_cache().get(cache_key) if stitch_groups: @@ -526,20 +535,27 @@ class EmbroideryElement(object): """ return False + def uses_next_element(self) -> bool: + """Returns True if the shape of the next element can affect this Element's stitches. + + This function may be overridden in a subclass. + """ + return False + @debug.time - def _save_cached_stitch_groups(self, stitch_groups, previous_stitch): + def _save_cached_stitch_groups(self, stitch_groups, previous_stitch, next_element): if is_cache_disabled(): return stitch_plan_cache = get_stitch_plan_cache() - cache_key = self.get_cache_key(previous_stitch) + cache_key = self.get_cache_key(previous_stitch, next_element) if cache_key not in stitch_plan_cache: stitch_plan_cache[cache_key] = stitch_groups if previous_stitch is not None: # Also store it with None as the previous stitch, so that it can be used next time # if we don't care about the previous stitch - cache_key = self.get_cache_key(None) + cache_key = self.get_cache_key(None, None) if cache_key not in stitch_plan_cache: stitch_plan_cache[cache_key] = stitch_groups @@ -569,10 +585,10 @@ class EmbroideryElement(object): def _get_tartan_key_data(self): return (self.node.get('inkstitch:tartan', None)) - def get_cache_key_data(self, previous_stitch): + def get_cache_key_data(self, previous_stitch, next_element): return [] - def get_cache_key(self, previous_stitch): + def get_cache_key(self, previous_stitch, next_element): cache_key_generator = CacheKeyGenerator() cache_key_generator.update(self.__class__.__name__) cache_key_generator.update(self.get_params_and_values()) @@ -580,18 +596,20 @@ class EmbroideryElement(object): cache_key_generator.update(list(self._get_specified_style().items())) cache_key_generator.update(self._get_gradient_cache_key_data()) cache_key_generator.update(previous_stitch) + if next_element is not None: + cache_key_generator.update(next_element.get_cache_key(None, None)) cache_key_generator.update([(c.command, c.target_point) for c in self.commands]) cache_key_generator.update(self._get_patterns_cache_key_data()) cache_key_generator.update(self._get_guides_cache_key_data()) - cache_key_generator.update(self.get_cache_key_data(previous_stitch)) + cache_key_generator.update(self.get_cache_key_data(previous_stitch, next_element)) cache_key_generator.update(self._get_tartan_key_data()) cache_key = cache_key_generator.get_cache_key() - debug.log(f"cache key for {self.node.get('id')} {self.node.get(INKSCAPE_LABEL)} {previous_stitch}: {cache_key}") + debug.log(f"cache key for {self.node.get('id')} {self.node.get(INKSCAPE_LABEL)} {previous_stitch} {next_element}: {cache_key}") return cache_key - def embroider(self, last_stitch_group: Optional[StitchGroup]) -> List[StitchGroup]: + def embroider(self, last_stitch_group: Optional[StitchGroup], next_element=None) -> List[StitchGroup]: debug.log(f"starting {self.node.get('id')} {self.node.get(INKSCAPE_LABEL)}") with self.handle_unexpected_exceptions(): @@ -599,12 +617,13 @@ class EmbroideryElement(object): previous_stitch = last_stitch_group.stitches[-1] else: previous_stitch = None - stitch_groups = self._load_cached_stitch_groups(previous_stitch) + + stitch_groups = self._load_cached_stitch_groups(previous_stitch, next_element) if not stitch_groups: self.validate() - stitch_groups = self.to_stitch_groups(last_stitch_group) + stitch_groups = self.to_stitch_groups(last_stitch_group, next_element) apply_patterns(stitch_groups, self.node) if stitch_groups: @@ -617,11 +636,27 @@ class EmbroideryElement(object): 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) + self._save_cached_stitch_groups(stitch_groups, previous_stitch, next_element) debug.log(f"ending {self.node.get('id')} {self.node.get(INKSCAPE_LABEL)}") return stitch_groups + def next_stitch(self, next_element): + next_stitch = None + if next_element is not None and self.uses_next_element(): + # in fact we really only try an approximation to the next stitch + if next_element.uses_previous_stitch(): + try: + next_stitch = nearest_points(next_element.shape, self.shape)[1] + except (ValueError, AttributeError): + pass + else: + try: + next_stitch = nearest_points(next_element.first_stitch, self.shape)[1] + except (ValueError, AttributeError): + pass + return next_stitch + def fatal(self, message, point_to_troubleshoot=False): label = self.node.get(INKSCAPE_LABEL) id = self.node.get("id") |
