From 048ae1e1a293424db5a302a81c5b1a448a9578dd Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Sat, 11 Dec 2021 02:26:39 +0100 Subject: [PATCH] better invis checks --- .../laser_check/laser_check.inx | 3 +- .../fablabchemnitz/laser_check/laser_check.py | 165 +++++++++++++++--- 2 files changed, 139 insertions(+), 29 deletions(-) diff --git a/extensions/fablabchemnitz/laser_check/laser_check.inx b/extensions/fablabchemnitz/laser_check/laser_check.inx index 1481f40f..34a9b596 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.inx +++ b/extensions/fablabchemnitz/laser_check/laser_check.inx @@ -62,8 +62,9 @@ 120.0 450.0 - 2 + 0.0 2.0 + true 12.0 diff --git a/extensions/fablabchemnitz/laser_check/laser_check.py b/extensions/fablabchemnitz/laser_check/laser_check.py index 31cd30c6..582b9675 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.py +++ b/extensions/fablabchemnitz/laser_check/laser_check.py @@ -7,7 +7,6 @@ import re import math from math import log import datetime -from email.policy import default class LaserCheck(inkex.EffectExtension): @@ -17,12 +16,11 @@ class LaserCheck(inkex.EffectExtension): - inx: - set speed manually or pick machine (epilog) - travel and cut speed are prefilled then - calculate cut estimation with linear or non-linear (epilog) speeds > select formula or like this - - select time estimation for specific speed percentage for for all speeds (100,90, ...) + - select time estimation for specific speed percentage or for all speeds (100,90, ...) - select material (parameters -> how to???) - select power of CO² source - add fields for additional costs like configuring the machine or grabbing parts out of the machine (weeding), etc. - add mode select: cut, engrave - - add some extra seconds for start, stop, removing parts, attaching material, ... - Handlungsempfehlungen einbauen - verweisen auf diverse plugins, die man nutzen kann: - migrate ungrouper @@ -66,9 +64,10 @@ class LaserCheck(inkex.EffectExtension): pars.add_argument('--machine_size', default="812x508") pars.add_argument('--max_cutting_speed', type=float, default=120.0) pars.add_argument('--max_travel_speed', type=float, default=450.0) - pars.add_argument('--job_time_offset', type=float, default=2.0) + pars.add_argument('--job_time_offset', type=float, default=0.0) pars.add_argument('--price_per_minute_gross', type=float, default=2.0) pars.add_argument('--vector_grid_xy', type=float, default=12.0) #TODO + pars.add_argument('--round_times', type=inkex.Boolean, default=True) pars.add_argument('--show_issues_only', type=inkex.Boolean, default=False) pars.add_argument('--checks', default="check_all") @@ -452,61 +451,159 @@ class LaserCheck(inkex.EffectExtension): inkex.utils.debug("\n---------- Invisible shapes") invisibles = [] for element in shapes: - if element.tag not in (inkex.addNS('tspan','svg')) and element.get('inkscape:groupmode') != 'layer' and not isinstance(element, inkex.Group): - stroke = element.style.get('stroke') - if stroke is None or stroke == "none": + if element.tag not in (inkex.addNS('tspan','svg')) and element.get('inkscape:groupmode') != 'layer' and not isinstance(element, inkex.Group): + strokeAttr = element.get('stroke') #same information could be in regular attribute instead nested in style attribute + if strokeAttr is None or strokeAttr == "none": strokeVis = 0 - elif stroke in ('#ffffff', 'white', 'rgb(255,255,255)'): + elif strokeAttr in ('#ffffff', 'white', 'rgb(255,255,255)'): strokeVis = 0 else: strokeVis = 1 + stroke = element.style.get('stroke') + if stroke is not None: + if stroke == "none": + strokeVis = 0 + elif stroke in ('#ffffff', 'white', 'rgb(255,255,255)'): + strokeVis = 0 + else: + strokeVis = 1 - stroke_width = element.style.get('stroke-width') - if stroke_width is None or stroke_width == "none": + + strokeWidthAttr = element.get('stroke-width') #same information could be in regular attribute instead nested in style attribute + if strokeWidthAttr == "none": widthVis = 0 - elif self.svg.unittouu(stroke_width) < 0.005: #really thin (0,005pc = 0,080px) + elif strokeWidthAttr is not None and self.svg.unittouu(strokeWidthAttr) < 0.005: #really thin (0,005pc = 0,080px) widthVis = 0 else: widthVis = 1 + stroke_width = element.style.get('stroke-width') + if stroke_width is not None: + if stroke_width == "none": + widthVis = 0 + elif stroke_width is not None and self.svg.unittouu(stroke_width) < 0.005: #really thin (0,005pc = 0,080px) + widthVis = 0 + else: + widthVis = 1 - stroke_opacity = element.style.get('stroke-opacity') - if stroke_opacity is None or stroke_opacity == "none": + + strokeOpacityAttr = element.get('stroke-opacity') #same information could be in regular attribute instead nested in style attribute + if strokeOpacityAttr == "none": strokeOpacityVis = 0 - elif float(stroke_opacity) < 0.05: #nearly invisible (<5% opacity) + elif strokeOpacityAttr is not None and self.svg.unittouu(strokeOpacityAttr) < 0.05: #nearly invisible (<5% opacity) strokeOpacityVis = 0 else: strokeOpacityVis = 1 + stroke_opacity = element.style.get('stroke-opacity') + if stroke_opacity is not None: + if stroke_opacity == "none": + strokeOpacityVis = 0 + elif stroke_opacity is not None and self.svg.unittouu(stroke_opacity) < 0.05: #nearly invisible (<5% opacity) + strokeOpacityVis = 0 + else: + strokeOpacityVis = 1 + if pagecolor == '#ffffff': invisColors = [pagecolor, 'white', 'rgb(255,255,255)'] else: invisColors = [pagecolor] #we could add some parser to convert pagecolor to rgb/hsl/cmyk - - fill = element.style.get('fill') - if fill is None or fill == "none": + fillAttr = element.get('fill') #same information could be in regular attribute instead nested in style attribute + if fillAttr is None or fillAttr == "none": fillVis = 0 elif fill in invisColors: fillVis = 0 else: fillVis = 1 + fill = element.style.get('fill') + if fill is not None: + if fill == "none": + fillVis = 0 + elif fill in invisColors: + fillVis = 0 + else: + fillVis = 1 - fill_opacity = element.style.get('fill-opacity') - if fill_opacity is None or fill_opacity == "none": #always is opaque if not set, so set to 1 - fillOpacityVis = 1 - elif float(fill_opacity) < 0.05: #nearly invisible (<5% opacity) + + fillOpacityAttr = element.get('fill-opacity') #same information could be in regular attribute instead nested in style attribute + if fillOpacityAttr == "none": + fillOpacityVis = 0 + elif strokeOpacityAttr is not None and self.svg.unittouu(fillOpacityAttr) < 0.05: #nearly invisible (<5% opacity) fillOpacityVis = 0 else: - fillOpacityVis = 1 + fillOpacityVis = 1 + fill_opacity = element.style.get('fill-opacity') + if fill_opacity is not None: + if fill_opacity == "none": + fillOpacityVis = 0 + elif fill_opacity is not None and self.svg.unittouu(fill_opacity) < 0.05: #nearly invisible (<5% opacity) + fillOpacityVis = 0 + else: + fillOpacityVis = 1 + + + display = element.style.get('display') + if display == "none": + displayVis = 0 + else: + displayVis = 1 + displayAttr = element.get('display') #same information could be in regular attribute instead nested in style attribute + if displayAttr == "none": + displayAttrVis = 0 + else: + displayAttrVis = 1 + + + #check for svg:path elements which have consistent slope (straight lines) and no a defined fill and no stroke. such (poly)lines are still not visible + pathVis = 1 + if element.tag == inkex.addNS('path','svg') and fillVis == 1 and strokeVis == 0: + segments = element.path.to_arrays() + chars = set('aAcCqQtTsS') + if not any((c in chars) for c in str(element.path)): #skip beziers (we only check for polylines) + slopes = [] + for i in range(0, len(segments)): + if i > 0: + x1, y1, x2, y2 = segments[i-1][1][0], segments[i-1][1][1], segments[i][1][0], segments[i][1][1] + if x1 < x2: + p0 = [x1, y1] + p1 = [x2, y2] + else: + p0 = [x2, y2] + p1 = [x1, y1] + dx = p1[0] - p0[0] + if dx == 0: + slope = sys.float_info.max #vertical + else: + slope = (p1[1] - p0[1]) / dx + slope = round(slope, 6) + if slope not in slopes: + slopes.append(slope) + if len(slopes) < 2: + pathVis = 0 - #inkex.utils.debug("id={}, strokeVis={}, widthVis={}, strokeOpacityVis={}, fillVis={}, fillOpacityVis={}".format(element.get('id'), strokeVis, widthVis, strokeOpacityVis, fillVis, fillOpacityVis)) + flags = "id={}, strokeVis={}, widthVis={}, strokeOpacityVis={} | fillVis={}, fillOpacityVis={} | displayVis={}, displayAttrVis = {} | pathVis = {}"\ + .format(element.get('id'), strokeVis, widthVis, strokeOpacityVis, fillVis, fillOpacityVis, displayVis, displayAttrVis, pathVis) if element.style is not None: #f if the style attribute is not set at all, the element will be visible with default black color fill and w/o stroke - if (strokeVis == 0 or widthVis == 0 or strokeOpacityVis == 0) and (fillVis == 0 or fillOpacityVis == 0): + if (strokeVis == 0 or widthVis == 0 or strokeOpacityVis == 0): + strokeInvis = True + else: + strokeInvis = False + if (fillVis == 0 or fillOpacityVis == 0): + fillInvis = True + else: + fillInvis = False + if strokeInvis is True and fillInvis is True: if element not in invisibles: - invisibles.append(element) + invisibles.append(flags) + if displayVis == 0 or displayAttrVis == 0: + if element not in invisibles: + invisibles.append(flags) + if pathVis == 0: + if element not in invisibles: + invisibles.append(flags) if so.show_issues_only is False: inkex.utils.debug("{} invisible shapes in total".format(len(invisibles))) for invisible in invisibles: - inkex.utils.debug("id={}".format(invisible.get('id'))) + inkex.utils.debug(invisible) ''' @@ -639,8 +736,20 @@ class LaserCheck(inkex.EffectExtension): tsec_travel = self.svg.uutounit(str(totalTravelLength)) / v_travel tsec_total = so.job_time_offset + tsec_cut + tsec_travel minutes, seconds = divmod(tsec_total, 60) # split the seconds to minutes and seconds - partial_minutes = round(seconds/60 * 2) / 2 - inkex.utils.debug("@{:03.0f}% (cut={:06.2f}mm/s | travel={:06.2f}mm/s) > {:03.0f}min {:02.0f}sec | cost={:02.0f}€".format(speedFactor, v_cut, v_travel, minutes, seconds, so.price_per_minute_gross * (minutes + partial_minutes))) + seconds_for_price = seconds + #round seconds up to 30 or 60 + if so.round_times is True: + if seconds_for_price < 30: + seconds_for_price = 30 + if seconds_for_price > 30 and seconds_for_price != 60: + seconds_for_price = 60 + + partial_minutes = round(seconds_for_price/60 * 2) / 2 + costs = so.price_per_minute_gross * (minutes + partial_minutes) + if "{:02.0f}".format(seconds) == "60": #for formatting reasons + seconds = 0 + minutes += 1 + inkex.utils.debug("@{:03.0f}% (cut={:06.2f}mm/s | travel={:06.2f}mm/s) > {:03.0f}min {:02.0f}sec | cost={:02.0f}€".format(speedFactor, v_cut, v_travel, minutes, seconds, costs)) ''' Measurements from Epilog Software Suite