added more options to contour scanner, fixed bugs

This commit is contained in:
Mario Voigt 2021-06-04 06:13:42 +02:00
parent 8eac0bcfb3
commit 7c2de7d7e3
2 changed files with 98 additions and 46 deletions

View File

@ -8,17 +8,30 @@
<param name="show_debug" type="bool" gui-text="Show debug infos">false</param>
<param name="break_apart" type="bool" gui-text="Break apart input" gui-description="Break apart input paths into sub paths. Modifies original paths">false</param>
<param name="handle_groups" type="bool" gui-text="Handle groups" gui-description="Also looks for paths in groups which are in the current selection. Note: The generated results have a different structure (less granularity due to grouping) than directly selected paths. The colorization for non-intersected paths will be different too.">false</param>
<param name="path_types" type="optiongroup" appearance="combo" gui-text="Scanning selection" gui-description="Process open paths by other open paths, closed paths by other closed paths, or all paths by all other paths">
<option value="both">all:all paths</option>
<option value="open_paths">open:open paths</option>
<option value="closed_paths">closed:closed paths</option>
</param>
<param name="flattenbezier" type="bool" gui-text="Quantization (flatten bezier curves to polylines)" gui-description="Convert bezier curves to polylines.">true</param>
<param name="flatness" type="float" min="0.001" max="99999.000" precision="3" gui-text="Flatness (tolerance)" gui-description="Minimum flatness = 0.001. The smaller the value the more fine segments you will get (quantization). Large values might destroy the line continuity.">0.100</param>
<param name="decimals" type="int" min="0" max="16" gui-text="Decimals" gui-description="Accuracy for sub split lines / lines trimmed by shapely (default: 3)">3</param>
<param name="snap_tolerance" type="float" min="0.01" max="10.0" gui-text="Snap tolerance" gui-description="Snap tolerance for intersection points on paths (default: 0.1)">0.1</param>
<param name="draw_subsplit" type="bool" gui-text="Draw sub split lines (for debugging purposes)" gui-description="Draws polylines">false</param>
<param name="draw_subsplit" type="bool" gui-text="Draw sub split lines (for debugging purposes)" gui-description="Draws polylines. Will be automatically enabled if any highlighting below is activated.">false</param>
</page>
<page name="tab_scanning" gui-text="Scanning and Trimming">
<hbox>
<vbox>
<label appearance="header">Scanning</label>
<label appearance="header">Removing</label>
<param name="remove_polylines" type="bool" gui-text="Remove original polylines paths">false</param>
<param name="remove_beziers" type="bool" gui-text="Remove original beziers paths">false</param>
<param name="remove_opened" type="bool" gui-text="Remove original opened paths">false</param>
<param name="remove_closed" type="bool" gui-text="Remove original closed paths">false</param>
<param name="remove_self_intersecting" type="bool" gui-text="Remove original self-intersecting paths">false</param>
<separator/>
<label appearance="header">Highlighting</label>
<param name="highlight_polylines" type="bool" gui-text="Highlight polyline paths">false</param>
<param name="highlight_beziers" type="bool" gui-text="Highlight bezier paths">false</param>
<param name="highlight_opened" type="bool" gui-text="Highlight opened paths">false</param>
<param name="highlight_closed" type="bool" gui-text="Highlight closed paths">false</param>
<param name="highlight_self_intersecting" type="bool" gui-text="Highlight self-intersecting paths" gui-description="Requires to draw sub split lines. Will override highlighting colors for open and closed paths (if those options are enabled)">false</param>
@ -28,11 +41,6 @@
<separator/>
<vbox>
<label appearance="header">Trimming</label>
<param name="path_types" type="optiongroup" appearance="combo" gui-text="Trimming selection" gui-description="Trim open paths by other open paths, closed paths by other closed paths, or all paths by all other paths">
<option value="both">all:all paths</option>
<option value="open_paths">open:open paths</option>
<option value="closed_paths">closed:closed paths</option>
</param>
<param name="draw_trimmed" type="bool" gui-text="Draw trimmed lines">false</param>
<param name="combine_nonintersects" type="bool" gui-text="Chain + combine non-intersected lines">true</param>
<param name="remove_duplicates" type="bool" gui-text="Remove duplicate trim lines">true</param>
@ -51,8 +59,10 @@
<param name="removefillsetstroke" type="bool" gui-text="Remove fill and define stroke" gui-description="Modifies original path style">false</param>
<param name="apply_original_style" type="bool" gui-text="Original style for trimmed lines" gui-description="Apply original path style to trimmed lines.">true</param>
<label appearance="header">Scanning Colors</label>
<param name="color_opened" type="color" appearance="colorbutton" gui-text="Color for opened contours">4012452351</param>
<param name="color_closed" type="color" appearance="colorbutton" gui-text="Color for closed contours">2330080511</param>
<param name="color_polyline" type="color" appearance="colorbutton" gui-text="Color for polyline paths">4289703935</param>
<param name="color_bezier" type="color" appearance="colorbutton" gui-text="Color for bezier paths">258744063</param>
<param name="color_opened" type="color" appearance="colorbutton" gui-text="Color for opened paths">4012452351</param>
<param name="color_closed" type="color" appearance="colorbutton" gui-text="Color for closed paths">2330080511</param>
<param name="color_self_intersecting_paths" type="color" appearance="colorbutton" gui-text="Color for self-intersecting contours">2593756927</param>
<param name="color_subsplit" type="color" appearance="colorbutton" gui-text="Color for sub split lines">1630897151</param>
<param name="color_self_intersections" type="color" appearance="colorbutton" gui-text="Color for self-intersecting line points">6320383</param>
@ -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

View File

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