Bug fixing and more features for Contour Scanner

This commit is contained in:
Mario Voigt 2020-09-05 02:33:07 +02:00
parent 91c25713b7
commit 4b009f75e8
2 changed files with 65 additions and 49 deletions

View File

@ -4,22 +4,23 @@
<id>fablabchemnitz.de.contour_scanner</id> <id>fablabchemnitz.de.contour_scanner</id>
<param name="main_tabs" type="notebook"> <param name="main_tabs" type="notebook">
<page name="tab_active" gui-text="Active"> <page name="tab_active" gui-text="Active">
<param name="desc" type="description">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</param> <label>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</label>
<param name="help_general" type="description" appearance="header">General</param> <label appearance="header">General</label>
<param name="breakapart" type="bool" gui-text="Break apart contours and groups">false</param> <param name="breakapart" type="bool" gui-text="Break apart selection into single contours" gui-description="(with ignoring the group hirarchy by taking all children elements)">false</param>
<param name="removefillsetstroke" type="bool" gui-text="Remove fill and define stroke">false</param> <param name="removefillsetstroke" type="bool" gui-text="Remove fill and define stroke">false</param>
<param name="strokewidth" min="0.0" max="10000.0" gui-text="Stroke width (px)" type="float">1.0</param> <param name="strokewidth" min="0.0" max="10000.0" gui-text="Stroke width (px)" type="float">1.0</param>
<param name="help_highlight" type="description" appearance="header">Highlight paths</param> <label appearance="header">Highlight paths</label>
<param name="highlight_opened" type="bool" gui-text="Highlight opened contours">true</param> <param name="highlight_opened" type="bool" gui-text="Highlight opened contours">true</param>
<param name="color_opened" type="color" appearance="colorbutton" gui-text="Color opened contours">4012452351</param> <param name="color_opened" type="color" appearance="colorbutton" gui-text="Color opened contours">4012452351</param>
<param name="highlight_closed" type="bool" gui-text="Highlight closed contours">true</param> <param name="highlight_closed" type="bool" gui-text="Highlight closed contours">true</param>
<param name="color_closed" type="color" appearance="colorbutton" gui-text="Color closed contours">2330080511</param> <param name="color_closed" type="color" appearance="colorbutton" gui-text="Color closed contours">2330080511</param>
<param name="highlight_selfintersecting" type="bool" gui-text="Highlight self-intersecting contours">true</param> <param name="highlight_selfintersecting" type="bool" gui-text="Highlight self-intersecting contours" gui-description="Due to the nature of the algorithm this might detect non-closed contours too.">true</param>
<param name="color_selfintersecting" type="color" appearance="colorbutton" gui-text="Color self-intersecting contours">1923076095</param> <param name="color_selfintersecting" type="color" appearance="colorbutton" gui-text="Color self-intersecting contours">1923076095</param>
<param name="highlight_intersectionpoints" type="bool" gui-text="Highlight self-intersecting points">true</param> <param name="highlight_intersectionpoints" type="bool" gui-text="Highlight self-intersecting points">true</param>
<param name="color_intersectionpoints" type="color" appearance="colorbutton" gui-text="Color self-intersecting points">4239343359</param> <param name="color_intersectionpoints" type="color" appearance="colorbutton" gui-text="Color self-intersecting points">4239343359</param>
<param name="dotsize" type="int" min="0" max="10000" gui-text="Dot size (px) for self-intersecting points">10</param> <param name="dotsize" type="int" min="0" max="10000" gui-text="Dot size (px) for self-intersecting points">10</param>
<param name="help_remove" type="description" appearance="header">Remove paths</param> <param name="addlines" type="bool" gui-text="Add closing lines for open self-crossing contours" gui-description="They will have the same color as the intersection points and help to better visualize possible virtual crossings. The algorithm can only detect intersections for closed contours by it's nature, but we handle open contours like they were closed. This may put put too much intersection points.">true</param>
<label appearance="header">Remove paths</label>
<param name="remove_opened" type="bool" gui-text="Remove opened contours">false</param> <param name="remove_opened" type="bool" gui-text="Remove opened contours">false</param>
<param name="remove_closed" type="bool" gui-text="Remove closed contours">false</param> <param name="remove_closed" type="bool" gui-text="Remove closed contours">false</param>
<param name="remove_selfintersecting" type="bool" gui-text="Remove self-intersecting contours">false</param> <param name="remove_selfintersecting" type="bool" gui-text="Remove self-intersecting contours">false</param>

View File

