kopia lustrzana https://github.com/inkstitch/inkstitch
handle self-intersecting paths in convert to satin
rodzic
6a62e0d82b
commit
d52373a32a
|
@ -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,24 +48,27 @@ 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'))
|
||||
|
||||
# revert any changes we've made
|
||||
self.document = deepcopy(self.original_document)
|
||||
|
||||
return
|
||||
|
||||
parent.insert(index, self.satin_to_svg_node(rails, rungs, correction_transform, path_style))
|
||||
for satin in self.convert_path_to_satins(path, element.stroke_width, style_args, correction_transform, path_style):
|
||||
parent.insert(index, satin)
|
||||
index += 1
|
||||
|
||||
parent.remove(element.node)
|
||||
|
||||
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.
|
||||
|
||||
half = int(len(path) / 2.0)
|
||||
halves = [path[:half + 1], path[half:]]
|
||||
|
||||
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]:
|
||||
# Looping paths seem to confuse shapely's parallel_offset(). It loses track
|
||||
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue