massive efforts in contour scanner and trimmer

This commit is contained in:
Mario Voigt 2021-06-23 23:52:13 +02:00
parent a2c079d8c9
commit 0513eb888f
2 changed files with 262 additions and 161 deletions

View File

@ -2,96 +2,21 @@
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Contour Scanner And Trimmer</name>
<id>fablabchemnitz.de.contour_scanner_and_trimmer</id>
<param name="tab" type="notebook">
<page name="tab_settings" gui-text="Settings">
<label appearance="header">General</label>
<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: converts to absolute paths and might create additional new path elements.">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 and conversion of absolute paths to relative paths) than directly selected paths. The colorization for non-intersected paths will be different too.">false</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. Will be automatically enabled if any highlighting is activated.">false</param>
<param name="remove_subsplit_collinear" type="bool" gui-text="Remove collinear overlapping lines (experimental)" gui-description="Removes any duplicates by merging (multiple) overlapping line segments into longer lines. Not possible to apply for original paths because this routine does not support bezier type paths.">true</param>
<param name="keep_original_after_split_trim" type="bool" gui-text="Keep original paths after sub splitting / trimming">false</param>
</page>
<page name="tab_scanning" gui-text="Scanning and Trimming">
<hbox>
<vbox>
<label appearance="header">Removing Original Paths</label>
<param name="remove_relative" type="bool" gui-text="relative cmd">false</param>
<param name="remove_absolute" type="bool" gui-text="absolute cmd">false</param>
<param name="remove_mixed" type="bool" gui-text="mixed cmd" gui-description="combined relative and absolute">false</param>
<param name="remove_polylines" type="bool" gui-text="polylines">false</param>
<param name="remove_beziers" type="bool" gui-text="beziers">false</param>
<param name="remove_opened" type="bool" gui-text="opened">false</param>
<param name="remove_closed" type="bool" gui-text="closed">false</param>
<param name="remove_self_intersecting" type="bool" gui-text="self-intersecting">false</param>
<separator/>
<label appearance="header">Highlighting</label>
<param name="highlight_relative" type="bool" gui-text="relative cmd paths">false</param>
<param name="highlight_absolute" type="bool" gui-text="absolute cmd paths">false</param>
<param name="highlight_mixed" type="bool" gui-text="mixed cmd paths" gui-description="combined relative and absolute">false</param>
<param name="highlight_polylines" type="bool" gui-text="polyline paths">false</param>
<param name="highlight_beziers" type="bool" gui-text="bezier paths">false</param>
<param name="highlight_opened" type="bool" gui-text="opened paths">false</param>
<param name="highlight_closed" type="bool" gui-text="closed paths">false</param>
<param name="highlight_self_intersecting" type="bool" gui-text="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>
<param name="visualize_self_intersections" type="bool" gui-text="self-intersecting path points">false</param>
<param name="visualize_global_intersections" type="bool" gui-text="global intersection points" gui-description="Will also contain self-intersecting points! Global intersections will only show if 'Draw trimmed lines' is enabled!">false</param>
</vbox>
<separator/>
<vbox>
<label appearance="header">Trimming</label>
<param name="trimming_path_types" type="optiongroup" appearance="combo" gui-text="Trimming selection" gui-description="Process open paths by other open paths, closed paths by other closed paths, or all paths by all other paths. This selection does not apply for paths which intersect themselves!">
<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" gui-description="This will colorize all paths segments which were not intersected ('non-intersected lines'). If the whole path was not intersected at all, it will get another color ('non-intersected paths').">true</param>
<param name="remove_duplicates" type="bool" gui-text="Remove duplicate trim lines">true</param>
<param name="reverse_removal_order" type="bool" gui-text="Reverse removal order" gui-description="Reverses the order of removal. Relevant for keeping certain styles of elements">false</param>
<param name="bezier_trimming" type="bool" gui-text="Trim original beziers (not working yet)" gui-description="If enabled we try to split the original bezier paths at the intersections points by finding the correct bezier segments and calculating t parameters from trimmed sub split lines. Not working yet. Will just print debug info if debug is enabled.">true</param>
<label appearance="header">Bentley Ottmann Sweep Line Settings</label>
<param name="bent_ott_use_ignore_segment_endings" type="bool" gui-text="Ignore segment endings" gui-description="Whether to ignore intersections of line segments when both their end points form the intersection point">true</param>
<param name="bent_ott_use_debug" type="bool" gui-text="Debug">false</param>
<param name="bent_ott_use_verbose" type="bool" gui-text="Verbose">false</param>
<param name="bent_ott_use_paranoid" type="bool" gui-text="Paranoid checks">false</param>
<param name="bent_ott_use_vertical" type="bool" gui-text="Support vertical segments">true</param>
<param name="bent_ott_number_type" type="optiongroup" appearance="combo" gui-text="Number type">
<option value="native">native (default)</option>
<option value="numpy">numpy</option>
</param>
</vbox>
</hbox>
</page>
<page name="tab_style" gui-text="Style">
<hbox>
<vbox>
<label appearance="header">Scanning Colors</label>
<param name="color_subsplit" type="color" appearance="colorbutton" gui-text="sub split lines">1630897151</param>
<param name="color_relative" type="color" appearance="colorbutton" gui-text="relative cmd paths">3419879935</param>
<param name="color_absolute" type="color" appearance="colorbutton" gui-text="absolute cmd paths">1592519679</param>
<param name="color_mixed" type="color" appearance="colorbutton" gui-text="mixed cmd paths" gui-description="combined relative and absolute">3351636735</param>
<param name="color_polyline" type="color" appearance="colorbutton" gui-text="polyline paths">4289703935</param>
<param name="color_bezier" type="color" appearance="colorbutton" gui-text="bezier paths">258744063</param>
<param name="color_opened" type="color" appearance="colorbutton" gui-text="opened paths">4012452351</param>
<param name="color_closed" type="color" appearance="colorbutton" gui-text="closed paths">2330080511</param>
<param name="color_self_intersecting_paths" type="color" appearance="colorbutton" gui-text="self-intersecting paths">2593756927</param>
<param name="color_self_intersections" type="color" appearance="colorbutton" gui-text="self-intersecting paths points">6320383</param>
<param name="color_global_intersections" type="color" appearance="colorbutton" gui-text="global intersection points">4239343359</param>
</vbox>
<separator/>
<vbox>
<label appearance="header">Trimming Colors</label>
<param name="color_trimmed" type="color" appearance="colorbutton" gui-text="trimmed lines">3227634687</param>
<param name="color_combined" type="color" appearance="colorbutton" gui-text="non-intersected lines" gui-description="Colorize non-trimmed lines differently than the trimmed ones. Does not apply if 'Original style for trimmed lines' is enabled">1923076095</param>
<param name="color_nonintersected" type="color" appearance="colorbutton" gui-text="non-intersected paths" gui-description="Colorize the complete path in case it does not contain any trim. Does not apply if 'Original style for trimmed lines' is enabled">3045284607</param>
<label appearance="header">General Style</label>
<param name="nb_main" type="notebook">
<page name="tab_settings_and_actions" gui-text="Settings and Actions">
<param name="nb_settings_and_actions" type="notebook">
<page name="tab_settings" gui-text="Settings">
<label appearance="header">General input/output</label>
<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: converts to absolute paths and might create additional new path elements.">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 and conversion of absolute paths to relative paths) than directly selected paths. The colorization for non-intersected paths will be different too.">false</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>
<label appearance="header">General style</label>
<param name="strokewidth" min="0.0" max="10000.0" precision="3" gui-text="Stroke width (px)" gui-description="Applies For sub split lines and trimmed lines" type="float">1.0</param>
<param name="dotsize_intersections" type="int" min="0" max="10000" gui-text="Dot size (px)" gui-description="For self-intersecting and global intersection points">30</param>
<param name="dotsize_intersections" type="int" min="0" max="10000" gui-text="Intersection dot size (px)" gui-description="For self-intersecting and global intersection points">30</param>
<param name="removefillsetstroke" type="bool" gui-text="Remove fill and define stroke" gui-description="Modifies original path style">false</param>
<param name="subsplit_style" type="optiongroup" appearance="combo" gui-text="Sub split line style">
<option value="default">Use default sub split style</option>
@ -101,17 +26,105 @@
<param name="trimmed_style" type="optiongroup" appearance="combo" gui-text="Trimmed line style">
<option value="apply_from_trimmed">Apply default trimming styles</option>
<option value="apply_from_original">Apply original path styles</option>
</param>
</vbox>
</hbox>
</param>
</page>
<page name="tab_removing" gui-text="Removing">
<label appearance="header">Applying to original paths and sub split lines</label>
<param name="remove_relative" type="bool" gui-text="relative cmd">false</param>
<param name="remove_absolute" type="bool" gui-text="absolute cmd">false</param>
<param name="remove_mixed" type="bool" gui-text="mixed cmd" gui-description="combined relative and absolute">false</param>
<param name="remove_polylines" type="bool" gui-text="polylines">false</param>
<param name="remove_beziers" type="bool" gui-text="beziers">false</param>
<param name="remove_opened" type="bool" gui-text="opened">false</param>
<param name="remove_closed" type="bool" gui-text="closed">false</param>
<param name="remove_self_intersecting" type="bool" gui-text="self-intersecting">false</param>
<label appearance="header">Applying to sub split lines only</label>
<param name="filter_subsplit_collinear" type="bool" gui-text="Filter collinear overlapping lines" gui-description="Removes any duplicates by merging (multiple) overlapping line segments into longer lines. Not possible to apply for original paths because this routine does not support bezier type paths.">true</param>
<param name="filter_subsplit_collinear_action" type="optiongroup" appearance="combo" gui-text="What to do with collinear overlapping lines?">
<option value="remove">remove</option>
<option value="separate_group">put to separate group</option>
</param>
<label appearance="header">Applying to original paths only</label>
<param name="keep_original_after_split_trim" type="bool" gui-text="Keep original paths after sub splitting / trimming">false</param>
</page>
<page name="tab_highlighting" gui-text="Highlighting">
<label appearance="header">Applying to original paths and sub split lines</label>
<param name="highlight_relative" type="bool" gui-text="relative cmd">false</param>
<param name="highlight_absolute" type="bool" gui-text="absolute cmd">false</param>
<param name="highlight_mixed" type="bool" gui-text="mixed cmd" gui-description="combined relative and absolute">false</param>
<param name="highlight_polylines" type="bool" gui-text="polylines">false</param>
<param name="highlight_beziers" type="bool" gui-text="beziers">false</param>
<param name="highlight_opened" type="bool" gui-text="opened">false</param>
<param name="highlight_closed" type="bool" gui-text="closed">false</param>
<param name="highlight_self_intersecting" type="bool" gui-text="self-intersecting" gui-description="Requires enabled 'Draw sub split lines' option (will auto-enable). Will override other highlighting colors (if those options are enabled)">false</param>
<label appearance="header">Applying to sub split lines only</label>
<param name="draw_subsplit" type="bool" gui-text="Draw sub split lines" gui-description="Draws polylines which are generated from all input paths">false</param>
<param name="highlight_duplicates" type="bool" gui-text="duplicates">false</param>
<param name="highlight_merges" type="bool" gui-text="merges" gui-description="Requires enabled 'Remove collinear overlapping lines' option (will auto-enable)">false</param>
<label appearance="header">Intersection points</label>
<param name="visualize_self_intersections" type="bool" gui-text="self-intersecting path points" gui-description="Will put into background (z-Index) by global intersection points and trimmed lines (if enabled)">false</param>
<param name="visualize_global_intersections" type="bool" gui-text="global intersection points" gui-description="Will also contain self-intersecting points! Global intersections will only show if 'Draw trimmed lines' is enabled!">false</param>
</page>
<page name="tab_trimming" gui-text="Trimming">
<label appearance="header">General trimming settings</label>
<param name="trimming_path_types" type="optiongroup" appearance="combo" gui-text="Trimming selection" gui-description="Process open paths by other open paths, closed paths by other closed paths, or all paths by all other paths. This selection does not apply for paths which intersect themselves!">
<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" gui-description="This will colorize all paths segments which were not intersected ('non-intersected lines'). If the whole path was not intersected at all, it will get another color ('non-intersected paths').">true</param>
<param name="remove_trim_duplicates" type="bool" gui-text="Remove duplicate trim lines" gui-description="Has no effect if option 'Filter collinear overlapping lines' is enabled because duplicates get pre-filtered.">true</param>
<param name="reverse_trim_removal_order" type="bool" gui-text="Reverse trim line removal order" gui-description="Reverses the order of removal. Relevant for keeping certain styles of elements">false</param>
<param name="bezier_trimming" type="bool" gui-text="Trim original beziers (not working yet)" gui-description="If enabled we try to split the original bezier paths at the intersections points by finding the correct bezier segments and calculating t parameters from trimmed sub split lines. Not working yet. Will just print debug info if debug is enabled.">true</param>
<label appearance="header">Bentley-Ottmann sweep line settings</label>
<param name="bent_ott_use_ignore_segment_endings" type="bool" gui-text="Ignore segment endings" gui-description="Whether to ignore intersections of line segments when both their end points form the intersection point">true</param>
<param name="bent_ott_use_debug" type="bool" gui-text="Debug">false</param>
<param name="bent_ott_use_verbose" type="bool" gui-text="Verbose">false</param>
<param name="bent_ott_use_paranoid" type="bool" gui-text="Paranoid checks">false</param>
<param name="bent_ott_use_vertical" type="bool" gui-text="Support vertical segments">true</param>
<param name="bent_ott_number_type" type="optiongroup" appearance="combo" gui-text="Number type">
<option value="native">native (default)</option>
<option value="numpy">numpy</option>
</param>
</page>
<page name="tab_colors" gui-text="Colors">
<hbox>
<vbox>
<label appearance="header">Sub split lines</label>
<param name="color_subsplit" type="color" appearance="colorbutton" gui-text="sub split lines">1630897151</param>
<label appearance="header">Path structure</label>
<param name="color_relative" type="color" appearance="colorbutton" gui-text="relative cmd paths">3419879935</param>
<param name="color_absolute" type="color" appearance="colorbutton" gui-text="absolute cmd paths">1592519679</param>
<param name="color_mixed" type="color" appearance="colorbutton" gui-text="mixed cmd paths" gui-description="combined relative and absolute">3351636735</param>
<param name="color_polyline" type="color" appearance="colorbutton" gui-text="polyline paths">4289703935</param>
<param name="color_bezier" type="color" appearance="colorbutton" gui-text="bezier paths">258744063</param>
<param name="color_opened" type="color" appearance="colorbutton" gui-text="opened paths">4012452351</param>
<param name="color_closed" type="color" appearance="colorbutton" gui-text="closed paths">2330080511</param>
</vbox>
<separator/>
<vbox>
<label appearance="header">Duplicates and merges</label>
<param name="color_duplicates" type="color" appearance="colorbutton" gui-text="duplicates" gui-description="Color for overlapping line segments / duplicated lines)">897901823</param>
<param name="color_merges" type="color" appearance="colorbutton" gui-text="merges" gui-description="Color for replaced merges">869366527</param>
<label appearance="header">Intersections</label>
<param name="color_self_intersecting_paths" type="color" appearance="colorbutton" gui-text="self-intersecting paths">2593756927</param>
<param name="color_self_intersections" type="color" appearance="colorbutton" gui-text="self-intersecting paths points">6320383</param>
<param name="color_global_intersections" type="color" appearance="colorbutton" gui-text="global intersection points">4239343359</param>
<label appearance="header">Trimming</label>
<param name="color_trimmed" type="color" appearance="colorbutton" gui-text="trimmed lines">3227634687</param>
<param name="color_combined" type="color" appearance="colorbutton" gui-text="non-intersected lines" gui-description="Colorize non-trimmed lines differently than the trimmed ones. Does not apply if 'Original style for trimmed lines' is enabled">1923076095</param>
<param name="color_nonintersected" type="color" appearance="colorbutton" gui-text="non-intersected paths" gui-description="Colorize the complete path in case it does not contain any trim. Does not apply if 'Original style for trimmed lines' is enabled">3045284607</param>
</vbox>
</hbox>
</page>
</param>
</page>
<page name="tab_tips" gui-text="Tips">
<label xml:space="preserve"> - Helps to find duplicate lines and to visualize intersections
- Works for self-intersecting paths too
- Uses Bentley-Ottmann algorithm to detect intersections
- Allows to separate different contour types by colors
<label xml:space="preserve"> - Allows to separate different contour types by colors
- Finds overlapping / collinear / (self-)intersecting
(using Bentley-Ottmann algorithm) lines
- Works with paths which have Live Path Effects (LPE)
- Finds overlapping / collinear lines
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.

