Browse Source

UBL_DELTA (#6695)

UBL on Delta's....     Should be close!    Should not affect any Cartesian printer.
oldmcg 7 years ago
parent
commit
91841d75c9
10 changed files with 589 additions and 207 deletions
  1. 29
    7
      Marlin/Conditionals_post.h
  2. 57
    57
      Marlin/G26_Mesh_Validation_Tool.cpp
  3. 66
    1
      Marlin/Marlin.h
  4. 42
    63
      Marlin/Marlin_main.cpp
  5. 35
    22
      Marlin/SanityCheck.h
  6. 45
    0
      Marlin/planner.cpp
  7. 1
    1
      Marlin/ubl.cpp
  8. 7
    5
      Marlin/ubl.h
  9. 61
    41
      Marlin/ubl_G29.cpp
  10. 246
    10
      Marlin/ubl_motion.cpp

+ 29
- 7
Marlin/Conditionals_post.h View File

@@ -730,11 +730,16 @@
730 730
   /**
731 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 738
   #define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
734 739
   #define ABL_GRID   (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
735 740
   #define HAS_ABL    (ABL_PLANAR || ABL_GRID || ENABLED(AUTO_BED_LEVELING_UBL))
736 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 743
   #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST))
739 744
   #if HAS_PROBING_PROCEDURE
740 745
     #define PROBE_BED_WIDTH abs(RIGHT_PROBE_BED_POSITION - (LEFT_PROBE_BED_POSITION))
@@ -779,12 +784,13 @@
779 784
     #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT
780 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 794
   #else
789 795
     // Boundaries for probing based on set limits
790 796
     #define MIN_PROBE_X (max(X_MIN_POS, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
@@ -814,4 +820,20 @@
814 820
     #define LCD_TIMEOUT_TO_STATUS 15000
815 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 839
 #endif // CONDITIONALS_POST_H

+ 57
- 57
Marlin/G26_Mesh_Validation_Tool.cpp View File

@@ -122,7 +122,7 @@
122 122
 
123 123
   // External references
124 124
 
125
-  extern float feedrate;
125
+  extern float feedrate_mm_s; // must set before calling prepare_move_to_destination
126 126
   extern Planner planner;
127 127
   #if ENABLED(ULTRA_LCD)
128 128
     extern char lcd_status_message[];
@@ -130,6 +130,7 @@
130 130
   extern float destination[XYZE];
131 131
   void set_destination_to_current();
132 132
   void set_current_to_destination();
133
+  void prepare_move_to_destination();
133 134
   float code_value_float();
134 135
   float code_value_linear_units();
135 136
   float code_value_axis_units(const AxisEnum axis);
@@ -137,9 +138,6 @@
137 138
   bool code_has_value();
138 139
   void lcd_init();
139 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 141
   void sync_plan_position_e();
144 142
   void chirp_at_user();
145 143
 
@@ -182,6 +180,13 @@
182 180
 
183 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 191
    * G26: Mesh Validation Pattern generation.
187 192
    *
@@ -271,21 +276,10 @@
271 276
         const float circle_x = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
272 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 284
         xi = location.x_index;  // Just to shrink the next few lines and make them easier to understand
291 285
         yi = location.y_index;
@@ -333,9 +327,11 @@
333 327
                 y = circle_y + sin_table[tmp_div_30],
334 328
                 xe = circle_x + cos_table[tmp_div_30 + 1],
335 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 335
           #else                                              // not, we need to skip
340 336
             x  = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
341 337
             y  = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1);
@@ -463,18 +459,22 @@
463 459
               sy = ey = constrain(pgm_read_float(&ubl.mesh_index_to_ypos[j]), Y_MIN_POS + 1, Y_MAX_POS - 1);
464 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,17 +494,21 @@
494 494
                 sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
495 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,7 +536,7 @@
532 536
       destination[Z_AXIS] = z;                          // We know the last_z==z or we wouldn't be in this block of code.
533 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 541
       stepper.synchronize();
538 542
       set_destination_to_current();
@@ -552,7 +556,7 @@
552 556
 
553 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 561
     //if (ubl.g26_debug_flag) debug_current_and_destination(PSTR(" in move_to() after last move"));
558 562
 
@@ -755,20 +759,16 @@
755 759
     y_pos = current_position[Y_AXIS];
756 760
 
757 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 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,7 +864,7 @@
864 864
           Total_Prime += 0.25;
865 865
           if (Total_Prime >= EXTRUDE_MAXLENGTH) return UBL_ERR;
866 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 869
         stepper.synchronize();    // Without this synchronize, the purge is more consistent,
870 870
                                   // but because the planner has a buffer, we won't be able
@@ -893,7 +893,7 @@
893 893
       #endif
894 894
       set_destination_to_current();
895 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 897
       stepper.synchronize();
898 898
       set_destination_to_current();
899 899
       retract_filament(destination);

+ 66
- 1
Marlin/Marlin.h View File

@@ -429,4 +429,69 @@ void do_blocking_move_to_xy(const float &x, const float &y, const float &fr_mm_s
429 429
   bool axis_unhomed_error(const bool x, const bool y, const bool z);
430 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,7 +401,7 @@ float constexpr homing_feedrate_mm_s[] = {
401 401
   #endif
402 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 405
 int feedrate_percentage = 100, saved_feedrate_percentage,
406 406
     flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100);
407 407
 
@@ -1677,6 +1677,8 @@ void do_blocking_move_to(const float &x, const float &y, const float &z, const f
1677 1677
 
1678 1678
   #if ENABLED(DELTA)
1679 1679
 
1680
+    if ( ! position_is_reachable_xy( x, y )) return;
1681
+
1680 1682
     feedrate_mm_s = fr_mm_s ? fr_mm_s : XY_PROBE_FEEDRATE_MM_S;
1681 1683
 
1682 1684
     set_destination_to_current();          // sync destination at the start
@@ -1731,6 +1733,8 @@ void do_blocking_move_to(const float &x, const float &y, const float &z, const f
1731 1733
 
1732 1734
   #elif IS_SCARA
1733 1735
 
1736
+    if ( ! position_is_reachable_xy( x, y )) return;
1737
+
1734 1738
     set_destination_to_current();
1735 1739
 
1736 1740
     // If Z needs to raise, do it before moving XY
@@ -2351,6 +2355,8 @@ static void clean_up_after_endstop_or_probe_move() {
2351 2355
       }
2352 2356
     #endif
2353 2357
 
2358
+    if ( ! position_is_reachable_by_probe_xy( x, y )) return NAN;
2359
+
2354 2360
     const float old_feedrate_mm_s = feedrate_mm_s;
2355 2361
 
2356 2362
     #if ENABLED(DELTA)
@@ -2419,8 +2425,13 @@ static void clean_up_after_endstop_or_probe_move() {
2419 2425
 
2420 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 2434
       ubl.state.active = enable;
2423
-      //set_current_from_steppers_for_axis(Z_AXIS);
2424 2435
 
2425 2436
     #else
2426 2437
 
@@ -3210,37 +3221,6 @@ void unknown_command_error() {
3210 3221
 
3211 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 3226
  ***************** GCode Handlers *****************
@@ -3676,18 +3656,12 @@ inline void gcode_G4() {
3676 3656
     destination[Y_AXIS] = LOGICAL_Y_POSITION(Z_SAFE_HOMING_Y_POINT);
3677 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 3666
       #if ENABLED(DEBUG_LEVELING_FEATURE)
3693 3667
         if (DEBUGGING(LEVELING)) DEBUG_POS("Z_SAFE_HOMING", destination);
@@ -4612,8 +4586,7 @@ void home_all_axes() { gcode_G28(); }
4612 4586
             indexIntoAB[xCount][yCount] = abl_probe_index;
4613 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 4590
           ++abl_probe_index;
4618 4591
         }
4619 4592
 
@@ -4724,8 +4697,7 @@ void home_all_axes() { gcode_G28(); }
4724 4697
 
4725 4698
             #if IS_KINEMATIC
4726 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 4701
             #endif
4730 4702
 
4731 4703
             measured_z = faux ? 0.001 * random(-100, 101) : probe_pt(xProbe, yProbe, stow_probe_after_each, verbose_level);
@@ -5028,10 +5000,9 @@ void home_all_axes() { gcode_G28(); }
5028 5000
    */
5029 5001
   inline void gcode_G30() {
5030 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 5007
     // Disable leveling so the planner won't mess with us
5037 5008
     #if HAS_LEVELING
@@ -6222,22 +6193,19 @@ inline void gcode_M42() {
6222 6193
     bool stow_probe_after_each = code_seen('E');
6223 6194
 
6224 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 6198
     #if DISABLED(DELTA)
6226 6199
       if (!WITHIN(X_probe_location, LOGICAL_X_POSITION(MIN_PROBE_X), LOGICAL_X_POSITION(MAX_PROBE_X))) {
6227 6200
         out_of_range_error(PSTR("X"));
6228 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 6203
       if (!WITHIN(Y_probe_location, LOGICAL_Y_POSITION(MIN_PROBE_Y), LOGICAL_Y_POSITION(MAX_PROBE_Y))) {
6235 6204
         out_of_range_error(PSTR("Y"));
6236 6205
         return;
6237 6206
       }
6238 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 6209
         SERIAL_PROTOCOLLNPGM("? (X,Y) location outside of probeable radius.");
6242 6210
         return;
6243 6211
       }
@@ -6335,7 +6303,7 @@ inline void gcode_M42() {
6335 6303
           #else
6336 6304
             // If we have gone out too far, we can do a simple fix and scale the numbers
6337 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 6307
               X_current *= 0.8;
6340 6308
               Y_current *= 0.8;
6341 6309
               if (verbose_level > 3) {
@@ -11138,7 +11106,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
11138 11106
 
11139 11107
 #endif // AUTO_BED_LEVELING_BILINEAR
11140 11108
 
11141
-#if IS_KINEMATIC
11109
+#if IS_KINEMATIC && DISABLED(UBL_DELTA)
11142 11110
 
11143 11111
   /**
11144 11112
    * Prepare a linear move in a DELTA or SCARA setup.
@@ -11157,6 +11125,9 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
11157 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 11131
     // Get the cartesian distances moved in XYZE
11161 11132
     float difference[XYZE];
11162 11133
     LOOP_XYZE(i) difference[i] = ltarget[i] - current_position[i];
@@ -11245,7 +11216,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
11245 11216
       // For SCARA scale the feed rate from mm/s to degrees/s
11246 11217
       // With segments > 1 length is 1 segment, otherwise total length
11247 11218
       inverse_kinematics(ltarget);
11248
-      ADJUST_DELTA(logical);
11219
+      ADJUST_DELTA(ltarget);
11249 11220
       const float adiff = abs(delta[A_AXIS] - oldA),
11250 11221
                   bdiff = abs(delta[B_AXIS] - oldB);
11251 11222
       planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], logical[E_AXIS], max(adiff, bdiff) * feed_factor, active_extruder);
@@ -11278,7 +11249,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
11278 11249
         else
11279 11250
       #elif ENABLED(AUTO_BED_LEVELING_UBL)
11280 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 11253
           return true;
11283 11254
         }
11284 11255
         else
@@ -11407,12 +11378,19 @@ void prepare_move_to_destination() {
11407 11378
   #endif
11408 11379
 
11409 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 11386
   #else
11412 11387
     #if ENABLED(DUAL_X_CARRIAGE)
11413 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 11393
     #endif
11415
-    if (prepare_move_to_destination_cartesian()) return;
11416 11394
   #endif
11417 11395
 
11418 11396
   set_current_to_destination();
@@ -12427,3 +12405,4 @@ void loop() {
12427 12405
   endstops.report_state();
12428 12406
   idle();
12429 12407
 }
12408
+

+ 35
- 22
Marlin/SanityCheck.h View File

@@ -248,8 +248,9 @@
248 248
 #if ENABLED(DELTA)
249 249
   #if DISABLED(USE_XMAX_PLUG) && DISABLED(USE_YMAX_PLUG) && DISABLED(USE_ZMAX_PLUG)
250 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 254
   #endif
254 255
   #if ABL_GRID
255 256
     #if (GRID_MAX_POINTS_X & 1) == 0 || (GRID_MAX_POINTS_Y & 1) == 0
@@ -430,11 +431,20 @@ static_assert(1 >= 0
430 431
  * Unified Bed Leveling
431 432
  */
432 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 444
     #error "AUTO_BED_LEVELING_UBL requires an LCD controller."
437 445
   #endif
446
+#elif ENABLED(UBL_DELTA)
447
+  #error "UBL_DELTA requires AUTO_BED_LEVELING_UBL."
438 448
 #endif
439 449
 
440 450
 /**
@@ -593,11 +603,9 @@ static_assert(1 >= 0
593 603
   /**
594 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 609
     #endif
602 610
   #endif
603 611
 
@@ -626,18 +634,23 @@ static_assert(1 >= 0
626 634
       #error "AUTO_BED_LEVELING_UBL requires EEPROM_SETTINGS. Please update your configuration."
627 635
     #elif !WITHIN(GRID_MAX_POINTS_X, 3, 15) || !WITHIN(GRID_MAX_POINTS_Y, 3, 15)
628 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 654
     #endif
642 655
   #else // AUTO_BED_LEVELING_3POINT
643 656
     #if !WITHIN(ABL_PROBE_PT_1_X, MIN_PROBE_X, MAX_PROBE_X)

+ 45
- 0
Marlin/planner.cpp View File

@@ -63,6 +63,7 @@
63 63
 #include "temperature.h"
64 64
 #include "ultralcd.h"
65 65
 #include "language.h"
66
+#include "ubl.h"
66 67
 
67 68
 #include "Marlin.h"
68 69
 
@@ -533,6 +534,17 @@ void Planner::check_axes_activity() {
533 534
    */
534 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 548
     #if HAS_ABL
537 549
       if (!abl_enabled) return;
538 550
     #endif
@@ -586,6 +598,39 @@ void Planner::check_axes_activity() {
586 598
 
587 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 634
     #if HAS_ABL
590 635
       if (!abl_enabled) return;
591 636
     #endif

+ 1
- 1
Marlin/ubl.cpp View File

@@ -41,7 +41,7 @@
41 41
 
42 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 45
     SERIAL_CHAR('(');
46 46
     SERIAL_ECHO(x);
47 47
     SERIAL_CHAR(',');

+ 7
- 5
Marlin/ubl.h View File

@@ -52,7 +52,8 @@
52 52
   // ubl_motion.cpp
53 53
 
54 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 58
   // ubl_G29.cpp
58 59
 
@@ -329,10 +330,8 @@
329 330
        *  Returns 0.0 if Z is past the specified 'Fade Height'.
330 331
        */
331 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 334
           if (planner.z_fade_height == 0.0) return 1.0;
335
-
336 335
           static float fade_scaling_factor = 1.0;
337 336
           const float rz = RAW_Z_POSITION(lz);
338 337
           if (last_specified_z != rz) {
@@ -344,7 +343,10 @@
344 343
           }
345 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 350
       #endif
349 351
 
350 352
   }; // class unified_bed_leveling

+ 61
- 41
Marlin/ubl_G29.cpp View File

@@ -436,8 +436,13 @@
436 436
              * It may make sense to have Delta printers default to the center of the bed.
437 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 448
           if (code_seen('C')) {
@@ -457,6 +462,11 @@
457 462
           }
458 463
 
459 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 471
           manually_probe_remaining_mesh(x_pos, y_pos, height, card_thickness, code_seen('O') || code_seen('M'));
462 472
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
@@ -470,17 +480,25 @@
470 480
            *   - Allow 'G29 P3' to choose a 'reasonable' constant.
471 481
            */
472 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 494
                 ubl.z_values[location.x_index][location.y_index] = ubl_constant;
495
+              }
477 496
             }
478
-            break;
479
-          }
480
-          else
497
+          } else {
481 498
             smart_fill_mesh(); // Do a 'Smart' fill using nearby known values
482
-
483
-        } break;
499
+          }
500
+          break;
501
+        }
484 502
 
