summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/extensions/outline.py72
-rw-r--r--templates/outline.xml17
2 files changed, 67 insertions, 22 deletions
diff --git a/lib/extensions/outline.py b/lib/extensions/outline.py
index d6a16e99..364cec6d 100644
--- a/lib/extensions/outline.py
+++ b/lib/extensions/outline.py
@@ -3,48 +3,80 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-import inkex
-from shapely import concave_hull
-from shapely.geometry import LineString, MultiPolygon
+from inkex import Boolean, Path, errormsg
+from shapely import offset_curve
+from shapely.geometry import LineString, MultiPolygon, Polygon
from ..i18n import _
+from ..svg import PIXELS_PER_MM
from ..svg.tags import SVG_PATH_TAG
+from ..utils.geometry import ensure_multi_line_string
+from ..utils.smoothing import smooth_path
from .base import InkstitchExtension
class Outline(InkstitchExtension):
def __init__(self, *args, **kwargs):
InkstitchExtension.__init__(self, *args, **kwargs)
- self.arg_parser.add_argument("-r", "--ratio", type=float, default=0.0, dest="ratio")
- self.arg_parser.add_argument("-a", "--allow-holes", type=inkex.Boolean, default=False, dest="allow_holes")
+ self.arg_parser.add_argument("-k", "--keep-original", type=Boolean, default=False, dest="keep_original")
+ self.arg_parser.add_argument("-b", "--buffer", type=float, default=0.001, dest="buffer")
+ self.arg_parser.add_argument("-s", "--smoothness", type=float, default=0.3, dest="smoothness")
+ self.arg_parser.add_argument("-t", "--threshold", type=float, default=10.0, dest="threshold")
+ self.arg_parser.add_argument("-i", "--inset", type=float, default=0.001, dest="inset")
def effect(self):
if not self.svg.selection:
- inkex.errormsg(_("Please select one or more shapes to convert to their outline."))
+ errormsg(_("Please select one or more shapes to convert to their outline."))
return
+ self.threshold = self.options.threshold * PIXELS_PER_MM
+ self.shape_buffer = max(self.options.buffer * PIXELS_PER_MM, 0.001)
+ self.smoothness = self.options.smoothness * PIXELS_PER_MM
+ self.inset = self.options.inset * PIXELS_PER_MM
+
for element in self.svg.selection:
self.element_to_outline(element)
+ def get_outline(self, element):
+ d = ''
+ transform = element.composed_transform()
+ path = Path(element.get_path()).transform(transform).break_apart()
+ for subpath in path:
+ points = subpath.end_points
+ shape = LineString(points).buffer(self.shape_buffer)
+ outline = ensure_multi_line_string(offset_curve(shape, -self.inset))
+
+ interiors = []
+ for interior in outline.geoms:
+ if Polygon(interior).area < self.threshold:
+ continue
+ interior_path = smooth_path(interior.coords, self.smoothness)
+ if len(interior_path) > 2:
+ interiors.append(Polygon(interior_path))
+ outline = MultiPolygon(interiors)
+
+ for geom in outline.geoms:
+ d += str(Path(geom.exterior.coords).transform(-transform))
+ return d
+
def element_to_outline(self, element):
if element.tag_name == 'g':
for element in element.iterdescendants(SVG_PATH_TAG):
self.element_to_outline(element)
return
-
- path = element.get_path()
- path = path.end_points
- hull = concave_hull(LineString(path), ratio=self.options.ratio, allow_holes=self.options.allow_holes)
- if isinstance(hull, LineString):
+ elif element.tag_name != 'path':
+ element_id = element.label or element.get_id()
+ errormsg(_("{element_id} is not a path element. "
+ "This extension is designed to generate an outline of an embroidery pattern.").format(element_id=element_id))
return
- if not isinstance(hull, MultiPolygon):
- hull = MultiPolygon([hull])
- d = ''
- for geom in hull.geoms:
- d += 'M '
- for x, y in geom.exterior.coords:
- d += f'{x}, {y} '
- d += "Z"
+ d = self.get_outline(element)
+ if not d:
+ errormsg(_("Could not generate path from element {element_id} with the given settings.").format(element_id=element_id))
+ return
- element.set('d', d)
+ if self.options.keep_original:
+ new_element = element.duplicate()
+ new_element.set('d', d)
+ else:
+ element.set('d', d)
diff --git a/templates/outline.xml b/templates/outline.xml
index b9db7500..06997c2d 100644
--- a/templates/outline.xml
+++ b/templates/outline.xml
@@ -11,8 +11,21 @@
</submenu>
</effects-menu>
</effect>
- <param name="ratio" type="float" appearance="full" gui-text="Ratio" precision="2" min="0" max="1">0</param>
- <param name="allow-holes" type="boolean" gui-text="Allow Holes">false</param>
+ <param name="keep-original" type="boolean" gui-text="Keep original element(s)">false</param>
+ <spacer />
+ <separator />
+ <spacer />
+ <param name="threshold" type="float" appearance="full" precision="2" min="0" max="100"
+ gui-text="Threshold (mm²)" gui-description="Removes smaller shape areas than that">10</param>
+ <param name="buffer" type="float" appearance="full" precision="3" min="0.001" max="1"
+ gui-text="Buffer (mm)" gui-description="Enlarge strokes by this amount">0.001</param>
+ <param name="smoothness" type="float" appearance="full" precision="2" min="0.01" max="5"
+ gui-text="Smoothness (mm)" gui-description="Smooth path by this amount">0.3</param>
+ <spacer />
+ <separator />
+ <spacer />
+ <param name="inset" type="float" appearance="full" precision="3" min="0" max="5"
+ gui-text="Inset (mm)" gui-description="Counteract the buffer effect">0</param>
<script>
{{ command_tag | safe }}
</script>