View File

@ -3,16 +3,21 @@
'''
Extension for InkScape 1.0+
- WARNING: HORRIBLY SLOW CODE. PLEASE HELP TO MAKE IT USEFUL FOR LARGE AMOUNT OF PATHS
- add options:
- replace trimmed paths by bezier paths (calculating lengths and required t parameter)
- filter/remove overlapping/duplicates in
- in original selection (not working bezier but for straight line segments!) We can use another extension for it
- split bezier
- ...
- maybe option: convert abs path to rel path
- maybe option: convert rel path to abs path
replacedelement.path = replacedelement.path.to_absolute().to_superpath().to_path()
- maybe option: break apart while keeping relative/absolute commands (more complex and not sure if we have a great advantage having this)
- ToDo:
- add more comments
- add more debug output
- add documentation at online page
- add statistics about type counts and path lengths (before/after sub splitting/trimming)
- add options:
- replace trimmed paths by bezier paths (calculating lengths and required t parameter)
- filter/remove overlapping/duplicates in
- in original selection (not working bezier but for straight line segments!) We can use another extension for it
- split bezier
- ...
- maybe option: convert abs path to rel path
- maybe option: convert rel path to abs path
replacedelement.path = replacedelement.path.to_absolute().to_superpath().to_path()
- maybe option: break apart while keeping relative/absolute commands (more complex and not sure if we have a great advantage having this)
- important to notice
- this algorithm might be really slow. Reduce flattening quality to speed up
@ -68,10 +73,11 @@ from shapely import speedups
if speedups.available:
speedups.enable()
idPrefixSubSplit = "subsplit"
idPrefixTrimming = "shapely"
intersectedVerb = "intersected"
collinearVerb = "collinear"
EPS_M = 0.01
class ContourScannerAndTrimmer(inkex.EffectExtension):
@ -216,6 +222,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
selfIntersectionPointCircle.set('id', self.svg.get_unique_id('selfIntersectionPoint-'))
selfIntersectionPointCircle.style = selfIntersectionPointStyle
selfIntersectionGroup.add(selfIntersectionPointCircle)
return selfIntersectionGroup
def visualize_global_intersections(self, globalIntersectionPoints):
@ -368,7 +375,10 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
def filter_collinear(self, lineArray):
'''
Loop through a set of lines and find + fiter all overlapping segments / duplicate segments
finally returns a set of merged-like lines and a set of original items which should be dropped
finally returns a set of merged-like lines and a set of original items which should be dropped.
Based on the style of the algorithm we have no good influence on the z-index of the items because
it is scanned by slope and point coordinates. That's why we have a more special
'remove_trim_duplicates()' function for trimmed duplicates!
'''
input_set = []
input_ids = []
@ -463,10 +473,14 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
return output_set, dropped_ids
def remove_duplicates(self, allTrimGroups):
''' find duplicate lines in a given array [] of groups '''
def remove_trim_duplicates(self, allTrimGroups):
'''
find duplicate lines in a given array [] of groups
note: this function is similar to filter_collinear but we keep it because we have a 'reverse_trim_removal_order' option.
We can use this option in some special situations where we work without the function 'filter_collinear()'.
'''
totalTrimPaths = []
if self.options.reverse_removal_order is True:
if self.options.reverse_trim_removal_order is True:
allTrimGroups = allTrimGroups[::-1]
for trimGroup in allTrimGroups:
for element in trimGroup:
@ -526,7 +540,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
self.msg("trim group {} has {} combinable segments:".format(trimGroup.get('id'), len(newPathData)))
self.msg("{}".format(newPathData))
combinedPath.path = Path(newPathData)
if self.options.trimmed_style is False:
if self.options.trimmed_style == "apply_from_trimmed":
combinedPath.style = trimNonIntersectedStyle
if totalIntersectionsAtPath == 0:
combinedPath.style = nonTrimLineStyle
@ -578,9 +592,10 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
def add_arguments(self, pars):
pars.add_argument("--tab")
pars.add_argument("--nb_main")
pars.add_argument("--nb_settings_and_actions")
#Settings - General
#Settings - General Input/Output
pars.add_argument("--show_debug", type=inkex.Boolean, default=False, help="Show debug infos")
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")
@ -589,11 +604,15 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
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")
pars.add_argument("--draw_subsplit", type=inkex.Boolean, default=False, help="Draw sub split lines (polylines)")
pars.add_argument("--remove_subsplit_collinear", type=inkex.Boolean, default=True, help="Removes any duplicates by merging (multiple) overlapping line segments into longer lines. Not possible to apply for original paths because this routine does not support bezier type paths.")
pars.add_argument("--keep_original_after_split_trim", type=inkex.Boolean, default=False, help="Keep original paths after sub splitting / trimming")
#Settings - General Style
pars.add_argument("--strokewidth", type=float, default=1.0, help="Stroke width (px)")
pars.add_argument("--dotsize_intersections", type=int, default=30, help="Dot size (px) for self-intersecting and global intersection points")
pars.add_argument("--removefillsetstroke", type=inkex.Boolean, default=False, help="Remove fill and define stroke for original paths")
pars.add_argument("--bezier_trimming", type=inkex.Boolean, default=False, help="If true we try to use the calculated t parameters from intersection points to receive splitted bezier curves")
pars.add_argument("--subsplit_style", default="default", help="Sub split line style")
pars.add_argument("--trimmed_style", default="apply_from_trimmed", help="Trimmed line style")
#Scanning - Removing of original paths
#Removing - Applying to original paths and sub split lines
pars.add_argument("--remove_relative", type=inkex.Boolean, default=False, help="relative cmd")
pars.add_argument("--remove_absolute", type=inkex.Boolean, default=False, help="absolute cmd")
pars.add_argument("--remove_mixed", type=inkex.Boolean, default=False, help="mixed cmd (relative + absolute)")
@ -602,8 +621,13 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
pars.add_argument("--remove_opened", type=inkex.Boolean, default=False, help="opened")
pars.add_argument("--remove_closed", type=inkex.Boolean, default=False, help="closed")
pars.add_argument("--remove_self_intersecting", type=inkex.Boolean, default=False, help="self-intersecting")
#Removing - Applying to sub split lines only
pars.add_argument("--filter_subsplit_collinear", type=inkex.Boolean, default=True, help="Removes any duplicates by merging (multiple) overlapping line segments into longer lines. Not possible to apply for original paths because this routine does not support bezier type paths.")
pars.add_argument("--filter_subsplit_collinear_action", default="remove", help="What to do with collinear overlapping lines?")
#Removing - Applying to original paths only
pars.add_argument("--keep_original_after_split_trim", type=inkex.Boolean, default=False, help="Keep original paths after sub splitting / trimming")
#Scanning - Highlighting of original paths (and sub split lines)
#Highlighting - Applying to original paths and sub split lines
pars.add_argument("--highlight_relative", type=inkex.Boolean, default=False, help="relative cmd paths")
pars.add_argument("--highlight_absolute", type=inkex.Boolean, default=False, help="absolute cmd paths")
pars.add_argument("--highlight_mixed", type=inkex.Boolean, default=False, help="mixed cmd (relative + absolute) paths")
@ -611,16 +635,21 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
pars.add_argument("--highlight_beziers", type=inkex.Boolean, default=False, help="bezier paths")
pars.add_argument("--highlight_opened", type=inkex.Boolean, default=False, help="opened paths")
pars.add_argument("--highlight_closed", type=inkex.Boolean, default=False, help="closed paths")
#Highlighting - Applying to sub split lines only
pars.add_argument("--draw_subsplit", type=inkex.Boolean, default=False, help="Draw sub split lines (polylines)")
pars.add_argument("--highlight_duplicates", type=inkex.Boolean, default=False, help="duplicates (only applies to sub split lines)")
pars.add_argument("--highlight_merges", type=inkex.Boolean, default=False, help="merges (only applies to sub split lines)")
#Highlighting - Intersection points
pars.add_argument("--highlight_self_intersecting", type=inkex.Boolean, default=False, help="self-intersecting paths")
pars.add_argument("--visualize_self_intersections", type=inkex.Boolean, default=False, help="self-intersecting path points")
pars.add_argument("--visualize_global_intersections", type=inkex.Boolean, default=False, help="global intersection points")
#Settings - Trimming of sub split lines
#Trimming - General trimming settings
pars.add_argument("--draw_trimmed", type=inkex.Boolean, default=False, help="Draw trimmed lines")
pars.add_argument("--combine_nonintersects", type=inkex.Boolean, default=True, help="Combine non-intersected lines")
pars.add_argument("--remove_duplicates", type=inkex.Boolean, default=True, help="Remove duplicate trim lines")
pars.add_argument("--reverse_removal_order", type=inkex.Boolean, default=False, help="Reverses the order of removal. Relevant for keeping certain styles of elements")
pars.add_argument("--remove_trim_duplicates", type=inkex.Boolean, default=True, help="Remove duplicate trim lines")
pars.add_argument("--reverse_trim_removal_order", type=inkex.Boolean, default=False, help="Reverses the order of removal. Relevant for keeping certain styles of elements")
#Trimming - Bentley-Ottmann sweep line settings
pars.add_argument("--bent_ott_use_ignore_segment_endings", type=inkex.Boolean, default=True, help="Whether to ignore intersections of line segments when both their end points form the intersection point")
pars.add_argument("--bent_ott_use_debug", type=inkex.Boolean, default=False)
pars.add_argument("--bent_ott_use_verbose", type=inkex.Boolean, default=False)
@ -628,28 +657,24 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
pars.add_argument("--bent_ott_use_vertical", type=inkex.Boolean, default=True)
pars.add_argument("--bent_ott_number_type", default="native")
#Style - General Style
pars.add_argument("--strokewidth", type=float, default=1.0, help="Stroke width (px)")
pars.add_argument("--dotsize_intersections", type=int, default=30, help="Dot size (px) for self-intersecting and global intersection points")
pars.add_argument("--removefillsetstroke", type=inkex.Boolean, default=False, help="Remove fill and define stroke for original paths")
pars.add_argument("--bezier_trimming", type=inkex.Boolean, default=False, help="If true we try to use the calculated t parameters from intersection points to receive splitted bezier curves")
pars.add_argument("--subsplit_style", default="default", help="Sub split line style")
pars.add_argument("--trimmed_style", default="apply_from_trimmed", help="Trimmed line style")
#Style - Scanning Colors (Highlighting things)
pars.add_argument("--color_subsplit", type=Color, default='1630897151', help="sub split lines")
#Colors
pars.add_argument("--color_subsplit", type=Color, default='1630897151', help="sub split lines")
#Colors - path structure
pars.add_argument("--color_relative", type=Color, default='3419879935', help="relative cmd paths")
pars.add_argument("--color_absolute", type=Color, default='1592519679', help="absolute cmd paths")
pars.add_argument("--color_mixed", type=Color, default='3351636735', help="mixed cmd (relative + absolute) paths")
pars.add_argument("--color_mixed", type=Color, default='3351636735', help="mixed cmd (relative + absolute) paths")
pars.add_argument("--color_polyline", type=Color, default='4289703935', help="polyline paths")
pars.add_argument("--color_bezier", type=Color, default='258744063', help="bezier paths")
pars.add_argument("--color_opened", type=Color, default='4012452351', help="opened paths")
pars.add_argument("--color_closed", type=Color, default='2330080511', help="closed paths")
#Colors - duplicates and merges
pars.add_argument("--color_duplicates", type=Color, default='897901823', help="duplicates")
pars.add_argument("--color_merges", type=Color, default='869366527', help="merges")
#Colors - intersections
pars.add_argument("--color_self_intersecting_paths", type=Color, default='2593756927', help="self-intersecting paths")
pars.add_argument("--color_self_intersections", type=Color, default='6320383', help="self-intersecting path points")
pars.add_argument("--color_global_intersections", type=Color, default='4239343359', help="global intersection points")
#Style - Trimming Colors
#Colors - trimming
pars.add_argument("--color_trimmed", type=Color, default='1923076095', help="trimmed lines")
pars.add_argument("--color_combined", type=Color, default='3227634687', help="non-intersected lines")
pars.add_argument("--color_nonintersected", type=Color, default='3045284607', help="non-intersected paths")
@ -662,6 +687,18 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
if so.break_apart is True and so.show_debug is True:
self.msg("Warning: 'Break apart input' setting is enabled. Cannot check accordingly for relative, absolute or mixed paths for breaked elements (they are always absolute)!")
#some configuration dependecies
if so.highlight_self_intersecting is True or \
so.highlight_duplicates is True or \
so.highlight_merges is True:
so.draw_subsplit = True
if so.highlight_merges is True:
so.filter_subsplit_collinear = True
if so.filter_subsplit_collinear_action == "separate_group":
so.draw_subsplit = True
#some constant stuff / styles
relativePathStyle = {'stroke': str(so.color_relative), 'fill': 'none', 'stroke-width': so.strokewidth}
absolutePathStyle = {'stroke': str(so.color_absolute), 'fill': 'none', 'stroke-width': so.strokewidth}
@ -670,6 +707,8 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
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}
duplicatesPathStyle = {'stroke': str(so.color_duplicates), 'fill': 'none', 'stroke-width': so.strokewidth}
mergesPathStyle = {'stroke': str(so.color_merges), '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}
@ -782,7 +821,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
subSplitId = "{}-{}-{}".format(idPrefixSubSplit, originalPathId, i)
line = inkex.PathElement(id=subSplitId)
#apply line path with composed negative transform from parent element
line.attrib['d'] = 'M {},{} L {},{}'.format(x1, y1, x2, y2) #we set the path of trimLine using 'd' attribute because if we use trimLine.path the decimals get cut off unwantedly
line.attrib['d'] = 'M {},{} L {},{}'.format(x1, y1, x2, y2) #we set the path of Line using 'd' attribute because if we use trimLine.path the decimals get cut off unwantedly
#line.path = [['M', [x1, y1]], ['L', [x2, y2]]]
if pathElement.getparent() != self.svg.root and pathElement.getparent() != None:
line.path = line.path.transform(-pathElement.getparent().composed_transform())
@ -836,15 +875,14 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
isSelfIntersecting = True
if so.show_debug is True:
self.msg("{} in {} intersects itself with {} intersections!".format(subSplitId, originalPathId, len(selfIntersectionPoints)))
if so.draw_subsplit is True:
if so.highlight_self_intersecting is True:
for subSplitLine in subSplitLineGroup:
subSplitLine.style = selfIntersectingPathStyle #adjusts line color
if so.highlight_self_intersecting is True:
for subSplitLine in subSplitLineGroup:
subSplitLine.style = selfIntersectingPathStyle #adjusts line color
#delete cosmetic sub split lines if desired
if so.remove_self_intersecting:
subSplitLineGroup.delete()
if so.visualize_self_intersections is True: #draw points (circles)
self.visualize_self_intersections(pathElement, selfIntersectionPoints)
selfIntersectionGroup = self.visualize_self_intersections(pathElement, selfIntersectionPoints)
#delete self-intersecting sub split lines and orginal paths
if so.remove_self_intersecting:
@ -894,31 +932,81 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
if so.show_debug is True:
self.msg("sub split line count: {}".format(len(subSplitLineArray)))
if so.remove_subsplit_collinear is True:
'''
check for collinear lines and apply filters to remove or regroup and to restyle them
Run this action only if one of the options requires it (to avoid useless calculation cycles)
'''
if so.filter_subsplit_collinear is True or \
so.highlight_duplicates is True or \
so.highlight_merges is True:
if so.show_debug is True: self.msg("filtering collinear overlapping lines / duplicate lines")
if len(subSplitLineArray) > 0:
output_set, dropped_ids = self.filter_collinear(subSplitLineArray)
deleteIndices = []
deleteIndice = 0
for subSplitLine in subSplitLineArray:
ssl_id = subSplitLine.get('id')
if ssl_id in dropped_ids:
ssl_parent = subSplitLine.getparent()
subSplitLine.delete() #delete the line
#and delete the containg group if empty
if ssl_parent is not None and len(ssl_parent) == 0:
if self.options.show_debug is True:
self.msg("Deleting group {}".format(ssl_parent.get('id')))
ssl_parent.delete()
# and now we replace the overlapping items with the new merged output
'''
Replace the overlapping items with the new merged output
'''
for output in output_set:
if output['id'] == subSplitLine.attrib['id']:
#self.msg(output['p0'])
subSplitLine.attrib['d'] = 'M {},{} L {},{}'.format(
output['p0'][0], output['p0'][1], output['p1'][0], output['p1'][1]) #we set the path of trimLine using 'd' attribute because if we use trimLine.path the decimals get cut off unwantedly
originalSplitLinePath = subSplitLine.path
output_line = 'M {},{} L {},{}'.format(
output['p0'][0], output['p0'][1], output['p1'][0], output['p1'][1])
output_line_reversed = 'M {},{} L {},{}'.format(
output['p1'][0], output['p1'][1], output['p0'][0], output['p0'][1])
subSplitLine.attrib['d'] = output_line #we set the path using 'd' attribute because if we use trimLine.path the decimals get cut off unwantedly
mergedSplitLinePath = subSplitLine.path
mergedSplitLinePathReversed = Path(output_line_reversed)
#subSplitLine.path = [['M', output['p0']], ['L', output['p1']]]
#self.msg("composed_transform = {}".format(output['composed_transform']))
#subSplitLine.transform = Transform(-output['composed_transform']) * subSplitLine.transform
subSplitLine.path = subSplitLine.path.transform(-output['composed_transform'])
if so.highlight_merges is True:
if originalSplitLinePath != mergedSplitLinePath and \
originalSplitLinePath != mergedSplitLinePathReversed: #if the path changed we are going to highlight it
subSplitLine.style = mergesPathStyle
'''
Delete or move sub split lines which are overlapping
'''
ssl_id = subSplitLine.get('id')
if ssl_id in dropped_ids:
if so.highlight_duplicates is True:
subSplitLine.style = duplicatesPathStyle
if so.filter_subsplit_collinear is True:
ssl_parent = subSplitLine.getparent()
if so.filter_subsplit_collinear_action == "remove":
if self.options.show_debug is True:
self.msg("Deleting sub split line {}".format(subSplitLine.get('id')))
subSplitLine.delete() #delete the line from XML tree
deleteIndices.append(deleteIndice) #store this id to remove it from stupid subSplitLineArray later
elif so.filter_subsplit_collinear_action == "separate_group":
if self.options.show_debug is True:
self.msg("Moving sub split line {}".format(subSplitLine.get('id')))
originalPathId = subSplitLine.attrib['originalPathId']
collinearGroupId = '{}-{}'.format(collinearVerb, originalPathId)
originalPathElement = self.svg.getElementById(originalPathId)
collinearGroup = self.find_group(collinearGroupId)
if collinearGroup is None:
collinearGroup = originalPathElement.getparent().add(inkex.Group(id=collinearGroupId))
collinearGroup.append(subSplitLine) #move to that group
#and delete the containg group if empty (can happen in "remove" or "separate_group" constellation
if ssl_parent is not None and len(ssl_parent) == 0:
if self.options.show_debug is True:
self.msg("Deleting group {}".format(ssl_parent.get('id')))
ssl_parent.delete()
deleteIndice += 1 #end the loop by incrementing +1
#shrink the sub split line array to kick out all unrequired indices
for deleteIndice in sorted(deleteIndices, reverse=True):
if self.options.show_debug is True:
self.msg("Deleting index {} from subSplitLineArray".format(deleteIndice))
del subSplitLineArray[deleteIndice]
'''
now we intersect the sub split lines to find the global intersection points using Bentley-Ottmann algorithm (contains self-intersections too!)
@ -956,7 +1044,7 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
for subSplitLine in subSplitLineArray:
csp = subSplitLine.path.to_arrays()
lineString = [(csp[0][1][0], csp[0][1][1]), (csp[1][1][0], csp[1][1][1])]
if so.remove_duplicates is True:
if so.remove_trim_duplicates is True:
if lineString not in allSubSplitLineStrings:
allSubSplitLineStrings.append(lineString)
else:
@ -996,9 +1084,9 @@ class ContourScannerAndTrimmer(inkex.EffectExtension):
if so.show_debug is True: self.msg("trimming beziers - not working yet")
self.trim_bezier(allTrimGroups)
if so.remove_duplicates is True:
if so.remove_trim_duplicates is True:
if so.show_debug is True: self.msg("checking for duplicate trim lines and deleting them")
self.remove_duplicates(allTrimGroups)
self.remove_trim_duplicates(allTrimGroups)
if so.combine_nonintersects is True:
if so.show_debug is True: self.msg("glueing together all non-intersected sub split lines to larger path structures again (cleaning up)")