summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/extensions/convert_to_satin.py45
1 files changed, 25 insertions, 20 deletions
diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py
index 1227b207..c666b625 100644
--- a/lib/extensions/convert_to_satin.py
+++ b/lib/extensions/convert_to_satin.py
@@ -1,18 +1,17 @@
-from copy import deepcopy
-from itertools import chain, groupby
import math
+from itertools import chain, groupby
import inkex
-from numpy import diff, sign, setdiff1d
import numpy
+from numpy import diff, setdiff1d, sign
from shapely import geometry as shgeo
+from .base import InkstitchExtension
from ..elements import Stroke
from ..i18n import _
-from ..svg import get_correction_transform, PIXELS_PER_MM
+from ..svg import PIXELS_PER_MM, get_correction_transform
from ..svg.tags import SVG_PATH_TAG
from ..utils import Point
-from .base import InkstitchExtension
class SelfIntersectionError(Exception):
@@ -49,23 +48,26 @@ class ConvertToSatin(InkstitchExtension):
# ignore paths with just one point -- they're not visible to the user anyway
continue
- self.fix_loop(path)
-
- try:
- rails, rungs = self.path_to_satin(path, element.stroke_width, style_args)
- except SelfIntersectionError:
- inkex.errormsg(
- _("Cannot convert %s to a satin column because it intersects itself. Try breaking it up into multiple paths.") %
- element.node.get('id'))
+ for satin in self.convert_path_to_satins(path, element.stroke_width, style_args, correction_transform, path_style):
+ parent.insert(index, satin)
+ index += 1
- # revert any changes we've made
- self.document = deepcopy(self.original_document)
+ parent.remove(element.node)
- return
+ def convert_path_to_satins(self, path, stroke_width, style_args, correction_transform, path_style):
+ try:
+ rails, rungs = self.path_to_satin(path, stroke_width, style_args)
+ yield self.satin_to_svg_node(rails, rungs, correction_transform, path_style)
+ except SelfIntersectionError:
+ # The path intersects itself. Split it in two and try doing the halves
+ # individually.
- parent.insert(index, self.satin_to_svg_node(rails, rungs, correction_transform, path_style))
+ half = int(len(path) / 2.0)
+ halves = [path[:half + 1], path[half:]]
- parent.remove(element.node)
+ for path in halves:
+ for satin in self.convert_path_to_satins(path, stroke_width, style_args, correction_transform, path_style):
+ yield satin
def fix_loop(self, path):
if path[0] == path[-1]:
@@ -107,13 +109,16 @@ class ConvertToSatin(InkstitchExtension):
return args
def path_to_satin(self, path, stroke_width, style_args):
+ if Point(*path[0]).distance(Point(*path[-1])) < 1:
+ raise SelfIntersectionError()
+
path = shgeo.LineString(path)
left_rail = path.parallel_offset(stroke_width / 2.0, 'left', **style_args)
right_rail = path.parallel_offset(stroke_width / 2.0, 'right', **style_args)
if not isinstance(left_rail, shgeo.LineString) or \
- not isinstance(right_rail, shgeo.LineString):
+ not isinstance(right_rail, shgeo.LineString):
# If the parallel offsets come out as anything but a LineString, that means the
# path intersects itself, when taking its stroke width into consideration. See
# the last example for parallel_offset() in the Shapely documentation:
@@ -248,7 +253,7 @@ class ConvertToSatin(InkstitchExtension):
# arbitrarily rejects the rung if there was one less than 2
# millimeters before this one.
if last_rung_center is not None and \
- (rung_center - last_rung_center).length() < 2 * PIXELS_PER_MM:
+ (rung_center - last_rung_center).length() < 2 * PIXELS_PER_MM:
continue
else:
last_rung_center = rung_center