485 503
         case 4:
486 504
           //
@@ -502,6 +520,12 @@
502 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 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 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 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,6 +734,8 @@
710 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 735
     DEPLOY_PROBE();
712 736
 
737
+    uint16_t max_iterations = ( GRID_MAX_POINTS_X * GRID_MAX_POINTS_Y );
738
+
713 739
     do {
714 740
       if (ubl_lcd_clicked()) {
715 741
         SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
@@ -723,27 +749,19 @@
723 749
       }
724 750
 
725 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 755
         const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
729 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 758
         const float measured_z = probe_pt(LOGICAL_X_POSITION(rawx), LOGICAL_Y_POSITION(rawy), stow_probe, g29_verbose_level);
739 759
         ubl.z_values[location.x_index][location.y_index] = measured_z;
740 760
       }
741 761
 
742 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 766
     STOW_PROBE();
749 767
     ubl.restore_ubl_active_state_and_leave();
@@ -939,17 +957,13 @@
939 957
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
940 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 960
       const float xProbe = LOGICAL_X_POSITION(rawx),
951 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 967
       do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
954 968
 
955 969
       LCD_MESSAGEPGM("Moving to next");
@@ -1361,13 +1375,17 @@
1361 1375
                       rawy = pgm_read_float(&ubl.mesh_index_to_ypos[j]);
