Browse Source

UBL_DELTA (#6695)

UBL on Delta's....     Should be close!    Should not affect any Cartesian printer.
oldmcg 8 years ago
parent
commit
91841d75c9

+ 29
- 7
Marlin/Conditionals_post.h View File

730
   /**
730
   /**
731
    * Set granular options based on the specific type of leveling
731
    * Set granular options based on the specific type of leveling
732
    */
732
    */
733
+
734
+  #if ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(DELTA)
735
+    #define UBL_DELTA
736
+  #endif
737
+
733
   #define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
738
   #define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
734
   #define ABL_GRID   (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
739
   #define ABL_GRID   (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
735
   #define HAS_ABL    (ABL_PLANAR || ABL_GRID || ENABLED(AUTO_BED_LEVELING_UBL))
740
   #define HAS_ABL    (ABL_PLANAR || ABL_GRID || ENABLED(AUTO_BED_LEVELING_UBL))
736
   #define HAS_LEVELING          (HAS_ABL || ENABLED(MESH_BED_LEVELING))
741
   #define HAS_LEVELING          (HAS_ABL || ENABLED(MESH_BED_LEVELING))
737
-  #define PLANNER_LEVELING      (ABL_PLANAR || ABL_GRID || ENABLED(MESH_BED_LEVELING))
742
+  #define PLANNER_LEVELING      (ABL_PLANAR || ABL_GRID || ENABLED(MESH_BED_LEVELING) || ENABLED(UBL_DELTA))
738
   #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST))
743
   #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST))
739
   #if HAS_PROBING_PROCEDURE
744
   #if HAS_PROBING_PROCEDURE
740
     #define PROBE_BED_WIDTH abs(RIGHT_PROBE_BED_POSITION - (LEFT_PROBE_BED_POSITION))
745
     #define PROBE_BED_WIDTH abs(RIGHT_PROBE_BED_POSITION - (LEFT_PROBE_BED_POSITION))
779
     #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT
784
     #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT
780
   #endif
785
   #endif
781
 
786
 
782
-  #if IS_KINEMATIC
783
-    // Check for this in the code instead
784
-    #define MIN_PROBE_X X_MIN_POS
785
-    #define MAX_PROBE_X X_MAX_POS
786
-    #define MIN_PROBE_Y Y_MIN_POS
787
-    #define MAX_PROBE_Y Y_MAX_POS
787
+  #if ENABLED(DELTA)
788
+    // These will be further constrained in code, but UBL_PROBE_PT values
789
+    // cannot be compile-time verified within the radius.
790
+    #define MIN_PROBE_X (-DELTA_PRINTABLE_RADIUS)
791
+    #define MAX_PROBE_X ( DELTA_PRINTABLE_RADIUS)
792
+    #define MIN_PROBE_Y (-DELTA_PRINTABLE_RADIUS)
793
+    #define MAX_PROBE_Y ( DELTA_PRINTABLE_RADIUS)
788
   #else
794
   #else
789
     // Boundaries for probing based on set limits
795
     // Boundaries for probing based on set limits
790
     #define MIN_PROBE_X (max(X_MIN_POS, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
796
     #define MIN_PROBE_X (max(X_MIN_POS, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
814
     #define LCD_TIMEOUT_TO_STATUS 15000
820
     #define LCD_TIMEOUT_TO_STATUS 15000
815
   #endif
821
   #endif
816
 
822
 
823
+  /**
824
+   * DELTA_SEGMENT_MIN_LENGTH for UBL_DELTA
825
+   */
826
+
827
+  #if ENABLED(UBL_DELTA)
828
+    #ifndef DELTA_SEGMENT_MIN_LENGTH
829
+      #if IS_SCARA
830
+        #define DELTA_SEGMENT_MIN_LENGTH 0.25 // SCARA minimum segment size is 0.25mm
831
+      #elif ENABLED(DELTA)
832
+        #define DELTA_SEGMENT_MIN_LENGTH 0.10 // mm (still subject to DELTA_SEGMENTS_PER_SECOND)
833
+      #else // CARTESIAN
834
+        #define DELTA_SEGMENT_MIN_LENGTH 1.00 // mm (similar to G2/G3 arc segmentation)
835
+      #endif
836
+    #endif
837
+  #endif
838
+
817
 #endif // CONDITIONALS_POST_H
839
 #endif // CONDITIONALS_POST_H

+ 57
- 57
Marlin/G26_Mesh_Validation_Tool.cpp View File

122
 
122
 
123
   // External references
123
   // External references
124
 
124
 
125
-  extern float feedrate;
125
+  extern float feedrate_mm_s; // must set before calling prepare_move_to_destination
126
   extern Planner planner;
126
   extern Planner planner;
127
   #if ENABLED(ULTRA_LCD)
127
   #if ENABLED(ULTRA_LCD)
128
     extern char lcd_status_message[];
128
     extern char lcd_status_message[];
130
   extern float destination[XYZE];
130
   extern float destination[XYZE];
131
   void set_destination_to_current();
131
   void set_destination_to_current();
132
   void set_current_to_destination();
132
   void set_current_to_destination();
133
+  void prepare_move_to_destination();
133
   float code_value_float();
134
   float code_value_float();
134
   float code_value_linear_units();
135
   float code_value_linear_units();
135
   float code_value_axis_units(const AxisEnum axis);
136
   float code_value_axis_units(const AxisEnum axis);
137
   bool code_has_value();
138
   bool code_has_value();
138
   void lcd_init();
139
   void lcd_init();
139
   void lcd_setstatuspgm(const char* const message, const uint8_t level);
140
   void lcd_setstatuspgm(const char* const message, const uint8_t level);
140
-  bool prepare_move_to_destination_cartesian();
141
-  void line_to_destination();
142
-  void line_to_destination(float);
143
   void sync_plan_position_e();
141
   void sync_plan_position_e();
144
   void chirp_at_user();
142
   void chirp_at_user();
145
 
143
 
182
 
180
 
183
   static int16_t g26_repeats;
181
   static int16_t g26_repeats;
184
 
182
 
183
+  void G26_line_to_destination(const float &feed_rate) {
184
+    const float save_feedrate = feedrate_mm_s;
185
+    feedrate_mm_s = feed_rate;      // use specified feed rate
186
+    prepare_move_to_destination();  // will ultimately call ubl_line_to_destination_cartesian or ubl_prepare_linear_move_to for UBL_DELTA
187
+    feedrate_mm_s = save_feedrate;  // restore global feed rate
188
+  }
189
+
185
   /**
190
   /**
186
    * G26: Mesh Validation Pattern generation.
191
    * G26: Mesh Validation Pattern generation.
187
    *
192
    *
271
         const float circle_x = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
276
         const float circle_x = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
272
                     circle_y = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
277
                     circle_y = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
273
 
278
 
274
-        // Let's do a couple of quick sanity checks.  We can pull this code out later if we never see it catch a problem
275
-        #ifdef DELTA
276
-          if (HYPOT2(circle_x, circle_y) > sq(DELTA_PRINTABLE_RADIUS)) {
277
-            SERIAL_ERROR_START;
278
-            SERIAL_ERRORLNPGM("Attempt to print outside of DELTA_PRINTABLE_RADIUS.");
279
-            goto LEAVE;
280
-          }
281
-        #endif
279
+        // If this mesh location is outside the printable_radius, skip it.
282
 
280
 
283
-        // TODO: Change this to use `position_is_reachable`
284
-        if (!WITHIN(circle_x, X_MIN_POS, X_MAX_POS) || !WITHIN(circle_y, Y_MIN_POS, Y_MAX_POS)) {
285
-          SERIAL_ERROR_START;
286
-          SERIAL_ERRORLNPGM("Attempt to print off the bed.");
287
-          goto LEAVE;
288
-        }
281
+        if ( ! position_is_reachable_raw_xy( circle_x, circle_y ))
282
+          continue;
289
 
283
 
290
         xi = location.x_index;  // Just to shrink the next few lines and make them easier to understand
284
         xi = location.x_index;  // Just to shrink the next few lines and make them easier to understand
291
         yi = location.y_index;
285
         yi = location.y_index;
333
                 y = circle_y + sin_table[tmp_div_30],
327
                 y = circle_y + sin_table[tmp_div_30],
334
                 xe = circle_x + cos_table[tmp_div_30 + 1],
328
                 xe = circle_x + cos_table[tmp_div_30 + 1],
335
                 ye = circle_y + sin_table[tmp_div_30 + 1];
329
                 ye = circle_y + sin_table[tmp_div_30 + 1];
336
-          #ifdef DELTA
337
-            if (HYPOT2(x, y) > sq(DELTA_PRINTABLE_RADIUS))   // Check to make sure this part of
338
-              continue;                                      // the 'circle' is on the bed.  If
330
+          #if IS_KINEMATIC
331
+            // Check to make sure this segment is entirely on the bed, skip if not.
332
+            if (( ! position_is_reachable_raw_xy( x , y  )) ||
333
+                ( ! position_is_reachable_raw_xy( xe, ye )))
334
+              continue;
339
           #else                                              // not, we need to skip
335
           #else                                              // not, we need to skip
340
             x  = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
336
             x  = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
341
             y  = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1);
337
             y  = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1);
463
               sy = ey = constrain(pgm_read_float(&ubl.mesh_index_to_ypos[j]), Y_MIN_POS + 1, Y_MAX_POS - 1);
459
               sy = ey = constrain(pgm_read_float(&ubl.mesh_index_to_ypos[j]), Y_MIN_POS + 1, Y_MAX_POS - 1);
464
               ex = constrain(ex, X_MIN_POS + 1, X_MAX_POS - 1);
460
               ex = constrain(ex, X_MIN_POS + 1, X_MAX_POS - 1);
465
 
461
 
466
-              if (ubl.g26_debug_flag) {
467
-                SERIAL_ECHOPAIR(" Connecting with horizontal line (sx=", sx);
468
-                SERIAL_ECHOPAIR(", sy=", sy);
469
-                SERIAL_ECHOPAIR(") -> (ex=", ex);
470
-                SERIAL_ECHOPAIR(", ey=", ey);
471
-                SERIAL_CHAR(')');
472
-                SERIAL_EOL;
473
-                //debug_current_and_destination(PSTR("Connecting horizontal line."));
474
-              }
462
+              if (( position_is_reachable_raw_xy( sx, sy )) &&
463
+                  ( position_is_reachable_raw_xy( ex, ey ))) {
475
 
464
 
476
-              print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), layer_height);
477
-              bit_set(horizontal_mesh_line_flags, i, j);   // Mark it as done so we don't do it again
465
+                if (ubl.g26_debug_flag) {
466
+                  SERIAL_ECHOPAIR(" Connecting with horizontal line (sx=", sx);
467
+                  SERIAL_ECHOPAIR(", sy=", sy);
468
+                  SERIAL_ECHOPAIR(") -> (ex=", ex);
469
+                  SERIAL_ECHOPAIR(", ey=", ey);
470
+                  SERIAL_CHAR(')');
471
+                  SERIAL_EOL;
472
+                  //debug_current_and_destination(PSTR("Connecting horizontal line."));
473
+                }
474
+  
475
+                print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), layer_height);
476
+              }
477
+              bit_set(horizontal_mesh_line_flags, i, j);   // Mark it as done so we don't do it again, even if we skipped it
478
             }
478
             }
479
           }
479
           }
480
 
480
 
494
                 sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
494
                 sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
495
                 ey = constrain(ey, Y_MIN_POS + 1, Y_MAX_POS - 1);
495
                 ey = constrain(ey, Y_MIN_POS + 1, Y_MAX_POS - 1);
496
 
496
 
497
-                if (ubl.g26_debug_flag) {
498
-                  SERIAL_ECHOPAIR(" Connecting with vertical line (sx=", sx);
499
-                  SERIAL_ECHOPAIR(", sy=", sy);
500
-                  SERIAL_ECHOPAIR(") -> (ex=", ex);
501
-                  SERIAL_ECHOPAIR(", ey=", ey);
502
-                  SERIAL_CHAR(')');
503
-                  SERIAL_EOL;
504
-                  debug_current_and_destination(PSTR("Connecting vertical line."));
497
+                if (( position_is_reachable_raw_xy( sx, sy )) &&
498
+                    ( position_is_reachable_raw_xy( ex, ey ))) {
499
+
500
+                  if (ubl.g26_debug_flag) {
501
+                    SERIAL_ECHOPAIR(" Connecting with vertical line (sx=", sx);
502
+                    SERIAL_ECHOPAIR(", sy=", sy);
503
+                    SERIAL_ECHOPAIR(") -> (ex=", ex);
504
+                    SERIAL_ECHOPAIR(", ey=", ey);
505
+                    SERIAL_CHAR(')');
506
+                    SERIAL_EOL;
507
+                    debug_current_and_destination(PSTR("Connecting vertical line."));
508
+                  }
509
+                  print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), layer_height);
505
                 }
510
                 }
506
-                print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), layer_height);
507
-                bit_set(vertical_mesh_line_flags, i, j);   // Mark it as done so we don't do it again
511
+                bit_set(vertical_mesh_line_flags, i, j);   // Mark it as done so we don't do it again, even if skipped
508
               }
512
               }
509
             }
513
             }
510
           }
514
           }
532
       destination[Z_AXIS] = z;                          // We know the last_z==z or we wouldn't be in this block of code.
536
       destination[Z_AXIS] = z;                          // We know the last_z==z or we wouldn't be in this block of code.
533
       destination[E_AXIS] = current_position[E_AXIS];
537
       destination[E_AXIS] = current_position[E_AXIS];
534
 
538
 
535
-      ubl_line_to_destination(feed_value, 0);
539
+      G26_line_to_destination(feed_value);
536
 
540
 
537
       stepper.synchronize();
541
       stepper.synchronize();
538
       set_destination_to_current();
542
       set_destination_to_current();
552
 
556
 
553
     //if (ubl.g26_debug_flag) debug_current_and_destination(PSTR(" in move_to() doing last move"));
557
     //if (ubl.g26_debug_flag) debug_current_and_destination(PSTR(" in move_to() doing last move"));
554
 
558
 
555
-    ubl_line_to_destination(feed_value, 0);
559
+    G26_line_to_destination(feed_value);
556
 
560
 
557
     //if (ubl.g26_debug_flag) debug_current_and_destination(PSTR(" in move_to() after last move"));
561
     //if (ubl.g26_debug_flag) debug_current_and_destination(PSTR(" in move_to() after last move"));
558
 
562
 
755
     y_pos = current_position[Y_AXIS];
759
     y_pos = current_position[Y_AXIS];
756
 
760
 
757
     if (code_seen('X')) {
761
     if (code_seen('X')) {
758
-      x_pos = code_value_axis_units(X_AXIS);
759
-      if (!WITHIN(x_pos, X_MIN_POS, X_MAX_POS)) {
760
-        SERIAL_PROTOCOLLNPGM("?Specified X coordinate not plausible.");
761
-        return UBL_ERR;
762
-      }
762
+      x_pos = code_value_float();
763
     }
763
     }
764
-    else
765
 
764
 
766
     if (code_seen('Y')) {
765
     if (code_seen('Y')) {
767
-      y_pos = code_value_axis_units(Y_AXIS);
768
-      if (!WITHIN(y_pos, Y_MIN_POS, Y_MAX_POS)) {
769
-        SERIAL_PROTOCOLLNPGM("?Specified Y coordinate not plausible.");
770
-        return UBL_ERR;
771
-      }
766
+      y_pos = code_value_float();
767
+    }
768
+
769
+    if ( ! position_is_reachable_xy( x_pos, y_pos )) {
770
+      SERIAL_PROTOCOLLNPGM("?Specified X,Y coordinate out of bounds.");
771
+      return UBL_ERR;
772
     }
772
     }
773
 
773
 
774
     /**
774
     /**
864
           Total_Prime += 0.25;
864
           Total_Prime += 0.25;
865
           if (Total_Prime >= EXTRUDE_MAXLENGTH) return UBL_ERR;
865
           if (Total_Prime >= EXTRUDE_MAXLENGTH) return UBL_ERR;
866
         #endif
866
         #endif
867
-        ubl_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0, 0);
867
+        G26_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0);
868
 
868
 
869
         stepper.synchronize();    // Without this synchronize, the purge is more consistent,
869
         stepper.synchronize();    // Without this synchronize, the purge is more consistent,
870
                                   // but because the planner has a buffer, we won't be able
870
                                   // but because the planner has a buffer, we won't be able
893
       #endif
893
       #endif
894
       set_destination_to_current();
894
       set_destination_to_current();
895
       destination[E_AXIS] += prime_length;
895
       destination[E_AXIS] += prime_length;
896
-      ubl_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0, 0);
896
+      G26_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0);
897
       stepper.synchronize();
897
       stepper.synchronize();
898
       set_destination_to_current();
898
       set_destination_to_current();
899
       retract_filament(destination);
899
       retract_filament(destination);

+ 66
- 1
Marlin/Marlin.h View File

429
   bool axis_unhomed_error(const bool x, const bool y, const bool z);
429
   bool axis_unhomed_error(const bool x, const bool y, const bool z);
430
 #endif
430
 #endif
431
 
431
 
432
-#endif // MARLIN_H
432
+/**
433
+ * position_is_reachable family of functions
434
+ */
435
+
436
+#if IS_KINEMATIC // (DELTA or SCARA)
437
+
438
+  #if ENABLED(DELTA)
439
+    #define DELTA_PRINTABLE_RADIUS_SQUARED ((float)DELTA_PRINTABLE_RADIUS * (float)DELTA_PRINTABLE_RADIUS )
440
+  #endif
441
+
442
+  #if IS_SCARA
443
+    extern const float L1, L2;
444
+  #endif
445
+
446
+  inline bool position_is_reachable_raw_xy( float raw_x, float raw_y ) {
447
+    #if ENABLED(DELTA)
448
+      return ( HYPOT2( raw_x, raw_y ) <= DELTA_PRINTABLE_RADIUS_SQUARED );
449
+    #elif IS_SCARA
450
+      #if MIDDLE_DEAD_ZONE_R > 0
451
+        const float R2 = HYPOT2(raw_x - SCARA_OFFSET_X, raw_y - SCARA_OFFSET_Y);
452
+        return R2 >= sq(float(MIDDLE_DEAD_ZONE_R)) && R2 <= sq(L1 + L2);
453
+      #else
454
+        return HYPOT2(raw_x - SCARA_OFFSET_X, raw_y - SCARA_OFFSET_Y) <= sq(L1 + L2);
455
+      #endif
456
+    #else // CARTESIAN
457
+      #error
458
+    #endif
459
+  }
460
+
461
+  inline bool position_is_reachable_by_probe_raw_xy( float raw_x, float raw_y ) {
462
+
463
+    // both the nozzle and the probe must be able to reach the point
464
+
465
+    return ( position_is_reachable_raw_xy( raw_x, raw_y ) &&
466
+             position_is_reachable_raw_xy(
467
+                raw_x - X_PROBE_OFFSET_FROM_EXTRUDER,
468
+                raw_y - Y_PROBE_OFFSET_FROM_EXTRUDER ));
469
+  }
470
+
471
+#else // CARTESIAN
472
+
473
+  inline bool position_is_reachable_raw_xy( float raw_x, float raw_y ) {
474
+      // note to reviewer: this +/-0.0001 logic is copied from original postion_is_reachable
475
+      return WITHIN(raw_x, X_MIN_POS - 0.0001, X_MAX_POS + 0.0001)
476
+          && WITHIN(raw_y, Y_MIN_POS - 0.0001, Y_MAX_POS + 0.0001);
477
+  }
478
+
479
+  inline bool position_is_reachable_by_probe_raw_xy( float raw_x, float raw_y ) {
480
+      // note to reviewer: this logic is copied from UBL_G29.cpp and does not contain the +/-0.0001 above
481
+      return WITHIN(raw_x, MIN_PROBE_X, MAX_PROBE_X)
482
+          && WITHIN(raw_y, MIN_PROBE_Y, MAX_PROBE_Y);
483
+  }
484
+
485
+#endif // CARTESIAN
486
+
487
+inline bool position_is_reachable_by_probe_xy( float target_x, float target_y ) {
488
+  return position_is_reachable_by_probe_raw_xy(
489
+            RAW_X_POSITION( target_x ),
490
+            RAW_Y_POSITION( target_y ));
491
+}
492
+
493
+inline bool position_is_reachable_xy( float target_x, float target_y ) {
494
+  return position_is_reachable_raw_xy( RAW_X_POSITION( target_x ), RAW_Y_POSITION( target_y ));
495
+}
496
+
497
+#endif //MARLIN_H

+ 42
- 63
Marlin/Marlin_main.cpp View File

401
   #endif
401
   #endif
402
   MMM_TO_MMS(HOMING_FEEDRATE_Z), 0
402
   MMM_TO_MMS(HOMING_FEEDRATE_Z), 0
403
 };
403
 };
404
-static float feedrate_mm_s = MMM_TO_MMS(1500.0), saved_feedrate_mm_s;
404
+float feedrate_mm_s = MMM_TO_MMS(1500.0), saved_feedrate_mm_s;
405
 int feedrate_percentage = 100, saved_feedrate_percentage,
405
 int feedrate_percentage = 100, saved_feedrate_percentage,
406
     flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100);
406
     flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100);
407
 
407
 
1677
 
1677
 
1678
   #if ENABLED(DELTA)
1678
   #if ENABLED(DELTA)
1679
 
1679
 
1680
+    if ( ! position_is_reachable_xy( x, y )) return;
1681
+
1680
     feedrate_mm_s = fr_mm_s ? fr_mm_s : XY_PROBE_FEEDRATE_MM_S;
1682
     feedrate_mm_s = fr_mm_s ? fr_mm_s : XY_PROBE_FEEDRATE_MM_S;
1681
 
1683
 
1682
     set_destination_to_current();          // sync destination at the start
1684
     set_destination_to_current();          // sync destination at the start
1731
 
1733
 
1732
   #elif IS_SCARA
1734
   #elif IS_SCARA
1733
 
1735
 
1736
+    if ( ! position_is_reachable_xy( x, y )) return;
1737
+
1734
     set_destination_to_current();
