|
@@ -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
|
+}
|