Rewrite outline extensions (#2881)

pull/2892/head
Kaalleen 2024-05-09 10:32:08 +02:00 zatwierdzone przez GitHub
rodzic 62b8529120
commit 958cb4210a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
2 zmienionych plików z 67 dodań i 22 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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>