1738
     set_destination_to_current();
1735
 
1739
 
1736
     // If Z needs to raise, do it before moving XY
1740
     // If Z needs to raise, do it before moving XY
2351
       }
2355
       }
2352
     #endif
2356
     #endif
2353
 
2357
 
2358
+    if ( ! position_is_reachable_by_probe_xy( x, y )) return NAN;
2359
+
2354
     const float old_feedrate_mm_s = feedrate_mm_s;
2360
     const float old_feedrate_mm_s = feedrate_mm_s;
2355
 
2361
 
2356
     #if ENABLED(DELTA)
2362
     #if ENABLED(DELTA)
2419
 
2425
 
2420
     #elif ENABLED(AUTO_BED_LEVELING_UBL)
2426
     #elif ENABLED(AUTO_BED_LEVELING_UBL)
2421
 
2427
 
2428
+      #if ENABLED(UBL_DELTA)
2429
+        if (( ubl.state.active ) && ( ! enable )) {   // leveling from on to off
2430
+          planner.unapply_leveling(current_position);
2431
+        }
2432
+      #endif
2433
+
2422
       ubl.state.active = enable;
2434
       ubl.state.active = enable;
2423
-      //set_current_from_steppers_for_axis(Z_AXIS);
2424
 
2435
 
2425
     #else
2436
     #else
2426
 
2437
 
3210
 
3221
 
3211
 #endif // HOST_KEEPALIVE_FEATURE
3222
 #endif // HOST_KEEPALIVE_FEATURE
3212
 
3223
 
3213
-bool position_is_reachable(const float target[XYZ]
3214
-  #if HAS_BED_PROBE
3215
-    , bool by_probe=false
3216
-  #endif
3217
-) {
3218
-  float dx = RAW_X_POSITION(target[X_AXIS]),
3219
-        dy = RAW_Y_POSITION(target[Y_AXIS]);
3220
-
3221
-  #if HAS_BED_PROBE
3222
-    if (by_probe) {
3223
-      dx -= X_PROBE_OFFSET_FROM_EXTRUDER;
3224
-      dy -= Y_PROBE_OFFSET_FROM_EXTRUDER;
3225
-    }
3226
-  #endif
3227
-
3228
-  #if IS_SCARA
3229
-    #if MIDDLE_DEAD_ZONE_R > 0
3230
-      const float R2 = HYPOT2(dx - SCARA_OFFSET_X, dy - SCARA_OFFSET_Y);
3231
-      return R2 >= sq(float(MIDDLE_DEAD_ZONE_R)) && R2 <= sq(L1 + L2);
3232
-    #else
3233
-      return HYPOT2(dx - SCARA_OFFSET_X, dy - SCARA_OFFSET_Y) <= sq(L1 + L2);
3234
-    #endif
3235
-  #elif ENABLED(DELTA)
3236
-    return HYPOT2(dx, dy) <= sq((float)(DELTA_PRINTABLE_RADIUS));
3237
-  #else
3238
-    const float dz = RAW_Z_POSITION(target[Z_AXIS]);
3239
-    return WITHIN(dx, X_MIN_POS - 0.0001, X_MAX_POS + 0.0001)
3240
-        && WITHIN(dy, Y_MIN_POS - 0.0001, Y_MAX_POS + 0.0001)
3241
-        && WITHIN(dz, Z_MIN_POS - 0.0001, Z_MAX_POS + 0.0001);
3242
-  #endif
3243
-}
3244
 
3224
 
3245
 /**************************************************
3225
 /**************************************************
3246
  ***************** GCode Handlers *****************
3226
  ***************** GCode Handlers *****************
3676
     destination[Y_AXIS] = LOGICAL_Y_POSITION(Z_SAFE_HOMING_Y_POINT);
3656
     destination[Y_AXIS] = LOGICAL_Y_POSITION(Z_SAFE_HOMING_Y_POINT);
3677
     destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height
3657
     destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height
3678
 
3658
 
3679
-    if (position_is_reachable(
3680
-          destination
3681
-          #if HOMING_Z_WITH_PROBE
3682
-            , true
3683
-          #endif
3684
-        )
3685
-    ) {
3659
+    #if HOMING_Z_WITH_PROBE
3660
+      destination[X_AXIS] -= X_PROBE_OFFSET_FROM_EXTRUDER;
3661
+      destination[Y_AXIS] -= Y_PROBE_OFFSET_FROM_EXTRUDER;
3662
+    #endif
3686
 
3663
 
3687
-      #if HOMING_Z_WITH_PROBE
3688
-        destination[X_AXIS] -= X_PROBE_OFFSET_FROM_EXTRUDER;
3689
-        destination[Y_AXIS] -= Y_PROBE_OFFSET_FROM_EXTRUDER;
3690
-      #endif
3664
+    if ( position_is_reachable_xy( destination[X_AXIS], destination[Y_AXIS] )) {
3691
 
3665
 
3692
       #if ENABLED(DEBUG_LEVELING_FEATURE)
3666
       #if ENABLED(DEBUG_LEVELING_FEATURE)
3693
         if (DEBUGGING(LEVELING)) DEBUG_POS("Z_SAFE_HOMING", destination);
3667
         if (DEBUGGING(LEVELING)) DEBUG_POS("Z_SAFE_HOMING", destination);
4612
             indexIntoAB[xCount][yCount] = abl_probe_index;
4586
             indexIntoAB[xCount][yCount] = abl_probe_index;
4613
           #endif
4587
           #endif
4614
 
4588
 
4615
-          float pos[XYZ] = { xProbe, yProbe, 0 };
4616
-          if (position_is_reachable(pos)) break;
4589
+          if (position_is_reachable_xy( xProbe, yProbe )) break;
4617
           ++abl_probe_index;
4590
           ++abl_probe_index;
4618
         }
4591
         }
4619
 
4592
 
4724
 
4697
 
4725
             #if IS_KINEMATIC
4698
             #if IS_KINEMATIC
4726
               // Avoid probing outside the round or hexagonal area
4699
               // Avoid probing outside the round or hexagonal area
4727
-              const float pos[XYZ] = { xProbe, yProbe, 0 };
4728
-              if (!position_is_reachable(pos, true)) continue;
4700
+              if (!position_is_reachable_by_probe_xy( xProbe, yProbe )) continue;
4729
             #endif
4701
             #endif
4730
 
4702
 
4731
             measured_z = faux ? 0.001 * random(-100, 101) : probe_pt(xProbe, yProbe, stow_probe_after_each, verbose_level);
4703
             measured_z = faux ? 0.001 * random(-100, 101) : probe_pt(xProbe, yProbe, stow_probe_after_each, verbose_level);
5028
    */
5000
    */
