better invis checks

This commit is contained in:
Mario Voigt 2021-12-11 02:26:39 +01:00
parent be7150979d
commit 048ae1e1a2
2 changed files with 139 additions and 29 deletions

View File

@ -62,8 +62,9 @@
</param> </param>
<param name="max_cutting_speed" type="float" min="1.000" max="9999.000" precision="2" gui-text="Max. cutting speed (mm/s)">120.0</param> <param name="max_cutting_speed" type="float" min="1.000" max="9999.000" precision="2" gui-text="Max. cutting speed (mm/s)">120.0</param>
<param name="max_travel_speed" type="float" min="1.000" max="9999.000" precision="2" gui-text="Max. travel speed (mm/s)">450.0</param> <param name="max_travel_speed" type="float" min="1.000" max="9999.000" precision="2" gui-text="Max. travel speed (mm/s)">450.0</param>
<param name="job_time_offset" type="float" min="0.000" max="9999.000" precision="2" gui-text="Job time offset" gui-description="The laser is not starting immediately. It has some delay.">2</param> <param name="job_time_offset" type="float" min="0.000" max="9999.000" precision="2" gui-text="Job time offset (s)" gui-description="The laser is not starting immediately. It has some delay.">0.0</param>
<param name="price_per_minute_gross" type="float" min="0.0" max="9999.0" precision="2" gui-text="Price/minute € (gross)">2.0</param> <param name="price_per_minute_gross" type="float" min="0.0" max="9999.0" precision="2" gui-text="Price/minute € (gross)">2.0</param>
<param name="round_times" type="bool" gui-text="Round up to 30/60 seconds" gui-description="For pricing">true</param>
<param name="vector_grid_xy" type="float" min="0.0" max="9999.0" precision="2" gui-text="Vector grid (mm)">12.0</param> <param name="vector_grid_xy" type="float" min="0.0" max="9999.0" precision="2" gui-text="Vector grid (mm)">12.0</param>
</page> </page>
<page name="tab_about" gui-text="About"> <page name="tab_about" gui-text="About">

View File

@ -7,7 +7,6 @@ import re
import math import math
from math import log from math import log
import datetime import datetime
from email.policy import default
class LaserCheck(inkex.EffectExtension): class LaserCheck(inkex.EffectExtension):
@ -17,12 +16,11 @@ class LaserCheck(inkex.EffectExtension):
- inx: - inx:
- set speed manually or pick machine (epilog) - travel and cut speed are prefilled then - 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 - 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 material (parameters -> how to???)
- select power of CO² source - select power of CO² source
- add fields for additional costs like configuring the machine or grabbing parts out of the machine (weeding), etc. - add fields for additional costs like configuring the machine or grabbing parts out of the machine (weeding), etc.
- add mode select: cut, engrave - add mode select: cut, engrave
- add some extra seconds for start, stop, removing parts, attaching material, ...
- Handlungsempfehlungen einbauen - Handlungsempfehlungen einbauen
- verweisen auf diverse plugins, die man nutzen kann: - verweisen auf diverse plugins, die man nutzen kann:
- migrate ungrouper - migrate ungrouper
@ -66,9 +64,10 @@ class LaserCheck(inkex.EffectExtension):
pars.add_argument('--machine_size', default="812x508") pars.add_argument('--machine_size', default="812x508")
pars.add_argument('--max_cutting_speed', type=float, default=120.0) 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('--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('--price_per_minute_gross', type=float, default=2.0)
pars.add_argument('--vector_grid_xy', type=float, default=12.0) #TODO 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('--show_issues_only', type=inkex.Boolean, default=False)
pars.add_argument('--checks', default="check_all") pars.add_argument('--checks', default="check_all")
@ -453,60 +452,158 @@ class LaserCheck(inkex.EffectExtension):
invisibles = [] invisibles = []
for element in shapes: 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): 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 strokeAttr in ('#ffffff', 'white', 'rgb(255,255,255)'):
strokeVis = 0
else:
strokeVis = 1
stroke = element.style.get('stroke') stroke = element.style.get('stroke')
if stroke is None or stroke == "none": if stroke is not None:
if stroke == "none":
strokeVis = 0 strokeVis = 0
elif stroke in ('#ffffff', 'white', 'rgb(255,255,255)'): elif stroke in ('#ffffff', 'white', 'rgb(255,255,255)'):
strokeVis = 0 strokeVis = 0
else: else:
strokeVis = 1 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 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 widthVis = 0
else: else:
widthVis = 1 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 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 strokeOpacityVis = 0
else: else:
strokeOpacityVis = 1 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': if pagecolor == '#ffffff':
invisColors = [pagecolor, 'white', 'rgb(255,255,255)'] invisColors = [pagecolor, 'white', 'rgb(255,255,255)']
else: else:
invisColors = [pagecolor] #we could add some parser to convert pagecolor to rgb/hsl/cmyk invisColors = [pagecolor] #we could add some parser to convert pagecolor to rgb/hsl/cmyk
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') fill = element.style.get('fill')
if fill is None or fill == "none": if fill is not None:
if fill == "none":
fillVis = 0 fillVis = 0
elif fill in invisColors: elif fill in invisColors:
fillVis = 0 fillVis = 0
else: else:
fillVis = 1 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 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
elif float(fill_opacity) < 0.05: #nearly invisible (<5% opacity) 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 fillOpacityVis = 0
else: else:
fillOpacityVis = 1 fillOpacityVis = 1
#inkex.utils.debug("id={}, strokeVis={}, widthVis={}, strokeOpacityVis={}, fillVis={}, fillOpacityVis={}".format(element.get('id'), strokeVis, widthVis, strokeOpacityVis, fillVis, fillOpacityVis))
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
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 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: 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: if so.show_issues_only is False:
inkex.utils.debug("{} invisible shapes in total".format(len(invisibles))) inkex.utils.debug("{} invisible shapes in total".format(len(invisibles)))
for invisible in 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_travel = self.svg.uutounit(str(totalTravelLength)) / v_travel
tsec_total = so.job_time_offset + tsec_cut + tsec_travel tsec_total = so.job_time_offset + tsec_cut + tsec_travel
minutes, seconds = divmod(tsec_total, 60) # split the seconds to minutes and seconds minutes, seconds = divmod(tsec_total, 60) # split the seconds to minutes and seconds
partial_minutes = round(seconds/60 * 2) / 2 seconds_for_price = seconds
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))) #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 ''' Measurements from Epilog Software Suite