From 3bdd7b1ed3f93de922f0ef15bd70dc6bfd979566 Mon Sep 17 00:00:00 2001
From: Scott Lahteine <github@thinkyhead.com>
Date: Sat, 3 Feb 2018 19:12:38 -0600
Subject: [PATCH] Reduce trig table down to 6 floats

---
 Marlin/G26_Mesh_Validation_Tool.cpp | 106 ++++++++++++----------------
 1 file changed, 45 insertions(+), 61 deletions(-)

diff --git a/Marlin/G26_Mesh_Validation_Tool.cpp b/Marlin/G26_Mesh_Validation_Tool.cpp
index b74ec05ac91..88a6252e4f7 100644
--- a/Marlin/G26_Mesh_Validation_Tool.cpp
+++ b/Marlin/G26_Mesh_Validation_Tool.cpp
@@ -48,11 +48,11 @@
   #define PRIME_LENGTH 10.0
   #define OOZE_AMOUNT 0.3
 
-  #define SIZE_OF_INTERSECTION_CIRCLES 5
-  #define SIZE_OF_CROSSHAIRS 3
+  #define INTERSECTION_CIRCLE_RADIUS 5
+  #define CROSSHAIRS_SIZE 3
 
-  #if SIZE_OF_CROSSHAIRS >= SIZE_OF_INTERSECTION_CIRCLES
-    #error "SIZE_OF_CROSSHAIRS must be less than SIZE_OF_INTERSECTION_CIRCLES."
+  #if CROSSHAIRS_SIZE >= INTERSECTION_CIRCLE_RADIUS
+    #error "CROSSHAIRS_SIZE must be less than INTERSECTION_CIRCLE_RADIUS."
   #endif
 
   #define G26_OK false
@@ -371,7 +371,7 @@
 
     // If the end point of the line is closer to the nozzle, flip the direction,
     // moving from the end to the start. On very small lines the optimization isn't worth it.
-    if (dist_end < dist_start && (SIZE_OF_INTERSECTION_CIRCLES) < FABS(line_length))
+    if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < FABS(line_length))
       return print_line_from_here_to_there(ex, ey, ez, sx, sy, sz);
 
     // Decide whether to retract & bump
@@ -411,8 +411,8 @@
               // We found two circles that need a horizontal line to connect them
               // Print it!
               //