5029
   inline void gcode_G30() {
5001
   inline void gcode_G30() {
5030
     const float xpos = code_seen('X') ? code_value_linear_units() : current_position[X_AXIS] + X_PROBE_OFFSET_FROM_EXTRUDER,
5002
     const float xpos = code_seen('X') ? code_value_linear_units() : current_position[X_AXIS] + X_PROBE_OFFSET_FROM_EXTRUDER,
5031
-                ypos = code_seen('Y') ? code_value_linear_units() : current_position[Y_AXIS] + Y_PROBE_OFFSET_FROM_EXTRUDER,
5032
-                pos[XYZ] = { xpos, ypos, LOGICAL_Z_POSITION(0) };
5003
+                ypos = code_seen('Y') ? code_value_linear_units() : current_position[Y_AXIS] + Y_PROBE_OFFSET_FROM_EXTRUDER;
5033
 
5004
 
5034
-    if (!position_is_reachable(pos, true)) return;
5005
+    if (!position_is_reachable_by_probe_xy( xpos, ypos )) return;
5035
 
5006
 
5036
     // Disable leveling so the planner won't mess with us
5007
     // Disable leveling so the planner won't mess with us
5037
     #if HAS_LEVELING
5008
     #if HAS_LEVELING
6222
     bool stow_probe_after_each = code_seen('E');
6193
     bool stow_probe_after_each = code_seen('E');
6223
 
6194
 
6224
     float X_probe_location = code_seen('X') ? code_value_linear_units() : X_current + X_PROBE_OFFSET_FROM_EXTRUDER;
6195
     float X_probe_location = code_seen('X') ? code_value_linear_units() : X_current + X_PROBE_OFFSET_FROM_EXTRUDER;
6196
+    float Y_probe_location = code_seen('Y') ? code_value_linear_units() : Y_current + Y_PROBE_OFFSET_FROM_EXTRUDER;
6197
+
6225
     #if DISABLED(DELTA)
6198
     #if DISABLED(DELTA)
6226
       if (!WITHIN(X_probe_location, LOGICAL_X_POSITION(MIN_PROBE_X), LOGICAL_X_POSITION(MAX_PROBE_X))) {
6199
       if (!WITHIN(X_probe_location, LOGICAL_X_POSITION(MIN_PROBE_X), LOGICAL_X_POSITION(MAX_PROBE_X))) {
6227
         out_of_range_error(PSTR("X"));
6200
         out_of_range_error(PSTR("X"));
6228
         return;
6201
         return;
6229
       }
6202
       }
6230
-    #endif
6231
-
6232
-    float Y_probe_location = code_seen('Y') ? code_value_linear_units() : Y_current + Y_PROBE_OFFSET_FROM_EXTRUDER;
6233
-    #if DISABLED(DELTA)
6234
       if (!WITHIN(Y_probe_location, LOGICAL_Y_POSITION(MIN_PROBE_Y), LOGICAL_Y_POSITION(MAX_PROBE_Y))) {
6203
       if (!WITHIN(Y_probe_location, LOGICAL_Y_POSITION(MIN_PROBE_Y), LOGICAL_Y_POSITION(MAX_PROBE_Y))) {
6235
         out_of_range_error(PSTR("Y"));
6204
         out_of_range_error(PSTR("Y"));
6236
         return;
6205
         return;
6237
       }
6206
       }
6238
     #else
6207
     #else
6239
-      float pos[XYZ] = { X_probe_location, Y_probe_location, 0 };
6240
-      if (!position_is_reachable(pos, true)) {
6208
+      if (!position_is_reachable_by_probe_xy(X_probe_location, Y_probe_location)) {
6241
         SERIAL_PROTOCOLLNPGM("? (X,Y) location outside of probeable radius.");
6209
         SERIAL_PROTOCOLLNPGM("? (X,Y) location outside of probeable radius.");
6242
         return;
6210
         return;
6243
       }
6211
       }
6335
           #else
6303
           #else
6336
             // If we have gone out too far, we can do a simple fix and scale the numbers
6304
             // If we have gone out too far, we can do a simple fix and scale the numbers
6337
             // back in closer to the origin.
6305
             // back in closer to the origin.
6338
-            while (HYPOT(X_current, Y_current) > DELTA_PROBEABLE_RADIUS) {
6306
+            while ( ! position_is_reachable_by_probe_xy( X_current, Y_current )) {
6339
               X_current *= 0.8;
6307
               X_current *= 0.8;
6340
               Y_current *= 0.8;
6308
               Y_current *= 0.8;
6341
               if (verbose_level > 3) {
6309
               if (verbose_level > 3) {
11138
 
11106
 
11139
 #endif // AUTO_BED_LEVELING_BILINEAR
11107
 #endif // AUTO_BED_LEVELING_BILINEAR
11140
 
11108
 
11141
-#if IS_KINEMATIC
11109
+#if IS_KINEMATIC && DISABLED(UBL_DELTA)
11142
 
11110
 
11143
   /**
11111
   /**
11144
    * Prepare a linear move in a DELTA or SCARA setup.
11112
    * Prepare a linear move in a DELTA or SCARA setup.
11157
       return false;
11125
       return false;
11158
     }
11126
     }
11159
 
11127
 
11128
+    // Fail if attempting move outside printable radius
11129
+    if ( ! position_is_reachable_xy( ltarget[X_AXIS], ltarget[Y_AXIS] )) return true;
11130
+
11160
     // Get the cartesian distances moved in XYZE
11131
     // Get the cartesian distances moved in XYZE
11161
     float difference[XYZE];
11132
     float difference[XYZE];
11162
     LOOP_XYZE(i) difference[i] = ltarget[i] - current_position[i];
11133
     LOOP_XYZE(i) difference[i] = ltarget[i] - current_position[i];
11245
       // For SCARA scale the feed rate from mm/s to degrees/s
11216
       // For SCARA scale the feed rate from mm/s to degrees/s
11246
       // With segments > 1 length is 1 segment, otherwise total length
11217
       // With segments > 1 length is 1 segment, otherwise total length
11247
       inverse_kinematics(ltarget);
11218
       inverse_kinematics(ltarget);
11248
-      ADJUST_DELTA(logical);
11219
+      ADJUST_DELTA(ltarget);
11249
       const float adiff = abs(delta[A_AXIS] - oldA),
11220
       const float adiff = abs(delta[A_AXIS] - oldA),
11250
                   bdiff = abs(delta[B_AXIS] - oldB);
11221
                   bdiff = abs(delta[B_AXIS] - oldB);
11251
       planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], max(adiff, bdiff) * feed_factor, active_extruder);
11222
       planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], max(adiff, bdiff) * feed_factor, active_extruder);
11278
         else
11249
         else
11279
       #elif ENABLED(AUTO_BED_LEVELING_UBL)
11250
       #elif ENABLED(AUTO_BED_LEVELING_UBL)
11280
         if (ubl.state.active) {
11251
         if (ubl.state.active) {
11281
-          ubl_line_to_destination(MMS_SCALED(feedrate_mm_s), active_extruder);
11252
+          ubl_line_to_destination_cartesian(MMS_SCALED(feedrate_mm_s), active_extruder);
11282
           return true;
11253
           return true;
11283
         }
11254
         }
11284
         else
11255
         else
11407
   #endif
11378
   #endif
11408
 
11379
 
11409
   #if IS_KINEMATIC
11380
   #if IS_KINEMATIC
11410
-    if (prepare_kinematic_move_to(destination)) return;
11381
+    #if ENABLED(UBL_DELTA)
11382
+      if (ubl_prepare_linear_move_to(destination,feedrate_mm_s)) return;
11383
+    #else
11384
+      if (prepare_kinematic_move_to(destination)) return;
11385
+    #endif
11411
   #else
11386
   #else
11412
     #if ENABLED(DUAL_X_CARRIAGE)
11387
     #if ENABLED(DUAL_X_CARRIAGE)
11413
       if (prepare_move_to_destination_dualx()) return;
11388
       if (prepare_move_to_destination_dualx()) return;
11389
+    #elif ENABLED(UBL_DELTA) // will work for CARTESIAN too (smaller segments follow mesh more closely)
11390
+      if (ubl_prepare_linear_move_to(destination,feedrate_mm_s)) return;
11391
+    #else
11392
+      if (prepare_move_to_destination_cartesian()) return;
11414
     #endif
11393
     #endif
11415
-    if (prepare_move_to_destination_cartesian()) return;
11416
   #endif
11394
   #endif
11417
 
11395
 
11418
   set_current_to_destination();
11396
   set_current_to_destination();
12427
   endstops.report_state();
12405
   endstops.report_state();
12428
   idle();
12406
   idle();
12429
 }
12407
 }
12408
+

+ 35
- 22
Marlin/SanityCheck.h View File

248
 #if ENABLED(DELTA)
248
 #if ENABLED(DELTA)
249
   #if DISABLED(USE_XMAX_PLUG) && DISABLED(USE_YMAX_PLUG) && DISABLED(USE_ZMAX_PLUG)
249
   #if DISABLED(USE_XMAX_PLUG) && DISABLED(USE_YMAX_PLUG) && DISABLED(USE_ZMAX_PLUG)
250
     #error "You probably want to use Max Endstops for DELTA!"
250
     #error "You probably want to use Max Endstops for DELTA!"
251
-  #elif ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
252
-    #error "DELTA is incompatible with ENABLE_LEVELING_FADE_HEIGHT. Please disable it."
251
+  #endif
252
+  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) && DISABLED(UBL_DELTA)
253
+    #error "ENABLE_LEVELING_FADE_HEIGHT for DELTA requires UBL_DELTA and AUTO_BED_LEVELING_UBL."
253
   #endif
254
   #endif
254
   #if ABL_GRID
255
   #if ABL_GRID
255
     #if (GRID_MAX_POINTS_X & 1) == 0 || (GRID_MAX_POINTS_Y & 1) == 0
256
     #if (GRID_MAX_POINTS_X & 1) == 0 || (GRID_MAX_POINTS_Y & 1) == 0
430
  * Unified Bed Leveling
431
  * Unified Bed Leveling
431
  */
432
  */
432
 #if ENABLED(AUTO_BED_LEVELING_UBL)
433
 #if ENABLED(AUTO_BED_LEVELING_UBL)
433
-  #if ENABLED(DELTA)
434
-    #error "AUTO_BED_LEVELING_UBL does not yet support DELTA printers."
435
-  #elif DISABLED(NEWPANEL)
434
+  #if IS_KINEMATIC
435
+    #if ENABLED(DELTA)
436
+      #if DISABLED(UBL_DELTA)
437
+        #error "AUTO_BED_LEVELING_UBL requires UBL_DELTA for DELTA printers."
438
+      #endif
439
+    #else // SCARA
440
+      #error "AUTO_BED_LEVELING_UBL not supported for SCARA printers."
441
+    #endif
442
+  #endif
443
+  #if DISABLED(NEWPANEL)
436
     #error "AUTO_BED_LEVELING_UBL requires an LCD controller."
444
     #error "AUTO_BED_LEVELING_UBL requires an LCD controller."
437
   #endif
445
   #endif
446
+#elif ENABLED(UBL_DELTA)
447
+  #error "UBL_DELTA requires AUTO_BED_LEVELING_UBL."
438
 #endif
448
 #endif
439
 
449
 
440
 /**
450
 /**
593
   /**
603
   /**
594
    * Delta and SCARA have limited bed leveling options
604
    * Delta and SCARA have limited bed leveling options
595
    */
605
    */
596
-  #if DISABLED(AUTO_BED_LEVELING_BILINEAR)
597
-    #if ENABLED(DELTA)
598
-      #error "Only AUTO_BED_LEVELING_BILINEAR is supported for DELTA bed leveling."
599
-    #elif ENABLED(SCARA)
600
-      #error "Only AUTO_BED_LEVELING_BILINEAR is supported for SCARA bed leveling."
606
+  #if IS_KINEMATIC
607
+    #if DISABLED(AUTO_BED_LEVELING_BILINEAR) && DISABLED(UBL_DELTA)
608
+      #error "Only AUTO_BED_LEVELING_BILINEAR or AUTO_BED_LEVELING_UBL with UBL_DELTA support DELTA and SCARA bed leveling."
601
     #endif
609
     #endif
602
   #endif
610
   #endif
603
 
611
 
626
       #error "AUTO_BED_LEVELING_UBL requires EEPROM_SETTINGS. Please update your configuration."
634
       #error "AUTO_BED_LEVELING_UBL requires EEPROM_SETTINGS. Please update your configuration."
627
     #elif !WITHIN(GRID_MAX_POINTS_X, 3, 15) || !WITHIN(GRID_MAX_POINTS_Y, 3, 15)
635
     #elif !WITHIN(GRID_MAX_POINTS_X, 3, 15) || !WITHIN(GRID_MAX_POINTS_Y, 3, 15)
628
       #error "GRID_MAX_POINTS_[XY] must be a whole number between 3 and 15."
636
       #error "GRID_MAX_POINTS_[XY] must be a whole number between 3 and 15."
629
-    #elif !WITHIN(UBL_PROBE_PT_1_X, MIN_PROBE_X, MAX_PROBE_X)
630
-      #error "The given UBL_PROBE_PT_1_X can't be reached by the Z probe."
631
-    #elif !WITHIN(UBL_PROBE_PT_2_X, MIN_PROBE_X, MAX_PROBE_X)
632
-      #error "The given UBL_PROBE_PT_2_X can't be reached by the Z probe."
633
-    #elif !WITHIN(UBL_PROBE_PT_3_X, MIN_PROBE_X, MAX_PROBE_X)
634
-      #error "The given UBL_PROBE_PT_3_X can't be reached by the Z probe."
635
-    #elif !WITHIN(UBL_PROBE_PT_1_Y, MIN_PROBE_Y, MAX_PROBE_Y)
636
-      #error "The given UBL_PROBE_PT_1_Y can't be reached by the Z probe."
637
-    #elif !WITHIN(UBL_PROBE_PT_2_Y, MIN_PROBE_Y, MAX_PROBE_Y)
638
-      #error "The given UBL_PROBE_PT_2_Y can't be reached by the Z probe."
639
-    #elif !WITHIN(UBL_PROBE_PT_3_Y, MIN_PROBE_Y, MAX_PROBE_Y)
640
-      #error "The given UBL_PROBE_PT_3_Y can't be reached by the Z probe."
637
+    #endif
638
+    #if IS_CARTESIAN
639
+      #if !WITHIN(GRID_MAX_POINTS_X, 3, 15) || !WITHIN(GRID_MAX_POINTS_Y, 3, 15)
640
+        #error "GRID_MAX_POINTS_[XY] must be a whole number between 3 and 15."
641
+      #elif !WITHIN(UBL_PROBE_PT_1_X, MIN_PROBE_X, MAX_PROBE_X)
642
+        #error "The given UBL_PROBE_PT_1_X can't be reached by the Z probe."
643
+      #elif !WITHIN(UBL_PROBE_PT_2_X, MIN_PROBE_X, MAX_PROBE_X)
644
+        #error "The given UBL_PROBE_PT_2_X can't be reached by the Z probe."
645
+      #elif !WITHIN(UBL_PROBE_PT_3_X, MIN_PROBE_X, MAX_PROBE_X)
646
+        #error "The given UBL_PROBE_PT_3_X can't be reached by the Z probe."
647
+      #elif !WITHIN(UBL_PROBE_PT_1_Y, MIN_PROBE_Y, MAX_PROBE_Y)
648
+        #error "The given UBL_PROBE_PT_1_Y can't be reached by the Z probe."
649
+      #elif !WITHIN(UBL_PROBE_PT_2_Y, MIN_PROBE_Y, MAX_PROBE_Y)
650
+        #error "The given UBL_PROBE_PT_2_Y can't be reached by the Z probe."
651
+      #elif !WITHIN(UBL_PROBE_PT_3_Y, MIN_PROBE_Y, MAX_PROBE_Y)
652
+        #error "The given UBL_PROBE_PT_3_Y can't be reached by the Z probe."
653
+      #endif
641
     #endif
654
     #endif
642
   #else // AUTO_BED_LEVELING_3POINT
655
   #else // AUTO_BED_LEVELING_3POINT
643
     #if !WITHIN(ABL_PROBE_PT_1_X, MIN_PROBE_X, MAX_PROBE_X)
656
     #if !WITHIN(ABL_PROBE_PT_1_X, MIN_PROBE_X, MAX_PROBE_X)

+ 45
- 0
Marlin/planner.cpp View File

63
 #include "temperature.h"
63
 #include "temperature.h"
64
 #include "ultralcd.h"
64
 #include "ultralcd.h"
65
 #include "language.h"
65
 #include "language.h"
66
+#include "ubl.h"
66
 
67
 
67
 #include "Marlin.h"
68
 #include "Marlin.h"
68
 
69
 
533
    */
534
    */
534
   void Planner::apply_leveling(float &lx, float &ly, float &lz) {
535
   void Planner::apply_leveling(float &lx, float &ly, float &lz) {
535
 
536
 
537
+    #if ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(UBL_DELTA)  // probably should also be enabled for UBL without UBL_DELTA
538
+      if (!ubl.state.active) return;
539
+      #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
540
+        // if z_fade_height enabled (nonzero) and raw_z above it, no leveling required
541
+        if ((planner.z_fade_height) && (planner.z_fade_height <= RAW_Z_POSITION(lz))) return;
542
+        lz += ubl.state.z_offset + ( ubl.get_z_correction(lx,ly) * ubl.fade_scaling_factor_for_z(lz));
543
+      #else // no fade
544
+        lz += ubl.state.z_offset + ubl.get_z_correction(lx,ly);
545
+      #endif // FADE
546
+    #endif // UBL
547
+
536
     #if HAS_ABL
548
     #if HAS_ABL
537
       if (!abl_enabled) return;
549
       if (!abl_enabled) return;
538
     #endif
550
     #endif
586
 
598
 
587
   void Planner::unapply_leveling(float logical[XYZ]) {
599
   void Planner::unapply_leveling(float logical[XYZ]) {
588
 
600
 
601
+    #if ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(UBL_DELTA)
602
+
603
+      if ( ubl.state.active ) {
604
+
605
+        float z_leveled = RAW_Z_POSITION(logical[Z_AXIS]);
606
+        float z_ublmesh = ubl.get_z_correction(logical[X_AXIS],logical[Y_AXIS]);
607
+        float z_unlevel = z_leveled - ubl.state.z_offset - z_ublmesh;
608
+
609
+        #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
610
+
611
+          // for L=leveled, U=unleveled, M=mesh, O=offset, H=fade_height,
612
+          // Given L==U+O+M(1-U/H) (faded mesh correction formula for U<H)
613
+          //  then U==L-O-M(1-U/H)
614
+          //    so U==L-O-M+MU/H
615
+          //    so U-MU/H==L-O-M
616
+          //    so U(1-M/H)==L-O-M
617
+          //    so U==(L-O-M)/(1-M/H) for U<H
618
+
619
+          if ( planner.z_fade_height ) {
620
+            float z_unfaded = z_unlevel / ( 1.0 - ( z_ublmesh * planner.inverse_z_fade_height ));
621
+            if ( z_unfaded < planner.z_fade_height )  // don't know until after compute
622
+              z_unlevel = z_unfaded;
623
+          }
624
+
625
+        #endif // ENABLE_LEVELING_FADE_HEIGHT
626
+
627
+        logical[Z_AXIS] = z_unlevel;
628
+      }
629
+
630
+      return; // don't fall thru to HAS_ABL or other ENABLE_LEVELING_FADE_HEIGHT logic
631
+
632
+    #endif
633
+
589
     #if HAS_ABL
634
     #if HAS_ABL
590
       if (!abl_enabled) return;
635
       if (!abl_enabled) return;
591
     #endif
636
     #endif

+ 1
- 1
Marlin/ubl.cpp View File

41
 
41
 
42
   uint8_t ubl_cnt = 0;
42
   uint8_t ubl_cnt = 0;
43
 
43
 
44
-  static void serial_echo_xy(const uint16_t x, const uint16_t y) {
44
+  static void serial_echo_xy(const int16_t x, const int16_t y) {
45
     SERIAL_CHAR('(');
45
     SERIAL_CHAR('(');
46
     SERIAL_ECHO(x);
46
     SERIAL_ECHO(x);
47
     SERIAL_CHAR(',');
47
     SERIAL_CHAR(',');

+ 7
- 5
Marlin/ubl.h View File

52
   // ubl_motion.cpp
52
   // ubl_motion.cpp
53
 
53
 
54
   void debug_current_and_destination(const char * const title);
54
   void debug_current_and_destination(const char * const title);
55
-  void ubl_line_to_destination(const float&, uint8_t);
55
+  void ubl_line_to_destination_cartesian(const float&, uint8_t);
56
+  bool ubl_prepare_linear_move_to(const float ltarget[XYZE], const float &feedrate );
56
 
57
 
57
   // ubl_G29.cpp
58
   // ubl_G29.cpp
58
 
59
 
329
        *  Returns 0.0 if Z is past the specified 'Fade Height'.
330
        *  Returns 0.0 if Z is past the specified 'Fade Height'.
330
        */
331
        */
331
       #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
332
       #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
332
-
333
-        FORCE_INLINE float fade_scaling_factor_for_z(const float &lz) {
333
+        inline float fade_scaling_factor_for_z(const float &lz) {
334
           if (planner.z_fade_height == 0.0) return 1.0;
334
           if (planner.z_fade_height == 0.0) return 1.0;
335
-
336
           static float fade_scaling_factor = 1.0;
335
           static float fade_scaling_factor = 1.0;
337
           const float rz = RAW_Z_POSITION(lz);
336
           const float rz = RAW_Z_POSITION(lz);
338
           if (last_specified_z != rz) {
337
           if (last_specified_z != rz) {
344
           }
343
           }
345
           return fade_scaling_factor;
344
           return fade_scaling_factor;
346
         }
345
         }
347
-
346
+      #else
347
+        inline float fade_scaling_factor_for_z(const float &lz) {
348
+          return 1.0;
349
+        }
348
       #endif
350
       #endif
349
 
351
 
350
   }; // class unified_bed_leveling
352
   }; // class unified_bed_leveling

+ 61
- 41
Marlin/ubl_G29.cpp View File

436
              * It may make sense to have Delta printers default to the center of the bed.
436
              * It may make sense to have Delta printers default to the center of the bed.
437
              * Until that is decided, this can be forced with the X and Y parameters.
437
              * Until that is decided, this can be forced with the X and Y parameters.
438
              */
438
              */
439
-            x_pos = X_PROBE_OFFSET_FROM_EXTRUDER > 0 ? UBL_MESH_MAX_X : UBL_MESH_MIN_X;
440
-            y_pos = Y_PROBE_OFFSET_FROM_EXTRUDER < 0 ? UBL_MESH_MAX_Y : UBL_MESH_MIN_Y;
439
+            #if IS_KINEMATIC
440
+              x_pos = X_HOME_POS;
441
+              y_pos = Y_HOME_POS;
442
+            #else // cartesian
443
+              x_pos = X_PROBE_OFFSET_FROM_EXTRUDER > 0 ? X_MAX_POS : X_MIN_POS;
444
+              y_pos = Y_PROBE_OFFSET_FROM_EXTRUDER < 0 ? Y_MAX_POS : Y_MIN_POS;
445
+            #endif
441
           }
446
           }
442
 
447
 
443
           if (code_seen('C')) {
448
           if (code_seen('C')) {
457
           }
462
           }
458
 
463
 
459
           if (code_seen('H') && code_has_value()) height = code_value_float();
464
           if (code_seen('H') && code_has_value()) height = code_value_float();
465
+          
466
+          if ( !position_is_reachable_xy( x_pos, y_pos )) {
467
+            SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius.");
468
+            return;
469
+          }
460
 
470
 
461
           manually_probe_remaining_mesh(x_pos, y_pos, height, card_thickness, code_seen('O') || code_seen('M'));
471
           manually_probe_remaining_mesh(x_pos, y_pos, height, card_thickness, code_seen('O') || code_seen('M'));
462
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
472
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
470
            *   - Allow 'G29 P3' to choose a 'reasonable' constant.
480
            *   - Allow 'G29 P3' to choose a 'reasonable' constant.
471
            */
481
            */
472
           if (c_flag) {
482
           if (c_flag) {
473
-            while (repetition_cnt--) {
474
-              const mesh_index_pair location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, USE_NOZZLE_AS_REFERENCE, NULL, false);
475
-              if (location.x_index < 0) break; // No more invalid Mesh Points to populate
483
+
484
+            if ( repetition_cnt >= ( GRID_MAX_POINTS_X * GRID_MAX_POINTS_Y )) {
485
+              for ( uint8_t x = 0; x < GRID_MAX_POINTS_X; x++ ) {
486
+                for ( uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++ ) {
487
+                  ubl.z_values[x][y] = ubl_constant;
488
+                }
489
+              }
490
+            } else {
491
+              while (repetition_cnt--) {  // this only populates reachable mesh points near 
492
+                const mesh_index_pair location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, USE_NOZZLE_AS_REFERENCE, NULL, false);
493
+                if (location.x_index < 0) break; // No more reachable invalid Mesh Points to populate
476
                 ubl.z_values[location.x_index][location.y_index] = ubl_constant;
494
                 ubl.z_values[location.x_index][location.y_index] = ubl_constant;
495
+              }
477
             }
496
             }
478
-            break;
479
-          }
480
-          else
497
+          } else {
481
             smart_fill_mesh(); // Do a 'Smart' fill using nearby known values
498
             smart_fill_mesh(); // Do a 'Smart' fill using nearby known values
482
-
483
-        } break;
499
+          }
500
+          break;
501
+        }
484
 
502
 
485
         case 4:
503
         case 4:
486
           //
504
           //
502
             z2 = probe_pt(LOGICAL_X_POSITION(UBL_PROBE_PT_2_X), LOGICAL_Y_POSITION(UBL_PROBE_PT_2_Y), false, g29_verbose_level),
520
             z2 = probe_pt(LOGICAL_X_POSITION(UBL_PROBE_PT_2_X), LOGICAL_Y_POSITION(UBL_PROBE_PT_2_Y), false, g29_verbose_level),
503
             z3 = probe_pt(LOGICAL_X_POSITION(UBL_PROBE_PT_3_X), LOGICAL_Y_POSITION(UBL_PROBE_PT_3_Y), true, g29_verbose_level);
521
             z3 = probe_pt(LOGICAL_X_POSITION(UBL_PROBE_PT_3_X), LOGICAL_Y_POSITION(UBL_PROBE_PT_3_Y), true, g29_verbose_level);
504
 
522
 
523
+      if ( isnan(z1) || isnan(z2) || isnan(z3)) {   // probe_pt will return NAN if unreachable
524
+          SERIAL_ERROR_START;
525
+          SERIAL_ERRORLNPGM("Attempt to probe off the bed.");
526
+          goto LEAVE;
527
+      }
528
+
505
       //  We need to adjust z1, z2, z3 by the Mesh Height at these points. Just because they are non-zero doesn't mean
529
       //  We need to adjust z1, z2, z3 by the Mesh Height at these points. Just because they are non-zero doesn't mean
506
       //  the Mesh is tilted!  (We need to compensate each probe point by what the Mesh says that location's height is)
530
       //  the Mesh is tilted!  (We need to compensate each probe point by what the Mesh says that location's height is)
507
 
531
 
710
     ubl.save_ubl_active_state_and_disable();   // we don't do bed level correction because we want the raw data when we probe
734
     ubl.save_ubl_active_state_and_disable();   // we don't do bed level correction because we want the raw data when we probe
711
     DEPLOY_PROBE();
735
     DEPLOY_PROBE();
712
 
736
 
737
+    uint16_t max_iterations = ( GRID_MAX_POINTS_X * GRID_MAX_POINTS_Y );
738
+
713
     do {
739
     do {
714
       if (ubl_lcd_clicked()) {
740
       if (ubl_lcd_clicked()) {
715
         SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
741
         SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
723
       }
749
       }
724
 
750
 
725
       location = find_closest_mesh_point_of_type(INVALID, lx, ly, USE_PROBE_AS_REFERENCE, NULL, do_furthest);
751
       location = find_closest_mesh_point_of_type(INVALID, lx, ly, USE_PROBE_AS_REFERENCE, NULL, do_furthest);
726
-      if (location.x_index >= 0 && location.y_index >= 0) {
752
+
753
+      if (location.x_index >= 0) {    // mesh point found and is reachable by probe
727
 
754
 
728
         const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
755
         const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
729
                     rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
756
                     rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
730
 
757
 
731
-        // TODO: Change to use `position_is_reachable` (for SCARA-compatibility)
732
-        if (!WITHIN(rawx, MIN_PROBE_X, MAX_PROBE_X) || !WITHIN(rawy, MIN_PROBE_Y, MAX_PROBE_Y)) {
733
-          SERIAL_ERROR_START;
734
-          SERIAL_ERRORLNPGM("Attempt to probe off the bed.");
735
-          ubl.has_control_of_lcd_panel = false;
736
-          goto LEAVE;
737
-        }
738
         const float measured_z = probe_pt(LOGICAL_X_POSITION(rawx), LOGICAL_Y_POSITION(rawy), stow_probe, g29_verbose_level);
758
         const float measured_z = probe_pt(LOGICAL_X_POSITION(rawx), LOGICAL_Y_POSITION(rawy), stow_probe, g29_verbose_level);
739
         ubl.z_values[location.x_index][location.y_index] = measured_z;
759
         ubl.z_values[location.x_index][location.y_index] = measured_z;
740
       }
760
       }
741
 
761
 
742
       if (do_ubl_mesh_map) ubl.display_map(map_type);
762
       if (do_ubl_mesh_map) ubl.display_map(map_type);
743
 
763
 
744
-    } while (location.x_index >= 0 && location.y_index >= 0);
745
-
746
-    LEAVE:
764
+    } while ((location.x_index >= 0) && (--max_iterations));
747
 
765
 
748
     STOW_PROBE();
766
     STOW_PROBE();
749
     ubl.restore_ubl_active_state_and_leave();
767
     ubl.restore_ubl_active_state_and_leave();
939
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
957
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
940
                   rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
958
                   rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
941
 
959
 
942
-      // TODO: Change to use `position_is_reachable` (for SCARA-compatibility)
943
-      if (!WITHIN(rawx, UBL_MESH_MIN_X, UBL_MESH_MAX_X) || !WITHIN(rawy, UBL_MESH_MIN_Y, UBL_MESH_MAX_Y)) {
944
-        SERIAL_ERROR_START;
945
-        SERIAL_ERRORLNPGM("Attempt to probe off the bed.");
946
-        ubl.has_control_of_lcd_panel = false;
947
-        goto LEAVE;
948
-      }
949
-
950
       const float xProbe = LOGICAL_X_POSITION(rawx),
960
       const float xProbe = LOGICAL_X_POSITION(rawx),
951
                   yProbe = LOGICAL_Y_POSITION(rawy);
961
                   yProbe = LOGICAL_Y_POSITION(rawy);
952
 
962
 
963
+      if ( ! position_is_reachable_raw_xy( rawx, rawy )) {    // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
964
+        break;
965
+      }
966
+
953
       do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
967
       do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
954
 
968
 
955
       LCD_MESSAGEPGM("Moving to next");
969
       LCD_MESSAGEPGM("Moving to next");
1361
                       rawy = pgm_read_float(&ubl.mesh_index_to_ypos[j]);
1375
                       rawy = pgm_read_float(&ubl.mesh_index_to_ypos[j]);
1362
 
1376
 
1363
           // If using the probe as the reference there are some unreachable locations.
1377
           // If using the probe as the reference there are some unreachable locations.
1378
+          // Also for round beds, there are grid points outside the bed that nozzle can't reach.
1364
           // Prune them from the list and ignore them till the next Phase (manual nozzle probing).
1379
           // Prune them from the list and ignore them till the next Phase (manual nozzle probing).
1365
 
1380
 
1366
-          if (probe_as_reference == USE_PROBE_AS_REFERENCE &&
1367
-            (!WITHIN(rawx, MIN_PROBE_X, MAX_PROBE_X) || !WITHIN(rawy, MIN_PROBE_Y, MAX_PROBE_Y))
1368
-          ) continue;
1381
+          bool reachable = probe_as_reference ?
1382
+                             position_is_reachable_by_probe_raw_xy( rawx, rawy ) :
1383
+                             position_is_reachable_raw_xy( rawx, rawy );
1369
 
1384
 
1370
-          // Unreachable. Check if it's the closest location to the nozzle.
1385
+          if ( ! reachable )
1386
+            continue;
1387
+
1388
+          // Reachable. Check if it's the closest location to the nozzle.
1371
           // Add in a weighting factor that considers the current location of the nozzle.
1389
           // Add in a weighting factor that considers the current location of the nozzle.
1372
 
1390
 
1373
           const float mx = LOGICAL_X_POSITION(rawx), // Check if we can probe this mesh location
1391
           const float mx = LOGICAL_X_POSITION(rawx), // Check if we can probe this mesh location
1415
     uint16_t not_done[16];
1433
     uint16_t not_done[16];
1416
     int32_t round_off;
1434
     int32_t round_off;
1417
 
1435
 
1436
+    if ( ! position_is_reachable_xy( lx, ly )) {
1437
+      SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius.");
1438
+      return;
1439
+    }
1440
+
1418
     ubl.save_ubl_active_state_and_disable();
1441
     ubl.save_ubl_active_state_and_disable();
1442
+
1419
     memset(not_done, 0xFF, sizeof(not_done));
1443
     memset(not_done, 0xFF, sizeof(not_done));
1420
 
1444
 
1421
     LCD_MESSAGEPGM("Fine Tuning Mesh");
1445
     LCD_MESSAGEPGM("Fine Tuning Mesh");
1425
     do {
1449
     do {
1426
       location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false);
1450
       location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false);
1427
 
1451
 
1428
-      if (location.x_index < 0 && location.y_index < 0) continue; // abort if we can't find any more points.
1452
+      if (location.x_index < 0 ) break; // stop when we can't find any more reachable points.
1429
 
1453
 
1430
       bit_clear(not_done, location.x_index, location.y_index);  // Mark this location as 'adjusted' so we will find a
1454
       bit_clear(not_done, location.x_index, location.y_index);  // Mark this location as 'adjusted' so we will find a
1431
                                                                 // different location the next time through the loop
1455
                                                                 // different location the next time through the loop
1433
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
1457
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
1434
                   rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
1458
                   rawy = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);
1435
 
1459
 
1436
-      // TODO: Change to use `position_is_reachable` (for SCARA-compatibility)
1437
-      if (!WITHIN(rawx, UBL_MESH_MIN_X, UBL_MESH_MAX_X) || !WITHIN(rawy, UBL_MESH_MIN_Y, UBL_MESH_MAX_Y)) { // In theory, we don't need this check.
1438
-        SERIAL_ERROR_START;
1439
-        SERIAL_ERRORLNPGM("Attempt to edit off the bed."); // This really can't happen, but do the check for now
1440
-        ubl.has_control_of_lcd_panel = false;
1441
-        goto FINE_TUNE_EXIT;
1460
+      if ( ! position_is_reachable_raw_xy( rawx, rawy )) { // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable
1461
+        break;
1442
       }
1462
       }
1443
 
1463
 
1444
       float new_z = ubl.z_values[location.x_index][location.y_index];
1464
       float new_z = ubl.z_values[location.x_index][location.y_index];
1494
 
1514
 
1495
       lcd_implementation_clear();
1515
       lcd_implementation_clear();
1496
 
1516
 
1497
-    } while (location.x_index >= 0 && location.y_index >= 0 && (--repetition_cnt>0));
1517
+    } while (( location.x_index >= 0 ) && (--repetition_cnt>0));
1498
 
1518
 
1499
     FINE_TUNE_EXIT:
1519
     FINE_TUNE_EXIT:
1500
 
1520
 

+ 246
- 10
Marlin/ubl_motion.cpp View File

26
   #include "Marlin.h"
26
   #include "Marlin.h"
27
   #include "ubl.h"
27
   #include "ubl.h"
28
   #include "planner.h"
28
   #include "planner.h"
29
+  #include "stepper.h"
29
   #include <avr/io.h>
30
   #include <avr/io.h>
30
   #include <math.h>
31
   #include <math.h>
31
 
32
 
32
   extern float destination[XYZE];
33
   extern float destination[XYZE];
33
   extern void set_current_to_destination();
34
   extern void set_current_to_destination();
35
+  extern float delta_segments_per_second;
34
 
36
 
35
   static void debug_echo_axis(const AxisEnum axis) {
37
   static void debug_echo_axis(const AxisEnum axis) {
36
     if (current_position[axis] == destination[axis])
38
     if (current_position[axis] == destination[axis])
87
 
89
 
88
   }
90
   }
89
 
91
 
90
-  void ubl_line_to_destination(const float &feed_rate, uint8_t extruder) {
92
+  void ubl_line_to_destination_cartesian(const float &feed_rate, uint8_t extruder) {
91
     /**
93
     /**
92
      * Much of the nozzle movement will be within the same cell. So we will do as little computation
94
      * Much of the nozzle movement will be within the same cell. So we will do as little computation
93
      * as possible to determine if this is the case. If this move is within the same cell, we will
95
      * as possible to determine if this is the case. If this move is within the same cell, we will
134
         // Note: There is no Z Correction in this case. We are off the grid and don't know what
136
         // Note: There is no Z Correction in this case. We are off the grid and don't know what
135
         // a reasonable correction would be.
137
         // a reasonable correction would be.
136
 
138
 
137
-        planner.buffer_line(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + ubl.state.z_offset, end[E_AXIS], feed_rate, extruder);
139
+        planner._buffer_line(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + ubl.state.z_offset, end[E_AXIS], feed_rate, extruder);
138
         set_current_to_destination();
140
         set_current_to_destination();
139
 
141
 
140
         if (ubl.g26_debug_flag)
142
         if (ubl.g26_debug_flag)
178
        */
180
        */
179
       if (isnan(z0)) z0 = 0.0;
181
       if (isnan(z0)) z0 = 0.0;
180
 
182
 
181
-      planner.buffer_line(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + z0 + ubl.state.z_offset, end[E_AXIS], feed_rate, extruder);
183
+      planner._buffer_line(end[X_AXIS], end[Y_AXIS], end[Z_AXIS] + z0 + ubl.state.z_offset, end[E_AXIS], feed_rate, extruder);
182
 
184
 
183
       if (ubl.g26_debug_flag)
185
       if (ubl.g26_debug_flag)
184
         debug_current_and_destination(PSTR("FINAL_MOVE in ubl_line_to_destination()"));
186
         debug_current_and_destination(PSTR("FINAL_MOVE in ubl_line_to_destination()"));
270
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
272
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
271
          * where the line is heading down and it is starting right on a Mesh Line boundary. For how often that
273
          * where the line is heading down and it is starting right on a Mesh Line boundary. For how often that
272
          * happens, it might be best to remove the check and always 'schedule' the move because
274
          * happens, it might be best to remove the check and always 'schedule' the move because
273
-         * the planner.buffer_line() routine will filter it if that happens.
275
+         * the planner._buffer_line() routine will filter it if that happens.
274
          */
276
          */
275
         if (y != start[Y_AXIS]) {
277
         if (y != start[Y_AXIS]) {
276
           if (!inf_normalized_flag) {
278
           if (!inf_normalized_flag) {
292
             z_position = end[Z_AXIS];
294
             z_position = end[Z_AXIS];
293
           }
295
           }
294
 
296
 
295
-          planner.buffer_line(x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
297
+          planner._buffer_line(x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
296
         } //else printf("FIRST MOVE PRUNED  ");
298
         } //else printf("FIRST MOVE PRUNED  ");
297
       }
299
       }
298
 
300
 
344
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
346
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
345
          * where the line is heading left and it is starting right on a Mesh Line boundary. For how often
347
          * where the line is heading left and it is starting right on a Mesh Line boundary. For how often
346
          * that happens, it might be best to remove the check and always 'schedule' the move because
348
          * that happens, it might be best to remove the check and always 'schedule' the move because
347
-         * the planner.buffer_line() routine will filter it if that happens.
349
+         * the planner._buffer_line() routine will filter it if that happens.
348
          */
350
          */
349
         if (x != start[X_AXIS]) {
351
         if (x != start[X_AXIS]) {
350
           if (!inf_normalized_flag) {
352
           if (!inf_normalized_flag) {
363
             z_position = end[Z_AXIS];
365
             z_position = end[Z_AXIS];
364
           }
366
           }
365
 
367
 
366
-          planner.buffer_line(x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
368
+          planner._buffer_line(x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
367
         } //else printf("FIRST MOVE PRUNED  ");
369
         } //else printf("FIRST MOVE PRUNED  ");
368
       }
370
       }
369
 
371
 
426
           e_position = end[E_AXIS];
428
           e_position = end[E_AXIS];
427
           z_position = end[Z_AXIS];
429
           z_position = end[Z_AXIS];
428
         }
430
         }
429
-        planner.buffer_line(x, next_mesh_line_y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
431
+        planner._buffer_line(x, next_mesh_line_y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
430
         current_yi += dyi;
432
         current_yi += dyi;
431
         yi_cnt--;
433
         yi_cnt--;
432
       }
434
       }
455
           z_position = end[Z_AXIS];
457
           z_position = end[Z_AXIS];
456
         }
