more features for filter by extension, add some outside canvas checker

to lasercheck
This commit is contained in:
Mario Voigt 2021-11-02 19:28:26 +01:00
parent 8694cf108f
commit f98e9719ab
4 changed files with 97 additions and 21 deletions

View File

@ -3,6 +3,7 @@
<name>Filter By Length/Area</name> <name>Filter By Length/Area</name>
<id>fablabchemnitz.de.filter_by_length_area</id> <id>fablabchemnitz.de.filter_by_length_area</id>
<label>Paths with value smaller than the given threshold will be deleted</label> <label>Paths with value smaller than the given threshold will be deleted</label>
<param name="debug" type="bool" gui-text="Enable debug">false</param>
<param name="unit" gui-text="Unit" type="optiongroup" appearance="combo"> <param name="unit" gui-text="Unit" type="optiongroup" appearance="combo">
<option value="mm">mm</option> <option value="mm">mm</option>
<option value="cm">cm</option> <option value="cm">cm</option>
@ -11,14 +12,20 @@
<option value="pc">pc</option> <option value="pc">pc</option>
<option value="in">in</option> <option value="in">in</option>
</param> </param>
<label>The unit applies to interval and thresholds</label>
<label appearance="header">Threshold</label> <label appearance="header">Threshold</label>
<param name="nodes_interval" type="float" min="0.000" max="99999.000" precision="3" gui-text="Interval">10.000</param>
<separator/>
<param name="min_filter_enable" type="bool" gui-text="Enable filtering min.">false</param> <param name="min_filter_enable" type="bool" gui-text="Enable filtering min.">false</param>
<param name="min_threshold" type="float" min="0.000" precision="3" max="10000000.000" gui-text="Min.">1.000</param> <param name="min_threshold" type="float" min="0.000" precision="3" max="10000000.000" gui-text="Min. length/area">1.000</param>
<param name="min_nodes" type="int" min="0" max="99999" gui-text="Min. nodes/&lt;interval&gt;">2</param>
<param name="max_filter_enable" type="bool" gui-text="Enable filtering max.">false</param> <param name="max_filter_enable" type="bool" gui-text="Enable filtering max.">false</param>
<param name="max_threshold" type="float" min="0.000" precision="3" max="10000000.000" gui-text="Max.">10000000.000</param> <param name="max_threshold" type="float" min="0.000" precision="3" max="10000000.000" gui-text="Max. length/area">10000000.000</param>
<label appearance="header">Equation</label> <param name="max_nodes" type="int" min="0" max="99999" gui-text="Max. nodes/&lt;interval&gt;">10000000</param>
<param name="measure" type="optiongroup" appearance="combo" gui-text="Measure by"> <label appearance="header">Filter</label>
<param name="measure" type="optiongroup" appearance="combo" gui-text="By">
<option value="length">Length (Unit)</option> <option value="length">Length (Unit)</option>
<option value="nodes">Nodes per length (Unit)</option>
<option value="area">Area (Unit^2)</option> <option value="area">Area (Unit^2)</option>
</param> </param>
<effect> <effect>

View File

