From 7c2de7d7e3580e7bb03a1c2d7dfd5d0426ce11a9 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Fri, 4 Jun 2021 06:13:42 +0200 Subject: [PATCH] added more options to contour scanner, fixed bugs --- .../contour_scanner_and_trimmer.inx | 29 +++-- .../contour_scanner_and_trimmer.py | 115 ++++++++++++------ 2 files changed, 98 insertions(+), 46 deletions(-) diff --git a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.inx b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.inx index 28bc2d4e..bdca404f 100644 --- a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.inx +++ b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.inx @@ -8,17 +8,30 @@ false false false + + + + + true 0.100 3 0.1 - false + false + + - + + false + false false false false + + + false + false false false false @@ -28,11 +41,6 @@ - - - - - false true true @@ -51,8 +59,10 @@ false true - 4012452351 - 2330080511 + 4289703935 + 258744063 + 4012452351 + 2330080511 2593756927 1630897151 6320383 @@ -76,6 +86,7 @@ - Does not find overlapping colinear lines (sweep line algorithm does not intersect them) Tips: + - If nothings is selected, the whole document will be processed, regardless of groups. In contrast, if you made a custom selection, check to handle or not to handle groups. - Convert your strokes and objects to paths before - Does not work for clones. You will need to unlink them before - Use extensions to filter short/unrequired lines diff --git a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py index 24a79d66..180e5137 100644 --- a/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py +++ b/extensions/fablabchemnitz/contour_scanner_and_trimmer/contour_scanner_and_trimmer.py @@ -41,6 +41,22 @@ Mail: mario.voigt@stadtfabrikanten.org Date: 09.08.2020 (extension originally called "Contour Scanner") Last patch: 01.06.2021 License: GNU GPL v3 + + +efficiently find overlapping lines +- loope durch alle straihgt lines und berechne deren steigung -< einsortieren der linien nach steigung +- wenn die steigung noch nicht erfasst wurde, dann adde die line in eine collection aller in frage kommenden linien +- loope durch die vorfilterung und check für shapely + + intersects() is equivalent to the OR-ing of contains(), crosses(), equals(), touches(), and within(). +So there might be some cases where two lines intersect eachother without crossing, +in particular when one line contains another or when two lines are equals. +More specifically: + crosses() returns True [...] if the dimension of the intersection is less than the dimension of the one or the other. +So if two lines overlap, they won't be considered as "crossing". + intersection() will return a geometric object. + + ''' import sys @@ -122,7 +138,6 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): if len(pathElements) == 0: self.msg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT + C') return - return pathElements if self.options.break_apart is True: breakApartElements = None @@ -133,6 +148,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): if self.options.show_debug is True: self.msg("total processing paths count: {}".format(len(pathElements))) + return pathElements def findGroup(self, groupId): ''' check if a group with a given id exists or not. Returns None if not found, else returns the group element ''' @@ -279,8 +295,12 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): if element.path not in totalTrimPaths: totalTrimPaths.append(element.path) else: + if self.options.show_debug is True: + self.msg("Deleting {}".format(element.get('id'))) element.delete() if len(trimGroup) == 0: + if self.options.show_debug is True: + self.msg("Deleting {}".format(trimGroup.get('id'))) trimGroup.delete() @@ -384,20 +404,24 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): #Settings - General pars.add_argument("--show_debug", type=inkex.Boolean, default=False, help="Show debug infos") - pars.add_argument("--path_types", default="closed_paths", help="Apply for closed paths, open paths or both") pars.add_argument("--break_apart", type=inkex.Boolean, default=False, help="Break apart input paths into sub paths") pars.add_argument("--handle_groups", type=inkex.Boolean, default=False, help="Also looks for paths in groups which are in the current selection") + pars.add_argument("--path_types", default="closed_paths", help="Process open paths by other open paths, closed paths by other closed paths, or all paths by all other paths") pars.add_argument("--flattenbezier", type=inkex.Boolean, default=True, help="Flatten bezier curves to polylines") pars.add_argument("--flatness", type=float, default=0.1, help="Minimum flatness = 0.001. The smaller the value the more fine segments you will get (quantization). Large values might destroy the line continuity.") pars.add_argument("--decimals", type=int, default=3, help="Accuracy for sub split lines / lines trimmed by shapely") pars.add_argument("--snap_tolerance", type=float, default=0.1, help="Snap tolerance for intersection points") #Settings - Scanning + pars.add_argument("--remove_polylines", type=inkex.Boolean, default=False, help="Remove original polyline paths") + pars.add_argument("--remove_beziers", type=inkex.Boolean, default=False, help="Remove original bezier paths") pars.add_argument("--remove_opened", type=inkex.Boolean, default=False, help="Remove original opened paths") pars.add_argument("--remove_closed", type=inkex.Boolean, default=False, help="Remove original closed paths") pars.add_argument("--remove_self_intersecting", type=inkex.Boolean, default=False, help="Remove original self-intersecting paths") - pars.add_argument("--highlight_opened", type=inkex.Boolean, default=False, help="Highlight opened contours") - pars.add_argument("--highlight_closed", type=inkex.Boolean, default=False, help="Highlight closed contours") + pars.add_argument("--highlight_polylines", type=inkex.Boolean, default=False, help="Highlight polyline paths") + pars.add_argument("--highlight_beziers", type=inkex.Boolean, default=False, help="Highlight bezier paths") + pars.add_argument("--highlight_opened", type=inkex.Boolean, default=False, help="Highlight opened paths") + pars.add_argument("--highlight_closed", type=inkex.Boolean, default=False, help="Highlight closed paths") pars.add_argument("--highlight_self_intersecting", type=inkex.Boolean, default=False, help="Highlight self-intersecting contours") pars.add_argument("--draw_subsplit", type=inkex.Boolean, default=False, help="Draw sub split lines (polylines)") pars.add_argument("--visualize_self_intersections", type=inkex.Boolean, default=False, help="Visualize self-intersecting path points") @@ -418,8 +442,10 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): pars.add_argument("--apply_original_style", type=inkex.Boolean, default=True, help="Apply original path style to trimmed lines") #Style - Scanning Colors - pars.add_argument("--color_opened", type=Color, default='4012452351', help="Color for opened contours") - pars.add_argument("--color_closed", type=Color, default='2330080511', help="Color for closed contours") + pars.add_argument("--color_polyline", type=Color, default='4289703935', help="Color for polyline paths") + pars.add_argument("--color_bezier", type=Color, default='258744063', help="Color for bezier paths") + pars.add_argument("--color_opened", type=Color, default='4012452351', help="Color for opened paths") + pars.add_argument("--color_closed", type=Color, default='2330080511', help="Color for closed paths") pars.add_argument("--color_self_intersecting_paths", type=Color, default='2593756927', help="Color for self-intersecting contours") pars.add_argument("--color_subsplit", type=Color, default='1630897151', help="Color for sub split lines") pars.add_argument("--color_self_intersections", type=Color, default='6320383', help="Color for self-intersecting line points") @@ -434,35 +460,29 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): def effect(self): so = self.options - - #warn if there is nothing to visualize - if \ - so.keep_original_after_trim is False and \ - so.remove_opened is True and \ - so.remove_closed is True and \ - so.visualize_self_intersections is False and \ - so.visualize_global_intersections is False and \ - so.draw_subsplit is False and \ - so.draw_trimmed is False: - self.msg("Nothing to draw. Select at least one visualization option.") - return #some dependent configuration for drawing modes if \ + so.highlight_beziers is True or \ + so.highlight_polylines is True or \ so.highlight_opened is True or \ so.highlight_closed is True or \ so.highlight_self_intersecting is True: so.draw_subsplit = True if so.draw_subsplit is False: + so.highlight_beziers = False + so.highlight_polylines = False so.highlight_open = False so.highlight_closed = False so.highlight_self_intersecting = False #some constant stuff / styles - keepOpenPathStyle = {'stroke': str(so.color_opened), 'fill': 'none', 'stroke-width': so.strokewidth} - keepClosedPathStyle = {'stroke': str(so.color_closed), 'fill': 'none', 'stroke-width': so.strokewidth} - keepSelfIntersectingPathStyle = {'stroke': str(so.color_self_intersecting_paths), 'fill': 'none', 'stroke-width': so.strokewidth} - subSplitLineStyle = {'stroke': str(so.color_subsplit), 'fill': 'none', 'stroke-width': so.strokewidth} + polylinePathStyle = {'stroke': str(so.color_polyline), 'fill': 'none', 'stroke-width': so.strokewidth} + bezierPathStyle = {'stroke': str(so.color_bezier), 'fill': 'none', 'stroke-width': so.strokewidth} + openPathStyle = {'stroke': str(so.color_opened), 'fill': 'none', 'stroke-width': so.strokewidth} + closedPathStyle = {'stroke': str(so.color_closed), 'fill': 'none', 'stroke-width': so.strokewidth} + selfIntersectingPathStyle = {'stroke': str(so.color_self_intersecting_paths), 'fill': 'none', 'stroke-width': so.strokewidth} + basicSubSplitLineStyle = {'stroke': str(so.color_subsplit), 'fill': 'none', 'stroke-width': so.strokewidth} ''' 1 // get all paths which are within selection or in document and generate sub split lines @@ -496,25 +516,25 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): Note: highlighting open/closed/self-intersecting contours does work best if you break apart combined paths before. ''' - pathIsClosed = False + isClosed = False path_arrays = path.to_arrays() if path_arrays[-1][0] == 'Z' or \ (path_arrays[-1][0] == 'L' and path_arrays[0][1] == path_arrays[-1][1]) or \ (path_arrays[-1][0] == 'C' and path_arrays[0][1] == [path_arrays[-1][1][-2], path_arrays[-1][1][-1]]) \ : #if first is last point the path is also closed. The "Z" command is not required - pathIsClosed = True + isClosed = True #Check if we should delete the path or not - if so.remove_opened is True and pathIsClosed is False: + if so.remove_opened is True and isClosed is False: pathElement.delete() continue #skip this loop iteration - if so.remove_closed is True and pathIsClosed is True: + if so.remove_closed is True and isClosed is True: pathElement.delete() continue #skip this loop iteration #Check if we should skip or process the path anyway - if so.path_types == 'open_paths' and pathIsClosed is True: continue #skip this loop iteration - elif so.path_types == 'closed_paths' and pathIsClosed is False: continue #skip this loop iteration + if so.path_types == 'open_paths' and isClosed is True: continue #skip this loop iteration + elif so.path_types == 'closed_paths' and isClosed is False: continue #skip this loop iteration elif so.path_types == 'both': pass #adjust the style of original paths if desired. Has influence to the finally trimmed lines style results too! @@ -544,6 +564,12 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): if so.show_debug is True: self.msg("sub path in {} is bezier: {}".format(originalPathId, isBezier)) + deleteFlag = False + if so.remove_beziers is True and isBezier is True: + deleteFlag = True + if so.remove_polylines is True and isBezier is False: + deleteFlag = True + #self.msg("sub path in {} = {}".format(element.get('id'), subPath)) #flatten the subpath if wanted subPathData = CubicSuperPath(subPath) @@ -582,7 +608,9 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): line.path = [['M', [x1, y1]], ['L', [x2, y2]]] if pathElement.getparent() != self.svg.root: line.path = line.path.transform(-pathElement.getparent().composed_transform()) - line.style = subSplitLineStyle + line.style = basicSubSplitLineStyle + line.attrib['isBezier'] = str(isBezier) + line.attrib['isClosed'] = str(isClosed) subSplitTrimLineGroup.add(line) subSplitLines.append([(x1, y1), (x2, y2)]) @@ -591,19 +619,32 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): subSplitIsBezier.append(isBezier) #some dirty flag we need subSplitOriginalPathIds.append(originalPathId) #some dirty flag we need + if deleteFlag is True: + pathElement.delete() + continue #skip the subPath loop + if so.draw_subsplit is True: #check for open/closed again (at first we checked for the combined path. Now we can do for each sub path too! - subPathIsClosed = False - if subSplitLines[0][0] == subSplitLines[-1][1]: - subPathIsClosed = True + #subPathIsClosed = False + #if subSplitLines[0][0] == subSplitLines[-1][1]: + # subPathIsClosed = True + for subSplitLine in subSplitTrimLineGroup: - if subPathIsClosed is True: + if subSplitLine.attrib['isBezier'] == 'True': + if so.highlight_beziers is True: + subSplitLine.style = bezierPathStyle + else: + if so.highlight_polylines is True: + subSplitLine.style = polylinePathStyle + + if subSplitLine.attrib['isClosed'] == 'True': + #if subPathIsClosed is True: if so.highlight_closed is True: - subSplitLine.style = keepClosedPathStyle + subSplitLine.style = closedPathStyle else: if so.highlight_opened is True: - subSplitLine.style = keepOpenPathStyle - + subSplitLine.style = openPathStyle + #check for self intersections selfIntersectionPoints = isect_segments(subSplitLines, validate=True) if len(selfIntersectionPoints) > 0: @@ -612,7 +653,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension): if so.draw_subsplit is True: if so.highlight_self_intersecting is True: for subSplitLine in subSplitTrimLineGroup: - subSplitLine.style = keepSelfIntersectingPathStyle #adjusts line color + subSplitLine.style = selfIntersectingPathStyle #adjusts line color #delete cosmetic sub split lines if desired if so.remove_self_intersecting: subSplitTrimLineGroup.delete()