Ben NanoNote 3D scans
Sign in or create your account | Project List | Help
Ben NanoNote 3D scans Git Source Tree
Root/
Source at commit 9c614db35a13a7ed7f0d745290d7d37ecf604717 created 13 years 6 months ago. By Werner Almesberger, Bottom face was flipped in overlap. | |
---|---|
1 | /* |
2 | * overlap.c - Overlap two parallel faces |
3 | * |
4 | * Written 2010 by Werner Almesberger |
5 | * Copyright 2010 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <math.h> |
17 | #include <limits.h> |
18 | #include <gtk/gtk.h> |
19 | |
20 | #include "util.h" |
21 | #include "face.h" |
22 | #include "solid.h" |
23 | #include "style.h" |
24 | #include "overlap.h" |
25 | |
26 | |
27 | #define UNDEF_F HUGE_VAL |
28 | |
29 | |
30 | static int has_osd; |
31 | |
32 | |
33 | static int sx(const struct solid *s) |
34 | { |
35 | return (s->a->sx > s->b->sx ? s->a->sx : s->b->sx)+2*OVERLAP_BORDER; |
36 | } |
37 | |
38 | |
39 | static int sy(const struct solid *s) |
40 | { |
41 | return (s->a->sy > s->b->sy ? s->a->sy : s->b->sy)+2*OVERLAP_BORDER; |
42 | } |
43 | |
44 | |
45 | static double r_center(const struct solid *s) |
46 | { |
47 | return hypot(sx(s), sy(s))/OVERLAP_CENTER_DIV; |
48 | } |
49 | |
50 | |
51 | static double ramp(int z0, double w0, int z1, double w1) |
52 | { |
53 | if (z0 != UNDEF && z1 != UNDEF) |
54 | return z0*w0+z1*w1; |
55 | if (z0 == UNDEF && z0 == UNDEF) |
56 | return UNDEF_F; |
57 | if (z0 == UNDEF && w0 < w1) |
58 | return z1; |
59 | if (z1 == UNDEF && w0 > w1) |
60 | return z0; |
61 | return UNDEF_F; |
62 | } |
63 | |
64 | |
65 | static double zmix(struct face *f, double x, double y) |
66 | { |
67 | int xa, xb, ya, yb; |
68 | double zx0, zx1; |
69 | |
70 | xa = floor(x); |
71 | xb = xa+1; |
72 | ya = floor(y); |
73 | yb = ya+1; |
74 | |
75 | zx0 = ramp( |
76 | get_bounded(f->a, xa, ya), yb-y, |
77 | get_bounded(f->a, xa, yb), y-ya); |
78 | zx1 = ramp( |
79 | get_bounded(f->a, xb, ya), yb-y, |
80 | get_bounded(f->a, xb, yb), y-ya); |
81 | |
82 | return ramp(zx0, xb-x, zx1, x-xa); |
83 | } |
84 | |
85 | |
86 | /* |
87 | * Coordinate transformations, on the example of the x coordinate: |
88 | * |
89 | * - the x coordinate runs from 0 to sx(s)-1 |
90 | * - since we work relative to the screen center, this becomes x-sx(s)/2 |
91 | * This is what we perform the coordinate transform on. |
92 | * - our model runs from min_x to max_x. Its center is at cx. |
93 | */ |
94 | |
95 | static void point(const struct solid *s, int x, int y, guchar *p, |
96 | const struct matrix *ma, const struct matrix *mb) |
97 | { |
98 | double za, zb, z; |
99 | double xaf, xbf, yaf, ybf; |
100 | |
101 | matrix_map(x, y, ma, &xaf, &yaf); |
102 | matrix_map(x, y, mb, &xbf, &ybf); |
103 | |
104 | za = zmix(s->a, xaf, yaf); |
105 | zb = zmix(s->b, xbf, ybf); |
106 | |
107 | if (za == UNDEF_F && zb == UNDEF_F) |
108 | return; |
109 | |
110 | if (za == UNDEF_F) { |
111 | z = 128.0*(zb-s->b->a->min_z)/(s->b->a->max_z-s->b->a->min_z); |
112 | if (z < 0) |
113 | z = 0; |
114 | if (z > 255) |
115 | z = 255; |
116 | p[0] = 255; |
117 | p[1] = z; |
118 | p[2] = z; |
119 | return; |
120 | } |
121 | if (zb == UNDEF_F) { |
122 | z = 128.0*(za-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); |
123 | if (z < 0) |
124 | z = 0; |
125 | if (z > 255) |
126 | z = 255; |
127 | p[0] = z; |
128 | p[1] = 255; |
129 | p[2] = z; |
130 | return; |
131 | } |
132 | |
133 | z = za; |
134 | za -= face_z0(s->a, xaf, yaf); |
135 | zb -= face_z0(s->b, xbf, ybf); |
136 | |
137 | if (za+zb < -s->dist) { |
138 | p[0] = 0; |
139 | p[1] = 0; |
140 | p[2] = 255; |
141 | return; |
142 | } |
143 | |
144 | z = 256.0*(z-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); |
145 | if (z < 0) |
146 | z = 0; |
147 | if (z > 255) |
148 | z = 255; |
149 | p[0] = z; |
150 | p[1] = z; |
151 | p[2] = z; |
152 | } |
153 | |
154 | |
155 | static void merge_matrix(struct matrix *m, const struct solid *s, |
156 | const struct face *f) |
157 | { |
158 | double tm[2][2], tm2[2][2]; |
159 | double tv[2]; |
160 | double f_x, f_y; |
161 | |
162 | /* |
163 | * Finally, we convert to model matrix coordinates. |
164 | * |
165 | * v' = v+c |
166 | */ |
167 | |
168 | m->b[0] += f->cx; |
169 | m->b[1] += f->cy; |
170 | |
171 | /* |
172 | * Apply shrinkage caused by rotation out of z0. We use that |
173 | * cos a = sqrt(1-sin^2 a) |
174 | */ |
175 | |
176 | f_x = 1.0/sqrt(1-f->fx*f->fx); |
177 | f_y = 1.0/sqrt(1-f->fy*f->fy); |
178 | |
179 | m->a[0][0] *= f_x; |
180 | m->a[0][1] *= f_x; |
181 | m->b[0] *= f_x; |
182 | m->a[1][0] *= f_y; |
183 | m->a[1][1] *= f_y; |
184 | m->b[1] *= f_y; |
185 | |
186 | /* |
187 | * The transformation matrix f->m describes a transformation of |
188 | * (centered) model coordinates. We therefore have to reverse it: |
189 | * |
190 | * v = v'A+b |
191 | * v-b = v'A |
192 | * (v-b)A^-1 = v' |
193 | * vA^-1-bA^-1 = v' |
194 | */ |
195 | |
196 | matrix_invert(f->m.a, tm); |
197 | matrix_multv(f->m.b, tm, tv); |
198 | tv[0] = -tv[0]; |
199 | tv[1] = -tv[1]; |
200 | |
201 | /* |
202 | * Merge with the transformations we have so far: |
203 | * |
204 | * v' = vA1+b1 the transformation we have so far |
205 | * v'' = v'A2+b2 the transformation we apply |
206 | * |
207 | * v'' = (vA1+b1)A2+b2 |
208 | * v'' = vA1A2+b1A2+b2 |
209 | */ |
210 | |
211 | /* |
212 | * So far, the theory. To make it really work, we have to calculate |
213 | * v'' = vA1A2+b1+b2 |
214 | * duh ?!? |
215 | */ |
216 | |
217 | matrix_mult(m->a, tm, tm2); /* A1A2 */ |
218 | matrix_copy(tm2, m->a); |
219 | // matrix_multv(m->b, tm, m->b); /* b1A2 */ |
220 | m->b[0] += tv[0]; /* b2 */ |
221 | m->b[1] += tv[1]; |
222 | |
223 | /* |
224 | * Our input is a screen coordinate, its origin is in a corner so we |
225 | * first have to make it center-based: |
226 | * |
227 | * v' = (v-s/2)A+b |
228 | * v' = vA+(b-s/2*A) |
229 | */ |
230 | |
231 | tv[0] = sx(s)/2; |
232 | tv[1] = sy(s)/2; |
233 | matrix_multv(tv, m->a, tv); |
234 | m->b[0] -= tv[0]; |
235 | m->b[1] -= tv[1]; |
236 | } |
237 | |
238 | |
239 | static void draw_map(GtkWidget *widget, struct solid *s) |
240 | { |
241 | guchar *rgbbuf, *p; |
242 | int x, y; |
243 | struct matrix ma = { |
244 | .a = { { 1, 0 }, { 0, 1 } }, |
245 | .b = { 0, 0 }, |
246 | }; |
247 | struct matrix mb = { |
248 | .a = { { -1, 0 }, { 0, 1 } }, |
249 | .b = { 0, 0 }, |
250 | }; |
251 | |
252 | rgbbuf = p = calloc(sx(s)*sy(s), 3); |
253 | if (!rgbbuf) { |
254 | perror("calloc"); |
255 | exit(1); |
256 | } |
257 | |
258 | merge_matrix(&ma, s, s->a); |
259 | merge_matrix(&mb, s, s->b); |
260 | |
261 | for (y = sy(s)-1; y >= 0; y--) |
262 | for (x = 0; x != sx(s) ; x++) { |
263 | point(s, x, y, p, &ma, &mb); |
264 | p += 3; |
265 | } |
266 | gdk_draw_rgb_image(widget->window, |
267 | widget->style->fg_gc[GTK_STATE_NORMAL], |
268 | 0, 0, sx(s), sy(s), GDK_RGB_DITHER_MAX, rgbbuf, sx(s)*3); |
269 | free(rgbbuf); |
270 | } |
271 | |
272 | |
273 | static void draw_image(GtkWidget *widget, struct solid *s, int osd) |
274 | { |
275 | int cx = sx(s)/2; |
276 | int cy = sy(s)/2; |
277 | int p; |
278 | |
279 | draw_map(widget, s); |
280 | has_osd = osd; |
281 | if (!osd) |
282 | return; |
283 | draw_circle(widget->window, gc_osd, cx, cy, r_center(s)); |
284 | p = r_center(s)/sqrt(2); |
285 | gdk_draw_line(widget->window, gc_osd, cx-p, cy-p, cx+p, cy+p); |
286 | gdk_draw_line(widget->window, gc_osd, cx-p, cy+p, cx+p, cy-p); |
287 | } |
288 | |
289 | |
290 | /* |
291 | * Rotate such that a point at distance "r" moves one unit. Rotate |
292 | * counter-clockwise for r > 1, clockwise for r < 0. |
293 | */ |
294 | |
295 | static void rotate(struct matrix *m, double r) |
296 | { |
297 | struct matrix t; |
298 | double s, c; |
299 | |
300 | s = 1/r; |
301 | c = sqrt(1-s*s); |
302 | t.a[0][0] = m->a[0][0]*c-m->a[1][0]*s; |
303 | t.a[0][1] = m->a[0][1]*c-m->a[1][1]*s; |
304 | t.a[1][0] = m->a[1][0]*c+m->a[0][0]*s; |
305 | t.a[1][1] = m->a[1][1]*c+m->a[0][1]*s; |
306 | t.b[0] = m->b[0]*c-m->b[1]*s; |
307 | t.b[1] = m->b[0]*s+m->b[1]*c; |
308 | *m = t; |
309 | } |
310 | |
311 | |
312 | static void do_shift(struct matrix *m, int dx, int dy) |
313 | { |
314 | m->b[0] += dx; |
315 | m->b[1] += dy; |
316 | } |
317 | |
318 | |
319 | static void shift(struct matrix *m, int dx, int dy, int dir) |
320 | { |
321 | /* |
322 | * Wheeling "up" in each quadrant shifts in the respective direction, |
323 | * wheeling "down" in the opposite direction. |
324 | * |
325 | * No rule without exception: we treat the "down" quadrant like the |
326 | * "up" quadrant, because it would be extremely counter-intuitive to |
327 | * wheel "up" to move "down". |
328 | */ |
329 | |
330 | if (dx > 0 && dy < dx && dy > -dx) |
331 | do_shift(m, dir, 0); |
332 | if (dx < 0 && dy < -dx && dy > dx) |
333 | do_shift(m, -dir, 0); |
334 | if (dy > 0 && dx < dy && dx > -dy) |
335 | do_shift(m, 0, dir); |
336 | if (dy < 0 && dx < -dy && dx > dy) |
337 | do_shift(m, 0, dir); /* exception ! */ |
338 | } |
339 | |
340 | |
341 | static int osd_proximity(const struct solid *s, int dx, int dy) |
342 | { |
343 | double r = hypot(dx, dy); |
344 | double rc = r_center(s); |
345 | |
346 | if (fabs(r-rc) < OSD_PROXIMITY) |
347 | return 1; |
348 | if (r > rc) |
349 | return 0; |
350 | if (abs(abs(dx)-abs(dy)) < OSD_PROXIMITY) |
351 | return 1; |
352 | return 0; |
353 | } |
354 | |
355 | |
356 | static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, |
357 | gpointer data) |
358 | { |
359 | GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget)); |
360 | struct solid *s = data; |
361 | int dx = event->x-sx(s)/2; |
362 | int dy = event->y-sy(s)/2; |
363 | double r = hypot(dx, dy); |
364 | double rc = r_center(s); |
365 | double rs, rot; |
366 | int center = r < rc; |
367 | int osd = osd_proximity(s, dx, dy); |
368 | |
369 | if (r < 1) |
370 | return TRUE; |
371 | |
372 | /* |
373 | * rot goes exponentially from SLOWEST_ROT*rs to FASTEST_ROT for |
374 | * r = rc to rs, with rs being half the canvas diagonal. |
375 | * |
376 | * The values are picked such that we achieve sufficient precision at |
377 | * a reasonably large distance from the circle (for accidently entering |
378 | * the circle would change the mode) but can also spin quickly, e.g., |
379 | * when a 180 degrees rotation is needed. |
380 | * |
381 | * First, normalize to 0 ... 1 |
382 | * Then, we start at exp(0) and end at |
383 | * exp(ln(SLOWEST_ROT*rs/FASTEST_ROT))) |
384 | */ |
385 | rs = hypot(sx(s), sy(s))/2; |
386 | rot = (r-rc)/(rs-rc); |
387 | rot = SLOWEST_ROT*rs*exp(-rot*log(SLOWEST_ROT*rs/FASTEST_ROT)); |
388 | |
389 | switch (event->direction) { |
390 | case GDK_SCROLL_UP: |
391 | if (center) |
392 | shift(&s->a->m, dx, dy, 1); |
393 | else |
394 | rotate(&s->a->m, dx > 0 ? rot : -rot); |
395 | draw_image(darea, s, osd); |
396 | break; |
397 | case GDK_SCROLL_DOWN: |
398 | if (center) |
399 | shift(&s->a->m, dx, dy, -1); |
400 | else |
401 | rotate(&s->a->m, dx > 0 ? -rot : rot); |
402 | draw_image(darea, s, osd); |
403 | break; |
404 | default: |
405 | /* ignore */; |
406 | } |
407 | return TRUE; |
408 | } |
409 | |
410 | |
411 | static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, |
412 | gpointer user_data) |
413 | { |
414 | draw_image(widget, user_data, has_osd); |
415 | return TRUE; |
416 | } |
417 | |
418 | |
419 | static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, |
420 | gpointer data) |
421 | { |
422 | struct solid *s = data; |
423 | int dx = event->x-sx(s)/2; |
424 | int dy = event->y-sy(s)/2; |
425 | int osd = osd_proximity(s, dx, dy); |
426 | |
427 | if (osd != has_osd) |
428 | draw_image(widget, s, osd); |
429 | return FALSE; |
430 | } |
431 | |
432 | |
433 | void overlap(GtkWidget *canvas, struct solid *s) |
434 | { |
435 | GtkWidget *evbox, *darea; |
436 | |
437 | evbox = gtk_event_box_new(); |
438 | darea = gtk_drawing_area_new(); |
439 | |
440 | gtk_widget_set_events(darea, |
441 | GDK_EXPOSE | GDK_KEY_PRESS_MASK | |
442 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | |
443 | GDK_SCROLL | |
444 | GDK_POINTER_MOTION_MASK); |
445 | |
446 | gtk_widget_set_size_request(darea, sx(s), sy(s)); |
447 | gtk_container_add(GTK_CONTAINER(canvas), evbox); |
448 | gtk_container_add(GTK_CONTAINER(evbox), darea); |
449 | |
450 | draw_image(darea, s, 0); |
451 | |
452 | g_signal_connect(G_OBJECT(evbox), "scroll-event", |
453 | G_CALLBACK(scroll_event), s); |
454 | g_signal_connect(G_OBJECT(darea), "expose-event", |
455 | G_CALLBACK(expose_event), s); |
456 | g_signal_connect(G_OBJECT(darea), "motion-notify-event", |
457 | G_CALLBACK(motion_notify_event), s); |
458 | |
459 | if (0) { |
460 | int i; |
461 | long t0 = time(NULL); |
462 | gtk_widget_show_all(canvas); |
463 | for (i = 0; i != 1000; i++) { |
464 | rotate(&s->a->m, 100); |
465 | draw_image(darea, s, 0); |
466 | while (gtk_events_pending()) |
467 | gtk_main_iteration(); |
468 | } |
469 | fprintf(stderr, "%lu\n", time(NULL)-t0); |
470 | } |
471 | |
472 | } |
473 |
Branches:
master