@ -18,23 +18,27 @@ from inkex.bezier import csplength, csparea
class FilterByLengthArea(inkex.EffectExtension): class FilterByLengthArea(inkex.EffectExtension):
def add_arguments(self, pars): def add_arguments(self, pars):
pars.add_argument('--debug', type=inkex.Boolean, default=False)
pars.add_argument('--unit') pars.add_argument('--unit')
pars.add_argument('--min_filter_enable', type=inkex.Boolean, default=True, help='Enable filtering min.') pars.add_argument('--min_filter_enable', type=inkex.Boolean, default=True, help='Enable filtering min.')
pars.add_argument('--min_threshold', type=float, default=0.000, help='Remove paths with an threshold smaller than this value') pars.add_argument('--min_threshold', type=float, default=0.000, help='Remove paths with an threshold smaller than this value')
pars.add_argument('--max_filter_enable', type=inkex.Boolean, default=False, help='Enable filtering max.') pars.add_argument('--max_filter_enable', type=inkex.Boolean, default=False, help='Enable filtering max.')
pars.add_argument('--max_threshold', type=float, default=10000000.000, help='Remove paths with an threshold bigger than this value') pars.add_argument('--max_threshold', type=float, default=10000000.000, help='Remove paths with an threshold bigger than this value')
pars.add_argument('--min_nodes', type=int, default=0, help='Min. nodes/<interval>')
pars.add_argument('--max_nodes', type=int, default=10000000, help='Max. nodes/<interval>')
pars.add_argument('--nodes_interval', type=float, default=10000000.000, help='Interval')
pars.add_argument('--measure', default="length") pars.add_argument('--measure', default="length")
def effect(self): def effect(self):
so = self.options
if self.options.min_filter_enable is False and self.options.max_filter_enable is False: if so.min_filter_enable is False and so.max_filter_enable is False:
inkex.utils.debug("You need to enabled at least one filter rule!") inkex.utils.debug("You need to enabled at least one filter rule!")
return return
self.options.min_threshold = self.svg.unittouu(str(self.options.min_threshold) + self.svg.unit) so.min_threshold = self.svg.unittouu(str(so.min_threshold) + self.svg.unit)
self.options.max_threshold = self.svg.unittouu(str(self.options.max_threshold) + self.svg.unit) so.max_threshold = self.svg.unittouu(str(so.max_threshold) + self.svg.unit)
unit_factor = 1.0 / self.svg.uutounit(1.0,self.options.unit) unit_factor = 1.0 / self.svg.uutounit(1.0, so.unit)
if self.options.min_threshold == 0 or self.options.max_threshold == 0: if so.min_threshold == 0 or so.max_threshold == 0:
inkex.utils.debug("One or both tresholds are zero. Please adjust.") inkex.utils.debug("One or both tresholds are zero. Please adjust.")
return return
@ -47,21 +51,36 @@ class FilterByLengthArea(inkex.EffectExtension):
try: try:
csp = element.path.transform(element.composed_transform()).to_superpath() csp = element.path.transform(element.composed_transform()).to_superpath()
if self.options.measure == "area": if so.measure == "area":
area = -csparea(csp) #is returned as negative value. we need to invert with - area = -csparea(csp) #is returned as negative value. we need to invert with
if self.options.min_filter_enable is True and area < (self.options.min_threshold * (unit_factor * unit_factor)): if so.debug is True:
inkex.utils.debug("id={}, area={:0.3f}{}^2".format(element.get('id'), area, so.unit))
if so.min_filter_enable is True and area < (so.min_threshold * (unit_factor * unit_factor)):
element.delete() element.delete()
if self.options.max_filter_enable is True and area >= (self.options.max_threshold * (unit_factor * unit_factor)): if so.max_filter_enable is True and area >= (so.max_threshold * (unit_factor * unit_factor)):
element.delete() element.delete()
elif self.options.measure == "length": elif so.measure == "length":
slengths, stotal = csplength(csp) #get segment lengths and total length of path in document's internal unit slengths, stotal = csplength(csp) #get segment lengths and total length of path in document's internal unit
if self.options.min_filter_enable is True and stotal < (self.options.min_threshold * unit_factor): if so.debug is True:
inkex.utils.debug("id={}, length={:0.3f}{}".format(element.get('id'), self.svg.uutounit(str(stotal), so.unit), so.unit))
if so.min_filter_enable is True and stotal < (so.min_threshold * unit_factor):
element.delete() element.delete()
if self.options.max_filter_enable is True and stotal >= (self.options.max_threshold * unit_factor): if self.options.max_filter_enable is True and stotal >= (so.max_threshold * unit_factor):
element.delete() element.delete()
elif so.measure == "nodes":
slengths, stotal = csplength(csp) #get segment lengths and total length of path in document's internal unit
nodes = len(element.path)
if so.debug is True:
inkex.utils.debug("id={}, length={:0.3f}{}, nodes={}".format(element.get('id'), self.svg.uutounit(str(stotal), so.unit), so.unit, nodes))
if so.min_filter_enable is True and nodes / stotal < so.min_nodes / self.svg.unittouu(str(so.nodes_interval) + so.unit):
element.delete()
if so.max_filter_enable is True and nodes / stotal < so.max_nodes / self.svg.unittouu(str(so.nodes_interval) + so.unit):
element.delete()
except Exception as e: except Exception as e:
#self.msg(e)
pass pass
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -23,6 +23,7 @@
<option value="1219x914">1219 x 914 mm (Fusion Pro 48)</option> <option value="1219x914">1219 x 914 mm (Fusion Pro 48)</option>
</param> </param>
<separator/> <separator/>
<param name="elements_outside_canvas" type="bool" gui-text="Elements outside canvas">false</param>
<param name="groups_and_layers" type="bool" gui-text="Groups and layers">false</param> <param name="groups_and_layers" type="bool" gui-text="Groups and layers">false</param>
<param name="clones" type="bool" gui-text="Clones">false</param> <param name="clones" type="bool" gui-text="Clones">false</param>
<param name="clippaths" type="bool" gui-text="Clippings">false</param> <param name="clippaths" type="bool" gui-text="Clippings">false</param>

