Ben NanoNote 3D scans
Sign in or create your account | Project List | Help
Ben NanoNote 3D scans Git Source Tree
Root/
Source at commit f3e7c1ec73e233a372105723ea5ace07f58fc740 created 13 years 6 months ago. By Werner Almesberger, Various coordinate transform corrections in overlap.c | |
---|---|
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 w0 == 0 && w1 == 0 ? z0 : 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 = ceil(x); |
72 | ya = floor(y); |
73 | yb = ceil(y); |
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 | { |
97 | double za, zb, z; |
98 | int xa, xb, ya, yb; |
99 | double xaf, xbf, yaf, ybf; |
100 | |
101 | xa = x-sx(s)/2; |
102 | ya = y-sy(s)/2; |
103 | xaf = xa*s->a->m.a[0][0]+ya*s->a->m.a[0][1]+s->a->m.b[0]+s->a->cx; |
104 | yaf = xa*s->a->m.a[1][0]+ya*s->a->m.a[1][1]+s->a->m.b[1]+s->a->cy; |
105 | za = zmix(s->a, xaf, yaf); |
106 | |
107 | xb = x-sx(s)/2; |
108 | yb = (sy(s)-1)/2-y; |
109 | xbf = xb*s->b->m.a[0][0]+yb*s->b->m.a[0][1]+s->b->m.b[0]+s->b->cx; |
110 | ybf = xb*s->b->m.a[1][0]+yb*s->b->m.a[1][1]+s->b->m.b[1]+s->b->cy; |
111 | zb = zmix(s->b, xbf, ybf); |
112 | |
113 | if (za == UNDEF_F && zb == UNDEF_F) |
114 | return; |
115 | |
116 | if (za == UNDEF_F) { |
117 | z = 128.0*(zb-s->b->a->min_z)/(s->b->a->max_z-s->b->a->min_z); |
118 | if (z < 0) |
119 | z = 0; |
120 | if (z > 255) |
121 | z = 255; |
122 | p[0] = 255; |
123 | p[1] = z; |
124 | p[2] = z; |
125 | return; |
126 | } |
127 | if (zb == UNDEF_F) { |
128 | z = 128.0*(za-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); |
129 | if (z < 0) |
130 | z = 0; |
131 | if (z > 255) |
132 | z = 255; |
133 | p[0] = z; |
134 | p[1] = 255; |
135 | p[2] = z; |
136 | return; |
137 | } |
138 | |
139 | z = za; |
140 | za -= face_z0(s->a, xaf, yaf); |
141 | zb -= face_z0(s->b, xbf, ybf); |
142 | |
143 | if (za+zb < -s->dist) { |
144 | p[0] = 0; |
145 | p[1] = 0; |
146 | p[2] = 255; |
147 | return; |
148 | } |
149 | |
150 | z = 256.0*(z-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); |
151 | if (z < 0) |
152 | z = 0; |
153 | if (z > 255) |
154 | z = 255; |
155 | p[0] = z; |
156 | p[1] = z; |
157 | p[2] = z; |
158 | } |
159 | |
160 | |
161 | static void draw_map(GtkWidget *widget, struct solid *s) |
162 | { |
163 | guchar *rgbbuf, *p; |
164 | int x, y; |
165 | |
166 | rgbbuf = p = calloc(sx(s)*sy(s), 3); |
167 | if (!rgbbuf) { |
168 | perror("calloc"); |
169 | exit(1); |
170 | } |
171 | for (y = sy(s)-1; y >= 0; y--) |
172 | for (x = 0; x != sx(s) ; x++) { |
173 | point(s, x, y, p); |
174 | p += 3; |
175 | } |
176 | gdk_draw_rgb_image(widget->window, |
177 | widget->style->fg_gc[GTK_STATE_NORMAL], |
178 | 0, 0, sx(s), sy(s), GDK_RGB_DITHER_MAX, rgbbuf, sx(s)*3); |
179 | free(rgbbuf); |
180 | } |
181 | |
182 | |
183 | static void draw_image(GtkWidget *widget, struct solid *s, int osd) |
184 | { |
185 | int cx = sx(s)/2; |
186 | int cy = sy(s)/2; |
187 | int p; |
188 | |
189 | draw_map(widget, s); |
190 | has_osd = osd; |
191 | if (!osd) |
192 | return; |
193 | draw_circle(widget->window, gc_osd, cx, cy, r_center(s)); |
194 | p = r_center(s)/sqrt(2); |
195 | gdk_draw_line(widget->window, gc_osd, cx-p, cy-p, cx+p, cy+p); |
196 | gdk_draw_line(widget->window, gc_osd, cx-p, cy+p, cx+p, cy-p); |
197 | } |
198 | |
199 | |
200 | /* |
201 | * Rotate such that a point at distance "r" moves one unit. Rotate |
202 | * counter-clockwise for r > 1, clockwise for r < 0. |
203 | */ |
204 | |
205 | static void rotate(struct matrix *m, double r) |
206 | { |
207 | struct matrix t; |
208 | double s, c; |
209 | |
210 | s = 1/r; |
211 | c = sqrt(1-s*s); |
212 | t.a[0][0] = m->a[0][0]*c-m->a[1][0]*s; |
213 | t.a[0][1] = m->a[0][1]*c-m->a[1][1]*s; |
214 | t.a[1][0] = m->a[1][0]*c+m->a[0][0]*s; |
215 | t.a[1][1] = m->a[1][1]*c+m->a[0][1]*s; |
216 | t.b[0] = m->b[0]*c-m->b[1]*s; |
217 | t.b[1] = m->b[0]*s+m->b[1]*c; |
218 | *m = t; |
219 | } |
220 | |
221 | |
222 | static void do_shift(struct matrix *m, int dx, int dy) |
223 | { |
224 | m->b[0] -= dx; |
225 | m->b[1] += dy; |
226 | } |
227 | |
228 | |
229 | static void shift(struct matrix *m, int dx, int dy, int dir) |
230 | { |
231 | if (dx > 0 && dy < dx && dy > -dx) |
232 | do_shift(m, dir, 0); |
233 | if (dx < 0 && dy < -dx && dy > dx) |
234 | do_shift(m, -dir, 0); |
235 | if (dy > 0 && dx < dy && dx > -dy) |
236 | do_shift(m, 0, dir); |
237 | if (dy < 0 && dx < -dy && dx > dy) |
238 | do_shift(m, 0, -dir); |
239 | } |
240 | |
241 | |
242 | static int osd_proximity(const struct solid *s, int dx, int dy) |
243 | { |
244 | double r = hypot(dx, dy); |
245 | double rc = r_center(s); |
246 | |
247 | if (fabs(r-rc) < OSD_PROXIMITY) |
248 | return 1; |
249 | if (r > rc) |
250 | return 0; |
251 | if (abs(abs(dx)-abs(dy)) < OSD_PROXIMITY) |
252 | return 1; |
253 | return 0; |
254 | } |
255 | |
256 | |
257 | static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, |
258 | gpointer data) |
259 | { |
260 | GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget)); |
261 | struct solid *s = data; |
262 | int dx = event->x-sx(s)/2; |
263 | int dy = event->y-sy(s)/2; |
264 | double r = hypot(dx, dy); |
265 | double rc = r_center(s); |
266 | int center = r < rc; |
267 | int osd = osd_proximity(s, dx, dy); |
268 | |
269 | if (r < 1) |
270 | return TRUE; |
271 | switch (event->direction) { |
272 | case GDK_SCROLL_UP: |
273 | if (center) |
274 | shift(&s->a->m, dx, dy, 1); |
275 | else |
276 | rotate(&s->a->m, -r); |
277 | draw_image(darea, s, osd); |
278 | break; |
279 | case GDK_SCROLL_DOWN: |
280 | if (center) |
281 | shift(&s->a->m, dx, dy, -1); |
282 | else |
283 | rotate(&s->a->m, r); |
284 | draw_image(darea, s, osd); |
285 | break; |
286 | default: |
287 | /* ignore */; |
288 | } |
289 | return TRUE; |
290 | } |
291 | |
292 | |
293 | static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, |
294 | gpointer user_data) |
295 | { |
296 | draw_image(widget, user_data, has_osd); |
297 | return TRUE; |
298 | } |
299 | |
300 | |
301 | static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, |
302 | gpointer data) |
303 | { |
304 | struct solid *s = data; |
305 | int dx = event->x-sx(s)/2; |
306 | int dy = event->y-sy(s)/2; |
307 | int osd = osd_proximity(s, dx, dy); |
308 | |
309 | if (osd != has_osd) |
310 | draw_image(widget, s, osd); |
311 | return FALSE; |
312 | } |
313 | |
314 | |
315 | void overlap(GtkWidget *canvas, struct solid *s) |
316 | { |
317 | GtkWidget *evbox, *darea; |
318 | |
319 | evbox = gtk_event_box_new(); |
320 | darea = gtk_drawing_area_new(); |
321 | |
322 | gtk_widget_set_events(darea, |
323 | GDK_EXPOSE | GDK_KEY_PRESS_MASK | |
324 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | |
325 | GDK_SCROLL | |
326 | GDK_POINTER_MOTION_MASK); |
327 | |
328 | gtk_widget_set_size_request(darea, sx(s), sy(s)); |
329 | gtk_container_add(GTK_CONTAINER(canvas), evbox); |
330 | gtk_container_add(GTK_CONTAINER(evbox), darea); |
331 | |
332 | draw_image(darea, s, 0); |
333 | |
334 | g_signal_connect(G_OBJECT(evbox), "scroll-event", |
335 | G_CALLBACK(scroll_event), s); |
336 | g_signal_connect(G_OBJECT(darea), "expose-event", |
337 | G_CALLBACK(expose_event), s); |
338 | g_signal_connect(G_OBJECT(darea), "motion-notify-event", |
339 | G_CALLBACK(motion_notify_event), s); |
340 | |
341 | } |
342 |
Branches:
master