Root/
| 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 | |
| 29 | static int has_osd; |
| 30 | static GtkWidget *xz, *zy; |
| 31 | |
| 32 | |
| 33 | static double r_center(const struct face *f) |
| 34 | { |
| 35 | return hypot(f->sx, f->sy)/LEVEL_CENTER_DIV; |
| 36 | } |
| 37 | |
| 38 | |
| 39 | static 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 | |
| 85 | static 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 | |
| 95 | static 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 | |
| 142 | static 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 | |
| 189 | static 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 | |
| 202 | static 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 | |
| 214 | static 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 | |
| 248 | static 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 | |
| 256 | static 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 | |
| 264 | static 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 | |
| 272 | static 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 | |
| 290 | void 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 |
Branches:
master
