1 |
ron |
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 |
}
|