@ -13,7 +13,7 @@ Features
Author: Mario Voigt / FabLab Chemnitz Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org Mail: mario.voigt@stadtfabrikanten.org
Date: 09.08.2020 Date: 09.08.2020
Last patch: 19.08.2020 Last patch: 05.09.2020
License: GNU GPL v3 License: GNU GPL v3
""" """
@ -40,12 +40,14 @@ def adjustStyle(self, node):
if prop == 'fill': if prop == 'fill':
declarations[i] = prop + ':none' declarations[i] = prop + ':none'
node.set('style', ';'.join(declarations) + ';stroke:#000000;stroke-opacity:1.0') node.set('style', ';'.join(declarations) + ';stroke:#000000;stroke-opacity:1.0')
else:
node.set('style', 'stroke:#000000;stroke-opacity:1.0')
class ContourScanner(inkex.Effect): class ContourScanner(inkex.Effect):
def __init__(self): def __init__(self):
inkex.Effect.__init__(self) inkex.Effect.__init__(self)
self.arg_parser.add_argument("--breakapart", type=inkex.Boolean, default=False, help="Break apart contours") self.arg_parser.add_argument("--breakapart", type=inkex.Boolean, default=False, help="Break apart selection into single contours")
self.arg_parser.add_argument("--removefillsetstroke", type=inkex.Boolean, default=False, help="Remove fill and define stroke") self.arg_parser.add_argument("--removefillsetstroke", type=inkex.Boolean, default=False, help="Remove fill and define stroke")
self.arg_parser.add_argument("--strokewidth", type=float, default=1.0, help="Stroke width (px)") self.arg_parser.add_argument("--strokewidth", type=float, default=1.0, help="Stroke width (px)")
self.arg_parser.add_argument("--highlight_opened", type=inkex.Boolean, default=True, help="Highlight opened contours") self.arg_parser.add_argument("--highlight_opened", type=inkex.Boolean, default=True, help="Highlight opened contours")
@ -56,6 +58,7 @@ class ContourScanner(inkex.Effect):
self.arg_parser.add_argument("--highlight_intersectionpoints", type=inkex.Boolean, default=True, help="Highlight self-intersecting points") self.arg_parser.add_argument("--highlight_intersectionpoints", type=inkex.Boolean, default=True, help="Highlight self-intersecting points")
self.arg_parser.add_argument("--color_selfintersecting", type=Color, default='1923076095', help="Color closed contours") self.arg_parser.add_argument("--color_selfintersecting", type=Color, default='1923076095', help="Color closed contours")
self.arg_parser.add_argument("--color_intersectionpoints", type=Color, default='4239343359', help="Color closed contours") self.arg_parser.add_argument("--color_intersectionpoints", type=Color, default='4239343359', help="Color closed contours")
self.arg_parser.add_argument("--addlines", type=inkex.Boolean, default=True, help="Add closing lines for self-crossing contours")
self.arg_parser.add_argument("--dotsize", type=int, default=10, help="Dot size (px) for self-intersecting points") self.arg_parser.add_argument("--dotsize", type=int, default=10, help="Dot size (px) for self-intersecting points")
self.arg_parser.add_argument("--remove_opened", type=inkex.Boolean, default=False, help="Remove opened contours") self.arg_parser.add_argument("--remove_opened", type=inkex.Boolean, default=False, help="Remove opened contours")
self.arg_parser.add_argument("--remove_closed", type=inkex.Boolean, default=False, help="Remove closed contours") self.arg_parser.add_argument("--remove_closed", type=inkex.Boolean, default=False, help="Remove closed contours")
@ -105,42 +108,13 @@ class ContourScanner(inkex.Effect):
prev = i prev = i
subpaths.append(raw[prev:]) subpaths.append(raw[prev:])
for simpath in subpaths:
if len(simpath) > 0:
closed = False
if simpath[-1][0] == 'Z':
closed = True
if not closed:
if self.options.highlight_opened:
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
'stroke': self.options.color_opened, 'stroke-linecap': 'butt', 'fill': 'none'}
node.attrib['style'] = Style(style).to_str()
if self.options.remove_opened:
try:
node.getparent().remove(node)
except AttributeError:
pass #we ignore that parent can be None
if closed:
if self.options.highlight_closed:
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
'stroke': self.options.color_closed, 'stroke-linecap': 'butt', 'fill': 'none'}
node.attrib['style'] = Style(style).to_str()
if self.options.remove_closed:
try:
node.getparent().remove(node)
except AttributeError:
pass #we ignore that parent can be None
for simpath in subpaths: for simpath in subpaths:
closed = False closed = False
if simpath[-1][0] == 'Z': if simpath[-1][0] == 'Z':
closed = True closed = True
if simpath[-2][0] == 'L': simpath[-1][1] = simpath[0][1] if simpath[-2][0] == 'L': simpath[-1][1] = simpath[0][1]
else: simpath.pop() else: simpath.pop()
nodes = [] points = []
for i in range(len(simpath)): for i in range(len(simpath)):
if simpath[i][0] == 'V': # vertical and horizontal lines only have one point in args, but 2 are required if simpath[i][0] == 'V': # vertical and horizontal lines only have one point in args, but 2 are required
simpath[i][0]='L' #overwrite V with regular L command simpath[i][0]='L' #overwrite V with regular L command
@ -150,14 +124,50 @@ class ContourScanner(inkex.Effect):
if simpath[i][0] == 'H': # vertical and horizontal lines only have one point in args, but 2 are required if simpath[i][0] == 'H': # vertical and horizontal lines only have one point in args, but 2 are required
simpath[i][0]='L' #overwrite H with regular L command simpath[i][0]='L' #overwrite H with regular L command
simpath[i][1].append(simpath[i-1][1][1]) #add the second (missing) argument by taking argument from previous segment simpath[i][1].append(simpath[i-1][1][1]) #add the second (missing) argument by taking argument from previous segment
nodes.append(simpath[i][1][-2:]) points.append(simpath[i][1][-2:])
if points[0] == points[-1]: #if first is last point the path is also closed. The "Z" command is not required
closed = True
if closed == False:
if self.options.highlight_opened:
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
'stroke': self.options.color_opened, 'stroke-linecap': 'butt', 'fill': 'none'}
node.attrib['style'] = Style(style).to_str()
if self.options.remove_opened:
try:
node.getparent().remove(node)
except AttributeError:
pass #we ignore that parent can be None
if closed == True:
if self.options.highlight_closed:
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
'stroke': self.options.color_closed, 'stroke-linecap': 'butt', 'fill': 'none'}
node.attrib['style'] = Style(style).to_str()
if self.options.remove_closed:
try:
node.getparent().remove(node)
except AttributeError:
pass #we ignore that parent can be None
#if one of the options is activated we also check for self-intersecting #if one of the options is activated we also check for self-intersecting
if self.options.highlight_selfintersecting or self.options.highlight_intersectionpoints: if self.options.highlight_selfintersecting or self.options.highlight_intersectionpoints:
try: try:
if len(nodes) > 0: #try to find self-intersecting /overlapping polygons if len(points) > 0: #try to find self-intersecting /overlapping polygons
isect = poly_point_isect.isect_polygon(nodes) #TODO: FIND OUT HOW TO HANDLE OPEN CONTOURS TO OMIT VIRTUALLY CROSSING LINES (WHICH DO NOT INTERSECT) isect = poly_point_isect.isect_polygon(points)
if len(isect) > 0: if len(isect) > 0:
if closed == False and self.options.addlines == True: #if contour is open and we found intersection points those points might be not relevant
line = dot_group.add(inkex.PathElement())
line.path = [
['M', [points[0][0],points[0][1]]],
['L', [points[-1][0],points[-1][1]]],
['Z', []]
]
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
'stroke': self.options.color_intersectionpoints, 'stroke-linecap': 'butt', 'fill': 'none'}
line.attrib['style'] = Style(style).to_str()
#make dot markings at the intersection points #make dot markings at the intersection points
if self.options.highlight_intersectionpoints: if self.options.highlight_intersectionpoints:
for xy in isect: for xy in isect:
@ -179,11 +189,16 @@ class ContourScanner(inkex.Effect):
print(str(e)) print(str(e))
#if the dot_group was created but nothing attached we delete it again to prevent messing the SVG XML tree #if the dot_group was created but nothing attached we delete it again to prevent messing the SVG XML tree
if len(dot_group.getchildren()) == 0: if len(dot_group.getchildren()) == 0:
dot_group.getparent().remove(dot_group) dot_parent = dot_group.getparent()
else: #put the node into the dot_group to bundle the path with it's error markers if dot_parent is not None:
dot_group.getparent().remove(dot_group)
#put the node into the dot_group to bundle the path with it's error markers. If removal is selected we need to avoid dot_group.insert(), because it will break the removal
elif self.options.remove_selfintersecting == False:
dot_group.insert(0, node) dot_group.insert(0, node)
for child in node: children = node.getchildren()
self.scanContours(child) if children is not None:
for child in children:
self.scanContours(child)
def effect(self): def effect(self):
if self.options.breakapart: if self.options.breakapart: