From 2f4ca0cf10d9aa09711034c683e148b309822b86 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 19 Apr 2020 18:38:28 +0200 Subject: [PATCH] New Extension: Break Apart and Retain Holes (#653) --- lib/extensions/__init__.py | 4 ++- lib/extensions/break_apart.py | 68 +++++++++++++++++++++++++++++++++++ templates/break_apart.inx | 17 +++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/extensions/break_apart.py create mode 100644 templates/break_apart.inx diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index cd8d79fbd..53a6638a0 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -1,4 +1,5 @@ from auto_satin import AutoSatin +from break_apart import BreakApart from convert_to_satin import ConvertToSatin from cut_satin import CutSatin from embroider import Embroider @@ -36,4 +37,5 @@ __all__ = extensions = [Embroider, AutoSatin, Lettering, Troubleshoot, - RemoveEmbroiderySettings] + RemoveEmbroiderySettings, + BreakApart] diff --git a/lib/extensions/break_apart.py b/lib/extensions/break_apart.py new file mode 100644 index 000000000..625ace55a --- /dev/null +++ b/lib/extensions/break_apart.py @@ -0,0 +1,68 @@ +from copy import deepcopy + +from shapely.geometry import Polygon + +import inkex + +from ..elements import AutoFill, Fill +from ..i18n import _ +from ..svg import get_correction_transform +from .base import InkstitchExtension + + +class BreakApart(InkstitchExtension): + def effect(self): # noqa: C901 + if not self.get_elements(): + return + + if not self.selected: + inkex.errormsg(_("Please select one or more fill areas to break apart.")) + return + + for element in self.elements: + if not isinstance(element, AutoFill) and not isinstance(element, Fill): + continue + if len(element.paths) <= 1: + continue + + polygons = [] + multipolygons = [] + holes = [] + + for path in element.paths: + polygons.append(Polygon(path)) + + # sort paths by size and convert to polygons + polygons.sort(key=lambda polygon: polygon.area, reverse=True) + + for shape in polygons: + if shape in holes: + continue + polygon_list = [shape] + + for other in polygons: + if shape != other and shape.contains(other) and other not in holes: + # check if "other" is inside a hole, before we add it to the list + if any(p.contains(other) for p in polygon_list[1:]): + continue + polygon_list.append(other) + holes.append(other) + multipolygons.append(polygon_list) + self.element_to_nodes(multipolygons, element) + + def element_to_nodes(self, multipolygons, element): + for polygons in multipolygons: + el = deepcopy(element) + d = "" + for polygon in polygons: + # copy element and replace path + el.node.set('id', self.uniqueId(element.node.get('id') + "_")) + d += "M" + for x, y in polygon.exterior.coords: + d += "%s,%s " % (x, y) + d += " " + d += "Z" + el.node.set('d', d) + el.node.set('transform', get_correction_transform(element.node)) + element.node.getparent().insert(0, el.node) + element.node.getparent().remove(element.node) diff --git a/templates/break_apart.inx b/templates/break_apart.inx new file mode 100644 index 000000000..2580ddc30 --- /dev/null +++ b/templates/break_apart.inx @@ -0,0 +1,17 @@ + + + {% trans %}Break Apart and Retain Holes{% endtrans %} + org.inkstitch.break_apart.{{ locale }} + break_apart + + all + + + + + + + +