Browse Source

Add hidden option to output Bilinear grids in JSON

Scott Lahteine 7 years ago
parent
commit
c961dd084d
3 changed files with 343 additions and 50 deletions
  1. 1
    0
      .gitattributes
  2. 86
    50
      Marlin/Marlin_main.cpp
  3. 256
    0
      buildroot/share/scripts/MarlinMesh.scad

+ 1
- 0
.gitattributes View File

@@ -8,6 +8,7 @@
8 8
 *.ino text eol=lf
9 9
 *.py  text eol=lf
10 10
 *.sh  text eol=lf
11
+*.scad text eol=lf
11 12
 
12 13
 # Files with native line endings
13 14
 # *.sln text

+ 86
- 50
Marlin/Marlin_main.cpp View File

@@ -399,11 +399,11 @@ int feedrate_percentage = 100, saved_feedrate_percentage,
399 399
 
400 400
 bool axis_relative_modes[] = AXIS_RELATIVE_MODES,
401 401
      volumetric_enabled =
402
-      #if ENABLED(VOLUMETRIC_DEFAULT_ON)
403
-        true
404
-      #else
405
-        false
406
-      #endif
402
+        #if ENABLED(VOLUMETRIC_DEFAULT_ON)
403
+          true
404
+        #else
405
+          false
406
+        #endif
407 407
       ;
408 408
 float filament_size[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_NOMINAL_FILAMENT_DIA),
409 409
       volumetric_multiplier[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0);
@@ -588,7 +588,6 @@ static uint8_t target_extruder;
588 588
 #endif
589 589
 
590 590
 #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
591
-  #define UNPROBED 9999.0f
592 591
   int bilinear_grid_spacing[2], bilinear_start[2];
593 592
   float bed_level_grid[ABL_GRID_MAX_POINTS_X][ABL_GRID_MAX_POINTS_Y];
594 593
 #endif
@@ -2344,7 +2343,7 @@ static void clean_up_after_endstop_or_probe_move() {
2344 2343
         bilinear_grid_spacing[X_AXIS] = bilinear_grid_spacing[Y_AXIS] = 0;
2345 2344
         for (uint8_t x = 0; x < ABL_GRID_MAX_POINTS_X; x++)
2346 2345
           for (uint8_t y = 0; y < ABL_GRID_MAX_POINTS_Y; y++)
2347
-            bed_level_grid[x][y] = UNPROBED;
2346
+            bed_level_grid[x][y] = NAN;
2348 2347
       #elif ENABLED(AUTO_BED_LEVELING_UBL)
2349 2348
         ubl.reset();
2350 2349
       #endif
@@ -2353,6 +2352,76 @@ static void clean_up_after_endstop_or_probe_move() {
2353 2352
 
2354 2353
 #endif // PLANNER_LEVELING
2355 2354
 
2355
+#if ENABLED(AUTO_BED_LEVELING_BILINEAR) || ENABLED(MESH_BED_LEVELING)
2356
+
2357
+  //
2358
+  // Enable if you prefer your output in JSON format
2359
+  // suitable for SCAD or JavaScript mesh visualizers.
2360
+  // 
2361
+  // Visualize meshes in OpenSCAD using the included script.
2362
+  // 
2363
+  //   buildroot/shared/scripts/MarlinMesh.scad
2364
+  //
2365
+  //#define SCAD_MESH_OUTPUT
2366
+
2367
+  /**
2368
+   * Print calibration results for plotting or manual frame adjustment.
2369
+   */
2370
+  static void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, float (*fn)(const uint8_t, const uint8_t)) {
2371
+    #ifndef SCAD_MESH_OUTPUT
2372
+      for (uint8_t x = 0; x < sx; x++) {
2373
+        for (uint8_t i = 0; i < precision + 2 + (x < 10 ? 1 : 0); i++)
2374
+          SERIAL_PROTOCOLCHAR(' ');
2375
+        SERIAL_PROTOCOL((int)x);
2376
+      }
2377
+      SERIAL_EOL;
2378
+    #endif
2379
+    #ifdef SCAD_MESH_OUTPUT
2380
+      SERIAL_PROTOCOLLNPGM("measured_z = ["); // open 2D array
2381
+    #endif
2382
+    for (uint8_t y = 0; y < sy; y++) {
2383
+      #ifdef SCAD_MESH_OUTPUT
2384
+        SERIAL_PROTOCOLLNPGM(" [");           // open sub-array
2385
+      #else
2386
+        if (y < 10) SERIAL_PROTOCOLCHAR(' ');
2387
+        SERIAL_PROTOCOL((int)y);
2388
+      #endif
2389
+      for (uint8_t x = 0; x < sx; x++) {
2390
+        SERIAL_PROTOCOLCHAR(' ');
2391
+        const float offset = fn(x, y);
2392
+        if (offset != NAN) {
2393
+          if (offset >= 0) SERIAL_PROTOCOLCHAR('+');
2394
+          SERIAL_PROTOCOL_F(offset, precision);
2395
+        }
2396
+        else {
2397
+          #ifdef SCAD_MESH_OUTPUT
2398
+            for (uint8_t i = 3; i < precision + 3; i++)
2399
+              SERIAL_PROTOCOLCHAR(' ');
2400
+            SERIAL_PROTOCOLPGM("NAN");
2401
+          #else
2402
+            for (uint8_t i = 0; i < precision + 3; i++)
2403
+              SERIAL_PROTOCOLCHAR(i ? '=' : ' ');
2404
+          #endif
2405
+        }
2406
+        #ifdef SCAD_MESH_OUTPUT
2407
+          if (x < sx - 1) SERIAL_PROTOCOLCHAR(',');
2408
+        #endif
2409
+      }
2410
+      #ifdef SCAD_MESH_OUTPUT
2411
+        SERIAL_PROTOCOLCHAR(' ');
2412
+        SERIAL_PROTOCOLCHAR(']');                     // close sub-array
2413
+        if (y < sy - 1) SERIAL_PROTOCOLCHAR(',');
2414
+      #endif
2415
+      SERIAL_EOL;
2416
+    }
2417
+    #ifdef SCAD_MESH_OUTPUT
2418
+      SERIAL_PROTOCOLPGM("\n];");                     // close 2D array
2419
+    #endif
2420
+    SERIAL_EOL;
2421
+  }
2422
+
2423
+#endif
2424
+
2356 2425
 #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
2357 2426
 
2358 2427
   /**
@@ -2372,7 +2441,7 @@ static void clean_up_after_endstop_or_probe_move() {
2372 2441
         SERIAL_CHAR(']');
2373 2442
       }
2374 2443
     #endif
2375
-    if (bed_level_grid[x][y] != UNPROBED) {
2444
+    if (bed_level_grid[x][y] != NAN) {
2376 2445
       #if ENABLED(DEBUG_LEVELING_FEATURE)
2377 2446
         if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM(" (done)");
2378 2447
       #endif
@@ -2386,9 +2455,9 @@ static void clean_up_after_endstop_or_probe_move() {
2386 2455
           c1 = bed_level_grid[x + xdir][y + ydir], c2 = bed_level_grid[x + xdir * 2][y + ydir * 2];
2387 2456
 
2388 2457
     // Treat far unprobed points as zero, near as equal to far
2389
-    if (a2 == UNPROBED) a2 = 0.0; if (a1 == UNPROBED) a1 = a2;
2390
-    if (b2 == UNPROBED) b2 = 0.0; if (b1 == UNPROBED) b1 = b2;
2391
-    if (c2 == UNPROBED) c2 = 0.0; if (c1 == UNPROBED) c1 = c2;
2458
+    if (a2 == NAN) a2 = 0.0; if (a1 == NAN) a1 = a2;
2459
+    if (b2 == NAN) b2 = 0.0; if (b1 == NAN) b1 = b2;
2460
+    if (c2 == NAN) c2 = 0.0; if (c1 == NAN) c1 = c2;
2392 2461
 
2393 2462
     const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2;
2394 2463
 
@@ -2453,39 +2522,10 @@ static void clean_up_after_endstop_or_probe_move() {
2453 2522
 
2454 2523
   }
2455 2524
 
2456
-  /**
2457
-   * Print calibration results for plotting or manual frame adjustment.
2458
-   */
2459
-  static void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, float (*fn)(const uint8_t, const uint8_t)) {
2460
-    for (uint8_t x = 0; x < sx; x++) {
2461
-      for (uint8_t i = 0; i < precision + 2 + (x < 10 ? 1 : 0); i++)
2462
-        SERIAL_PROTOCOLCHAR(' ');
2463
-      SERIAL_PROTOCOL((int)x);
2464
-    }
2465
-    SERIAL_EOL;
2466
-    for (uint8_t y = 0; y < sy; y++) {
2467
-      if (y < 10) SERIAL_PROTOCOLCHAR(' ');
2468
-      SERIAL_PROTOCOL((int)y);
2469
-      for (uint8_t x = 0; x < sx; x++) {
2470
-        SERIAL_PROTOCOLCHAR(' ');
2471
-        float offset = fn(x, y);
2472
-        if (offset != UNPROBED) {
2473
-          if (offset >= 0) SERIAL_PROTOCOLCHAR('+');
2474
-          SERIAL_PROTOCOL_F(offset, precision);
2475
-        }
2476
-        else
2477
-          for (uint8_t i = 0; i < precision + 3; i++)
2478
-            SERIAL_PROTOCOLCHAR(i ? '=' : ' ');
2479
-      }
2480
-      SERIAL_EOL;
2481
-    }
2482
-    SERIAL_EOL;
2483
-  }
2484
-
2485 2525
   static void print_bilinear_leveling_grid() {
2486 2526
     SERIAL_ECHOLNPGM("Bilinear Leveling Grid:");
2487
-    print_2d_array(ABL_GRID_MAX_POINTS_X, ABL_GRID_MAX_POINTS_Y, 2,
2488
-      [](const uint8_t x, const uint8_t y) { return bed_level_grid[x][y]; }
2527
+    print_2d_array(ABL_GRID_MAX_POINTS_X, ABL_GRID_MAX_POINTS_Y, 3,
2528
+      [](const uint8_t ix, const uint8_t iy) { return bed_level_grid[ix][iy]; }
2489 2529
     );
2490 2530
   }
2491 2531
 
@@ -2501,7 +2541,7 @@ static void clean_up_after_endstop_or_probe_move() {
2501 2541
     static void bed_level_virt_print() {
2502 2542
       SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:");
2503 2543
       print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5,
2504
-        [](const uint8_t x, const uint8_t y) { return bed_level_grid_virt[x][y]; }
2544
+        [](const uint8_t ix, const uint8_t iy) { return bed_level_grid_virt[ix][iy]; }
2505 2545
       );
2506 2546
     }
2507 2547
 
@@ -3715,13 +3755,9 @@ inline void gcode_G28() {
3715 3755
     SERIAL_PROTOCOLLNPGM("Num X,Y: " STRINGIFY(MESH_NUM_X_POINTS) "," STRINGIFY(MESH_NUM_Y_POINTS));
3716 3756
     SERIAL_PROTOCOLPGM("Z offset: "); SERIAL_PROTOCOL_F(mbl.z_offset, 5);
3717 3757
     SERIAL_PROTOCOLLNPGM("\nMeasured points:");
3718
-    for (uint8_t py = 0; py < MESH_NUM_Y_POINTS; py++) {
3719
-      for (uint8_t px = 0; px < MESH_NUM_X_POINTS; px++) {
3720
-        SERIAL_PROTOCOLPGM("  ");
3721
-        SERIAL_PROTOCOL_F(mbl.z_values[py][px], 5);
3722
-      }
3723
-      SERIAL_EOL;
3724
-    }
3758
+    print_2d_array(MESH_NUM_X_POINTS, MESH_NUM_Y_POINTS, 5,
3759
+      [](const uint8_t ix, const uint8_t iy) { return mbl.z_values[ix][iy]; }
3760
+    );
3725 3761
   }
3726 3762
 
3727 3763
   /**

+ 256
- 0
buildroot/share/scripts/MarlinMesh.scad View File

@@ -0,0 +1,256 @@
1
+ /**************************************\
2
+ *                                      *
3
+ *   OpenSCAD Mesh Display              *
4
+ *   by Thinkyhead - April 2017         *
5
+ *                                      *
6
+ *   Copy the grid output from Marlin,  *
7
+ *   paste below as shown, and use      *
8
+ *   OpenSCAD to see a visualization    *
9
+ *   of your mesh.                      *
10
+ *                                      *
11
+ \**************************************/
12
+
13
+//$t = 0.15; // comment out during animation
14
+
15
+//
16
+// Mesh info and points
17
+//
18
+
19
+mesh_width    = 200;   // X Size in mm of the probed area
20
+mesh_height   = 200;   // Y Size...
21
+zprobe_offset = 0;     // Added to the points
22
+NAN           = 0;     // Z to use for un-measured points
23
+
24
+measured_z = [
25
+  [ -1.20, -1.13, -1.09, -1.03, -1.19 ],
26
+  [ -1.16, -1.25, -1.27, -1.25, -1.08 ],
27
+  [ -1.13, -1.26, -1.39, -1.31, -1.18 ],
28
+  [ -1.09, -1.20, -1.26, -1.21, -1.18 ],
29
+  [ -1.13, -0.99, -1.03, -1.06, -1.32 ]
30
+];
31
+
32
+//
33
+// Geometry
34
+//
35
+
36
+max_z_scale   = 100;   // Scale at Time 0.5
37
+min_z_scale   = 10;    // Scale at Time 0.0 and 1.0
38
+thickness     = 0.5;   // thickness of the mesh triangles
39
+tesselation   = 1;     // levels of tesselation from 0-2
40
+alternation   = 2;     // direction change modulus (try it)
41
+
42
+//
43
+// Appearance
44
+//
45
+
46
+show_plane    = true;
47
+show_labels   = true;
48
+arrow_length  = 5;
49
+
50
+label_font_lg = "Arial";
51
+label_font_sm = "Arial";
52
+mesh_color    = [1,1,1,0.5];
53
+plane_color   = [0.4,0.6,0.9,0.6];
54
+
55
+//================================================ Derive useful values
56
+
57
+big_z = max_2D(measured_z,0);
58
+lil_z = min_2D(measured_z,0);
59
+
60
+mean_value = (big_z + lil_z) / 2.0;
61
+
62
+mesh_points_y = len(measured_z);
63
+mesh_points_x = len(measured_z[0]);
64
+
65
+xspace = mesh_width / (mesh_points_x - 1);
66
+yspace = mesh_height / (mesh_points_y - 1);
67
+
68
+// At $t=0 and $t=1 scale will be 100%
69
+z_scale_factor = min_z_scale + (($t > 0.5) ? 1.0 - $t : $t) * (max_z_scale - min_z_scale) * 2;
70
+
71
+//
72
+// Min and max recursive functions for 1D and 2D arrays
73
+// Return the smallest or largest value in the array
74
+//
75
+function min_1D(b,i) = (i<len(b)-1) ? min(b[i], min_1D(b,i+1)) : b[i];
76
+function min_2D(a,j) = (j<len(a)-1) ? min_2D(a,j+1) : min_1D(a[j], 0);
77
+function max_1D(b,i) = (i<len(b)-1) ? max(b[i], max_1D(b,i+1)) : b[i];
78
+function max_2D(a,j) = (j<len(a)-1) ? max_2D(a,j+1) : max_1D(a[j], 0);
79
+
80
+//
81
+// Get the corner probe points of a grid square.
82
+//
83
+// Input  : x,y grid indexes
84
+// Output : An array of the 4 corner points
85
+//
86
+function grid_square(x,y) = [
87
+  [x * xspace, y * yspace, z_scale_factor * (measured_z[y][x] - mean_value)],
88
+  [x * xspace, (y+1) * yspace, z_scale_factor * (measured_z[y+1][x] - mean_value)],
89
+  [(x+1) * xspace, (y+1) * yspace, z_scale_factor * (measured_z[y+1][x+1] - mean_value)],
90
+  [(x+1) * xspace, y * yspace, z_scale_factor * (measured_z[y][x+1] - mean_value)]
91
+];
92
+
93
+// The corner point of a grid square with Z centered on the mean
94
+function pos(x,y,z) = [x * xspace, y * yspace, z_scale_factor * (z - mean_value)];
95
+
96
+//
97
+// Draw the point markers and labels
98
+//
99
+module point_markers(show_home=true) {
100
+  // Mark the home position 0,0
101
+  color([0,0,0,0.25]) translate([1,1]) cylinder(r=1, h=z_scale_factor, center=true);
102
+
103
+  for (x=[0:mesh_points_x-1], y=[0:mesh_points_y-1]) {
104
+    z = measured_z[y][x];
105
+    down = z < mean_value;
106
+    translate(pos(x, y, z)) {
107
+
108
+      // Label each point with the Z
109
+      if (show_labels) {
110
+        v = z - mean_value;
111
+
112
+        color(abs(v) < 0.1 ? [0,0.5,0] : [0.25,0,0])
113
+        translate([0,0,down?-10:10]) {
114
+
115
+          $fn=8;
116
+          rotate([90,0])
117
+            text(str(z), 6, label_font_lg, halign="center", valign="center");
118
+      
119
+          translate([0,0,down?-6:6]) rotate([90,0])
120
+            text(str(down ? "" : "+", v), 3, label_font_sm, halign="center", valign="center");
121
+        }
122
+      }
123
+
124
+      // Show an arrow pointing up or down
125
+      rotate([0, down ? 180 : 0]) translate([0,0,-1])
126
+        cylinder(
127
+          r1=0.5,
128
+          r2=0.1,
129
+          h=arrow_length, $fn=12, center=1
130
+        );
131
+    }
132
+  }
133
+}
134
+
135
+//
136
+// Split a square on the diagonal into
137
+// two triangles and render them.
138
+//
139
+//     s : a square
140
+//   alt : a flag to split on the other diagonal
141
+//
142
+module tesselated_square(s, alt=false) {
143
+  add = [0,0,thickness];
144
+  p1 = [
145
+    s[0], s[1], s[2], s[3],
146
+    s[0]+add, s[1]+add, s[2]+add, s[3]+add
147
+  ];
148
+  f1 = alt
149
+      ? [ [0,1,3], [4,5,1,0], [4,7,5], [5,7,3,1], [7,4,0,3] ]
150
+      : [ [0,1,2], [4,5,1,0], [4,6,5], [5,6,2,1], [6,4,0,2] ];
151
+  f2 = alt
152
+      ? [ [1,2,3], [5,6,2,1], [5,6,7], [6,7,3,2], [7,5,1,3] ]
153
+      : [ [0,2,3], [4,6,2,0], [4,7,6], [6,7,3,2], [7,4,0,3] ];
154
+
155
+  // Use the other diagonal
156
+  polyhedron(points=p1, faces=f1);
157
+  polyhedron(points=p1, faces=f2);
158
+}
159
+
160
+/**
161
+ * The simplest mesh display
162
+ */
163
+module simple_mesh(show_plane=show_plane) {
164
+  if (show_plane) color(plane_color) cube([mesh_width, mesh_height, thickness]);
165
+  color(mesh_color)
166
+    for (x=[0:mesh_points_x-2], y=[0:mesh_points_y-2])
167
+      tesselated_square(grid_square(x, y));
168
+}
169
+
170
+/**
171
+ * Subdivide the mesh into smaller squares.
172
+ */
173
+module bilinear_mesh(show_plane=show_plane,tesselation=tesselation) {
174
+  if (show_plane) color(plane_color) translate([-5,-5]) cube([mesh_width+10, mesh_height+10, thickness]);
175
+  tesselation = tesselation % 4;
176
+  color(mesh_color)
177
+  for (x=[0:mesh_points_x-2], y=[0:mesh_points_y-2]) {
178
+    square = grid_square(x, y);
179
+    if (tesselation < 1) {
180
+      tesselated_square(square,(x%alternation)-(y%alternation));
181
+    }
182
+    else {
183
+      subdiv_4 = subdivided_square(square);
184
+      if (tesselation < 2) {
185
+        for (i=[0:3]) tesselated_square(subdiv_4[i],i%alternation);
186
+      }
187
+      else {
188
+        for (i=[0:3]) {
189
+          subdiv_16 = subdivided_square(subdiv_4[i]);
190
+          if (tesselation < 3) {
191
+            for (j=[0:3]) tesselated_square(subdiv_16[j],j%alternation);
192
+          }
193
+          else {
194
+            for (j=[0:3]) {
195
+              subdiv_64 = subdivided_square(subdiv_16[j]);
196
+              if (tesselation < 4) {
197
+                for (k=[0:3]) tesselated_square(subdiv_64[k]);
198
+              }
199
+            }
200
+          }
201
+        }
202
+      }
203
+    }
204
+
205
+  }
206
+}
207
+
208
+//
209
+// Subdivision helpers
210
+//
211
+function ctrz(a) = (a[0][2]+a[1][2]+a[3][2]+a[2][2])/4;
212
+function avgx(a,i) = (a[i][0]+a[(i+1)%4][0])/2;
213
+function avgy(a,i) = (a[i][1]+a[(i+1)%4][1])/2;
214
+function avgz(a,i) = (a[i][2]+a[(i+1)%4][2])/2;
215
+
216
+//
217
+// Convert one square into 4, applying bilinear averaging
218
+//
219
+// Input  : 1 square (4 points)
220
+// Output : An array of 4 squares
221
+//
222
+function subdivided_square(a) = [
223
+  [ // SW square
224
+    a[0],                          // SW
225
+    [a[0][0],avgy(a,0),avgz(a,0)], // CW
226
+    [avgx(a,1),avgy(a,0),ctrz(a)], // CC
227
+    [avgx(a,1),a[0][1],avgz(a,3)]  // SC
228
+  ],
229
+  [ // NW square
230
+    [a[0][0],avgy(a,0),avgz(a,0)], // CW
231
+    a[1],                          // NW
232
+    [avgx(a,1),a[1][1],avgz(a,1)], // NC
233
+    [avgx(a,1),avgy(a,0),ctrz(a)]  // CC
234
+  ],
235
+  [ // NE square
236
+    [avgx(a,1),avgy(a,0),ctrz(a)], // CC
237
+    [avgx(a,1),a[1][1],avgz(a,1)], // NC
238
+    a[2],                          // NE
239
+    [a[2][0],avgy(a,0),avgz(a,2)]  // CE
240
+  ],
241
+  [ // SE square
242
+    [avgx(a,1),a[0][1],avgz(a,3)], // SC
243
+    [avgx(a,1),avgy(a,0),ctrz(a)], // CC
244
+    [a[2][0],avgy(a,0),avgz(a,2)], // CE
245
+    a[3]                           // SE
246
+  ]
247
+];
248
+
249
+
250
+//================================================ Run the plan
251
+
252
+translate([-mesh_width / 2, -mesh_height / 2]) {
253
+  $fn = 12;
254
+  point_markers();
255
+  bilinear_mesh();
256
+}

Loading…
Cancel
Save