diff --git a/solidify/Makefile b/solidify/Makefile index 9319f18..46cd807 100644 --- a/solidify/Makefile +++ b/solidify/Makefile @@ -12,12 +12,12 @@ SHELL = /bin/bash -OBJS = array.o face.o histo.o level.o solidify.o +OBJS = array.o face.o histo.o level.o overlap.o solidify.o CFLAGS_WARN = -Wall -Wshadow -Wmissing-prototypes \ -Wmissing-declarations -Wno-format-zero-length -CFLAGS = $(CFLAGS_WARN) -g `pkg-config --cflags gtk+-2.0` +CFLAGS = $(CFLAGS_WARN) -g -O9 `pkg-config --cflags gtk+-2.0` LDFLAGS = -lm `pkg-config --libs gtk+-2.0` # ----- Verbosity control ----------------------------------------------------- diff --git a/solidify/array.c b/solidify/array.c index d7423e9..f0a05aa 100644 --- a/solidify/array.c +++ b/solidify/array.c @@ -83,3 +83,13 @@ void set(struct array *a, int x, int y, int z) a->data[x-a->min_x+(a->max_x-a->min_x+1)*(y-a->min_y)] = z; } } + + +int get_bounded(const struct array *a, int x, int y) +{ + if (x < a->min_x || x > a->max_x) + return UNDEF; + if (y < a->min_y || y > a->max_y) + return UNDEF; + return get(a, x, y); +} diff --git a/solidify/array.h b/solidify/array.h index 81a15e1..0896eaa 100644 --- a/solidify/array.h +++ b/solidify/array.h @@ -31,6 +31,7 @@ struct array *new_array(void); void free_array(struct array *a); void set(struct array *a, int x, int y, int z); +int get_bounded(const struct array *a, int x, int y); static inline int get(const struct array *a, int x, int y) diff --git a/solidify/face.c b/solidify/face.c index d3a84e5..b4f5175 100644 --- a/solidify/face.c +++ b/solidify/face.c @@ -74,11 +74,16 @@ static struct face *read_file(const char *name) f->sx = f->a->max_x-f->a->min_x+1; f->sy = f->a->max_y-f->a->min_y+1; + h = make_histo(f->a); f->z_ref = f->a->min_z+median(h); free_histo(h); f->fx = f->fy = 0; + f->m.a[0][0] = f->m.a[1][1] = 1; + f->m.a[0][1] = f->m.a[1][0] = 0; + f->m.b[0] = f->m.b[1] = 0; + fprintf(stderr, "%d-%d / %d-%d / %d-%d\n", f->a->min_x, f->a->max_x, f->a->min_y, f->a->max_y, f->a->min_z, f->a->max_z); diff --git a/solidify/face.h b/solidify/face.h index b0d5100..fefc3de 100644 --- a/solidify/face.h +++ b/solidify/face.h @@ -16,11 +16,25 @@ #include "array.h" +/* + * 2D transformation: + * + * x' = x*a[0][0]+y*a[0][1]+b[0] + * y' = x*a[1][0]+y*a[1][1]+b[1] + */ + + +struct matrix { + double a[2][2]; + double b[2]; +}; + struct face { struct array *a; int sx, sy; /* size */ int z_ref; double fx, fy; /* inclination factor */ + struct matrix m; }; diff --git a/solidify/overlap.c b/solidify/overlap.c new file mode 100644 index 0000000..a260398 --- /dev/null +++ b/solidify/overlap.c @@ -0,0 +1,188 @@ +/* + * overlap.c - Overlap two parallel faces + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include +#include +#include + +#include "face.h" +#include "overlap.h" + + +#define UNDEF_F HUGE_VAL + + +static double ramp(int z0, double w0, int z1, double w1) +{ + if (z0 != UNDEF && z1 != UNDEF) + return w0 == 0 && w1 == 0 ? z0 : z0*w0+z1*w1; + if (z0 == UNDEF && z0 == UNDEF) + return UNDEF_F; + if (z0 == UNDEF && w0 < w1) + return z1; + if (z1 == UNDEF && w0 > w1) + return z0; + return UNDEF_F; +} + + +static double zmix(struct face *f, double x, double y) +{ + int xa, xb, ya, yb; + double zx0, zx1; + + xa = floor(x); + xb = ceil(x); + ya = floor(y); + yb = ceil(y); + + zx0 = ramp( + get_bounded(f->a, xa, ya), yb-y, + get_bounded(f->a, xa, yb), y-ya); + zx1 = ramp( + get_bounded(f->a, xb, ya), yb-y, + get_bounded(f->a, xb, yb), y-ya); + + return ramp(zx0, xb-x, zx1, x-xa); +} + + +static void draw_image(GtkWidget *widget, struct face *f) +{ + guchar *rgbbuf, *p; + int x, y; + double z; + + rgbbuf = p = calloc(f->sx*f->sy, 3); + if (!rgbbuf) { + perror("calloc"); + exit(1); + } + for (y = f->sy-1; y >= 0; y--) + for (x = 0; x != f->sx ; x++) { + int xa = x+f->a->min_x; + int ya = y+f->a->min_y; + + z = zmix(f, + xa*f->m.a[0][0]+ya*f->m.a[0][1]+f->m.b[0], + xa*f->m.a[1][0]+ya*f->m.a[1][1]+f->m.b[1]); + if (z == UNDEF_F) { + p += 3; + continue; + } + z = 256.0*(z-f->a->min_z)/(f->a->max_z-f->a->min_z); + if (z < 0) + z = 0; + if (z > 255) + z = 255; + *p++ = z; + *p++ = z; + *p++ = z; + } + gdk_draw_rgb_image(widget->window, + widget->style->fg_gc[GTK_STATE_NORMAL], + 0, 0, f->sx, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3); + free(rgbbuf); +} + + +/* + * Rotate such that a point at distance "r" moves one unit. Rotate + * counter-clockwise for r > 1, clockwise for r < 0. + */ + +static void rotate(struct matrix *m, double r) +{ + struct matrix t; + double s, c; + + s = 1/r; + c = sqrt(1-s*s); + t.a[0][0] = m->a[0][0]*c-m->a[1][0]*s; + t.a[0][1] = m->a[0][1]*c-m->a[1][1]*s; + t.a[1][0] = m->a[1][0]*c+m->a[0][0]*s; + t.a[1][1] = m->a[1][1]*c+m->a[0][1]*s; + t.b[0] = m->b[0]*c-m->b[1]*s; + t.b[1] = m->b[0]*s+m->b[1]*c; + *m = t; +} + + +static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + GtkWidget *da = gtk_bin_get_child(GTK_BIN(widget)); + struct face *f = data; + int dx = event->x-f->sx/2; + int dy = event->y-f->sy/2; + double r = hypot(dx, dy); + + if (r < 1) + return TRUE; + switch (event->direction) { + case GDK_SCROLL_UP: + rotate(&f->m, r); + draw_image(da, f); + break; + case GDK_SCROLL_DOWN: + rotate(&f->m, -r); + draw_image(da, f); + break; + default: + /* ignore */; + } + return TRUE; +} + + +static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, + gpointer user_data) +{ + draw_image(widget, user_data); + return TRUE; +} + + + +static void make_screen(GtkWidget *root, struct face *f) +{ + GtkWidget *evbox, *da; + + da = gtk_drawing_area_new(); + gtk_widget_set_size_request(da, f->sx, f->sy); + evbox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(evbox), da); + gtk_container_add(GTK_CONTAINER(root), evbox); + gtk_widget_show_all(root); + draw_image(da, f); + + g_signal_connect(G_OBJECT(evbox), "scroll-event", + G_CALLBACK(scroll_event), f); + g_signal_connect(G_OBJECT(da), "expose-event", + G_CALLBACK(expose_event), f); +} + +void overlap(struct face *face) +{ + GtkWidget *root; + + root = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER); + + g_signal_connect(G_OBJECT(root), "destroy", + G_CALLBACK(gtk_main_quit), NULL); + make_screen(root, face); + gtk_main(); +} diff --git a/solidify/overlap.h b/solidify/overlap.h new file mode 100644 index 0000000..d86be11 --- /dev/null +++ b/solidify/overlap.h @@ -0,0 +1,21 @@ +/* + * overlap.h - Overlap two parallel faces + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef OVERLAP_H +#define OVERLAP_H + + +void overlap(struct face *face); + + +#endif /* !OVERLAP_H */