-              sx = _GET_MESH_X(  i  ) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // right edge
-              ex = _GET_MESH_X(i + 1) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // left edge
+              sx = _GET_MESH_X(  i  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge
+              ex = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge
 
               sx = constrain(sx, X_MIN_POS + 1, X_MAX_POS - 1);
               sy = ey = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
@@ -444,8 +444,8 @@
                 // We found two circles that need a vertical line to connect them
                 // Print it!
                 //
-                sy = _GET_MESH_Y(  j  ) + (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // top edge
-                ey = _GET_MESH_Y(j + 1) - (SIZE_OF_INTERSECTION_CIRCLES - (SIZE_OF_CROSSHAIRS)); // bottom edge
+                sy = _GET_MESH_Y(  j  ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge
+                ey = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge
 
                 sx = ex = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1);
                 sy = constrain(sy, Y_MIN_POS + 1, Y_MAX_POS - 1);
@@ -555,9 +555,6 @@
    */
   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
@@ -730,17 +727,18 @@
     //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
+     * Pre-generate radius offset values at 30 degree intervals to reduce CPU load.
+     * All angles are offset by 15 degrees to allow for a smaller table.
      */
-    float sin_table[360 / 15 + 1], cos_table[360 / 15 + 1];
-    for (i = 0; i <= 360 / 15; i++) {
-      cos_table[i] = SIZE_OF_INTERSECTION_CIRCLES * cos(RADIANS(valid_trig_angle(i * 15.0)));
-      sin_table[i] = SIZE_OF_INTERSECTION_CIRCLES * sin(RADIANS(valid_trig_angle(i * 15.0)));
-    }
+    #define A_CNT ((360 / 30) / 2)
+    #define _COS(A) (trig_table[((N + A_CNT * 8) % A_CNT)] * (A >= A_CNT ? -1 : 1))
+    #define _SIN(A) (-_COS((A + A_CNT / 2) % (A_CNT * 2)))
+    float trig_table[A_CNT];
+    for (uint8_t i = 0; i < A_CNT; i++)
+      trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cos(RADIANS(i * 30 + 15));
 
     do {
-      location = g26_continue_with_closest
+      const mesh_index_pair 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.
 
@@ -749,12 +747,29 @@
                     circle_y = _GET_MESH_Y(location.y_index);
 
         // If this mesh location is outside the printable_radius, skip it.
-
         if (!position_is_reachable(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;
-
+        // Determine where to start and end the circle,
+        // which is always drawn counter-clockwise.
+        const uint8_t xi = location.x_index, yi = location.y_index;
+        const bool f = yi == 0, r = xi == GRID_MAX_POINTS_X - 1, b = yi == GRID_MAX_POINTS_Y - 1;
+        int8_t start_ind = -2, end_ind = 10;  // Assume a full circle (from 4:30 to 4:30)
+        if (xi == 0) {                        // Left edge? Just right half.
+          start_ind = f ?  0 : -3;            // 05:30 (02:30 for front-left)
+          end_ind   = b ? -1 :  2;            // 12:30 (03:30 for back-left)
+        }
+        else if (r) {                         // Right edge? Just left half.
+          start_ind = f ? 5 : 3;              // 11:30 (09:30 for front-right)
+          end_ind   = b ? 6 : 8;              // 06:30 (08:30 for back-right)
+        }
+        else if (f) {                         // Front edge? Just back half.
+          start_ind = 0;                      // 02:30
+          end_ind   = 5;                      // 09:30
+        }
+        else if (b) {                         // Back edge? Just front half.
+          start_ind =  6;                     // 08:30
+          end_ind   = 11;                     // 03:30
+        }
         if (g26_debug_flag) {
           SERIAL_ECHOPAIR("   Doing circle at: (xi=", xi);
           SERIAL_ECHOPAIR(", yi=", yi);
@@ -762,48 +777,17 @@
           SERIAL_EOL();
         }
 
-        // start and end the circle at an 45 degree angle to avoid lines crossing start-/end-points
-        start_angle = -45.0;    // assume it is going to be a full circle
-        end_angle   = 315.0;
-        if (xi == 0) {       // Check for bottom edge
-          start_angle = -75.0;
-          end_angle   =  75.0;
-          if (yi == 0)        // it is an edge, check for the two left corners
-            start_angle = 15.0;
-          else if (yi == GRID_MAX_POINTS_Y - 1)
-            end_angle = -15.0;
-        }
-        else if (xi == GRID_MAX_POINTS_X - 1) { // Check for top edge
-          start_angle = 105.0;
-          end_angle   = 255.0;
-          if (yi == 0)                  // it is an edge, check for the two right corners
-            end_angle = 165.0;
-          else if (yi == GRID_MAX_POINTS_Y - 1)
-            start_angle = 195.0;
-        }
-        else if (yi == 0) {
-          start_angle =  15.0;         // only do the top   side of the circle
-          end_angle   = 165.0;
-        }
-        else if (yi == GRID_MAX_POINTS_Y - 1) {
-          start_angle = 195.0;         // only do the bottom side of the circle
-          end_angle   = 345.0;
-        }
-
-        for (tmp = start_angle; tmp < end_angle - 0.1; tmp += 30.0) {
+        for (int8_t ind = start_ind; ind < end_ind; ind++) {
 
           #if ENABLED(NEWPANEL)
-            if (user_canceled()) goto LEAVE;              // Check if the user wants to stop the Mesh Validation
+            if (user_canceled()) goto LEAVE;          // Check if the user wants to stop the Mesh Validation
           #endif
 
-          int tmp_div_15 = tmp / 15.0;
-          while (tmp_div_15 < 0) tmp_div_15 += 360 / 15;
-          while (tmp_div_15 >= 360 / 15) tmp_div_15 -= 360 / 15;
+          float rx = circle_x + _COS(ind),            // For speed, these are now a lookup table entry
+                ry = circle_y + _SIN(ind),
+                xe = circle_x + _COS(ind + 1),
+                ye = circle_y + _SIN(ind + 1);
 
-          float rx = circle_x + cos_table[tmp_div_15],    // for speed, these are now a lookup table entry
-                ry = circle_y + sin_table[tmp_div_15],
-                xe = circle_x + cos_table[tmp_div_15 + 1],
-                ye = circle_y + sin_table[tmp_div_15 + 1];
           #if IS_KINEMATIC
             // Check to make sure this segment is entirely on the bed, skip if not.
             if (!position_is_reachable(rx, ry) || !position_is_reachable(xe, ye)) continue;