diff --git a/extensions/fablabchemnitz/laser_check/laser_check.inx b/extensions/fablabchemnitz/laser_check/laser_check.inx index 34a9b596..0fbb4310 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.inx +++ b/extensions/fablabchemnitz/laser_check/laser_check.inx @@ -18,8 +18,13 @@ 5.000 false + 100 90 80 70 60 50 40 30 20 10 9 8 7 6 5 4 3 2 1 + false + false + 2 + false false false @@ -28,12 +33,12 @@ false false - 3 + 3 false - 1 + 1 false false @@ -66,6 +71,7 @@ 2.0 true 12.0 + 60.0 diff --git a/extensions/fablabchemnitz/laser_check/laser_check.py b/extensions/fablabchemnitz/laser_check/laser_check.py index 582b9675..c56b368f 100644 --- a/extensions/fablabchemnitz/laser_check/laser_check.py +++ b/extensions/fablabchemnitz/laser_check/laser_check.py @@ -12,13 +12,10 @@ class LaserCheck(inkex.EffectExtension): ''' ToDos: - - check for elements which have the attribute "display:none" (some groups have this) - 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 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 - Handlungsempfehlungen einbauen @@ -44,7 +41,6 @@ class LaserCheck(inkex.EffectExtension): - run as script to generate quick results for users - check for old styles which should be upgraded (cleanup styles tool) - check for elements which have no style attribute (should be created) -> (cleanup styles tool) - - migrate styles from groups/layers to path styles (cleanup styles tool) - self-intersecting paths - number of parts (isles) to weed in total - this is an indicator for manually picking work; if we add bridges we have less work - number of parts which are smaller than vector grid @@ -67,6 +63,7 @@ class LaserCheck(inkex.EffectExtension): 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('--co2_power', type=float, default=60.0) #TODO pars.add_argument('--round_times', type=inkex.Boolean, default=True) pars.add_argument('--show_issues_only', type=inkex.Boolean, default=False) @@ -74,8 +71,10 @@ class LaserCheck(inkex.EffectExtension): pars.add_argument('--bbox', type=inkex.Boolean, default=False) pars.add_argument('--bbox_offset', type=float, default=5.000) pars.add_argument('--cutting_estimation', type=inkex.Boolean, default=False) + pars.add_argument('--cutting_speedfactors', default="100 90 80 70 60 50 40 30 20 10 9 8 7 6 5 4 3 2 1") 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('--nest_depth_max', type=int, default=2) pars.add_argument('--clones', type=inkex.Boolean, default=False) pars.add_argument('--clippaths', type=inkex.Boolean, default=False) pars.add_argument('--images', type=inkex.Boolean, default=False) @@ -140,7 +139,7 @@ class LaserCheck(inkex.EffectExtension): like svg:defs, svg:desc, gradients, etc. ''' nonShapes = [] - shapes = [] + shapes = [] #this may contains paths, rectangles, circles, groups and more for element in selected: if not isinstance(element, inkex.ShapeElement): nonShapes.append(element) @@ -232,6 +231,11 @@ class LaserCheck(inkex.EffectExtension): inkex.utils.debug("page height... fail: {:0.3f} mm".format(bb_height)) + ''' + We check for possible deep nested groups/layers, empty groups/layers or groups/layers with styles. + We want to avoid styles at groups. Its better to style the elements like svg:path directly. + We can use "Cleanup Styles" extension to change this. + ''' if so.checks == "check_all" or so.groups_and_layers is True: inkex.utils.debug("\n---------- Groups and layers") global md @@ -245,19 +249,23 @@ class LaserCheck(inkex.EffectExtension): maxDepth(self.document.getroot(), -1) if so.show_issues_only is False: inkex.utils.debug("Maximum group depth={}".format(md - 1)) - if md - 1 > 2: - self.msg("Warning: this group depth might cause issues!") + if md - 1 > so.nest_depth_max: + inkex.utils.debug("Warning: maximum allowed group depth reached: {}".format(so.nest_depth_max)) groups = [] layers = [] + styles = [] for element in selected: if element.tag == inkex.addNS('g','svg'): if element.get('inkscape:groupmode') == 'layer': layers.append(element) else: groups.append(element) + if element.style is not None: + styles.append(element) if so.show_issues_only is False: inkex.utils.debug("{} groups in total".format(len(groups))) inkex.utils.debug("{} layers in total".format(len(layers))) + inkex.utils.debug("{} groups/layers with style in total".format(len(styles))) #check for empty groups for group in groups: @@ -269,6 +277,10 @@ class LaserCheck(inkex.EffectExtension): if len(layer) == 0: inkex.utils.debug("id={} is empty layer".format(layer.get('id'))) + #check for groups/layers which have a style + for style in styles: + inkex.utils.debug("id={} has style".format(style.get('id'))) + ''' Clones should be unlinked because they cause similar issues like transformations ''' @@ -386,9 +398,7 @@ class LaserCheck(inkex.EffectExtension): strokeColors = [] for element in shapes: strokeColor = element.style.get('stroke') - if strokeColor is None or strokeColor == "none": - strokeColor = "none" - if strokeColor not in strokeColors: + if strokeColor is not None and strokeColor not in strokeColors: strokeColors.append(strokeColor) if so.show_issues_only is False: inkex.utils.debug("{} different stroke colors in total".format(len(strokeColors))) @@ -406,15 +416,13 @@ class LaserCheck(inkex.EffectExtension): strokeWidths = [] for element in shapes: strokeWidth = element.style.get('stroke-width') - if strokeWidth is None or strokeWidth == "none": - strokeWidth = "none" - if strokeWidth not in strokeWidths: + if strokeWidth is not None and strokeWidth not in strokeWidths: strokeWidths.append(strokeWidth) if so.show_issues_only is False: inkex.utils.debug("{} different stroke widths in total".format(len(strokeWidths))) if len(strokeWidths) > so.stroke_widths_max: for strokeWidth in strokeWidths: - swConverted = self.svg.uutounit(float(self.svg.unittouu(strokeWidth))) #possibly w/o units. we unify to some internal float + swConverted = self.svg.uutounit(float(self.svg.unittouu(strokeWidth))) #possibly w/o units. we unify to some internal float. The value "none" converts to 0.0 inkex.utils.debug("stroke width {}px ({}mm)".format( round(self.svg.uutounit(swConverted, "px"),4), round(self.svg.uutounit(swConverted, "mm"),4), @@ -431,9 +439,7 @@ class LaserCheck(inkex.EffectExtension): strokeDasharrays = [] for element in shapes: strokeDasharray = element.style.get('stroke-dasharray') - if strokeDasharray is None or strokeDasharray == "none": - strokeDasharray = "none" - if strokeDasharray not in strokeDasharrays: + if strokeDasharray is not None and strokeDasharray not in strokeDasharrays: strokeDasharrays.append(strokeDasharray) if so.show_issues_only is False: inkex.utils.debug("{} different stroke dash arrays in total".format(len(strokeDasharrays))) @@ -615,9 +621,7 @@ class LaserCheck(inkex.EffectExtension): transparencies = [] for element in shapes: stroke_opacity = element.style.get('stroke-opacity') - if stroke_opacity is None or stroke_opacity == "none": - stroke_opacity = "none" - if stroke_opacity not in transparencies: + if stroke_opacity is not stroke_opacity and stroke_opacity not in transparencies: if stroke_opacity != "none": if float(stroke_opacity) < 1.0: transparencies.append([element, stroke_opacity]) @@ -716,22 +720,32 @@ class LaserCheck(inkex.EffectExtension): totalCuttingLength += stotal cuttingPathCount += 1 totalLength = totalCuttingLength + totalTravelLength + v_travel = so.max_travel_speed #this is always at maximum inkex.utils.debug("total cutting paths={}".format(cuttingPathCount)) inkex.utils.debug("total travel paths={}".format(travelPathCount)) inkex.utils.debug("(measured) cutting length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalCuttingLength), "mm"), self.svg.uutounit(str(totalCuttingLength), "mm"))) inkex.utils.debug("(measured) travel length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalTravelLength), "mm"), self.svg.uutounit(str(totalTravelLength), "mm"))) inkex.utils.debug("(measured) total length (mm) = {:0.2f} mm".format(self.svg.uutounit(str(totalLength), "mm"), self.svg.uutounit(str(totalLength), "mm"))) + inkex.utils.debug("travel speed={:06.2f}mm/s".format(v_travel)) ''' from https://www.epiloglaser.com/assets/downloads/fusion-material-settings.pdf "Speed Settings: The speed setting scale of 1% to 100% is not linear – i.e. 100% speed will not be twice as fast as 50% speed. This non-linear scale is very useful in compensating for the different factors that affect engraving time." ''' - for speedFactor in [100,90,80,70,60,50,40,30,20,10,9,8,7,6,5,4,3,2,1]: + speedFactors = [] + try: + for speed in re.findall(r"[+]?\d*\.\d+|\d+", self.options.cutting_speedfactors): #allow only positive values + if float(speed) > 0: + speedFactors.append(float(speed)) + speedFactors = sorted(speedFactors)[::-1] + except: + inkex.utils.debug("Error parsing cutting estimation speeds. Please try again!") + exit(1) + for speedFactor in speedFactors: speedFactorR = speedFactor / 100.0 adjusted_speed = 480.0 / so.max_cutting_speed #empiric - found out by trying for hours ... empiric_scale = 1 + (speedFactorR**2) / 15.25 #empiric - found out by trying for hours ... v_cut = so.max_cutting_speed * speedFactorR - v_travel = so.max_travel_speed #this is always at maximum tsec_cut = (self.svg.uutounit(str(totalCuttingLength)) / (adjusted_speed * so.max_cutting_speed * speedFactorR)) * empiric_scale tsec_travel = self.svg.uutounit(str(totalTravelLength)) / v_travel tsec_total = so.job_time_offset + tsec_cut + tsec_travel @@ -749,7 +763,7 @@ class LaserCheck(inkex.EffectExtension): 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)) + inkex.utils.debug("@{:05.1f}% (cut={:06.2f}mm/s > {:03.0f}min {:02.0f}sec | cost={:02.0f}€".format(speedFactor, v_cut, minutes, seconds, costs)) ''' Measurements from Epilog Software Suite