From e8d68370657fccd353745e0773d599db66a54be8 Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Sat, 25 Sep 2010 04:46:16 -0300 Subject: [PATCH] Initial commit. For older history, see project ben-scans. http://projects.qi-hardware.com/index.php/p/ben-scans/ --- solidify/Makefile | 90 ++++++++ solidify/array.c | 85 ++++++++ solidify/array.h | 52 +++++ solidify/face.c | 204 +++++++++++++++++++ solidify/face.h | 39 ++++ solidify/histo.c | 52 +++++ solidify/histo.h | 29 +++ solidify/level.c | 200 ++++++++++++++++++ solidify/level.h | 23 +++ solidify/main.pov | 51 +++++ solidify/matrix.c | 66 ++++++ solidify/matrix.h | 45 ++++ solidify/overlap.c | 487 ++++++++++++++++++++++++++++++++++++++++++++ solidify/overlap.h | 23 +++ solidify/povray.c | 120 +++++++++++ solidify/project.c | 192 +++++++++++++++++ solidify/project.h | 48 +++++ solidify/solid.h | 24 +++ solidify/solidify.c | 163 +++++++++++++++ solidify/style.c | 53 +++++ solidify/style.h | 33 +++ solidify/util.c | 22 ++ solidify/util.h | 38 ++++ 23 files changed, 2139 insertions(+) create mode 100644 solidify/Makefile create mode 100644 solidify/array.c create mode 100644 solidify/array.h create mode 100644 solidify/face.c create mode 100644 solidify/face.h create mode 100644 solidify/histo.c create mode 100644 solidify/histo.h create mode 100644 solidify/level.c create mode 100644 solidify/level.h create mode 100644 solidify/main.pov create mode 100644 solidify/matrix.c create mode 100644 solidify/matrix.h create mode 100644 solidify/overlap.c create mode 100644 solidify/overlap.h create mode 100644 solidify/povray.c create mode 100644 solidify/project.c create mode 100644 solidify/project.h create mode 100644 solidify/solid.h create mode 100644 solidify/solidify.c create mode 100644 solidify/style.c create mode 100644 solidify/style.h create mode 100644 solidify/util.c create mode 100644 solidify/util.h diff --git a/solidify/Makefile b/solidify/Makefile new file mode 100644 index 0000000..a36925f --- /dev/null +++ b/solidify/Makefile @@ -0,0 +1,90 @@ +# +# Makefile - Makefile of solidify +# +# 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. +# + +SHELL = /bin/bash + +OBJS = array.o face.o histo.o level.o matrix.o overlap.o povray.o project.o \ + solidify.o style.o util.o + +CFLAGS_WARN = -Wall -Wshadow -Wmissing-prototypes \ + -Wmissing-declarations -Wno-format-zero-length + +CFLAGS = $(CFLAGS_WARN) -g -O9 `pkg-config --cflags gtk+-2.0` +LDFLAGS = -lm `pkg-config --libs gtk+-2.0` + +# ----- Verbosity control ----------------------------------------------------- + +CC_normal := $(CC) +DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG + +CC_quiet = @echo " CC " $@ && $(CC_normal) +DEPEND_quiet = @$(DEPEND_normal) + +ifeq ($(V),1) + CC = $(CC_normal) + DEPEND = $(DEPEND_normal) +else + CC = $(CC_quiet) + DEPEND = $(DEPEND_quiet) +endif + +# ----- Rules ----------------------------------------------------------------- + +.PHONY: all clean spotless + +all: solidify + +solidify: $(OBJS) + +clean: + rm -f $(OBJS) $(OBJS:.o=.d) + +spotless: clean + rm -f solidify + +# ----- Experimental execution ------------------------------------------------ + +PRJ=http://projects.qi-hardware.com/index.php/p/ben-scans/source/tree/master +DIR=$(PRJ)/data/csv +FACE_A=$(DIR)/ben-batcvr-outside-100um.txt.bz2 +FACE_B=$(DIR)/ben-batcvr-inside-100um.txt.bz2 +D=1.16 + +.PHONY: new run pov disp + +new: solidify + rm -f batcvr.sfy + ./solidify batcvr.sfy $(FACE_A) $(FACE_B) $(D) >batcvr.inc + +run: solidify + ./solidify batcvr.sfy >batcvr.inc + +pov: + povray +A -W1280 -H1024 main.pov + +disp: + display main.png + +# ----- Dependencies ---------------------------------------------------------- + +# compile and generate dependencies, from fped, based on +# http://scottmcpeak.com/autodepend/autodepend.html + +%.o: %.c + $(CC) -c $(CFLAGS) $*.c -o $*.o + $(DEPEND) $*.c | \ + sed -e \ + '/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/ */:\n/g;H;}' \ + -e '$${g;p;}' -e d >$*.d; \ + [ "$${PIPESTATUS[*]}" = "0 0" ] || { rm -f $*.d; exit 1; } + +-include $(OBJS:.o=.d) diff --git a/solidify/array.c b/solidify/array.c new file mode 100644 index 0000000..d7423e9 --- /dev/null +++ b/solidify/array.c @@ -0,0 +1,85 @@ +/* + * array.c - Growable baseless 2D array + * + * 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 "util.h" +#include "array.h" + + +static void resize(struct array *a, + int nx0, int nx1, int ny0, int ny1) +{ + int ox, oy, nx, ny; + int n, x, y; + int *tmp; + + ox = a->max_x-a->min_x; + oy = a->max_y-a->min_y; + nx = nx1-nx0; + ny = ny1-ny0; + if (ox == nx && oy == ny) + return; + n = (nx+1)*(ny+1); + tmp = alloc_size(n*sizeof(int)); + for (x = 0; x != n; x++) + tmp[x] = UNDEF; + for (x = a->min_x; x <= a->max_x; x++) + for (y = a->min_y; y <= a->max_y; y++) + tmp[x-nx0+(nx+1)*(y-ny0)] = + a->data[x-a->min_x+(ox+1)*(y-a->min_y)]; + free(a->data); + a->data = tmp; + a->min_x = nx0; + a->max_x = nx1; + a->min_y = ny0; + a->max_y = ny1; +} + + +struct array *new_array(void) +{ + struct array *a; + + a = alloc_type(struct array); + a->data = NULL; + return a; +} + + +void free_array(struct array *a) +{ + free(a->data); + free(a); +} + + +void set(struct array *a, int x, int y, int z) +{ + if (!a->data) { + a->min_x = a->max_x = x; + a->min_y = a->max_y = y; + a->min_z = a->max_z = z; + a->data = alloc_type(int); + *a->data = z; + } else { + resize(a, + x < a->min_x ? x : a->min_x, x > a->max_x ? x : a->max_x, + y < a->min_y ? y : a->min_y, y > a->max_y ? y : a->max_y); + if (z < a->min_z) + a->min_z = z; + if (z > a->max_z) + a->max_z = z; + a->data[x-a->min_x+(a->max_x-a->min_x+1)*(y-a->min_y)] = z; + } +} diff --git a/solidify/array.h b/solidify/array.h new file mode 100644 index 0000000..565e0f8 --- /dev/null +++ b/solidify/array.h @@ -0,0 +1,52 @@ +/* + * array.h - Growable baseless 2D array + * + * 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 ARRAY_H +#define ARRAY_H + +#include + + +#define UNDEF INT_MAX + + +struct array { + int min_x, max_x; + int min_y, max_y; + int min_z, max_z; + int *data; /* NULL if there are no points */ +}; + + +struct array *new_array(void); +void free_array(struct array *a); + +void set(struct array *a, int x, int y, int z); + + +static inline int get(const struct array *a, int x, int y) +{ + return a->data[(x)-a->min_x+(a->max_x-a->min_x+1)*((y)-a->min_y)]; +} + + +static inline 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); +} + + +#endif /* !ARRAY_H */ diff --git a/solidify/face.c b/solidify/face.c new file mode 100644 index 0000000..d444a14 --- /dev/null +++ b/solidify/face.c @@ -0,0 +1,204 @@ +/* + * face.c - Data structure and handling of one face of a part + * + * 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 +#include + +#include "util.h" +#include "array.h" +#include "histo.h" +#include "face.h" + + +#define CACHE_DIR ".cache" +#define DEFAULT_STEP 1 /* 1 mm */ +#define MIN_STEP 0.005 /* 5 um */ + + +struct coord { + float x, y, z; +}; + + +static struct coord *load_file(const char *name) +{ + FILE *file; + struct coord *v, *n; + int s; + + if (!strcmp(name, "-")) { + file = stdin; + } else { + int len; + + len = strlen(name); + if (len > 4 && !strcmp(name+len-4, ".bz2")) { + char tmp[1000]; /* @@@ enough */ + + sprintf(tmp, "bzcat \"%s\"", name); + file = popen(tmp, "r"); + if (!file) { + perror(tmp); + exit(1); + } + } else { + file = fopen(name, "r"); + if (!file) { + perror(name); + exit(1); + } + } + } + + v = n = alloc_type(struct coord); + s = 1; + + while (fscanf(file, "%f,%f,%f\r\n", &n->x, &n->y, &n->z) == 3) { + n++; + if (n-v == s) { + struct coord *tmp; + + s += s; + tmp = realloc(v, sizeof(struct coord)*s); + if (!tmp) { + perror("realloc"); + exit(1); + } + n = n-v+tmp; + v = tmp; + } + } + if (file != stdin) + (void) fclose(file); + + n->x = n->y = n->z = 0; + return v; +} + + +static void adjust_step(double *step, double delta) +{ + double n = round(delta/MIN_STEP); + double s = n*MIN_STEP; + + if (n && s < *step) + *step = s; +} + + +static struct face *read_file(const char *name) +{ + struct coord *v, *p; + struct face *f; + struct histo *h; + int xi, yi, zi; + + v = load_file(name); + + f = alloc_type(struct face); + f->a = new_array(); + + /* + * Hack: the MDX-15 measures bumps along the x axis with 25 um + * resolution, so we just ignore the x resultion we find and use the + * y resolution instead. + */ + f->x_step = f->y_step =f->z_step = DEFAULT_STEP; + for (p = v; p[1].x || p[1].y || p[1].z; p++) { + adjust_step(&f->y_step, fabs(p[0].y-p[1].y)); + adjust_step(&f->z_step, fabs(p[0].z-p[1].z)); + } + f->x_step = f->y_step; + + for (p = v; p->x || p->y || p->z; p++) { + xi = round(p->x/f->x_step); + yi = round(p->y/f->y_step); + zi = round(p->z/f->z_step); + set(f->a, xi, yi, zi); + } + + free(v); + + f->sx = f->a->max_x-f->a->min_x+1; + f->sy = f->a->max_y-f->a->min_y+1; + + f->cx = (f->a->min_x+f->a->max_x)/2; + f->cy = (f->a->min_y+f->a->max_y)/2; + + 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, "%g %g %g\n", f->x_step, f->y_step, f->z_step); + 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); + + return f; +} + + +struct face *read_face(const char *name) +{ + const char *p; + int cwd; + struct face *face; + + if (strncmp(name, "http:", 5) && strncmp(name, "https:", 6)) + return read_file(name); + p = strrchr(name, '/'); + if (!p || !p[1]) { + fprintf(stderr, "malformed URL: \"%s\"\n", name); + exit(1); + } + cwd = open(".", O_RDONLY); + if (cwd < 0) { + perror("."); + exit(1); + } + if (chdir(CACHE_DIR) < 0) { + perror(CACHE_DIR); + exit(1); + } + if (access(p+1, R_OK) < 0) { + char tmp[1000]; /* @@@ enough */ + int res; + + sprintf(tmp, "wget '%s'", name); + res = system(tmp); + if (res < 0) { + perror("system"); + exit(1); + } + if (!WIFEXITED(res) || WEXITSTATUS(res)) { + fprintf(stderr, "%s: status %d\n", tmp, res); + exit(1); + } + } + face = read_file(p+1); + if (fchdir(cwd) < 0) { + perror("fchdir"); + exit(1); + } + return face; +} diff --git a/solidify/face.h b/solidify/face.h new file mode 100644 index 0000000..46fba86 --- /dev/null +++ b/solidify/face.h @@ -0,0 +1,39 @@ +/* + * face.h - Data structure and handling of one face of a part + * + * 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 FACE_H +#define FACE_H + +#include "array.h" +#include "matrix.h" + + +struct face { + struct array *a; + double x_step, y_step, z_step; + int sx, sy; /* size */ + int cx, cy; /* center */ + int z_ref; + double fx, fy; /* inclination factor */ + struct matrix m; +}; + + +static inline double face_z0(const struct face *f, int x, int y) +{ + return f->z_ref+f->fx*(x-f->sx/2)+f->fy*(y-f->sy/2); +} + + +struct face *read_face(const char *name); + +#endif /* !FACE_H */ diff --git a/solidify/histo.c b/solidify/histo.c new file mode 100644 index 0000000..46f9d92 --- /dev/null +++ b/solidify/histo.c @@ -0,0 +1,52 @@ +/* + * histo.c - Distribution of Z values + * + * 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 "util.h" +#include "array.h" +#include "histo.h" + + +struct histo *make_histo(struct array *a) +{ + int i; + struct histo *h; + + h = alloc_type(struct histo); + h->n = a->max_z-a->min_z+1; + h->b = alloc_size(h->n*sizeof(int)); + for (i = 0; i != h->n; i++) + h->b[i] = 0; + for (i = 0; i != (a->max_x-a->min_x+1)*(a->max_y-a->min_y+1); i++) + if (a->data[i] != UNDEF) + h->b[a->data[i]-a->min_z]++; + return h; +} + + +void free_histo(struct histo *h) +{ + free(h); +} + + +int median(const struct histo *h) +{ + int tot = 0, sum = 0; + int i; + + for (i = 0; i != h->n; i++) + tot += h->b[i]; + for (i = 0; sum < tot/2; i++) + sum += h->b[i]; + return i-1; +} diff --git a/solidify/histo.h b/solidify/histo.h new file mode 100644 index 0000000..34269d2 --- /dev/null +++ b/solidify/histo.h @@ -0,0 +1,29 @@ +/* + * histo.h - Distribution of Z values + * + * 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 HISTO_H +#define HISTO_H + +#include "array.h" + + +struct histo { + int n; /* number of bins */ + int *b; /* bins */ +}; + + +struct histo *make_histo(struct array *a); +void free_histo(struct histo *h); +int median(const struct histo *h); + +#endif /* !HISTO_H */ diff --git a/solidify/level.c b/solidify/level.c new file mode 100644 index 0000000..882253b --- /dev/null +++ b/solidify/level.c @@ -0,0 +1,200 @@ +/* + * level.c - Interactively align a nearly horizontal plane with a face + * + * 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 "util.h" +#include "array.h" +#include "face.h" +#include "style.h" +#include "level.h" + + +#define NEAR 1 + + +static int has_osd; + + +static double r_center(const struct face *f) +{ + return hypot(f->sx, f->sy)/LEVEL_CENTER_DIV; +} + + +static void draw_map(GtkWidget *widget, struct face *f) +{ + int x, y, z; + double z0; + guchar *rgbbuf, *p; + + 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++) { + z = get(f->a, x+f->a->min_x, y+f->a->min_y); + if (z == UNDEF) { + p += 3; + continue; + } + z0 = face_z0(f, x, y); + if (fabs(z-z0) < NEAR) { + *p++ = 255*fabs(z-z0); + *p++ = 255*fabs(z-z0); + *p++ = 255; + continue; + } + if (z < z0) { + z = z > z0-2*NEAR ? 255*(z-z0)/NEAR : + 255.0*(z-z0)/(z0-f->a->min_z); + *p++ = 255; + *p++ = z; + *p++ = z; + } else { + z = z < z0+2*NEAR ? 255*(z0-z)/NEAR : + 255.0*(z0-z)/(f->a->max_z-z0); + *p++ = z; + *p++ = 255; + *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); +} + + +static void draw_image(GtkWidget *widget, struct face *f, int osd) +{ + draw_map(widget, f); + has_osd = osd; + if (osd) + draw_circle(widget->window, gc_osd, + f->sx/2, f->sy/2, r_center(f)); +} + + +static void scroll_z(GtkWidget *darea, struct face *f, int up, int osd) +{ + if (up) { + if (f->z_ref < f->a->max_z) + f->z_ref++; + } else { + if (f->z_ref > f->a->min_z) + f->z_ref--; + } + draw_image(darea, f, osd); +} + + +static void scroll_xy(GtkWidget *darea, struct face *f, int dx, int dy, int up, + int osd) +{ + double d; + + d = (double) (up ? 1 : -1)/(dx*dx+dy*dy)/2.0; + f->fx += d*dx; + f->fy += d*dy; + draw_image(darea, f, osd); +} + + +static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + GtkWidget *darea = 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); + double rc = r_center(f); + int center = r < rc; + int osd = fabs(r-rc) < OSD_PROXIMITY; + + switch (event->direction) { + case GDK_SCROLL_UP: + if (center) + scroll_z(darea, f, 0, osd); + else + scroll_xy(darea, f, dx, dy, 1, osd); + break; + case GDK_SCROLL_DOWN: + if (center) + scroll_z(darea, f, 1, osd); + else + scroll_xy(darea, f, dx, dy, 0, osd); + break; + default: + /* ignore */; + } + return TRUE; +} + + +static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, + gpointer user_data) +{ + draw_image(widget, user_data, has_osd); + return TRUE; +} + + +static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, + gpointer data) +{ + struct face *f = data; + int dx = event->x-f->sx/2; + int dy = event->y-f->sy/2; + double r = hypot(dx, dy); + double rc = r_center(f); + int osd = fabs(r-rc) < OSD_PROXIMITY; + + if (osd != has_osd) + draw_image(widget, f, osd); + return FALSE; +} + + +void level(GtkWidget *canvas, struct face *f) +{ + GtkWidget *evbox, *darea; + + evbox = gtk_event_box_new(); + darea = gtk_drawing_area_new(); + + gtk_widget_set_events(darea, + GDK_EXPOSE | GDK_KEY_PRESS_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL | + GDK_POINTER_MOTION_MASK); + + gtk_widget_set_size_request(darea, f->sx, f->sy); + gtk_container_add(GTK_CONTAINER(canvas), evbox); + gtk_container_add(GTK_CONTAINER(evbox), darea); + + draw_image(darea, f, 0); + + g_signal_connect(G_OBJECT(evbox), "scroll-event", + G_CALLBACK(scroll_event), f); + g_signal_connect(G_OBJECT(darea), "expose-event", + G_CALLBACK(expose_event), f); + g_signal_connect(G_OBJECT(darea), "motion-notify-event", + G_CALLBACK(motion_notify_event), f); +} diff --git a/solidify/level.h b/solidify/level.h new file mode 100644 index 0000000..9a6f7f5 --- /dev/null +++ b/solidify/level.h @@ -0,0 +1,23 @@ +/* + * level.h - Interactively align a nearly horizontal plane with a face + * + * 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 LEVEL_H +#define LEVEL_H + +#include + +#include "face.h" + + +void level(GtkWidget *canvas, struct face *f); + +#endif /* !LEVEL_H */ diff --git a/solidify/main.pov b/solidify/main.pov new file mode 100644 index 0000000..44b79e7 --- /dev/null +++ b/solidify/main.pov @@ -0,0 +1,51 @@ +#include "colors.inc" +#include "batcvr.inc" + +/* + * POV-Ray defaults to a "camera" coordinate system that can be confusing. + * We use a traditional mathematical/engineering view, with a view from + * X-/Y-/Z+ into the X/Y plane. + */ + +camera { + location <-30, -80, 40> + look_at <20, 20, 0> + sky z + right -4/3*x +} + +background { color White } + +light_source { + <-200, -300, 200> + color White +} + +/* + * Mark the coordinate axes: + * - a red unit sphere at the center + * - three green spheres at x = 10*i + * - two blue spheres at y = 10*i + * - one yellow sphere at z = 10 + */ + +sphere { < 0, 0, 0>, 1 pigment { color Red } } +sphere { <10, 0, 0>, 1 pigment { color Green } } +sphere { <20, 0, 0>, 1 pigment { color Green } } +sphere { <30, 0, 0>, 1 pigment { color Green } } +sphere { < 0, 10, 0>, 1 pigment { color Blue } } +sphere { < 0, 20, 0>, 1 pigment { color Blue } } +sphere { < 0, 0, 10>, 1 pigment { color Yellow } } + +#declare Finish = finish { + brilliance 2 + phong 0.8 + phong_size 100 + metallic +} + +union { + Part_batcvr + pigment { rgbf <0.9, 0.9, 0.9, 0.5> } + finish { Finish } +} diff --git a/solidify/matrix.c b/solidify/matrix.c new file mode 100644 index 0000000..63e90fd --- /dev/null +++ b/solidify/matrix.c @@ -0,0 +1,66 @@ +/* + * matrix.c - 2D matrix operations + * + * 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 "matrix.h" + + +void matrix_identity(struct matrix *m) +{ + m->a[0][0] = m->a[1][1] = 1; + m->a[0][1] = m->a[1][0] = 0; + m->b[0] = m->b[1] = 0; +} + + +void matrix_invert(const double m[2][2], double res[2][2]) +{ + double det = m[0][0]*m[1][1]-m[0][1]*m[1][0]; + + assert(res != (void *) m); + res[0][0] = m[1][1]/det; + res[0][1] = -m[0][1]/det; + res[1][0] = -m[1][0]/det; + res[1][1] = m[0][0]/det; +} + + +void matrix_mult(double a[2][2], double b[2][2], double res[2][2]) +{ + assert(res != a); + assert(res != b); + res[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0]; + res[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1]; + res[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0]; + res[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1]; +} + + +void matrix_multv(const double v[2], double m[2][2], double res[2]) +{ + double tmp; + + tmp = v[0]*m[0][0]+v[1]*m[0][1]; + res[1] = v[0]*m[1][0]+v[1]*m[1][1]; + res[0] = tmp; +} + + +void matrix_copy(double from[2][2], double to[2][2]) +{ + to[0][0] = from[0][0]; + to[0][1] = from[0][1]; + to[1][0] = from[1][0]; + to[1][1] = from[1][1]; +} diff --git a/solidify/matrix.h b/solidify/matrix.h new file mode 100644 index 0000000..4b83404 --- /dev/null +++ b/solidify/matrix.h @@ -0,0 +1,45 @@ +/* + * matrix.h - 2D matrix operations + * + * 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 MATRIX_H +#define MATRIX_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]; +}; + + +void matrix_identity(struct matrix *m); +void matrix_invert(const double m[2][2], double res[2][2]); +void matrix_mult(double a[2][2], double b[2][2], double res[2][2]); +void matrix_multv(const double v[2], double m[2][2], double res[2]); +void matrix_copy(double from[2][2], double to[2][2]); + + +static inline void matrix_map(int x, int y, const struct matrix *m, + double *res_x, double *res_y) +{ + *res_x = x*m->a[0][0]+y*m->a[0][1]+m->b[0]; + *res_y = x*m->a[1][0]+y*m->a[1][1]+m->b[1]; + +} + +#endif /* !MATRIX_H */ diff --git a/solidify/overlap.c b/solidify/overlap.c new file mode 100644 index 0000000..4ecec71 --- /dev/null +++ b/solidify/overlap.c @@ -0,0 +1,487 @@ +/* + * 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 "util.h" +#include "face.h" +#include "solid.h" +#include "style.h" +#include "overlap.h" + + +#define UNDEF_F HUGE_VAL + + +static int has_osd; + + +static int sx(const struct solid *s) +{ + return (s->a->sx > s->b->sx ? s->a->sx : s->b->sx)+2*OVERLAP_BORDER; +} + + +static int sy(const struct solid *s) +{ + return (s->a->sy > s->b->sy ? s->a->sy : s->b->sy)+2*OVERLAP_BORDER; +} + + +static double r_center(const struct solid *s) +{ + return hypot(sx(s), sy(s))/OVERLAP_CENTER_DIV; +} + + +static double ramp(int z0, double w0, int z1, double w1) +{ + if (z0 != UNDEF && z1 != UNDEF) + return 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 = xa+1; + ya = floor(y); + yb = ya+1; + + 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); +} + + +/* + * Coordinate transformations, on the example of the x coordinate: + * + * - the x coordinate runs from 0 to sx(s)-1 + * - since we work relative to the screen center, this becomes x-sx(s)/2 + * This is what we perform the coordinate transform on. + * - our model runs from min_x to max_x. Its center is at cx. + */ + +static void point(const struct solid *s, int x, int y, guchar *p, + const struct matrix *ma, const struct matrix *mb) +{ + double za, zb, z; + double xaf, xbf, yaf, ybf; + + matrix_map(x, y, ma, &xaf, &yaf); + matrix_map(x, y, mb, &xbf, &ybf); + + za = zmix(s->a, xaf, yaf); + zb = zmix(s->b, xbf, ybf); + + if (za == UNDEF_F && zb == UNDEF_F) + return; + + if (za == UNDEF_F) { + z = 128.0*(zb-s->b->a->min_z)/(s->b->a->max_z-s->b->a->min_z); + if (z < 0) + z = 0; + if (z > 255) + z = 255; + p[0] = 255; + p[1] = z; + p[2] = z; + return; + } + if (zb == UNDEF_F) { + z = 128.0*(za-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); + if (z < 0) + z = 0; + if (z > 255) + z = 255; + p[0] = z; + p[1] = 255; + p[2] = z; + return; + } + + z = za; + za -= face_z0(s->a, xaf, yaf); + zb -= face_z0(s->b, xbf, ybf); + + if (za+zb < -s->dist) { + p[0] = 0; + p[1] = 0; + p[2] = 255; + return; + } + + z = 256.0*(z-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z); + if (z < 0) + z = 0; + if (z > 255) + z = 255; + p[0] = z; + p[1] = z; + p[2] = z; +} + + +static void merge_matrix(struct matrix *m, const struct solid *s, + const struct face *f) +{ + double tm[2][2], tm2[2][2]; + double tv[2]; + double f_x, f_y; + + /* + * Finally, we convert to model matrix coordinates. + * + * v' = v+c + */ + + m->b[0] += f->cx; + m->b[1] += f->cy; + + /* + * Apply shrinkage caused by rotation out of z0. + * We need to divide by x = cos a. We have f = tan a. + * With sin^2 a + cos^2 a = 1, we get + * + * f = sqrt(1-cos^2 a)/cos a + * = sqrt(1-x^2)/x + * f^2 = 1/x^2-1 + * 1/(f^2+1) = x^2 + * cos a = sqrt(1/(f^2+1)) + */ + + f_x = sqrt(f->fx*f->fx+1); + f_y = sqrt(f->fy*f->fy+1); + + m->a[0][0] *= f_x; + m->a[0][1] *= f_x; + m->b[0] *= f_x; + m->a[1][0] *= f_y; + m->a[1][1] *= f_y; + m->b[1] *= f_y; + + /* + * The transformation matrix f->m describes a transformation of + * (centered) model coordinates. We therefore have to reverse it: + * + * v = v'A+b + * v-b = v'A + * (v-b)A^-1 = v' + * vA^-1-bA^-1 = v' + */ + + matrix_invert(f->m.a, tm); + matrix_multv(f->m.b, tm, tv); + tv[0] = -tv[0]; + tv[1] = -tv[1]; + + /* + * Merge with the transformations we have so far: + * + * v' = vA1+b1 the transformation we have so far + * v'' = v'A2+b2 the transformation we apply + * + * v'' = (vA1+b1)A2+b2 + * v'' = vA1A2+b1A2+b2 + */ + + /* + * So far, the theory. To make it really work, we have to calculate + * v'' = vA1A2+b1+b2 + * duh ?!? + */ + + matrix_mult(m->a, tm, tm2); /* A1A2 */ + matrix_copy(tm2, m->a); +// matrix_multv(m->b, tm, m->b); /* b1A2 */ + m->b[0] += tv[0]; /* b2 */ + m->b[1] += tv[1]; + + /* + * Our input is a screen coordinate, its origin is in a corner so we + * first have to make it center-based: + * + * v' = (v-s/2)A+b + * v' = vA+(b-s/2*A) + */ + + tv[0] = sx(s)/2; + tv[1] = sy(s)/2; + matrix_multv(tv, m->a, tv); + m->b[0] -= tv[0]; + m->b[1] -= tv[1]; +} + + +static void draw_map(GtkWidget *widget, struct solid *s) +{ + guchar *rgbbuf, *p; + int x, y; + struct matrix ma = { + .a = { { 1, 0 }, { 0, 1 } }, + .b = { 0, 0 }, + }; + struct matrix mb = { + .a = { { -1, 0 }, { 0, 1 } }, + .b = { 0, 0 }, + }; + + rgbbuf = p = calloc(sx(s)*sy(s), 3); + if (!rgbbuf) { + perror("calloc"); + exit(1); + } + + merge_matrix(&ma, s, s->a); + merge_matrix(&mb, s, s->b); + + for (y = sy(s)-1; y >= 0; y--) + for (x = 0; x != sx(s) ; x++) { + point(s, x, y, p, &ma, &mb); + p += 3; + } + gdk_draw_rgb_image(widget->window, + widget->style->fg_gc[GTK_STATE_NORMAL], + 0, 0, sx(s), sy(s), GDK_RGB_DITHER_MAX, rgbbuf, sx(s)*3); + free(rgbbuf); +} + + +static void draw_image(GtkWidget *widget, struct solid *s, int osd) +{ + int cx = sx(s)/2; + int cy = sy(s)/2; + int p; + + draw_map(widget, s); + has_osd = osd; + if (!osd) + return; + draw_circle(widget->window, gc_osd, cx, cy, r_center(s)); + p = r_center(s)/sqrt(2); + gdk_draw_line(widget->window, gc_osd, cx-p, cy-p, cx+p, cy+p); + gdk_draw_line(widget->window, gc_osd, cx-p, cy+p, cx+p, cy-p); +} + + +/* + * 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 void do_shift(struct matrix *m, double dx, double dy) +{ + m->b[0] += dx; + m->b[1] += dy; +} + + +static void shift(struct matrix *m, int dx, int dy, double dist) +{ + /* + * Wheeling "up" in each quadrant shifts in the respective direction, + * wheeling "down" in the opposite direction. + * + * No rule without exception: we treat the "down" quadrant like the + * "up" quadrant, because it would be extremely counter-intuitive to + * wheel "up" to move "down". + */ + + if (dx > 0 && dy < dx && dy > -dx) + do_shift(m, dist, 0); + if (dx < 0 && dy < -dx && dy > dx) + do_shift(m, -dist, 0); + if (dy > 0 && dx < dy && dx > -dy) + do_shift(m, 0, dist); + if (dy < 0 && dx < -dy && dx > dy) + do_shift(m, 0, dist); /* exception ! */ +} + + +static int osd_proximity(const struct solid *s, int dx, int dy) +{ + double r = hypot(dx, dy); + double rc = r_center(s); + + if (fabs(r-rc) < OSD_PROXIMITY) + return 1; + if (r > rc) + return 0; + if (abs(abs(dx)-abs(dy)) < OSD_PROXIMITY) + return 1; + return 0; +} + + +static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget)); + struct solid *s = data; + int dx = event->x-sx(s)/2; + int dy = event->y-sy(s)/2; + double r = hypot(dx, dy); + double rc = r_center(s); + double rs, rot, dist; + int center = r < rc; + int osd = osd_proximity(s, dx, dy); + + if (r < 1) + return TRUE; + + /* + * rot goes exponentially from SLOWEST_ROT*rs to FASTEST_ROT for + * r = rc to rs, with rs being half the canvas diagonal. + * + * The values are picked such that we achieve sufficient precision at + * a reasonably large distance from the circle (for accidently entering + * the circle would change the mode) but can also spin quickly, e.g., + * when a 180 degrees rotation is needed. + * + * First, normalize to 0 ... 1 + * Then, we start at exp(0) and end at + * exp(ln(SLOWEST_ROT*rs/FASTEST_ROT))) + */ + rs = hypot(sx(s), sy(s))/2; + rot = (r-rc)/(rs-rc); + rot = SLOWEST_ROT*rs*exp(-rot*log(SLOWEST_ROT*rs/FASTEST_ROT)); + + /* + * dist stays at 1 from 0...rc/DIST_STEPS, then linearly goes up to + * DIST_STEPS from rc/DIST_STEPS...rc + */ + dist = r/rc*DIST_STEPS; + if (dist < 0) + dist = 1; + + switch (event->direction) { + case GDK_SCROLL_UP: + if (center) + shift(&s->a->m, dx, dy, dist); + else + rotate(&s->a->m, dx > 0 ? rot : -rot); + draw_image(darea, s, osd); + break; + case GDK_SCROLL_DOWN: + if (center) + shift(&s->a->m, dx, dy, -dist); + else + rotate(&s->a->m, dx > 0 ? -rot : rot); + draw_image(darea, s, osd); + break; + default: + /* ignore */; + } + return TRUE; +} + + +static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, + gpointer user_data) +{ + draw_image(widget, user_data, has_osd); + return TRUE; +} + + +static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, + gpointer data) +{ + struct solid *s = data; + int dx = event->x-sx(s)/2; + int dy = event->y-sy(s)/2; + int osd = osd_proximity(s, dx, dy); + + if (osd != has_osd) + draw_image(widget, s, osd); + return FALSE; +} + + +void overlap(GtkWidget *canvas, struct solid *s) +{ + GtkWidget *evbox, *darea; + + evbox = gtk_event_box_new(); + darea = gtk_drawing_area_new(); + + gtk_widget_set_events(darea, + GDK_EXPOSE | GDK_KEY_PRESS_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL | + GDK_POINTER_MOTION_MASK); + + gtk_widget_set_size_request(darea, sx(s), sy(s)); + gtk_container_add(GTK_CONTAINER(canvas), evbox); + gtk_container_add(GTK_CONTAINER(evbox), darea); + + draw_image(darea, s, 0); + + g_signal_connect(G_OBJECT(evbox), "scroll-event", + G_CALLBACK(scroll_event), s); + g_signal_connect(G_OBJECT(darea), "expose-event", + G_CALLBACK(expose_event), s); + g_signal_connect(G_OBJECT(darea), "motion-notify-event", + G_CALLBACK(motion_notify_event), s); + +if (0) { +int i; +long t0 = time(NULL); +gtk_widget_show_all(canvas); +for (i = 0; i != 1000; i++) { + rotate(&s->a->m, 100); + draw_image(darea, s, 0); + while (gtk_events_pending()) + gtk_main_iteration(); +} +fprintf(stderr, "%lu\n", time(NULL)-t0); +} + +} diff --git a/solidify/overlap.h b/solidify/overlap.h new file mode 100644 index 0000000..f21bd79 --- /dev/null +++ b/solidify/overlap.h @@ -0,0 +1,23 @@ +/* + * 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 + +#include + +#include "solid.h" + + +void overlap(GtkWidget *canvas, struct solid *solid); + +#endif /* !OVERLAP_H */ diff --git a/solidify/povray.c b/solidify/povray.c new file mode 100644 index 0000000..b31b853 --- /dev/null +++ b/solidify/povray.c @@ -0,0 +1,120 @@ +/* + * povray.c - Generate POV-Ray output + * + * 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 "face.h" +#include "solid.h" + + +static void height_field(const char *name, const struct face *f, + const struct matrix *m) +{ + FILE *file; + int x, y; + int z; + uint16_t g; + uint8_t v[2]; + + file = fopen(name, "w"); + if (!file) { + perror(name); + exit(1); + } + fprintf(file, "P5\n%d %d\n65535\n", f->sx, f->sy); + for (y = 0; y != f->sy; y++) + for (x = 0; x != f->sx; x++) { + z = get(f->a, x+f->a->min_x, y+f->a->min_y); + g = z == UNDEF ? 0 : + 65535*(z-f->a->min_z)/(f->a->max_z-f->a->min_z); + v[0] = g >> 8; + v[1] = g; + fwrite(v, 2, 1, file); + } + fclose(file); +} + + +static void sanitize(const char *s, char *res) +{ + while (*s) { + if ((*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z') || + (*s >= '0' && *s <= '9') || *s == '_') + *res = *s; + else + *res = '_'; + res++; + s++; + } + *res = 0; +} + + +/* + * For now, we put the part such that its x/y/z center is at the origin. + * Later, we will also have to consider the changes the user made and the + * relation with the opposing face. + */ + +static void povray_face(const struct face *f, const char *side, + const char *prefix, int flip, double dist) +{ + int sz = f->a->max_z-f->a->min_z; + + /* + * 1/65535 = 0.000015..., so we set the water level a bit lower, e.g., + * to 0.0001 + */ + printf( +"\theight_field {\n" +"\t pgm \"%s-%s.pgm\"\n" +"\t water_level 0.00001\n" +"\t smooth\n" +"\t scale <%g, %g, %g>\n" +"\t rotate <90, 0, 0>\n" +"\t translate <%g, %g, %g>\n" +"\t translate <0, 0, %g>\n" +"%s" /* flip bottom face */ +"\t translate <0, 0, %g>\n" +"\t}\n", prefix, side, + f->sx*f->x_step, sz*f->z_step, f->sy*f->y_step, + -f->sx*f->x_step/2, f->sy*f->y_step/2, f->a->min_z*f->z_step, + -f->z_ref*f->z_step, + flip ? "\t rotate <180, 0, 0>\n" : "", + dist*f->z_step); +} + + +void povray(const char *name, const struct solid *s) +{ + struct matrix m; + char tmp[1000]; /* @@@ enough */ + + m.a[0][0] = m.a[1][1] = 1; + m.a[0][1] = m.a[1][0] = 0; + m.b[0] = m.b[1] = 0; + + sprintf(tmp, "%s-top.pgm", name); + height_field(tmp, s->a, &m); + sprintf(tmp, "%s-bot.pgm", name); + height_field(tmp, s->b, &m); + + sanitize(name, tmp); + printf("#declare Part_%s =\n intersection {\n", tmp); + povray_face(s->a, "top", name, 0, s->dist/2); + povray_face(s->b, "bot", name, 1, -s->dist/2); + printf(" }\n"); +} diff --git a/solidify/project.c b/solidify/project.c new file mode 100644 index 0000000..792d3fc --- /dev/null +++ b/solidify/project.c @@ -0,0 +1,192 @@ +/* + * project.c - Load and save solidify project descriptions + * + * 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 "util.h" +#include "face.h" +#include "solid.h" +#include "project.h" + + +static struct project *make_project(const char *name, + const char *top, const char *bottom, double dist_mm) +{ + struct project *prj; + + prj = alloc_type(struct project); + prj->name = stralloc(name); + prj->top = stralloc(top); + prj->bottom = stralloc(bottom); + prj->s.a = read_face(top); + prj->s.b = read_face(bottom); + + if (prj->s.a->x_step != prj->s.a->x_step || + prj->s.a->y_step != prj->s.a->y_step || + prj->s.a->z_step != prj->s.a->z_step) { + fprintf(stderr, "both faces must have the same resolution\n"); + exit(1); + } + + prj->s.dist = dist_mm/prj->s.a->z_step; + return prj; +} + + +struct project *new_project(const char *name, + const char *top, const char *bottom, double dist_mm) +{ + FILE *file; + + file = fopen(name, "r"); + if (file) { + fprintf(stderr, "%s: already exists\n", name); + exit(1); + } + return make_project(name, top, bottom, dist_mm); +} + + +static void read_face_data(FILE *file, struct face *f) +{ + float v; + + if (fscanf(file, "%f", &v) != 1) + return; + f->z_ref = v/f->z_step; + + if (fscanf(file, "%f", &v) != 1) + return; + f->fx = tan(v/180*M_PI); + + if (fscanf(file, "%f", &v) != 1) + return; + f->fy = tan(v/180*M_PI); + + if (fscanf(file, "%f", &v) != 1) + return; + v = v/180*M_PI; + f->m.a[0][0] = cos(v); + f->m.a[0][1] = -sin(v); + f->m.a[1][0] = sin(v); + f->m.a[1][1] = cos(v); + + if (fscanf(file, "%f", &v) != 1) + return; + f->m.b[0] = v/f->x_step; + + if (fscanf(file, "%f", &v) != 1) + return; + f->m.b[1] = v/f->y_step; +} + + +static void read_optional(FILE *file, struct project *prj) +{ + float v; + + if (fscanf(file, "%f", &v) != 1) + return; + prj->s.dist = v/prj->s.a->z_step; + + read_face_data(file, prj->s.a); + read_face_data(file, prj->s.b); +} + + +struct project *load_project(const char *name) +{ + FILE *file; + char top[1000], bottom[1000]; /* @@@ enough */ + struct project *prj; + + file = fopen(name, "r"); + if (!file) { + perror(name); + exit(1); + } + + if (!fgets(top, sizeof(top), file)) { + fprintf(stderr, "%s: can't read name of top face\n", name); + exit(1); + } + if (strchr(top, '\n')) + *strchr(top, '\n') = 0; + + if (!fgets(bottom, sizeof(bottom), file)) { + fprintf(stderr, "%s: can't read name of bottom face\n", + bottom); + exit(1); + } + if (strchr(bottom, '\n')) + *strchr(bottom, '\n') = 0; + + prj = make_project(name, top, bottom, 0); + + read_optional(file, prj); + + return prj; +} + + +static void save_face_data(FILE *file, const char *name, const struct face *f) +{ + double a; + + a = asin(-f->m.a[0][1])/M_PI*180; + if (f->m.a[0][0] < 0) + a = 180-a; + if (fprintf(file, "%g %g %g\n%g %g %g\n", + f->z_ref*f->z_step, + atan(f->fx)/M_PI*180, atan(f->fy)/M_PI*180, + a, f->m.b[0]*f->x_step, f->m.b[1]*f->y_step) < 0) { + perror(name); + exit(1); + } +} + + +void save_project(const struct project *prj) +{ + char tmp[1000]; /* @@@ enough */ + FILE *file; + + sprintf(tmp, "%s~", prj->name); + file = fopen(tmp, "w"); + if (!file) { + perror(tmp); + exit(1); + } + if (fprintf(file, + "%s\n%s\n%g\n", prj->top, prj->bottom, + prj->s.dist*prj->s.a->z_step) < 0) { + perror(tmp); + exit(1); + } + save_face_data(file, tmp, prj->s.a); + save_face_data(file, tmp, prj->s.b); + if (fclose(file) < 0) { + perror(tmp); + exit(1); + } + + if (rename(tmp, prj->name) < 0) { + fprintf(stderr, "rename %s to %s: %s\n", tmp, prj->name, + strerror(errno)); + exit(1); + } +} diff --git a/solidify/project.h b/solidify/project.h new file mode 100644 index 0000000..cf7b502 --- /dev/null +++ b/solidify/project.h @@ -0,0 +1,48 @@ +/* + * project.h - Load and save solidify project descriptions + * + * 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. + */ + +/* + * Project file structure: + * + * line 1: file name of top face (required) + * line 2: file name of bottom face (required) + * line 3 and beyond, separated by whitespace: + * - z distance between faces, in mm + * - z distance of the z0 plane from the midpoint of the top face, in mm + * - inclination of the x axis of the z0 plane of the top face, in degrees + * - inclination of the y axis of the z0 plane of the top face, in degrees + * - rotation of the top face, in degrees + * - x shift of the top face, in mm + * - y shift of the top face, in mm + * - the above 6 fields for the bottom face + */ + +#ifndef PROJECT_H +#define PROJECT_H + +#include "solid.h" + + +struct project { + const char *name; + const char *top; + const char *bottom; + struct solid s; +}; + + +struct project *new_project(const char *name, + const char *top, const char *bottom, double dist_mm); +struct project *load_project(const char *name); +void save_project(const struct project *prj); + +#endif /* !PROJECT_H */ diff --git a/solidify/solid.h b/solidify/solid.h new file mode 100644 index 0000000..06a4ba8 --- /dev/null +++ b/solidify/solid.h @@ -0,0 +1,24 @@ +/* + * solid.h - Data structure and handling of a solid made of two opposing 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 SOLID_H +#define SOLID_H + +struct solid { + struct face *a, *b; + double dist; +}; + + +void povray(const char *name, const struct solid *s); + +#endif /* !SOLID_H */ diff --git a/solidify/solidify.c b/solidify/solidify.c new file mode 100644 index 0000000..0fc41f7 --- /dev/null +++ b/solidify/solidify.c @@ -0,0 +1,163 @@ +/* + * solidify.c - Merge two opposing faces of a part into a solid + * + * Written 2010 by Werner Almesberger + * Copyright 2010 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 + +#include "face.h" +#include "solid.h" +#include "project.h" +#include "style.h" +#include "level.h" +#include "overlap.h" + + +static struct project *prj; +static const struct face *active; /* NULL if overlapping */ +static GtkWidget *canvas; + + +static void clicked(GtkButton *button, gpointer user_data) +{ + struct face *face = user_data; + + if (active == face) + return; + + gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(canvas))); + + if (face) + level(canvas, face); + else + overlap(canvas, &prj->s); + active = face; + + gtk_widget_show_all(canvas); +} + + +static GtkWidget *gui_buttons(void) +{ + GtkWidget *vbox, *but; + + vbox = gtk_vbox_new(FALSE, 0); + + but = gtk_button_new_with_label("A"); + gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(but), "clicked", + G_CALLBACK(clicked), prj->s.a); + + but = gtk_button_new_with_label("B"); + gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(but), "clicked", + G_CALLBACK(clicked), prj->s.b); + + but = gtk_button_new_with_label("A+B"); + gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(but), "clicked", + G_CALLBACK(clicked), NULL); + + return vbox; +} + + +static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + if (event->keyval == 'q') + gtk_main_quit(); + return TRUE; +} + + +static void gui(void) +{ + GtkWidget *root, *hbox, *buttons; + + root = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(root), hbox); + + canvas = gtk_event_box_new(); + gtk_box_pack_start(GTK_BOX(hbox), canvas, FALSE, FALSE, 0); + + /* initialize root->window */ + gtk_widget_show_all(root); + + buttons = gui_buttons(); + gtk_box_pack_start(GTK_BOX(hbox), buttons, FALSE, FALSE, 0); + + level(canvas, prj->s.a); + active = prj->s.a; + + init_style(root->window); + + gtk_widget_show_all(root); + + g_signal_connect(G_OBJECT(root), "key-press-event", + G_CALLBACK(key_press_event), NULL); + g_signal_connect(G_OBJECT(root), "destroy", + G_CALLBACK(gtk_main_quit), NULL); + + gtk_main(); +} + + +static void usage(const char *name) +{ + fprintf(stderr, "usage: %s project [top bottom dist]\n", name); + exit(1); +} + + +int main(int argc, char **argv) +{ + double dist; + + gtk_init(&argc, &argv); + setlocale(LC_ALL, "C"); /* damage control */ + + switch (argc) { + case 2: + prj = load_project(argv[1]); + break; + case 5: + dist = atof(argv[4]); + prj = new_project(argv[1], argv[2], argv[3], dist); + break; + default: + usage(*argv); + } + + gui(); + + save_project(prj); + + if (!isatty(1)) { + const char *slash = strrchr(prj->name, '/'); + char tmp[1000]; /* @@@ enough */ + + strcpy(tmp, slash ? slash+1 : prj->name); + if (strchr(tmp, '.')) + *strchr(tmp, '.') = 0; + povray(tmp, &prj->s); + } + + return 0; +} diff --git a/solidify/style.c b/solidify/style.c new file mode 100644 index 0000000..6b260a8 --- /dev/null +++ b/solidify/style.c @@ -0,0 +1,53 @@ +/* + * style.c - GUI style parameters and items + * + * 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 "style.h" + + +GdkGC *gc_osd; + + +static GdkColor get_color(GdkDrawable *da, const char *spec) +{ + GdkColormap *cmap; + GdkColor color; + + cmap = gdk_drawable_get_colormap(da); + if (!gdk_color_parse(spec, &color)) + abort(); + if (!gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE)) + abort(); + return color; +} + + +static GdkGC *gc(GdkDrawable *da, const char *spec, int width) +{ + GdkGCValues gc_values = { + .background = get_color(da, "black"), + .foreground = get_color(da, spec), + .line_width = width, + }; + + return gdk_gc_new_with_values(da, &gc_values, + GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_LINE_WIDTH); +} + + +void init_style(GdkDrawable *da) +{ + gc_osd = gc(da, "#ffff00", 4); +} diff --git a/solidify/style.h b/solidify/style.h new file mode 100644 index 0000000..39085fb --- /dev/null +++ b/solidify/style.h @@ -0,0 +1,33 @@ +/* + * style.h - GUI style parameters and items + * + * 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 STYLE_H +#define STYLE_H + +#include + + +extern GdkGC *gc_osd; + + +#define OSD_PROXIMITY 20 /* pixels */ +#define LEVEL_CENTER_DIV 5 /* fraction of diagonal */ +#define OVERLAP_BORDER 10 /* pixels around min. drawing area */ +#define OVERLAP_CENTER_DIV 5 /* fraction of diagonal */ +#define SLOWEST_ROT 3 /* thrice the half-diagonal */ +#define FASTEST_ROT 2 /* one pixel in distance of 2 pixels */ +#define DIST_STEPS 5 /* fastest shift is 5 px/wheel step */ + + +void init_style(GdkDrawable *da); + +#endif /* !STYLE_H */ diff --git a/solidify/util.c b/solidify/util.c new file mode 100644 index 0000000..40206d1 --- /dev/null +++ b/solidify/util.c @@ -0,0 +1,22 @@ +/* + * util.c - Common utility functions + * + * 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 "util.h" + + +void draw_circle(GdkDrawable *da, GdkGC *gc, int x, int y, int r) +{ + gdk_draw_arc(da, gc, FALSE, x-r, y-r, 2*r, 2*r, 0, 360*64); +} diff --git a/solidify/util.h b/solidify/util.h new file mode 100644 index 0000000..635f258 --- /dev/null +++ b/solidify/util.h @@ -0,0 +1,38 @@ +/* + * util.h - Common utility functions + * + * Written 2009 by Werner Almesberger + * Copyright 2009 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 UTIL_H +#define UTIL_H + +#include +#include +#include + + +#define alloc_size(s) \ + ({ void *alloc_size_tmp = malloc(s); \ + if (!alloc_size_tmp) \ + abort(); \ + alloc_size_tmp; }) + +#define alloc_type(t) ((t *) alloc_size(sizeof(t))) + +#define stralloc(s) \ + ({ char *stralloc_tmp = strdup(s); \ + if (!stralloc_tmp) \ + abort(); \ + stralloc_tmp; }) + + +void draw_circle(GdkDrawable *da, GdkGC *gc, int x, int y, int r); + +#endif /* !UTIL_H */