Root/solidify/level.c

1/*
2 * level.c - Interactively align a nearly horizontal plane with a face
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 <gtk/gtk.h>
18
19#include "array.h"
20#include "face.h"
21#include "gui_util.h"
22#include "style.h"
23#include "level.h"
24
25
26#define NEAR 1
27
28
29static int has_osd;
30static GtkWidget *xz, *zy;
31
32
33static double r_center(const struct face *f)
34{
35    return hypot(f->sx, f->sy)/LEVEL_CENTER_DIV;
36}
37
38
39static void draw_map(GtkWidget *widget, struct face *f)
40{
41    int x, y, z;
42    double z0;
43    guchar *rgbbuf, *p;
44
45    rgbbuf = p = calloc(f->sx*f->sy, 3);
46    if (!rgbbuf) {
47        perror("calloc");
48        exit(1);
49    }
50    for (y = f->sy-1; y >= 0; y--)
51        for (x = 0; x != f->sx ; x++) {
52            z = get(f->a, x+f->a->min_x, y+f->a->min_y);
53            if (z == UNDEF) {
54                p += 3;
55                continue;
56            }
57            z0 = face_z0(f, x, y);
58            if (fabs(z-z0) < NEAR) {
59                *p++ = 255*fabs(z-z0);
60                *p++ = 255*fabs(z-z0);
61                *p++ = 255;
62                continue;
63            }
64            if (z < z0) {
65                z = z > z0-2*NEAR ? 255*(z-z0)/NEAR :
66                    255.0*(z-z0)/(z0-f->a->min_z);
67                *p++ = 255;
68                *p++ = z;
69                *p++ = z;
70            } else {
71                z = z < z0+2*NEAR ? 255*(z0-z)/NEAR :
72                    255.0*(z0-z)/(f->a->max_z-z0);
73                *p++ = z;
74                *p++ = 255;
75                *p++ = z;
76            }
77        }
78    gdk_draw_rgb_image(widget->window,
79        widget->style->fg_gc[GTK_STATE_NORMAL],
80        0, 0, f->sx, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3);
81    free(rgbbuf);
82}
83
84
85static void draw_image(GtkWidget *widget, struct face *f, int osd)
86{
87    draw_map(widget, f);
88    has_osd = osd;
89    if (osd)
90        draw_circle(widget->window, gc_osd,
91            f->sx/2, f->sy/2, r_center(f));
92}
93
94
95static void draw_xz(GtkWidget *widget, struct face *f, int y)
96{
97    int zm = f->a->max_z;
98    int sz = f->sz*z0_scale(f)+2*PROJECTION_BORDER;
99    int x, z;
100    double z0;
101    guchar *rgbbuf, *p;
102
103    rgbbuf = p = calloc(f->sx*sz, 3);
104    if (!rgbbuf) {
105        perror("calloc");
106        exit(1);
107    }
108    if (y != -1)
109        for (x = 0; x != f->sx ; x++) {
110            z = get(f->a, x+f->a->min_x, y+f->a->min_y);
111            z0 = face_z0(f, x, y);
112            if (z == UNDEF || z == z0) {
113                aa_line(rgbbuf, x,
114                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
115                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
116                    sz-1, f->sx,
117                    (uint8_t *) "\0\0\xff", vpoint);
118                                continue;
119                        }
120            if (z > z0) {
121                aa_line(rgbbuf, x,
122                    (zm-z)*z0_scale(f)+PROJECTION_BORDER,
123                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
124                    sz-1, f->sx,
125                    (uint8_t *) "\0\xff\0", vpoint);
126            } else {
127                aa_line(rgbbuf, x,
128                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
129                    (zm-z)*z0_scale(f)+PROJECTION_BORDER,
130                    sz-1, f->sx,
131                    (uint8_t *) "\xff\0\0", vpoint);
132            }
133        }
134    gdk_draw_rgb_image(widget->window,
135        widget->style->fg_gc[GTK_STATE_NORMAL],
136        0, 0, f->sx, sz, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3);
137    free(rgbbuf);
138    
139}
140
141
142static void draw_zy(GtkWidget *widget, struct face *f, int x)
143{
144    int zm = f->a->max_z;
145    int sz = f->sz*z0_scale(f)+2*PROJECTION_BORDER;
146    int y, z;
147    double z0;
148    guchar *rgbbuf, *p;
149
150    rgbbuf = p = calloc(f->sy*sz, 3);
151    if (!rgbbuf) {
152        perror("calloc");
153        exit(1);
154    }
155    if (x != -1)
156        for (y = 0; y != f->sy ; y++) {
157            z = get(f->a, x+f->a->min_x, f->a->max_y-y);
158            z0 = face_z0(f, x, f->sy-y-1);
159            if (z == UNDEF || z == z0) {
160                aa_line(rgbbuf, y,
161                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
162                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
163                    sz-1, sz,
164                    (uint8_t *) "\0\0\xff", hpoint);
165                continue;
166            }
167            if (z > z0) {
168                aa_line(rgbbuf, y,
169                    (zm-z)*z0_scale(f)+PROJECTION_BORDER,
170                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
171                    sz-1, sz,
172                    (uint8_t *) "\0\xff\0", hpoint);
173            } else {
174                aa_line(rgbbuf, y,
175                    (zm-z0)*z0_scale(f)+PROJECTION_BORDER,
176                    (zm-z)*z0_scale(f)+PROJECTION_BORDER,
177                    sz-1, sz,
178                    (uint8_t *) "\xff\0\0", hpoint);
179            }
180        }
181    gdk_draw_rgb_image(widget->window,
182        widget->style->fg_gc[GTK_STATE_NORMAL],
183        0, 0, sz, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, sz*3);
184    free(rgbbuf);
185    
186}
187
188
189static void scroll_z(GtkWidget *darea, struct face *f, int up, int osd)
190{
191    if (up) {
192        if (f->z_ref < f->a->max_z)
193            f->z_ref++;
194    } else {
195        if (f->z_ref > f->a->min_z)
196            f->z_ref--;
197    }
198    draw_image(darea, f, osd);
199}
200
201
202static void scroll_xy(GtkWidget *darea, struct face *f, int dx, int dy, int up,
203    int osd)
204{
205    double d;
206
207    d = (double) (up ? 1 : -1)/(dx*dx+dy*dy)/2.0;
208    f->fx += d*dx;
209    f->fy += d*dy;
210    draw_image(darea, f, osd);
211}
212
213
214static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
215    gpointer data)
216{
217    GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget));
218    struct face *f = data;
219    int dx = event->x-f->sx/2;
220    int dy = event->y-f->sy/2;
221    double r = hypot(dx, dy);
222    double rc = r_center(f);
223    int center = r < rc;
224    int osd = fabs(r-rc) < OSD_PROXIMITY;
225
226    switch (event->direction) {
227    case GDK_SCROLL_UP:
228        if (center)
229            scroll_z(darea, f, 0, osd);
230        else
231            scroll_xy(darea, f, dx, dy, 1, osd);
232        break;
233    case GDK_SCROLL_DOWN:
234        if (center)
235            scroll_z(darea, f, 1, osd);
236        else
237            scroll_xy(darea, f, dx, dy, 0, osd);
238        break;
239    default:
240        /* ignore */;
241    }
242    draw_xz(xz, f, f->sy-1-event->y);
243    draw_zy(zy, f, event->x);
244    return TRUE;
245}
246
247
248static gboolean expose_event_xy(GtkWidget *widget, GdkEventExpose *event,
249    gpointer user_data)
250{
251    draw_image(widget, user_data, has_osd);
252    return TRUE;
253}
254
255
256static gboolean expose_event_xz(GtkWidget *widget, GdkEventExpose *event,
257    gpointer user_data)
258{
259    draw_xz(widget, user_data, -1);
260    return TRUE;
261}
262
263
264static gboolean expose_event_zy(GtkWidget *widget, GdkEventExpose *event,
265    gpointer user_data)
266{
267    draw_zy(widget, user_data, -1);
268    return TRUE;
269}
270
271
272static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
273    gpointer data)
274{
275    struct face *f = data;
276    int dx = event->x-f->sx/2;
277    int dy = event->y-f->sy/2;
278    double r = hypot(dx, dy);
279    double rc = r_center(f);
280    int osd = fabs(r-rc) < OSD_PROXIMITY;
281
282    if (osd != has_osd)
283        draw_image(widget, f, osd);
284    draw_xz(xz, f, f->sy-1-event->y);
285    draw_zy(zy, f, event->x);
286    return FALSE;
287}
288
289
290void level(GtkWidget *canvas, struct face *f)
291{
292    GtkWidget *evbox, *tab, *xy;
293
294    evbox = gtk_event_box_new();
295    tab = gtk_table_new(2, 2, FALSE);
296    xy = gtk_drawing_area_new();
297    xz = gtk_drawing_area_new();
298    zy = gtk_drawing_area_new();
299
300    gtk_widget_set_events(xy,
301        GDK_EXPOSE | GDK_KEY_PRESS_MASK |
302        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
303        GDK_SCROLL |
304        GDK_POINTER_MOTION_MASK);
305
306    gtk_widget_set_size_request(xy, f->sx, f->sy);
307    gtk_widget_set_size_request(xz,
308        f->sx, f->sz*z0_scale(f)+2*PROJECTION_BORDER);
309    gtk_widget_set_size_request(zy,
310        f->sz*z0_scale(f)+2*PROJECTION_BORDER, f->sy);
311
312    gtk_table_set_row_spacings(GTK_TABLE(tab), 4);
313    gtk_table_set_col_spacings(GTK_TABLE(tab), 4);
314
315    gtk_table_attach_defaults(GTK_TABLE(tab), evbox, 0, 1, 0, 1);
316    gtk_table_attach_defaults(GTK_TABLE(tab), xz, 0, 1, 1, 2);
317    gtk_table_attach_defaults(GTK_TABLE(tab), zy, 1, 2, 0, 1);
318
319    gtk_container_add(GTK_CONTAINER(canvas), tab);
320    gtk_container_add(GTK_CONTAINER(evbox), xy);
321
322    gtk_widget_show_all(canvas);
323    has_osd = 0;
324
325    g_signal_connect(G_OBJECT(evbox), "scroll-event",
326        G_CALLBACK(scroll_event), f);
327    g_signal_connect(G_OBJECT(xy), "expose-event",
328        G_CALLBACK(expose_event_xy), f);
329    g_signal_connect(G_OBJECT(xz), "expose-event",
330        G_CALLBACK(expose_event_xz), f);
331    g_signal_connect(G_OBJECT(zy), "expose-event",
332        G_CALLBACK(expose_event_zy), f);
333    g_signal_connect(G_OBJECT(xy), "motion-notify-event",
334        G_CALLBACK(motion_notify_event), f);
335}
336

Archive Download this file

Branches:
master



interactive