コード例 #1
0
ファイル: bedlevel.cpp プロジェクト: simonepri/MK4duo
  /**
   * lx, ly, lz - Logical (cartesian, not delta) positions in mm
   */
  void Bed_level::apply_leveling(float &lx, float &ly, float &lz) {

    #if HAS_ABL
      if (!abl_enabled) return;
    #endif

    #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
      static float z_fade_factor = 1.0, last_raw_lz = -999.0;
      if (z_fade_height) {
        const float raw_lz = RAW_Z_POSITION(lz);
        if (raw_lz >= z_fade_height) return;
        if (last_raw_lz != raw_lz) {
          last_raw_lz = raw_lz;
          z_fade_factor = 1.0 - raw_lz * inverse_z_fade_height;
        }
      }
      else
        z_fade_factor = 1.0;
    #endif

    #if ENABLED(MESH_BED_LEVELING)

      if (mbl.active())
        lz += mbl.get_z(RAW_X_POSITION(lx), RAW_Y_POSITION(ly)
          #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
            , z_fade_factor
          #endif
          );

    #elif ABL_PLANAR

      float dx = RAW_X_POSITION(lx) - (X_TILT_FULCRUM),
            dy = RAW_Y_POSITION(ly) - (Y_TILT_FULCRUM),
            dz = RAW_Z_POSITION(lz);

      apply_rotation_xyz(bed_level_matrix, dx, dy, dz);

      lx = LOGICAL_X_POSITION(dx + X_TILT_FULCRUM);
      ly = LOGICAL_Y_POSITION(dy + Y_TILT_FULCRUM);
      lz = LOGICAL_Z_POSITION(dz);

    #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)

      float tmp[XYZ] = { lx, ly, 0 };
      lz += bilinear_z_offset(tmp)
        #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
          * z_fade_factor
        #endif
      ;

    #endif
  }
コード例 #2
0
ファイル: bedlevel.cpp プロジェクト: simonepri/MK4duo
  void Bed_level::unapply_leveling(float logical[XYZ]) {

    #if HAS_ABL
      if (!abl_enabled) return;
    #endif

    #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
      if (z_fade_height && RAW_Z_POSITION(logical[Z_AXIS]) >= z_fade_height) return;
    #endif

    #if ENABLED(MESH_BED_LEVELING)

      if (mbl.active()) {
        #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
          const float c = mbl.get_z(RAW_X_POSITION(logical[X_AXIS]), RAW_Y_POSITION(logical[Y_AXIS]), 1.0);
          logical[Z_AXIS] = (z_fade_height * (RAW_Z_POSITION(logical[Z_AXIS]) - c)) / (z_fade_height - c);
        #else
          logical[Z_AXIS] -= mbl.get_z(RAW_X_POSITION(logical[X_AXIS]), RAW_Y_POSITION(logical[Y_AXIS]));
        #endif
      }

    #elif ABL_PLANAR

      matrix_3x3 inverse = matrix_3x3::transpose(bed_level_matrix);

      float dx = RAW_X_POSITION(logical[X_AXIS]) - (X_TILT_FULCRUM),
            dy = RAW_Y_POSITION(logical[Y_AXIS]) - (Y_TILT_FULCRUM),
            dz = RAW_Z_POSITION(logical[Z_AXIS]);

      apply_rotation_xyz(inverse, dx, dy, dz);

      logical[X_AXIS] = LOGICAL_X_POSITION(dx + X_TILT_FULCRUM);
      logical[Y_AXIS] = LOGICAL_Y_POSITION(dy + Y_TILT_FULCRUM);
      logical[Z_AXIS] = LOGICAL_Z_POSITION(dz);

    #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)

      #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
        const float c = bilinear_z_offset(logical);
        logical[Z_AXIS] = (z_fade_height * (RAW_Z_POSITION(logical[Z_AXIS]) - c)) / (z_fade_height - c);
      #else
        logical[Z_AXIS] -= bilinear_z_offset(logical);
      #endif

    #endif
  }
コード例 #3
0
  bool unified_bed_leveling::look_for_lines_to_connect() {
    float sx, sy, ex, ey;

    for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
      for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {

        #if ENABLED(NEWPANEL)
          if (user_canceled()) return true;     // Check if the user wants to stop the Mesh Validation
        #endif

        if (i < GRID_MAX_POINTS_X) { // We can't connect to anything to the right than GRID_MAX_POINTS_X.
                                     // This is already a half circle because we are at the edge of the bed.

          if (is_bit_set(circle_flags, i, j) && is_bit_set(circle_flags, i + 1, j)) { // check if we can do a line to the left
            if (!is_bit_set(horizontal_mesh_line_flags, i, j)) {

              //
              // We found two circles that need a horizontal line to connect them
              // Print it!
              //
              sx = mesh_index_to_xpos(  i  ) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // right edge
              ex = mesh_index_to_xpos(i + 1) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // left edge

              sx = constrain(sx, X_MIN_POS + 1, X_MAX_POS - 1);
              sy = ey = constrain(mesh_index_to_ypos(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
              ex = constrain(ex, X_MIN_POS + 1, X_MAX_POS - 1);

              if (position_is_reachable_raw_xy(sx, sy) && position_is_reachable_raw_xy(ex, ey)) {

                if (g26_debug_flag) {
                  SERIAL_ECHOPAIR(" Connecting with horizontal line (sx=", sx);
                  SERIAL_ECHOPAIR(", sy=", sy);
                  SERIAL_ECHOPAIR(") -> (ex=", ex);
                  SERIAL_ECHOPAIR(", ey=", ey);
                  SERIAL_CHAR(')');
                  SERIAL_EOL();
                  //debug_current_and_destination(PSTR("Connecting horizontal line."));
                }

                print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), g26_layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), g26_layer_height);
              }
              bit_set(horizontal_mesh_line_flags, i, j);   // Mark it as done so we don't do it again, even if we skipped it
            }
          }

          if (j < GRID_MAX_POINTS_Y) { // We can't connect to anything further back than GRID_MAX_POINTS_Y.
                                           // This is already a half circle because we are at the edge  of the bed.

            if (is_bit_set(circle_flags, i, j) && is_bit_set(circle_flags, i, j + 1)) { // check if we can do a line straight down
              if (!is_bit_set( vertical_mesh_line_flags, i, j)) {
                //
                // We found two circles that need a vertical line to connect them
                // Print it!
                //
                sy = mesh_index_to_ypos(  j  ) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // top edge
                ey = mesh_index_to_ypos(j + 1) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // bottom edge

                sx = ex = constrain(mesh_index_to_xpos(i), X_MIN_POS + 1, X_MAX_POS - 1);
                sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
                ey = constrain(ey, Y_MIN_POS + 1, Y_MAX_POS - 1);

                if (position_is_reachable_raw_xy(sx, sy) && position_is_reachable_raw_xy(ex, ey)) {

                  if (g26_debug_flag) {
                    SERIAL_ECHOPAIR(" Connecting with vertical line (sx=", sx);
                    SERIAL_ECHOPAIR(", sy=", sy);
                    SERIAL_ECHOPAIR(") -> (ex=", ex);
                    SERIAL_ECHOPAIR(", ey=", ey);
                    SERIAL_CHAR(')');
                    SERIAL_EOL();
                    debug_current_and_destination(PSTR("Connecting vertical line."));
                  }
                  print_line_from_here_to_there(LOGICAL_X_POSITION(sx), LOGICAL_Y_POSITION(sy), g26_layer_height, LOGICAL_X_POSITION(ex), LOGICAL_Y_POSITION(ey), g26_layer_height);
                }
                bit_set(vertical_mesh_line_flags, i, j);   // Mark it as done so we don't do it again, even if skipped
              }
            }
          }
        }
      }
    }
    return false;
  }