458
         }
457
 
459
 
458
-        planner.buffer_line(next_mesh_line_x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
460
+        planner._buffer_line(next_mesh_line_x, y, z_position + z0 + ubl.state.z_offset, e_position, feed_rate, extruder);
459
         current_xi += dxi;
461
         current_xi += dxi;
460
         xi_cnt--;
462
         xi_cnt--;
461
       }
463
       }
472
     set_current_to_destination();
474
     set_current_to_destination();
473
   }
475
   }
474
 
476
 
475
-#endif
477
+
478
+  #ifdef UBL_DELTA
479
+
480
+    #define COPY_XYZE( target, source ) { \
481
+                target[X_AXIS] = source[X_AXIS]; \
482
+                target[Y_AXIS] = source[Y_AXIS]; \
483
+                target[Z_AXIS] = source[Z_AXIS]; \
484
+                target[E_AXIS] = source[E_AXIS]; \
485
+            }
486
+
487
+    #if IS_SCARA // scale the feed rate from mm/s to degrees/s
488
+      static float scara_feed_factor;
489
+      static float scara_oldA;
490
+      static float scara_oldB;
491
+    #endif
492
+
493
+    // We don't want additional apply_leveling() performed by regular buffer_line or buffer_line_kinematic, 
494
+    // so we call _buffer_line directly here.  Per-segmented leveling performed first.
495
+
496
+    static inline void ubl_buffer_line_segment(const float ltarget[XYZE], const float &fr_mm_s, const uint8_t extruder) {
497
+
498
+      #if IS_KINEMATIC
499
+
500
+        inverse_kinematics(ltarget); // this writes delta[ABC] from ltarget[XYZ] but does not modify ltarget
501
+        float feedrate = fr_mm_s;
502
+
503
+        #if IS_SCARA // scale the feed rate from mm/s to degrees/s
504
+          float adiff = abs(delta[A_AXIS] - scara_oldA);
505
+          float bdiff = abs(delta[B_AXIS] - scara_oldB);
506
+          scara_oldA = delta[A_AXIS];
507
+          scara_oldB = delta[B_AXIS];
508
+          feedrate = max(adiff, bdiff) * scara_feed_factor;
509
+        #endif
510
+
511
+        planner._buffer_line( delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], ltarget[E_AXIS], feedrate, extruder );
512
+
513
+      #else // cartesian
514
+
515
+        planner._buffer_line( ltarget[X_AXIS], ltarget[Y_AXIS], ltarget[Z_AXIS], ltarget[E_AXIS], fr_mm_s, extruder );
516
+
517
+      #endif
518
+    }
519
+
520
+    /**
521
+     * Prepare a linear move for DELTA/SCARA/CARTESIAN with UBL and FADE semantics.
522
+     * This calls planner._buffer_line multiple times for small incremental moves.
523
+     * Returns true if the caller did NOT update current_position, otherwise false.
524
+     */
525
+
526
+    static bool ubl_prepare_linear_move_to(const float ltarget[XYZE], const float &feedrate) {
527
+
528
+      if ( ! position_is_reachable_xy( ltarget[X_AXIS], ltarget[Y_AXIS] ))  // fail if moving outside reachable boundary
529
+        return true; // did not move, so current_position still accurate
530
+
531
+      const float difference[XYZE] = {    // cartesian distances moved in XYZE
532
+                    ltarget[X_AXIS] - current_position[X_AXIS],
533
+                    ltarget[Y_AXIS] - current_position[Y_AXIS],
534
+                    ltarget[Z_AXIS] - current_position[Z_AXIS],
535
+                    ltarget[E_AXIS] - current_position[E_AXIS]
536
+                    };
537
+
538
+      float cartesian_xy_mm = sqrtf( sq(difference[X_AXIS]) + sq(difference[Y_AXIS]) ); // total horizontal xy distance
539
+
540
+      #if IS_KINEMATIC
541
+        float    seconds  = cartesian_xy_mm / feedrate;                                   // seconds to move xy distance at requested rate
542
+        uint16_t segments = lroundf( delta_segments_per_second * seconds );               // preferred number of segments for distance @ feedrate
543
+        uint16_t seglimit = lroundf( cartesian_xy_mm * (1.0/(DELTA_SEGMENT_MIN_LENGTH))); // number of segments at minimum segment length
544
+        NOMORE( segments, seglimit );                                                     // limit to minimum segment length (fewer segments)
545
+      #else
546
+        uint16_t segments = lroundf( cartesian_xy_mm * (1.0/(DELTA_SEGMENT_MIN_LENGTH))); // cartesian fixed segment length
547
+      #endif
548
+
549
+      NOLESS( segments, 1 );                // must have at least one segment
550
+      float inv_segments = 1.0 / segments;  // divide once, multiply thereafter
551
+
552
+      #if IS_SCARA // scale the feed rate from mm/s to degrees/s
553
+        scara_feed_factor = cartesian_xy_mm * inv_segments * feedrate;
554
+        scara_oldA = stepper.get_axis_position_degrees(A_AXIS);
555
+        scara_oldB = stepper.get_axis_position_degrees(B_AXIS);
556
+      #endif
557
+
558
+      const float segment_distance[XYZE] = {            // length for each segment
559
+                    difference[X_AXIS] * inv_segments,
560
+                    difference[Y_AXIS] * inv_segments,
561
+                    difference[Z_AXIS] * inv_segments,
562
+                    difference[E_AXIS] * inv_segments 
563
+                    };
564
+
565
+      // Note that E segment distance could vary slightly as z mesh height
566
+      // changes for each segment, but small enough to ignore.
567
+
568
+      bool above_fade_height = false;
569
+      #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
570
+        if (( planner.z_fade_height != 0 ) && 
571
+            ( planner.z_fade_height < RAW_Z_POSITION(ltarget[Z_AXIS]) )) {
572
+          above_fade_height = true;
573
+          }
574
+      #endif
575
+
576
+      // Only compute leveling per segment if ubl active and target below z_fade_height.
577
+
578
+      if (( ! ubl.state.active ) || ( above_fade_height )) {   // no mesh leveling
579
+
580
+        const float z_offset = ubl.state.active ? ubl.state.z_offset : 0.0;
581
+
582
+        float seg_dest[XYZE];                     // per-segment destination,
583
+        COPY_XYZE( seg_dest, current_position );  // starting from current position
584
+
585
+        while (--segments) {
586
+          LOOP_XYZE(i) seg_dest[i] += segment_distance[i];
587
+          float ztemp = seg_dest[Z_AXIS];
588
+          seg_dest[Z_AXIS] += z_offset;
589
+          ubl_buffer_line_segment( seg_dest, feedrate, active_extruder );
590
+          seg_dest[Z_AXIS] = ztemp;
591
+        }
592
+
593
+        // Since repeated adding segment_distance accumulates small errors, final move to exact destination.
594
+        COPY_XYZE( seg_dest, ltarget );
595
+        seg_dest[Z_AXIS] += z_offset;
596
+        ubl_buffer_line_segment( seg_dest, feedrate, active_extruder );
597
+        return false; // moved but did not set_current_to_destination();
598
+      }
599
+
600
+      // Otherwise perform per-segment leveling
601
+
602
+      #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
603
+        float fade_scaling_factor = ubl.fade_scaling_factor_for_z(ltarget[Z_AXIS]);
604
+      #endif
605
+
606
+      float seg_dest[XYZE];  // per-segment destination, initialize to first segment
607
+      LOOP_XYZE(i) seg_dest[i] = current_position[i] + segment_distance[i];
608
+
609
+      const float& dx_seg = segment_distance[X_AXIS];  // alias for clarity
610
+      const float& dy_seg = segment_distance[Y_AXIS];
611
+
612
+      float rx = RAW_X_POSITION(seg_dest[X_AXIS]);  // assume raw vs logical coordinates shifted but not scaled.
613
+      float ry = RAW_Y_POSITION(seg_dest[Y_AXIS]);
614
+
615
+      do {  // for each mesh cell encountered during the move
616
+
617
+        // Compute mesh cell invariants that remain constant for all segments within cell.
618
+        // Note for cell index, if point is outside the mesh grid (in MESH_INSET perimeter)
619
+        // the bilinear interpolation from the adjacent cell within the mesh will still work.
620
+        // Inner loop will exit each time (because out of cell bounds) but will come back
621
+        // in top of loop and again re-find same adjacent cell and use it, just less efficient
622
+        // for mesh inset area.
623
+
624
+        int8_t cell_xi = (rx - (UBL_MESH_MIN_X)) * (1.0 / (MESH_X_DIST));
625
+               cell_xi = constrain( cell_xi, 0, (GRID_MAX_POINTS_X) - 1 );
626
+
627
+        int8_t cell_yi = (ry - (UBL_MESH_MIN_Y)) * (1.0 / (MESH_X_DIST));
628
+               cell_yi = constrain( cell_yi, 0, (GRID_MAX_POINTS_Y) - 1 );
629
+
630
+        // float x0 = (UBL_MESH_MIN_X) + ((MESH_X_DIST) * cell_xi );         // lower left cell corner
631
+        // float y0 = (UBL_MESH_MIN_Y) + ((MESH_Y_DIST) * cell_yi );         // lower left cell corner
632
+        // float x1 = x0 + MESH_X_DIST;                                      // upper right cell corner
633
+        // float y1 = y0 + MESH_Y_DIST;                                      // upper right cell corner
634
+
635
+        float x0 = pgm_read_float(&(ubl.mesh_index_to_xpos[cell_xi  ]));  // 64 byte table lookup avoids mul+add
636
+        float y0 = pgm_read_float(&(ubl.mesh_index_to_ypos[cell_yi  ]));  // 64 byte table lookup avoids mul+add
637
+        float x1 = pgm_read_float(&(ubl.mesh_index_to_xpos[cell_xi+1]));  // 64 byte table lookup avoids mul+add
638
+        float y1 = pgm_read_float(&(ubl.mesh_index_to_ypos[cell_yi+1]));  // 64 byte table lookup avoids mul+add
639
+
640
+        float cx = rx - x0;   // cell-relative x
641
+        float cy = ry - y0;   // cell-relative y
642
+
643
+        float z_x0y0 = ubl.z_values[cell_xi  ][cell_yi  ];  // z at lower left corner
644
+        float z_x1y0 = ubl.z_values[cell_xi+1][cell_yi  ];  // z at upper left corner
645
+        float z_x0y1 = ubl.z_values[cell_xi  ][cell_yi+1];  // z at lower right corner
646
+        float z_x1y1 = ubl.z_values[cell_xi+1][cell_yi+1];  // z at upper right corner
647
+
648
+        if ( isnan( z_x0y0 )) z_x0y0 = 0;     // ideally activating ubl.state.active (G29 A) 
649
+        if ( isnan( z_x1y0 )) z_x1y0 = 0;     //   should refuse if any invalid mesh points
650
+        if ( isnan( z_x0y1 )) z_x0y1 = 0;     //   in order to avoid isnan tests per cell,
651
+        if ( isnan( z_x1y1 )) z_x1y1 = 0;     //   thus guessing zero for undefined points
652
+
653
+        float z_xmy0 = (z_x1y0 - z_x0y0) * (1.0/MESH_X_DIST);   // z slope per x along y0 (lower left to lower right)
654
+        float z_xmy1 = (z_x1y1 - z_x0y1) * (1.0/MESH_X_DIST);   // z slope per x along y1 (upper left to upper right)
655
+
656
+        float z_cxy0 = z_x0y0 + z_xmy0 * cx;        // z height along y0 at cx
657
+        float z_cxy1 = z_x0y1 + z_xmy1 * cx;        // z height along y1 at cx
658
+        float z_cxyd = z_cxy1 - z_cxy0;             // z height difference along cx from y0 to y1
659
+
660
+        float z_cxym = z_cxyd * (1.0/MESH_Y_DIST);  // z slope per y along cx from y0 to y1
661
+        float z_cxcy = z_cxy0 + z_cxym * cy;        // z height along cx at cy
662
+
663
+        // As subsequent segments step through this cell, the z_cxy0 intercept will change
664
+        // and the z_cxym slope will change, both as a function of cx within the cell, and
665
+        // each change by a constant for fixed segment lengths.
666
+
667
+        float z_sxy0 = z_xmy0 * dx_seg;                                   // per-segment adjustment to z_cxy0
668
+        float z_sxym = ( z_xmy1 - z_xmy0 ) * (1.0/MESH_Y_DIST) * dx_seg;  // per-segment adjustment to z_cxym
669
+
670
+        do {  // for all segments within this mesh cell
671
+
672
+          z_cxcy += ubl.state.z_offset;
673
+
674
+          if ( --segments == 0 ) {          // this is last segment, use ltarget for exact
675
+            COPY_XYZE( seg_dest, ltarget );
676
+            seg_dest[Z_AXIS] += z_cxcy;
677
+            ubl_buffer_line_segment( seg_dest, feedrate, active_extruder );
678
+            return false;   // did not set_current_to_destination()
679
+          }
680
+
681
+          float z_orig = seg_dest[Z_AXIS];    // remember the pre-leveled segment z value
682
+          seg_dest[Z_AXIS] = z_orig + z_cxcy; // adjust segment z height per mesh leveling
683
+          ubl_buffer_line_segment( seg_dest, feedrate, active_extruder );
684
+          seg_dest[Z_AXIS] = z_orig;          // restore pre-leveled z before incrementing
685
+
686
+          LOOP_XYZE(i) seg_dest[i] += segment_distance[i];  // adjust seg_dest for next segment
687
+
688
+          cx += dx_seg;
689
+          cy += dy_seg;
690
+
691
+          if ( !WITHIN(cx,0,MESH_X_DIST) || !WITHIN(cy,0,MESH_Y_DIST)) {  // done within this cell, break to next
692
+            rx = RAW_X_POSITION(seg_dest[X_AXIS]);
693
+            ry = RAW_Y_POSITION(seg_dest[Y_AXIS]);
694
+            break;  
695
+          }
696
+
697
+          // Next segment still within same mesh cell, adjust the per-segment
698
+          // slope and intercept and compute next z height.
699
+
700
+          z_cxy0 += z_sxy0;                 // adjust z_cxy0 by per-segment z_sxy0
701
+          z_cxym += z_sxym;                 // adjust z_cxym by per-segment z_sxym
702
+          z_cxcy  = z_cxy0 + z_cxym * cy;   // recompute z_cxcy from adjusted slope and intercept
703
+
704
+        } while (true);   // per-segment loop exits by break after last segment within cell, or by return on final segment
705
+      } while (true);   // per-cell loop
706
+    }                 // end of function
707
+
708
+  #endif // UBL_DELTA
709
+
710
+#endif // AUTO_BED_LEVELING_UBL
711
+

Loading…
Cancel
Save