1362 1376
 
1363 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 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 1389
           // Add in a weighting factor that considers the current location of the nozzle.
1372 1390
 
1373 1391
           const float mx = LOGICAL_X_POSITION(rawx), // Check if we can probe this mesh location
@@ -1415,7 +1433,13 @@
1415 1433
     uint16_t not_done[16];
1416 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 1441
     ubl.save_ubl_active_state_and_disable();
1442
+
1419 1443
     memset(not_done, 0xFF, sizeof(not_done));
1420 1444
 
1421 1445
     LCD_MESSAGEPGM("Fine Tuning Mesh");
@@ -1425,7 +1449,7 @@
1425 1449
     do {
1426 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 1454
       bit_clear(not_done, location.x_index, location.y_index);  // Mark this location as 'adjusted' so we will find a
1431 1455
                                                                 // different location the next time through the loop
@@ -1433,12 +1457,8 @@
1433 1457
       const float rawx = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
1434 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 1464
       float new_z = ubl.z_values[location.x_index][location.y_index];
@@ -1494,7 +1514,7 @@
1494 1514
 
1495 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 1519
     FINE_TUNE_EXIT:
1500 1520
 

+ 246
- 10
Marlin/ubl_motion.cpp View File

@@ -26,11 +26,13 @@
26 26
   #include "Marlin.h"
27 27
   #include "ubl.h"
28 28
   #include "planner.h"
29
+  #include "stepper.h"
29 30
   #include <avr/io.h>
30 31
   #include <math.h>
31 32
 
32 33
   extern float destination[XYZE];
33 34
   extern void set_current_to_destination();
35
+  extern float delta_segments_per_second;
34 36
 
35 37
   static void debug_echo_axis(const AxisEnum axis) {
36 38
     if (current_position[axis] == destination[axis])
@@ -87,7 +89,7 @@
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 94
      * Much of the nozzle movement will be within the same cell. So we will do as little computation
93 95
      * as possible to determine if this is the case. If this move is within the same cell, we will
@@ -134,7 +136,7 @@
134 136
         // Note: There is no Z Correction in this case. We are off the grid and don't know what
135 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 140
         set_current_to_destination();
139 141
 
140 142
         if (ubl.g26_debug_flag)
@@ -178,7 +180,7 @@
178 180
        */
179 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 185
       if (ubl.g26_debug_flag)
184 186
         debug_current_and_destination(PSTR("FINAL_MOVE in ubl_line_to_destination()"));
@@ -270,7 +272,7 @@
270 272
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
271 273
          * where the line is heading down and it is starting right on a Mesh Line boundary. For how often that
272 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 277
         if (y != start[Y_AXIS]) {
276 278
           if (!inf_normalized_flag) {
@@ -292,7 +294,7 @@
292 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 298
         } //else printf("FIRST MOVE PRUNED  ");
297 299
       }
298 300
 
@@ -344,7 +346,7 @@
344 346
          * Without this check, it is possible for the algorithm to generate a zero length move in the case
345 347
          * where the line is heading left and it is starting right on a Mesh Line boundary. For how often
346 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 351
         if (x != start[X_AXIS]) {
350 352
           if (!inf_normalized_flag) {
@@ -363,7 +365,7 @@
363 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 369
         } //else printf("FIRST MOVE PRUNED  ");
368 370
       }
369 371
 
@@ -426,7 +428,7 @@
426 428
           e_position = end[E_AXIS];
427 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 432
         current_yi += dyi;
431 433
         yi_cnt--;
432 434
       }
@@ -455,7 +457,7 @@
455 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 461
         current_xi += dxi;
460 462
         xi_cnt--;
461 463
       }
@@ -472,4 +474,238 @@
472 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