コード例 #4
0
  /**
   * G26: Mesh Validation Pattern generation.
   *
   * Used to interactively edit UBL's Mesh by placing the
   * nozzle in a problem area and doing a G29 P4 R command.
   */
  void unified_bed_leveling::G26() {
    SERIAL_ECHOLNPGM("G26 command started.  Waiting for heater(s).");
    float tmp, start_angle, end_angle;
    int   i, xi, yi;
    mesh_index_pair location;

    // Don't allow Mesh Validation without homing first,
    // or if the parameter parsing did not go OK, abort
    if (axis_unhomed_error() || parse_G26_parameters()) return;

    if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) {
      do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
      stepper.synchronize();
      set_current_to_destination();
    }

    if (turn_on_heaters()) goto LEAVE;

    current_position[E_AXIS] = 0.0;
    sync_plan_position_e();

    if (g26_prime_flag && prime_nozzle()) goto LEAVE;

    /**
     *  Bed is preheated
     *
     *  Nozzle is at temperature
     *
     *  Filament is primed!
     *
     *  It's  "Show Time" !!!
     */

    ZERO(circle_flags);
    ZERO(horizontal_mesh_line_flags);
    ZERO(vertical_mesh_line_flags);

    // Move nozzle to the specified height for the first layer
    set_destination_to_current();
    destination[Z_AXIS] = g26_layer_height;
    move_to(destination, 0.0);
    move_to(destination, g26_ooze_amount);

    has_control_of_lcd_panel = true;
    //debug_current_and_destination(PSTR("Starting G26 Mesh Validation Pattern."));

    /**
     * Declare and generate a sin() & cos() table to be used during the circle drawing.  This will lighten
     * the CPU load and make the arc drawing faster and more smooth
     */
    float sin_table[360 / 30 + 1], cos_table[360 / 30 + 1];
    for (i = 0; i <= 360 / 30; i++) {
      cos_table[i] = SIZE_OF_INTERSECTION_CIRCLES * cos(RADIANS(valid_trig_angle(i * 30.0)));
      sin_table[i] = SIZE_OF_INTERSECTION_CIRCLES * sin(RADIANS(valid_trig_angle(i * 30.0)));
    }

    do {
      location = g26_continue_with_closest
        ? find_closest_circle_to_print(current_position[X_AXIS], current_position[Y_AXIS])
        : find_closest_circle_to_print(g26_x_pos, g26_y_pos); // Find the closest Mesh Intersection to where we are now.

      if (location.x_index >= 0 && location.y_index >= 0) {
        const float circle_x = mesh_index_to_xpos(location.x_index),
                    circle_y = mesh_index_to_ypos(location.y_index);

        // If this mesh location is outside the printable_radius, skip it.

        if (!position_is_reachable_raw_xy(circle_x, circle_y)) continue;

        xi = location.x_index;  // Just to shrink the next few lines and make them easier to understand
        yi = location.y_index;

        if (g26_debug_flag) {
          SERIAL_ECHOPAIR("   Doing circle at: (xi=", xi);
          SERIAL_ECHOPAIR(", yi=", yi);
          SERIAL_CHAR(')');
          SERIAL_EOL();
        }

        start_angle = 0.0;    // assume it is going to be a full circle
        end_angle   = 360.0;
        if (xi == 0) {       // Check for bottom edge
          start_angle = -90.0;
          end_angle   =  90.0;
          if (yi == 0)        // it is an edge, check for the two left corners
            start_angle = 0.0;
          else if (yi == GRID_MAX_POINTS_Y - 1)
            end_angle = 0.0;
        }
        else if (xi == GRID_MAX_POINTS_X - 1) { // Check for top edge
          start_angle =  90.0;
          end_angle   = 270.0;
          if (yi == 0)                  // it is an edge, check for the two right corners
            end_angle = 180.0;
          else if (yi == GRID_MAX_POINTS_Y - 1)
            start_angle = 180.0;
        }
        else if (yi == 0) {
          start_angle =   0.0;         // only do the top   side of the cirlce
          end_angle   = 180.0;
        }
        else if (yi == GRID_MAX_POINTS_Y - 1) {
          start_angle = 180.0;         // only do the bottom side of the cirlce
          end_angle   = 360.0;
        }

        for (tmp = start_angle; tmp < end_angle - 0.1; tmp += 30.0) {

          #if ENABLED(NEWPANEL)
            if (user_canceled()) goto LEAVE;              // Check if the user wants to stop the Mesh Validation
          #endif

          int tmp_div_30 = tmp / 30.0;
          if (tmp_div_30 < 0) tmp_div_30 += 360 / 30;
          if (tmp_div_30 > 11) tmp_div_30 -= 360 / 30;

          float x = circle_x + cos_table[tmp_div_30],    // for speed, these are now a lookup table entry
                y = circle_y + sin_table[tmp_div_30],
                xe = circle_x + cos_table[tmp_div_30 + 1],
                ye = circle_y + sin_table[tmp_div_30 + 1];
          #if IS_KINEMATIC
            // Check to make sure this segment is entirely on the bed, skip if not.
            if (!position_is_reachable_raw_xy(x, y) || !position_is_reachable_raw_xy(xe, ye)) continue;
          #else                                              // not, we need to skip
            x  = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
            y  = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1);
            xe = constrain(xe, X_MIN_POS + 1, X_MAX_POS - 1);
            ye = constrain(ye, Y_MIN_POS + 1, Y_MAX_POS - 1);
          #endif

          //if (g26_debug_flag) {
          //  char ccc, *cptr, seg_msg[50], seg_num[10];
          //  strcpy(seg_msg, "   segment: ");
          //  strcpy(seg_num, "    \n");
          //  cptr = (char*) "01234567890ABCDEF????????";
          //  ccc = cptr[tmp_div_30];
          //  seg_num[1] = ccc;
          //  strcat(seg_msg, seg_num);
          //  debug_current_and_destination(seg_msg);
          //}

          print_line_from_here_to_there(LOGICAL_X_POSITION(x), LOGICAL_Y_POSITION(y), g26_layer_height, LOGICAL_X_POSITION(xe), LOGICAL_Y_POSITION(ye), g26_layer_height);

        }
        if (look_for_lines_to_connect())
          goto LEAVE;
      }
    } while (--g26_repeats && location.x_index >= 0 && location.y_index >= 0);

    LEAVE:
    lcd_setstatusPGM(PSTR("Leaving G26"), -1);

    retract_filament(destination);
    destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;

    //debug_current_and_destination(PSTR("ready to do Z-Raise."));
    move_to(destination, 0); // Raise the nozzle
    //debug_current_and_destination(PSTR("done doing Z-Raise."));

    destination[X_AXIS] = g26_x_pos;                                               // Move back to the starting position
    destination[Y_AXIS] = g26_y_pos;
    //destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;                        // Keep the nozzle where it is

    move_to(destination, 0); // Move back to the starting position
    //debug_current_and_destination(PSTR("done doing X/Y move."));

    has_control_of_lcd_panel = false;     // Give back control of the LCD Panel!

    if (!g26_keep_heaters_on) {
      #if HAS_TEMP_BED
        thermalManager.setTargetBed(0);
      #endif
      thermalManager.setTargetHotend(0, 0);
    }
  }