View File

@ -11,6 +11,7 @@ class LaserCheck(inkex.EffectExtension):
''' '''
ToDos: ToDos:
- check for old styles which should be upgraded - check for old styles which should be upgraded
- add some inkex.Desc to all elements which were checked and which have some issue. use special syntax to remove old stuff each time the check is applied again
- this code is horrible ugly stuff - this code is horrible ugly stuff
''' '''
@ -20,7 +21,8 @@ class LaserCheck(inkex.EffectExtension):
pars.add_argument('--show_issues_only', type=inkex.Boolean, default=False) pars.add_argument('--show_issues_only', type=inkex.Boolean, default=False)
pars.add_argument('--bbox', type=inkex.Boolean, default=False) pars.add_argument('--bbox', type=inkex.Boolean, default=False)
pars.add_argument('--bbox_offset', type=float, default=5.000) pars.add_argument('--bbox_offset', type=float, default=5.000)
pars.add_argument("--machine_size", default="812x508") pars.add_argument('--machine_size', default="812x508")
pars.add_argument('--elements_outside_canvas', type=inkex.Boolean, default=False)
pars.add_argument('--groups_and_layers', type=inkex.Boolean, default=False) pars.add_argument('--groups_and_layers', type=inkex.Boolean, default=False)
pars.add_argument('--clones', type=inkex.Boolean, default=False) pars.add_argument('--clones', type=inkex.Boolean, default=False)
pars.add_argument('--clippaths', type=inkex.Boolean, default=False) pars.add_argument('--clippaths', type=inkex.Boolean, default=False)
@ -538,9 +540,56 @@ class LaserCheck(inkex.EffectExtension):
round(self.svg.uutounit(str(heavyPath[2]), "mm"), 3), round(self.svg.uutounit(str(heavyPath[2]), "mm"), 3),
round(heavyPath[1] / self.svg.uutounit(str(heavyPath[2]), "mm"), 3) round(heavyPath[1] / self.svg.uutounit(str(heavyPath[2]), "mm"), 3)
) )
) )
'''
Elements outside canvas or touching the border. These are critical because they won't be lasered
'''
if so.checks == "check_all" or so.elements_outside_canvas is True:
inkex.utils.debug("\n---------- Elements outside canvas or touching the border")
elementsOutside = []
for element in shapes:
if element.tag != inkex.addNS('g', 'svg'):
ebbox = element.bounding_box()
precision = 3
#inkex.utils.debug("{} | bbox: left = {:0.3f} right = {:0.3f} top = {:0.3f} bottom = {:0.3f}".format(element.get('id'), ebbox.left, ebbox.right, ebbox.top, ebbox.bottom))
pagew = round(self.svg.unittouu(self.svg.get('width')), precision)
pageh = round(self.svg.unittouu(self.svg.get('height')), precision)
if round(ebbox.right, precision) == 0 or \
round(ebbox.left, precision) == pagew or \
round(ebbox.top, precision) == 0 or \
round(ebbox.bottom, precision) == pageh:
elementsOutside.append([element, "touching"])
elif \
round(ebbox.right, precision) < 0 or \
round(ebbox.left, precision) > pagew or \
round(ebbox.top, precision) < 0 or \
round(ebbox.bottom, precision) > pageh:
elementsOutside.append([element, "fully outside"])
else: #fully inside or partially inside/outside. we check if one or more corners is outside the canvas
rightOutside = False
leftOutside = False
topOutside = False
bottomOutside = False
if round(ebbox.right, precision) < 0 or round(ebbox.right, precision) > pagew:
rightOutside = True
if round(ebbox.left, precision) < 0 or round(ebbox.left, precision) > pagew:
leftOutside = True
if round(ebbox.top, precision) < 0 or round(ebbox.top, precision) > pageh:
topOutside = True
if round(ebbox.bottom, precision) < 0 or round(ebbox.bottom, precision) > pageh:
bottomOutside = True
if rightOutside is True or leftOutside is True or topOutside is True or bottomOutside is True:
elementsOutside.append([element, "partially outside"])
if self.options.show_issues_only is False:
inkex.utils.debug("{} Elements outside canvas or touching the border in total".format(len(elementsOutside)))
for elementOutside in elementsOutside:
inkex.utils.debug("id={}, status={}".format(
elementOutside[0].get('id'),
elementOutside[1]
)
)
''' '''
Shapes like rectangles, ellipses, arcs, spirals should be converted to svg:path to have more Shapes like rectangles, ellipses, arcs, spirals should be converted to svg:path to have more