From 303a3f38edf9df380b41d98a47e7e7765cda2116 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Thu, 13 Aug 2020 01:28:19 +0200 Subject: [PATCH] Added Migrate Groups extension, some small fixes --- extensions/fablabchemnitz_cleangroups.py | 19 ++++-- extensions/fablabchemnitz_contour_scanner.inx | 10 +-- extensions/fablabchemnitz_contour_scanner.py | 13 ++-- extensions/fablabchemnitz_migrategroups.inx | 16 +++++ extensions/fablabchemnitz_migrategroups.py | 64 +++++++++++++++++++ 5 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 extensions/fablabchemnitz_migrategroups.inx create mode 100644 extensions/fablabchemnitz_migrategroups.py diff --git a/extensions/fablabchemnitz_cleangroups.py b/extensions/fablabchemnitz_cleangroups.py index 4257f162..cd24417c 100644 --- a/extensions/fablabchemnitz_cleangroups.py +++ b/extensions/fablabchemnitz_cleangroups.py @@ -8,9 +8,18 @@ class CleanGroups(inkex.Effect): inkex.Effect.__init__(self) def effect(self): - groups = self.document.xpath('//svg:g',namespaces=inkex.NSS) - for group in groups: - if len(group.getchildren()) == 0: - group.getparent().remove(group) - + while True: + groups = self.document.xpath('//svg:g',namespaces=inkex.NSS) + oldLen = len(groups) + #leave the loop if there are no groups at all + if len(groups) == 0: + break + #loop trough groups. we have minimum of one to check for emptyness + for group in groups: + if len(group.getchildren()) == 0 and group.getparent() is not None: + group.getparent().remove(group) #deletes the deepest empty group + continue #we found minimum of one element to delete. so we should run another cycle to check if the parent of this group is empty after deletion + newLen = len(self.document.xpath('//svg:g',namespaces=inkex.NSS)) + if newLen == oldLen: #found no more empty groups. Leaving the loop + break CleanGroups().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz_contour_scanner.inx b/extensions/fablabchemnitz_contour_scanner.inx index 58fa1641..04c8e1f8 100644 --- a/extensions/fablabchemnitz_contour_scanner.inx +++ b/extensions/fablabchemnitz_contour_scanner.inx @@ -4,20 +4,20 @@ fablabchemnitz.de.contour_scanner - This tool helps you to find nasty contours which might bug you and prevent your work from being ready for production. It will find open contours, closed contours and self-intersecting contours. Intersecting contours can be closed or open contours so you can select this option additionally! Last ones usually happen if two or more handles (points) are coincident but which you might don't see. Or you just have large overlaps where the contour crosses itself like an 'eight' character for example. Using the highlighting it's easy to find contours with unproper path handles. Note that if you did not select any paths it will scan the whole document instead. + This tool helps you to find nasty contours which might bug you and prevent your work from being ready for production. You can find the complete documentation at the Wiki space of https://fablabchemnitz.de General false false 1.0 Highlight paths true - #FF0000FF + 4012452351 true - #00FF00FF + 2330080511 true - #0000FFFF + 1923076095 true - #0066FFFF + 4239343359 10 Remove paths false diff --git a/extensions/fablabchemnitz_contour_scanner.py b/extensions/fablabchemnitz_contour_scanner.py index 73fb988c..f0080116 100644 --- a/extensions/fablabchemnitz_contour_scanner.py +++ b/extensions/fablabchemnitz_contour_scanner.py @@ -6,17 +6,17 @@ Features - helps to find contours which are closed or not. Good for repairing contours, closing contours,... - works for paths which are packed into groups or groups of groups. # - can break contours apart like in "Path -> Break Apart" - - implements Bentley-Ottmann algorithm from https://github.com/ideasman42/isect_segments-bentley_ottmann to scan for self-intersecting paths. This only works correctly if your path is within the canvas correctly. Otherwise you might get "assert(event.in_sweep == False) AssertionError". This is commented out yet + - implements Bentley-Ottmann algorithm from https://github.com/ideasman42/isect_segments-bentley_ottmann to scan for self-intersecting paths. You might get "assert(event.in_sweep == False) AssertionError". Don't know how to fix rgis - colorized paths respective to their type - can add dots to intersection points you'd like to fix Author: Mario Voigt / FabLab Chemnitz Mail: mario.voigt@stadtfabrikanten.org Date: 09.08.2020 +Last patch: 11.08.2020 License: GNU GPL v3 """ -import sys from math import * import inkex from inkex.paths import Path, CubicSuperPath @@ -25,9 +25,6 @@ from lxml import etree import fablabchemnitz_poly_point_isect import copy -def pout(t): - sys.exit() - def adjustStyle(self, node): if node.attrib.has_key('style'): style = node.get('style') @@ -185,9 +182,9 @@ class ContourScanner(inkex.Effect): if self.options.remove_selfintersecting: if node.getparent() is not None: #might be already been deleted by previously checked settings so check again node.getparent().remove(node) - except AssertionError as e: # we skip AssertionError - pass - #inkex.utils.debug("Found some path which cannot be tested for self-intersecting behaviour") + except Exception as e: # we skip AssertionError + #inkex.utils.debug("Accuracy Error. Try to reduce the precision of the paths using the extension called Rounder to cutoff unrequired decimals.") + print(str(e)) for child in node: self.scanContours(child) diff --git a/extensions/fablabchemnitz_migrategroups.inx b/extensions/fablabchemnitz_migrategroups.inx new file mode 100644 index 00000000..a2ab7095 --- /dev/null +++ b/extensions/fablabchemnitz_migrategroups.inx @@ -0,0 +1,16 @@ + + + Migrate Groups + fablabchemnitz.de.migrategroups + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz_migrategroups.py b/extensions/fablabchemnitz_migrategroups.py new file mode 100644 index 00000000..fe57dbd5 --- /dev/null +++ b/extensions/fablabchemnitz_migrategroups.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +""" +Extension for InkScape 1.0 + +This extension parses the selection and will put all paths into one single group. If you have a cluster with lots of groups and paths you will clean up this way (one top level group, all paths below it). If you select a single path or a set of paths you just wrap it like using CTRL + G (like making a usual group) + +Author: Mario Voigt / FabLab Chemnitz +Mail: mario.voigt@stadtfabrikanten.org +Date: 13.08.2020 +License: GNU GPL v3 +""" + +import inkex + +class MigrateGroups(inkex.Effect): + + allPaths = [] + allGroups = [] + + def __init__(self): + inkex.Effect.__init__(self) + + def effect(self): + + #get all paths and groups from selection. Remove all groups from the selection and form a new single group of it + def parseNodes(self, node): + if node.tag == inkex.addNS('path','svg'): + if node not in self.allPaths: + self.allPaths.append(node) + if node.tag == inkex.addNS('g','svg'): + groups = node.getchildren() + if groups is not None: + for group in groups: + parseNodes(self, group) + if group not in self.allGroups: + self.allGroups.append(group) + + for id, item in self.svg.selected.items(): + parseNodes(self, item) + + if len(self.allPaths) > 0: + #make a new group at root level - TODO: respect the position where the first selected object is in XML tree and put it there instead (or make this optional) + newGroup = self.document.getroot().add(inkex.Group()) + + #copy all paths into the new group + for path in self.allPaths: + newGroup.add(path.copy()) + + #then remove all the old stuff + path.getparent().remove(path) + + #now remove all the obsolete groups + if len(self.allGroups) > 0: + for group in self.allGroups: + if group.getparent() is not None: + group.getparent().remove(group) + + #remove the selected, now empty group (if it's the case) + if self.svg.selected[0].tag == inkex.addNS('g','svg'): + self.svg.selected[0].getparent().remove(self.svg.selected[0]) + + #TODO: make newGroup selected now. How ? +MigrateGroups().run() \ No newline at end of file