コード例 #5
0
  void look_for_lines_to_connect() {
    float sx, sy, ex, ey;

    for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
      for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {

        if (i < GRID_MAX_POINTS_X) { // We can't connect to anything to the right than GRID_MAX_POINTS_X.
                                         // This is already a half circle because we are at the edge of the bed.

          if (is_bit_set(circle_flags, i, j) && is_bit_set(circle_flags, i + 1, j)) { // check if we can do a line to the left
            if (!is_bit_set(horizontal_mesh_line_flags, i, j)) {

              //
              // We found two circles that need a horizontal line to connect them
              // Print it!
              //
              sx = pgm_read_float(&ubl.mesh_index_to_xpos[  i  ]) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // right edge
              ex = pgm_read_float(&ubl.mesh_index_to_xpos[i + 1]) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // left edge

              sx = constrain(sx, X_MIN_POS + 1, X_MAX_POS - 1);
              sy = ey = constrain(pgm_read_float(&ubl.mesh_index_to_ypos[j]), Y_MIN_POS + 1, Y_MAX_POS - 1);
              ex = constrain(ex, X_MIN_POS + 1, X_MAX_POS - 1);

              if (ubl.g26_debug_flag) {
                SERIAL_ECHOPAIR(" Connecting with horizontal line (sx=", sx);
                SERIAL_ECHOPAIR(", sy=", sy);
                SERIAL_ECHOPAIR(") -> (ex=", ex);
                SERIAL_ECHOPAIR(", ey=", ey);
                SERIAL_CHAR(')');
                SERIAL_EOL;
                //debug_current_and_destination(PSTR("Connecting horizontal line."));
              }

              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);
              bit_set(horizontal_mesh_line_flags, i, j);   // Mark it as done so we don't do it again
            }
          }

          if (j < GRID_MAX_POINTS_Y) { // We can't connect to anything further back than GRID_MAX_POINTS_Y.
                                           // This is already a half circle because we are at the edge  of the bed.

            if (is_bit_set(circle_flags, i, j) && is_bit_set(circle_flags, i, j + 1)) { // check if we can do a line straight down
              if (!is_bit_set( vertical_mesh_line_flags, i, j)) {
                //
                // We found two circles that need a vertical line to connect them
                // Print it!
                //
                sy = pgm_read_float(&ubl.mesh_index_to_ypos[  j  ]) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // top edge
                ey = pgm_read_float(&ubl.mesh_index_to_ypos[j + 1]) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // bottom edge

                sx = ex = constrain(pgm_read_float(&ubl.mesh_index_to_xpos[i]), X_MIN_POS + 1, X_MAX_POS - 1);
                sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
                ey = constrain(ey, Y_MIN_POS + 1, Y_MAX_POS - 1);

                if (ubl.g26_debug_flag) {
                  SERIAL_ECHOPAIR(" Connecting with vertical line (sx=", sx);
                  SERIAL_ECHOPAIR(", sy=", sy);
                  SERIAL_ECHOPAIR(") -> (ex=", ex);
                  SERIAL_ECHOPAIR(", ey=", ey);
                  SERIAL_CHAR(')');
                  SERIAL_EOL;
                  debug_current_and_destination(PSTR("Connecting vertical line."));
                }
                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);
                bit_set(vertical_mesh_line_flags, i, j);   // Mark it as done so we don't do it again
              }
            }
          }
        }
      }
    }
  }
コード例 #6
0
  /**
   * G26: Mesh Validation Pattern generation.
   *
   * Used to interactively edit UBL's Mesh by placing the
   * nozzle in a problem area and doing a G29 P4 R command.
   */
  void gcode_G26() {
    SERIAL_ECHOLNPGM("G26 command started.  Waiting for heater(s).");
    float tmp, start_angle, end_angle;
    int   i, xi, yi;
    mesh_index_pair location;

    // Don't allow Mesh Validation without homing first,
    // or if the parameter parsing did not go OK, abort
    if (axis_unhomed_error(true, true, true) || parse_G26_parameters()) return;

    if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) {
      do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
      stepper.synchronize();
      set_current_to_destination();
    }

    if (turn_on_heaters()) goto LEAVE;

    current_position[E_AXIS] = 0.0;
    sync_plan_position_e();

    if (prime_flag && prime_nozzle()) goto LEAVE;

    /**
     *  Bed is preheated
     *
     *  Nozzle is at temperature
     *
     *  Filament is primed!
     *
     *  It's  "Show Time" !!!
     */

    ZERO(circle_flags);
    ZERO(horizontal_mesh_line_flags);
    ZERO(vertical_mesh_line_flags);

    // Move nozzle to the specified height for the first layer
    set_destination_to_current();
    destination[Z_AXIS] = layer_height;
    move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0.0);
    move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], ooze_amount);

    ubl.has_control_of_lcd_panel = true;
    //debug_current_and_destination(PSTR("Starting G26 Mesh Validation Pattern."));

    /**
     * Declare and generate a sin() & cos() table to be used during the circle drawing.  This will lighten
     * the CPU load and make the arc drawing faster and more smooth
     */
    float sin_table[360 / 30 + 1], cos_table[360 / 30 + 1];
    for (i = 0; i <= 360 / 30; i++) {
      cos_table[i] = SIZE_OF_INTERSECTION_CIRCLES * cos(RADIANS(valid_trig_angle(i * 30.0)));
      sin_table[i] = SIZE_OF_INTERSECTION_CIRCLES * sin(RADIANS(valid_trig_angle(i * 30.0)));
    }

    do {

      if (ubl_lcd_clicked()) {              // Check if the user wants to stop the Mesh Validation
        #if ENABLED(ULTRA_LCD)
          lcd_setstatuspgm(PSTR("Mesh Validation Stopped."), 99);
          lcd_quick_feedback();
        #endif
        while (!ubl_lcd_clicked()) {         // Wait until the user is done pressing the
          idle();                            // Encoder Wheel if that is why we are leaving
          lcd_reset_alert_level();
          lcd_setstatuspgm(PSTR(""));
        }
        while (ubl_lcd_clicked()) {          // Wait until the user is done pressing the
          idle();                            // Encoder Wheel if that is why we are leaving
          lcd_setstatuspgm(PSTR("Unpress Wheel"), 99);
        }
        goto LEAVE;
      }

      location = continue_with_closest
        ? find_closest_circle_to_print(current_position[X_AXIS], current_position[Y_AXIS])
        : find_closest_circle_to_print(x_pos, y_pos); // Find the closest Mesh Intersection to where we are now.

      if (location.x_index >= 0 && location.y_index >= 0) {
        const float circle_x = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]),
                    circle_y = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]);

        // Let's do a couple of quick sanity checks.  We can pull this code out later if we never see it catch a problem
        #ifdef DELTA
          if (HYPOT2(circle_x, circle_y) > sq(DELTA_PRINTABLE_RADIUS)) {
            SERIAL_ERROR_START;
            SERIAL_ERRORLNPGM("Attempt to print outside of DELTA_PRINTABLE_RADIUS.");
            goto LEAVE;
          }
        #endif

        // TODO: Change this to use `position_is_reachable`
        if (!WITHIN(circle_x, X_MIN_POS, X_MAX_POS) || !WITHIN(circle_y, Y_MIN_POS, Y_MAX_POS)) {
          SERIAL_ERROR_START;
          SERIAL_ERRORLNPGM("Attempt to print off the bed.");
          goto LEAVE;
        }

        xi = location.x_index;  // Just to shrink the next few lines and make them easier to understand
        yi = location.y_index;

        if (ubl.g26_debug_flag) {
          SERIAL_ECHOPAIR("   Doing circle at: (xi=", xi);
          SERIAL_ECHOPAIR(", yi=", yi);
          SERIAL_CHAR(')');
          SERIAL_EOL;
        }

        start_angle = 0.0;    // assume it is going to be a full circle
        end_angle   = 360.0;
        if (xi == 0) {       // Check for bottom edge
          start_angle = -90.0;
          end_angle   =  90.0;
          if (yi == 0)        // it is an edge, check for the two left corners
            start_angle = 0.0;
          else if (yi == GRID_MAX_POINTS_Y - 1)
            end_angle = 0.0;
        }
        else if (xi == GRID_MAX_POINTS_X - 1) { // Check for top edge
          start_angle =  90.0;
          end_angle   = 270.0;
          if (yi == 0)                  // it is an edge, check for the two right corners
            end_angle = 180.0;
          else if (yi == GRID_MAX_POINTS_Y - 1)
            start_angle = 180.0;
        }
        else if (yi == 0) {
          start_angle =   0.0;         // only do the top   side of the cirlce
          end_angle   = 180.0;
        }
        else if (yi == GRID_MAX_POINTS_Y - 1) {
          start_angle = 180.0;         // only do the bottom side of the cirlce
          end_angle   = 360.0;
        }

        for (tmp = start_angle; tmp < end_angle - 0.1; tmp += 30.0) {
          int tmp_div_30 = tmp / 30.0;
          if (tmp_div_30 < 0) tmp_div_30 += 360 / 30;
          if (tmp_div_30 > 11) tmp_div_30 -= 360 / 30;

          float x = circle_x + cos_table[tmp_div_30],    // for speed, these are now a lookup table entry
                y = circle_y + sin_table[tmp_div_30],
                xe = circle_x + cos_table[tmp_div_30 + 1],
                ye = circle_y + sin_table[tmp_div_30 + 1];
          #ifdef DELTA
            if (HYPOT2(x, y) > sq(DELTA_PRINTABLE_RADIUS))   // Check to make sure this part of
              continue;                                      // the 'circle' is on the bed.  If
          #else                                              // not, we need to skip
            x  = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
            y  = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1);
            xe = constrain(xe, X_MIN_POS + 1, X_MAX_POS - 1);
            ye = constrain(ye, Y_MIN_POS + 1, Y_MAX_POS - 1);
          #endif

          //if (ubl.g26_debug_flag) {
          //  char ccc, *cptr, seg_msg[50], seg_num[10];
          //  strcpy(seg_msg, "   segment: ");
          //  strcpy(seg_num, "    \n");
          //  cptr = (char*) "01234567890ABCDEF????????";
          //  ccc = cptr[tmp_div_30];
          //  seg_num[1] = ccc;
          //  strcat(seg_msg, seg_num);
          //  debug_current_and_destination(seg_msg);
          //}

          print_line_from_here_to_there(LOGICAL_X_POSITION(x), LOGICAL_Y_POSITION(y), layer_height, LOGICAL_X_POSITION(xe), LOGICAL_Y_POSITION(ye), layer_height);

        }

        //debug_current_and_destination(PSTR("Looking for lines to connect."));
        look_for_lines_to_connect();
        //debug_current_and_destination(PSTR("Done with line connect."));
      }

      //debug_current_and_destination(PSTR("Done with current circle."));

    } while (location.x_index >= 0 && location.y_index >= 0);

    LEAVE:
    lcd_reset_alert_level();
    lcd_setstatuspgm(PSTR("Leaving G26"));

    retract_filament(destination);
    destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;

    //debug_current_and_destination(PSTR("ready to do Z-Raise."));
    move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0); // Raise the nozzle
    //debug_current_and_destination(PSTR("done doing Z-Raise."));

    destination[X_AXIS] = x_pos;                                               // Move back to the starting position
    destination[Y_AXIS] = y_pos;
    //destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;                        // Keep the nozzle where it is

    move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0); // Move back to the starting position
    //debug_current_and_destination(PSTR("done doing X/Y move."));

    ubl.has_control_of_lcd_panel = false;     // Give back control of the LCD Panel!

    if (!keep_heaters_on) {
      #if HAS_TEMP_BED
        thermalManager.setTargetBed(0);
      #endif
      thermalManager.setTargetHotend(0, 0);
    }
  }