add some effort to #37

This commit is contained in:
Mario Voigt 2024-01-18 00:19:26 +01:00
parent 99e42b77b9
commit da9aa9bd80
3 changed files with 58 additions and 14 deletions

View File

@ -16,6 +16,8 @@
<label appearance="header">Custom Checks</label> <label appearance="header">Custom Checks</label>
<hbox> <hbox>
<vbox> <vbox>
<param name="filesize_max" type="float" min="0.000" max="9999.000" precision="3" gui-text="Maximum allowed file size (KB)">2048</param>
<separator/>
<param name="bbox" type="bool" gui-text="Bounding box" gui-description="Check if any element touches the canvas border or is completely outside the canvas">false</param> <param name="bbox" type="bool" gui-text="Bounding box" gui-description="Check if any element touches the canvas border or is completely outside the canvas">false</param>
<param name="bbox_offset" type="float" min="0.000" max="9999.000" precision="3" gui-text="Minimum required offset (mm)">5.000</param> <param name="bbox_offset" type="float" min="0.000" max="9999.000" precision="3" gui-text="Minimum required offset (mm)">5.000</param>
<separator/> <separator/>

View File

@ -8,6 +8,8 @@ import math
import sys import sys
from math import log from math import log
import datetime import datetime
import os
from collections import Counter
class LaserCheck(inkex.EffectExtension): class LaserCheck(inkex.EffectExtension):
@ -64,6 +66,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('--checks', default="check_all") pars.add_argument('--checks', default="check_all")
pars.add_argument('--statistics', type=inkex.Boolean, default=False)
pars.add_argument('--filesize_max', type=float, default=2048.000)
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('--cutting_estimation', type=inkex.Boolean, default=False) pars.add_argument('--cutting_estimation', type=inkex.Boolean, default=False)
@ -97,6 +101,8 @@ class LaserCheck(inkex.EffectExtension):
def effect(self): def effect(self):
so = self.options so = self.options
docroot = self.document.getroot()
machineWidth = self.svg.unittouu(so.machine_size.split('x')[0] + "mm") machineWidth = self.svg.unittouu(so.machine_size.split('x')[0] + "mm")
machineHeight = self.svg.unittouu(so.machine_size.split('x')[1] + "mm") machineHeight = self.svg.unittouu(so.machine_size.split('x')[1] + "mm")
selected = [] #total list of elements to parse selected = [] #total list of elements to parse
@ -114,15 +120,15 @@ class LaserCheck(inkex.EffectExtension):
#check if we have selected elements or if we should parse the whole document instead #check if we have selected elements or if we should parse the whole document instead
if len(self.svg.selected) == 0: if len(self.svg.selected) == 0:
for element in self.document.getroot().iter(tag=etree.Element): for element in docroot.iter(tag=etree.Element):
if element != self.document.getroot(): if element != docroot:
selected.append(element) selected.append(element)
else: else:
for element in self.svg.selected.values(): for element in self.svg.selected.values():
parseChildren(element) parseChildren(element)
namedView = self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')) namedView = docroot.find(inkex.addNS('namedview', 'sodipodi'))
doc_units = namedView.get(inkex.addNS('document-units', 'inkscape')) doc_units = namedView.get(inkex.addNS('document-units', 'inkscape'))
user_units = namedView.get(inkex.addNS('units')) user_units = namedView.get(inkex.addNS('units'))
pagecolor = namedView.get('pagecolor') pagecolor = namedView.get('pagecolor')
@ -170,7 +176,7 @@ class LaserCheck(inkex.EffectExtension):
viewboxOk = False viewboxOk = False
# values may be lower than 0, but it does not make sense. The viewbox defines the top-left corner, which is usually 0,0. In case we want to allow that, we need to convert all bounding boxes accordingly. See also https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox. # values may be lower than 0, but it does not make sense. The viewbox defines the top-left corner, which is usually 0,0. In case we want to allow that, we need to convert all bounding boxes accordingly. See also https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox.
inkex.utils.debug("WARNING: Viewbox does not start at 0,0. Visible results will differ from real coordinates.") inkex.utils.debug("WARNING: Viewbox does not start at 0,0. Visible results will differ from real coordinates.")
''' '''
The SVG format is highly complex and offers a lot of possibilities. Most things of SVG we do not The SVG format is highly complex and offers a lot of possibilities. Most things of SVG we do not
need for a laser cutter. Usually we need svg:path and maybe svg:image; we can drop a lot of stuff need for a laser cutter. Usually we need svg:path and maybe svg:image; we can drop a lot of stuff
@ -186,7 +192,6 @@ class LaserCheck(inkex.EffectExtension):
"{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview", "{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview",
"{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF", "{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF",
"{http://creativecommons.org/ns#}Work"): "{http://creativecommons.org/ns#}Work"):
inkex.utils.debug(element.tag)
nonShapes.append(element) nonShapes.append(element)
else: else:
shapes.append(element) shapes.append(element)
@ -197,6 +202,34 @@ class LaserCheck(inkex.EffectExtension):
inkex.utils.debug("non-shape id={}".format(nonShape.get('id'))) inkex.utils.debug("non-shape id={}".format(nonShape.get('id')))
#that size is actually not the stored one on file system
#filesize = len(etree.tostring(self.document, pretty_print=True).decode('UTF-8')) / 1000
filesize = 0
if os.path.exists(self.document_path()) is False:
inkex.utils.debug("WARNING: File was not saved yet!")
else:
filesize = os.path.getsize(self.document_path()) / 1000
inkex.utils.debug("File size: {:0.1f} KB (That might be wrong. Check first for recently saved file)".format(filesize))
if filesize > so.filesize_max:
inkex.utils.debug("WARNING: file size is larger than allowed: {} KB > {} KB".format(filesize, so.filesize_max))
inkex.utils.debug("Total overview of element types:")
elementTypes = []
for element in selected:
if element not in elementTypes:
elementTypes.append(element.tag
.replace("{http://www.w3.org/2000/svg}", "")
.replace("{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}", "")
.replace("{http://www.w3.org/1999/02/22-rdf-syntax-ns#}", "")
.replace("{http://creativecommons.org/ns#}", "")
.replace("{http://www.inkscape.org/namespaces/inkscape}", "")
)
counter = Counter(elementTypes)
uniqElementTypes = counter
for key in counter.keys():
inkex.utils.debug(" - {}: {}x".format(key, counter[key]))
''' '''
Nearly each laser job needs a bit of border to place the material inside the laser. Often Nearly each laser job needs a bit of border to place the material inside the laser. Often
we have to fixate on vector grid, pin grid or task plate. Thus we need tapes or pins. So we we have to fixate on vector grid, pin grid or task plate. Thus we need tapes or pins. So we
@ -210,8 +243,8 @@ class LaserCheck(inkex.EffectExtension):
inkex.utils.debug("WARNING: Viewbox does not start at 0,0. Calculating bounding boxes might create wrong results.") inkex.utils.debug("WARNING: Viewbox does not start at 0,0. Calculating bounding boxes might create wrong results.")
bbox = inkex.BoundingBox() bbox = inkex.BoundingBox()
for element in selected: for element in selected:
#for element in self.document.getroot().iter(tag=etree.Element): #for element in docroot.iter(tag=etree.Element):
if element != self.document.getroot() and isinstance(element, inkex.ShapeElement) and element.tag != inkex.addNS('use','svg') and element.get('inkscape:groupmode') != 'layer': #bbox fails for svg:use elements and layers if element != docroot and isinstance(element, inkex.ShapeElement) and element.tag != inkex.addNS('use','svg') and element.get('inkscape:groupmode') != 'layer': #bbox fails for svg:use elements and layers
transform = inkex.Transform() transform = inkex.Transform()
parent = element.getparent() parent = element.getparent()
if parent is not None and isinstance(parent, inkex.ShapeElement): if parent is not None and isinstance(parent, inkex.ShapeElement):
@ -232,8 +265,8 @@ class LaserCheck(inkex.EffectExtension):
#else: #else:
# inkex.utils.debug("bounding box is {}".format(bbox)) # inkex.utils.debug("bounding box is {}".format(bbox))
inkex.utils.debug("bounding box is:\n x.min = {}\n y.min = {}\n x.max = {}\n y.max = {}".format(bbox.left, bbox.top, bbox.right, bbox.bottom)) inkex.utils.debug("bounding box is:\n x.min = {}\n y.min = {}\n x.max = {}\n y.max = {}".format(bbox.left, bbox.top, bbox.right, bbox.bottom))
page_width = self.svg.unittouu(self.document.getroot().attrib['width']) page_width = self.svg.unittouu(docroot.attrib['width'])
width_height = self.svg.unittouu(self.document.getroot().attrib['height']) width_height = self.svg.unittouu(docroot.attrib['height'])
fmm = self.svg.unittouu(str(so.bbox_offset) + "mm") fmm = self.svg.unittouu(str(so.bbox_offset) + "mm")
bb_left = round(bbox.left, 3) bb_left = round(bbox.left, 3)
bb_right = round(bbox.right, 3) bb_right = round(bbox.right, 3)
@ -290,7 +323,7 @@ class LaserCheck(inkex.EffectExtension):
md += 1 md += 1
for child in element: for child in element:
maxDepth(child, level + 1) maxDepth(child, level + 1)
maxDepth(self.document.getroot(), -1) maxDepth(docroot, -1)
if so.show_issues_only is False: if so.show_issues_only is False:
inkex.utils.debug("Maximum group depth={}".format(md - 1)) inkex.utils.debug("Maximum group depth={}".format(md - 1))
if md - 1 > so.nest_depth_max: if md - 1 > so.nest_depth_max:
@ -916,6 +949,7 @@ class LaserCheck(inkex.EffectExtension):
if so.checks == "check_all" or so.nodes_per_path is True: if so.checks == "check_all" or so.nodes_per_path is True:
inkex.utils.debug("\n---------- Heavy node-loaded paths (allowed: {} node(s) per {} mm) - should be simplified".format(so.nodes_per_path_max, round(so.nodes_per_path_interval, 3))) inkex.utils.debug("\n---------- Heavy node-loaded paths (allowed: {} node(s) per {} mm) - should be simplified".format(so.nodes_per_path_max, round(so.nodes_per_path_interval, 3)))
heavyPaths = [] heavyPaths = []
totalNodesCount = 0
for element in shapes: for element in shapes:
if isinstance(element, inkex.PathElement): if isinstance(element, inkex.PathElement):
slengths, stotal = csplength(element.path.transform(element.composed_transform()).to_superpath()) slengths, stotal = csplength(element.path.transform(element.composed_transform()).to_superpath())
@ -926,6 +960,7 @@ class LaserCheck(inkex.EffectExtension):
if so.show_issues_only is False: if so.show_issues_only is False:
inkex.utils.debug("{} Heavy node-loaded paths in total".format(len(heavyPaths))) inkex.utils.debug("{} Heavy node-loaded paths in total".format(len(heavyPaths)))
for heavyPath in heavyPaths: for heavyPath in heavyPaths:
totalNodesCount += heavyPath[1]
inkex.utils.debug("id={}, nodes={}, length={}mm, density={}nodes/mm".format( inkex.utils.debug("id={}, nodes={}, length={}mm, density={}nodes/mm".format(
heavyPath[0].get('id'), heavyPath[0].get('id'),
heavyPath[1], heavyPath[1],
@ -933,7 +968,15 @@ class LaserCheck(inkex.EffectExtension):
round(heavyPath[1] / self.svg.uutounit(str(heavyPath[2]), "mm"), 3) round(heavyPath[1] / self.svg.uutounit(str(heavyPath[2]), "mm"), 3)
) )
) )
inkex.utils.debug("Total nodes on paths: {}".format(totalNodesCount))
pathCount = 0
for key in counter.keys():
if key == "path":
pathCount = counter[key]
if pathCount > 0:
inkex.utils.debug("Average nodes per path: {:0.0f}".format(totalNodesCount/pathCount))
''' '''
Elements outside canvas or touching the border. These are critical because they won't be lasered or not correctly lasered Elements outside canvas or touching the border. These are critical because they won't be lasered or not correctly lasered
''' '''

View File

@ -82,10 +82,10 @@ class UngrouperAndElementMigratorFilter(inkex.EffectExtension):
def effect(self): def effect(self):
so = self.options so = self.options
docroot = self.document.getroot()
#remove comments #remove comments
if so.comments is True: if so.comments is True:
docroot = self.document.getroot()
for element in docroot.iter(): for element in docroot.iter():
if "cyfunction Comment" in str(element.tag): if "cyfunction Comment" in str(element.tag):
self.allDrops.append(element) self.allDrops.append(element)
@ -95,8 +95,7 @@ class UngrouperAndElementMigratorFilter(inkex.EffectExtension):
#remove newlines (tails of elements) #remove newlines (tails of elements)
if so.tails is True: if so.tails is True:
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
root = self.document.getroot() for element in docroot.iter('*'):
for element in root.iter('*'):
if element.text is not None: if element.text is not None:
element.text = element.text.strip() element.text = element.text.strip()
if element.tail is not None: if element.tail is not None: