jschuh / klipper-macros

A collection of useful macros for the Klipper 3D printer firmware
GNU General Public License v3.0
921 stars 167 forks source link

[BUG] 'dict object' has no attribute 'polygon' #128

Closed lettore closed 1 year ago

lettore commented 1 year ago

Starting a print with variable_start_random_placement_max: 100 returns Error evaluating 'gcode_macro _km_check_and_set_print_bounds:gcode': UndefinedError: 'dict object' has no attribute 'polygon' This is the GCODE

; Pre-Processed for Cancel-Object support by preprocess_cancellation v(0, 3, 'a1')
; 3 known objects
EXCLUDE_OBJECT_DEFINE NAME=test_m10_stl_id_0_copy_0 CENTER=112.500,99.500 POLYGON=[[102.71,89.71],[102.71,109.29],[122.29,109.29],[122.29,89.71],[102.71,89.71]]
EXCLUDE_OBJECT_DEFINE NAME=test_m10_stl_id_1_copy_0 CENTER=112.500,125.456 POLYGON=[[110.442,115.5],[102.71,115.71],[102.71,135.29],[114.623,135.412],[122.29,135.29],[122.29,115.71],[110.442,115.5]]
EXCLUDE_OBJECT_DEFINE NAME=ENDGCODE
; generated by SuperSlicer 2.4 on 2023-06-10 at 10:59:45 UTC
; external perimeters extrusion width = 0.42mm
; perimeters extrusion width = 0.46mm
; infill extrusion width = 0.44mm
; solid infill extrusion width = 0.46mm
; top infill extrusion width = 0.44mm
; first layer extrusion width = 0.56mm

; external perimeters extrusion width = 0.42mm
; perimeters extrusion width = 0.46mm
; infill extrusion width = 0.44mm
; solid infill extrusion width = 0.46mm
; top infill extrusion width = 0.44mm
; first layer extrusion width = 0.56mm

; object:{"name":"test-m10","id":"test-m10.stl id:0 copy 0","object_center":[112.500000,99.500000,0.000000],"boundingbox_center":[112.500000,99.500000,10.000000],"boundingbox_size":[20.000000,20.000000,20.000000]}
; object:{"name":"test-m10","id":"test-m10.stl id:1 copy 0","object_center":[112.500001,125.500000,0.000000],"boundingbox_center":[112.500001,125.500000,10.000000],"boundingbox_size":[20.000000,20.000000,20.000000]}
; Total objects to print: 2
M486 T2
; plater:{"center":[112.500000,112.500000,0.000000],"boundingbox_center":[112.500000,112.500000,10.000000],"boundingbox_size":[20.000001,46.000000,20.000000]}

M73 P0 R34
M117 Time Left 0h34m38s
;TYPE:Custom
; custom gcode: start_gcode
PRINT_START_SET MODEL_MIN=102.5,89.5,0 MODEL_MAX=122.5,135.5,20 CHAMBER=0 FILAMENT=PLA PRINTER=ENDER3S1
_PRINT_START_PHASE_INIT EXTRUDER=210 BED=60 MESH_MIN=98.9444,85.9444 MESH_MAX=126.056,139.056 LAYERS=100 NOZZLE_SIZE=0.4
_PRINT_START_PHASE_PREHEAT
_PRINT_START_PHASE_PROBING
_PRINT_START_PHASE_EXTRUDER
_PRINT_START_PHASE_PURGE

At the moment I managed to make work random placement skipping the exclude object section if it's provided MODEL_MIN and MODEL_MAX.

  # Find all the model bounds (including any bounds passed in).
  {% set points = [] %}
  {% if print.MODEL_MIN and print.MODEL_MAX %}
    {% set MODEL_MIN = print.MODEL_MIN.split(",")|map('float')|list %}
    {% set MODEL_MAX = print.MODEL_MAX.split(",")|map('float')|list %}
    {% set points = [MODEL_MIN, MODEL_MAX] %}
  {% elif (printer.exclude_object|default({})).objects %}
    {% set points = printer.exclude_object.objects|map(attribute='polygon')|
                      sum(start=points) %}
    {% set points_len = points|length %}
    {% if points_len >= 2 %}
      {% set x_coords = (points|map(attribute=0)|sort|list)[0::points_len-1] %}
      {% set y_coords = (points|map(attribute=1)|sort|list)[0::points_len-1] %}
      {% set MODEL_MIN = (x_coords[0],y_coords[0])|map('float')|list %}
      {% set MODEL_MAX = (x_coords[1],y_coords[1])|map('float')|list %}
      PRINT_START_SET MODEL_MIN="{MODEL_MIN|join(',')
                   }" MODEL_MAX="{MODEL_MAX|join(',')}"
    {% endif %}
  {% endif %}
lettore commented 1 year ago

This is the contents of printer.exclude_object.objects printer.exclude_object.objects : [{'name': '0'}, {'name': '1'}, {'name': 'ENDGCODE'}, {'center': [112.5, 99.5], 'polygon': [[102.71, 89.71], [102.71, 109.29], [122.29, 109.29], [122.29, 89.71], [102.71, 89.71]], 'name': 'TEST_M10_STL_ID_0_COPY_0'}, {'center': [112.5, 125.456], 'polygon': [[110.442, 115.5], [102.71, 115.71], [102.71, 135.29], [114.623, 135.412], [122.29, 135.29], [122.29, 115.71], [110.442, 115.5]], 'name': 'TEST_M10_STL_ID_1_COPY_0'}]

jschuh commented 1 year ago

Hm... hadn't occurred to me that something like EXCLUDE_OBJECT_DEFINE NAME=ENDGCODE would produce an objects entry. It's an easy fix though; just needs a selectattr('polygon') filter. I'll land it with some other changes in the next day or so, but here's the diff:

diff --git a/start_end.cfg b/start_end.cfg
index 49f431d..8031702 100644
--- a/start_end.cfg
+++ b/start_end.cfg
@@ -445,8 +445,8 @@ gcode:
     {% set points = [MODEL_MIN, MODEL_MAX] %}
   {% endif %}
   {% if (printer.exclude_object|default({})).objects %}
-    {% set points = printer.exclude_object.objects|map(attribute='polygon')|
-                      sum(start=points) %}
+    {% set points = printer.exclude_object.objects|selectattr('polygon')|
+                      map(attribute='polygon')|sum(start=points) %}
     {% set points_len = points|length %}
     {% if points_len >= 2 %}
       {% set x_coords = (points|map(attribute=0)|sort|list)[0::points_len-1] %}
lettore commented 1 year ago

I found the problem, if the Gcode is exported as is Superslicer or Prusaslicer labels the objects but there's no polygon variable in the object. I used the postprocessing tool from https://github.com/kageurufu/preprocess_cancellation and this adds the polygon section but it not removes the "legacy" objects, and it also adds an ENDGCODE empty object. The correct way to load the prints it's to add enable_object_processing: True in [file_manager] section of moonraker.conf and NOT do any postprocess in slicer. In this way the objects are populated with the polygon section. But yes dealing with empty objects it's correct.

jschuh commented 1 year ago

Fixed in 8aa2e47b920c8824888e8a7c8f131b99922c7d6f.