Date:2010-09-25 09:50:18 (8 years 5 months ago)
Author:Werner Almesberger
Commit:bec698814eb3cfdd40368c54e29747b4c934fef1
Message:Moved solidify to a new and more suitable project: cae-tools. http://projects.qi-hardware.com/index.php/p/cae-tools/

Keeping code in ben-scans wasn't such a good idea, because one has to
check out all the huge data files along with it too. Better keep to two
separate.
Files: solidify/Makefile (1 diff)
solidify/array.c (1 diff)
solidify/array.h (1 diff)
solidify/face.c (1 diff)
solidify/face.h (1 diff)
solidify/histo.c (1 diff)
solidify/histo.h (1 diff)
solidify/level.c (1 diff)
solidify/level.h (1 diff)
solidify/main.pov (1 diff)
solidify/matrix.c (1 diff)
solidify/matrix.h (1 diff)
solidify/overlap.c (1 diff)
solidify/overlap.h (1 diff)
solidify/povray.c (1 diff)
solidify/project.c (1 diff)
solidify/project.h (1 diff)
solidify/solid.h (1 diff)
solidify/solidify.c (1 diff)
solidify/style.c (1 diff)
solidify/style.h (1 diff)
solidify/util.c (1 diff)
solidify/util.h (1 diff)

Change Details

solidify/Makefile
1#
2# Makefile - Makefile of solidify
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
13SHELL = /bin/bash
14
15OBJS = array.o face.o histo.o level.o matrix.o overlap.o povray.o project.o \
16       solidify.o style.o util.o
17
18CFLAGS_WARN = -Wall -Wshadow -Wmissing-prototypes \
19          -Wmissing-declarations -Wno-format-zero-length
20
21CFLAGS = $(CFLAGS_WARN) -g -O9 `pkg-config --cflags gtk+-2.0`
22LDFLAGS = -lm `pkg-config --libs gtk+-2.0`
23
24# ----- Verbosity control -----------------------------------------------------
25
26CC_normal := $(CC)
27DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG
28
29CC_quiet = @echo " CC " $@ && $(CC_normal)
30DEPEND_quiet = @$(DEPEND_normal)
31
32ifeq ($(V),1)
33    CC = $(CC_normal)
34    DEPEND = $(DEPEND_normal)
35else
36    CC = $(CC_quiet)
37    DEPEND = $(DEPEND_quiet)
38endif
39
40# ----- Rules -----------------------------------------------------------------
41
42.PHONY: all clean spotless
43
44all: solidify
45
46solidify: $(OBJS)
47
48clean:
49        rm -f $(OBJS) $(OBJS:.o=.d)
50
51spotless: clean
52        rm -f solidify
53
54# ----- Experimental execution ------------------------------------------------
55
56PRJ=http://projects.qi-hardware.com/index.php/p/ben-scans/source/tree/master
57DIR=$(PRJ)/data/csv
58FACE_A=$(DIR)/ben-batcvr-outside-100um.txt.bz2
59FACE_B=$(DIR)/ben-batcvr-inside-100um.txt.bz2
60D=1.16
61
62.PHONY: new run pov disp
63
64new: solidify
65        rm -f batcvr.sfy
66        ./solidify batcvr.sfy $(FACE_A) $(FACE_B) $(D) >batcvr.inc
67
68run: solidify
69        ./solidify batcvr.sfy >batcvr.inc
70
71pov:
72        povray +A -W1280 -H1024 main.pov
73
74disp:
75        display main.png
76
77# ----- Dependencies ----------------------------------------------------------
78
79# compile and generate dependencies, from fped, based on
80# http://scottmcpeak.com/autodepend/autodepend.html
81
82%.o: %.c
83        $(CC) -c $(CFLAGS) $*.c -o $*.o
84        $(DEPEND) $*.c | \
85          sed -e \
86            '/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/ */:\n/g;H;}' \
87            -e '$${g;p;}' -e d >$*.d; \
88          [ "$${PIPESTATUS[*]}" = "0 0" ] || { rm -f $*.d; exit 1; }
89
90-include $(OBJS:.o=.d)
solidify/array.c
1/*
2 * array.c - Growable baseless 2D array
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
16#include "util.h"
17#include "array.h"
18
19
20static void resize(struct array *a,
21    int nx0, int nx1, int ny0, int ny1)
22{
23    int ox, oy, nx, ny;
24    int n, x, y;
25    int *tmp;
26
27    ox = a->max_x-a->min_x;
28    oy = a->max_y-a->min_y;
29    nx = nx1-nx0;
30    ny = ny1-ny0;
31    if (ox == nx && oy == ny)
32        return;
33    n = (nx+1)*(ny+1);
34    tmp = alloc_size(n*sizeof(int));
35    for (x = 0; x != n; x++)
36        tmp[x] = UNDEF;
37    for (x = a->min_x; x <= a->max_x; x++)
38        for (y = a->min_y; y <= a->max_y; y++)
39            tmp[x-nx0+(nx+1)*(y-ny0)] =
40                a->data[x-a->min_x+(ox+1)*(y-a->min_y)];
41    free(a->data);
42    a->data = tmp;
43    a->min_x = nx0;
44    a->max_x = nx1;
45    a->min_y = ny0;
46    a->max_y = ny1;
47}
48
49
50struct array *new_array(void)
51{
52    struct array *a;
53
54    a = alloc_type(struct array);
55    a->data = NULL;
56    return a;
57}
58
59
60void free_array(struct array *a)
61{
62    free(a->data);
63    free(a);
64}
65
66
67void set(struct array *a, int x, int y, int z)
68{
69    if (!a->data) {
70        a->min_x = a->max_x = x;
71        a->min_y = a->max_y = y;
72        a->min_z = a->max_z = z;
73        a->data = alloc_type(int);
74        *a->data = z;
75    } else {
76        resize(a,
77            x < a->min_x ? x : a->min_x, x > a->max_x ? x : a->max_x,
78            y < a->min_y ? y : a->min_y, y > a->max_y ? y : a->max_y);
79        if (z < a->min_z)
80            a->min_z = z;
81        if (z > a->max_z)
82            a->max_z = z;
83        a->data[x-a->min_x+(a->max_x-a->min_x+1)*(y-a->min_y)] = z;
84    }
85}
solidify/array.h
1/*
2 * array.h - Growable baseless 2D array
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#ifndef ARRAY_H
14#define ARRAY_H
15
16#include <limits.h>
17
18
19#define UNDEF INT_MAX
20
21
22struct array {
23    int min_x, max_x;
24    int min_y, max_y;
25    int min_z, max_z;
26    int *data; /* NULL if there are no points */
27};
28
29
30struct array *new_array(void);
31void free_array(struct array *a);
32
33void set(struct array *a, int x, int y, int z);
34
35
36static inline int get(const struct array *a, int x, int y)
37{
38    return a->data[(x)-a->min_x+(a->max_x-a->min_x+1)*((y)-a->min_y)];
39}
40
41
42static inline int get_bounded(const struct array *a, int x, int y)
43{
44    if (x < a->min_x || x > a->max_x)
45        return UNDEF;
46    if (y < a->min_y || y > a->max_y)
47        return UNDEF;
48    return get(a, x, y);
49}
50
51
52#endif /* !ARRAY_H */
solidify/face.c
1/*
2 * face.c - Data structure and handling of one face of a part
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 <unistd.h>
17#include <string.h>
18#include <math.h>
19#include <fcntl.h>
20#include <sys/wait.h>
21
22#include "util.h"
23#include "array.h"
24#include "histo.h"
25#include "face.h"
26
27
28#define CACHE_DIR ".cache"
29#define DEFAULT_STEP 1 /* 1 mm */
30#define MIN_STEP 0.005 /* 5 um */
31
32
33struct coord {
34    float x, y, z;
35};
36
37
38static struct coord *load_file(const char *name)
39{
40    FILE *file;
41    struct coord *v, *n;
42    int s;
43
44    if (!strcmp(name, "-")) {
45        file = stdin;
46    } else {
47        int len;
48
49        len = strlen(name);
50        if (len > 4 && !strcmp(name+len-4, ".bz2")) {
51            char tmp[1000]; /* @@@ enough */
52
53            sprintf(tmp, "bzcat \"%s\"", name);
54            file = popen(tmp, "r");
55            if (!file) {
56                perror(tmp);
57                exit(1);
58            }
59        } else {
60            file = fopen(name, "r");
61            if (!file) {
62                perror(name);
63                exit(1);
64            }
65        }
66    }
67
68    v = n = alloc_type(struct coord);
69    s = 1;
70
71    while (fscanf(file, "%f,%f,%f\r\n", &n->x, &n->y, &n->z) == 3) {
72        n++;
73        if (n-v == s) {
74            struct coord *tmp;
75
76            s += s;
77            tmp = realloc(v, sizeof(struct coord)*s);
78            if (!tmp) {
79                perror("realloc");
80                exit(1);
81            }
82            n = n-v+tmp;
83            v = tmp;
84        }
85    }
86    if (file != stdin)
87        (void) fclose(file);
88
89    n->x = n->y = n->z = 0;
90    return v;
91}
92
93
94static void adjust_step(double *step, double delta)
95{
96    double n = round(delta/MIN_STEP);
97    double s = n*MIN_STEP;
98
99    if (n && s < *step)
100        *step = s;
101}
102
103
104static struct face *read_file(const char *name)
105{
106    struct coord *v, *p;
107    struct face *f;
108    struct histo *h;
109    int xi, yi, zi;
110
111    v = load_file(name);
112
113    f = alloc_type(struct face);
114    f->a = new_array();
115
116    /*
117     * Hack: the MDX-15 measures bumps along the x axis with 25 um
118     * resolution, so we just ignore the x resultion we find and use the
119     * y resolution instead.
120     */
121    f->x_step = f->y_step =f->z_step = DEFAULT_STEP;
122    for (p = v; p[1].x || p[1].y || p[1].z; p++) {
123        adjust_step(&f->y_step, fabs(p[0].y-p[1].y));
124        adjust_step(&f->z_step, fabs(p[0].z-p[1].z));
125    }
126    f->x_step = f->y_step;
127
128    for (p = v; p->x || p->y || p->z; p++) {
129        xi = round(p->x/f->x_step);
130        yi = round(p->y/f->y_step);
131        zi = round(p->z/f->z_step);
132        set(f->a, xi, yi, zi);
133    }
134
135    free(v);
136
137    f->sx = f->a->max_x-f->a->min_x+1;
138    f->sy = f->a->max_y-f->a->min_y+1;
139
140    f->cx = (f->a->min_x+f->a->max_x)/2;
141    f->cy = (f->a->min_y+f->a->max_y)/2;
142
143    h = make_histo(f->a);
144    f->z_ref = f->a->min_z+median(h);
145    free_histo(h);
146    f->fx = f->fy = 0;
147
148    f->m.a[0][0] = f->m.a[1][1] = 1;
149    f->m.a[0][1] = f->m.a[1][0] = 0;
150    f->m.b[0] = f->m.b[1] = 0;
151
152    fprintf(stderr, "%g %g %g\n", f->x_step, f->y_step, f->z_step);
153    fprintf(stderr, "%d-%d / %d-%d / %d-%d\n",
154        f->a->min_x, f->a->max_x, f->a->min_y, f->a->max_y,
155        f->a->min_z, f->a->max_z);
156
157    return f;
158}
159
160
161struct face *read_face(const char *name)
162{
163    const char *p;
164    int cwd;
165    struct face *face;
166
167    if (strncmp(name, "http:", 5) && strncmp(name, "https:", 6))
168        return read_file(name);
169    p = strrchr(name, '/');
170    if (!p || !p[1]) {
171        fprintf(stderr, "malformed URL: \"%s\"\n", name);
172        exit(1);
173    }
174    cwd = open(".", O_RDONLY);
175    if (cwd < 0) {
176        perror(".");
177        exit(1);
178    }
179    if (chdir(CACHE_DIR) < 0) {
180        perror(CACHE_DIR);
181        exit(1);
182    }
183    if (access(p+1, R_OK) < 0) {
184        char tmp[1000]; /* @@@ enough */
185        int res;
186
187        sprintf(tmp, "wget '%s'", name);
188        res = system(tmp);
189        if (res < 0) {
190            perror("system");
191            exit(1);
192        }
193        if (!WIFEXITED(res) || WEXITSTATUS(res)) {
194            fprintf(stderr, "%s: status %d\n", tmp, res);
195            exit(1);
196        }
197    }
198    face = read_file(p+1);
199    if (fchdir(cwd) < 0) {
200        perror("fchdir");
201        exit(1);
202    }
203    return face;
204}
solidify/face.h
1/*
2 * face.h - Data structure and handling of one face of a part
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#ifndef FACE_H
14#define FACE_H
15
16#include "array.h"
17#include "matrix.h"
18
19
20struct face {
21    struct array *a;
22    double x_step, y_step, z_step;
23    int sx, sy; /* size */
24    int cx, cy; /* center */
25    int z_ref;
26    double fx, fy; /* inclination factor */
27    struct matrix m;
28};
29
30
31static inline double face_z0(const struct face *f, int x, int y)
32{
33    return f->z_ref+f->fx*(x-f->sx/2)+f->fy*(y-f->sy/2);
34}
35
36
37struct face *read_face(const char *name);
38
39#endif /* !FACE_H */
solidify/histo.c
1/*
2 * histo.c - Distribution of Z values
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 "util.h"
15#include "array.h"
16#include "histo.h"
17
18
19struct histo *make_histo(struct array *a)
20{
21    int i;
22    struct histo *h;
23
24    h = alloc_type(struct histo);
25    h->n = a->max_z-a->min_z+1;
26    h->b = alloc_size(h->n*sizeof(int));
27    for (i = 0; i != h->n; i++)
28        h->b[i] = 0;
29    for (i = 0; i != (a->max_x-a->min_x+1)*(a->max_y-a->min_y+1); i++)
30        if (a->data[i] != UNDEF)
31            h->b[a->data[i]-a->min_z]++;
32    return h;
33}
34
35
36void free_histo(struct histo *h)
37{
38    free(h);
39}
40
41
42int median(const struct histo *h)
43{
44    int tot = 0, sum = 0;
45    int i;
46
47    for (i = 0; i != h->n; i++)
48        tot += h->b[i];
49    for (i = 0; sum < tot/2; i++)
50        sum += h->b[i];
51    return i-1;
52}
solidify/histo.h
1/*
2 * histo.h - Distribution of Z values
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#ifndef HISTO_H
14#define HISTO_H
15
16#include "array.h"
17
18
19struct histo {
20    int n; /* number of bins */
21    int *b; /* bins */
22};
23
24
25struct histo *make_histo(struct array *a);
26void free_histo(struct histo *h);
27int median(const struct histo *h);
28
29#endif /* !HISTO_H */
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 "util.h"
20#include "array.h"
21#include "face.h"
22#include "style.h"
23#include "level.h"
24
25
26#define NEAR 1
27
28
29static int has_osd;
30
31
32static double r_center(const struct face *f)
33{
34    return hypot(f->sx, f->sy)/LEVEL_CENTER_DIV;
35}
36
37
38static void draw_map(GtkWidget *widget, struct face *f)
39{
40    int x, y, z;
41    double z0;
42    guchar *rgbbuf, *p;
43
44    rgbbuf = p = calloc(f->sx*f->sy, 3);
45    if (!rgbbuf) {
46        perror("calloc");
47        exit(1);
48    }
49    for (y = f->sy-1; y >= 0; y--)
50        for (x = 0; x != f->sx ; x++) {
51            z = get(f->a, x+f->a->min_x, y+f->a->min_y);
52            if (z == UNDEF) {
53                p += 3;
54                continue;
55            }
56            z0 = face_z0(f, x, y);
57            if (fabs(z-z0) < NEAR) {
58                *p++ = 255*fabs(z-z0);
59                *p++ = 255*fabs(z-z0);
60                *p++ = 255;
61                continue;
62            }
63            if (z < z0) {
64                z = z > z0-2*NEAR ? 255*(z-z0)/NEAR :
65                    255.0*(z-z0)/(z0-f->a->min_z);
66                *p++ = 255;
67                *p++ = z;
68                *p++ = z;
69            } else {
70                z = z < z0+2*NEAR ? 255*(z0-z)/NEAR :
71                    255.0*(z0-z)/(f->a->max_z-z0);
72                *p++ = z;
73                *p++ = 255;
74                *p++ = z;
75            }
76        }
77    gdk_draw_rgb_image(widget->window,
78        widget->style->fg_gc[GTK_STATE_NORMAL],
79        0, 0, f->sx, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3);
80    free(rgbbuf);
81}
82
83
84static void draw_image(GtkWidget *widget, struct face *f, int osd)
85{
86    draw_map(widget, f);
87    has_osd = osd;
88    if (osd)
89        draw_circle(widget->window, gc_osd,
90            f->sx/2, f->sy/2, r_center(f));
91}
92
93
94static void scroll_z(GtkWidget *darea, struct face *f, int up, int osd)
95{
96    if (up) {
97        if (f->z_ref < f->a->max_z)
98            f->z_ref++;
99    } else {
100        if (f->z_ref > f->a->min_z)
101            f->z_ref--;
102    }
103    draw_image(darea, f, osd);
104}
105
106
107static void scroll_xy(GtkWidget *darea, struct face *f, int dx, int dy, int up,
108    int osd)
109{
110    double d;
111
112    d = (double) (up ? 1 : -1)/(dx*dx+dy*dy)/2.0;
113    f->fx += d*dx;
114    f->fy += d*dy;
115    draw_image(darea, f, osd);
116}
117
118
119static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
120    gpointer data)
121{
122    GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget));
123    struct face *f = data;
124    int dx = event->x-f->sx/2;
125    int dy = event->y-f->sy/2;
126    double r = hypot(dx, dy);
127    double rc = r_center(f);
128    int center = r < rc;
129    int osd = fabs(r-rc) < OSD_PROXIMITY;
130
131    switch (event->direction) {
132    case GDK_SCROLL_UP:
133        if (center)
134            scroll_z(darea, f, 0, osd);
135        else
136            scroll_xy(darea, f, dx, dy, 1, osd);
137        break;
138    case GDK_SCROLL_DOWN:
139        if (center)
140            scroll_z(darea, f, 1, osd);
141        else
142            scroll_xy(darea, f, dx, dy, 0, osd);
143        break;
144    default:
145        /* ignore */;
146    }
147    return TRUE;
148}
149
150
151static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
152    gpointer user_data)
153{
154    draw_image(widget, user_data, has_osd);
155    return TRUE;
156}
157
158
159static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
160    gpointer data)
161{
162    struct face *f = data;
163    int dx = event->x-f->sx/2;
164    int dy = event->y-f->sy/2;
165    double r = hypot(dx, dy);
166    double rc = r_center(f);
167    int osd = fabs(r-rc) < OSD_PROXIMITY;
168
169    if (osd != has_osd)
170        draw_image(widget, f, osd);
171    return FALSE;
172}
173
174
175void level(GtkWidget *canvas, struct face *f)
176{
177    GtkWidget *evbox, *darea;
178
179    evbox = gtk_event_box_new();
180    darea = gtk_drawing_area_new();
181
182    gtk_widget_set_events(darea,
183        GDK_EXPOSE | GDK_KEY_PRESS_MASK |
184        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
185        GDK_SCROLL |
186        GDK_POINTER_MOTION_MASK);
187
188    gtk_widget_set_size_request(darea, f->sx, f->sy);
189    gtk_container_add(GTK_CONTAINER(canvas), evbox);
190    gtk_container_add(GTK_CONTAINER(evbox), darea);
191
192    draw_image(darea, f, 0);
193
194    g_signal_connect(G_OBJECT(evbox), "scroll-event",
195        G_CALLBACK(scroll_event), f);
196    g_signal_connect(G_OBJECT(darea), "expose-event",
197        G_CALLBACK(expose_event), f);
198    g_signal_connect(G_OBJECT(darea), "motion-notify-event",
199        G_CALLBACK(motion_notify_event), f);
200}
solidify/level.h
1/*
2 * level.h - 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#ifndef LEVEL_H
14#define LEVEL_H
15
16#include <gtk/gtk.h>
17
18#include "face.h"
19
20
21void level(GtkWidget *canvas, struct face *f);
22
23#endif /* !LEVEL_H */
solidify/main.pov
1#include "colors.inc"
2#include "batcvr.inc"
3
4/*
5 * POV-Ray defaults to a "camera" coordinate system that can be confusing.
6 * We use a traditional mathematical/engineering view, with a view from
7 * X-/Y-/Z+ into the X/Y plane.
8 */
9
10camera {
11    location <-30, -80, 40>
12    look_at <20, 20, 0>
13    sky z
14    right -4/3*x
15}
16
17background { color White }
18
19light_source {
20    <-200, -300, 200>
21    color White
22}
23
24/*
25 * Mark the coordinate axes:
26 * - a red unit sphere at the center
27 * - three green spheres at x = 10*i
28 * - two blue spheres at y = 10*i
29 * - one yellow sphere at z = 10
30 */
31
32sphere { < 0, 0, 0>, 1 pigment { color Red } }
33sphere { <10, 0, 0>, 1 pigment { color Green } }
34sphere { <20, 0, 0>, 1 pigment { color Green } }
35sphere { <30, 0, 0>, 1 pigment { color Green } }
36sphere { < 0, 10, 0>, 1 pigment { color Blue } }
37sphere { < 0, 20, 0>, 1 pigment { color Blue } }
38sphere { < 0, 0, 10>, 1 pigment { color Yellow } }
39
40#declare Finish = finish {
41    brilliance 2
42    phong 0.8
43    phong_size 100
44    metallic
45}
46
47union {
48    Part_batcvr
49    pigment { rgbf <0.9, 0.9, 0.9, 0.5> }
50    finish { Finish }
51}
solidify/matrix.c
1/*
2 * matrix.c - 2D matrix operations
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 <assert.h>
15
16#include "matrix.h"
17
18
19void matrix_identity(struct matrix *m)
20{
21    m->a[0][0] = m->a[1][1] = 1;
22    m->a[0][1] = m->a[1][0] = 0;
23    m->b[0] = m->b[1] = 0;
24}
25
26
27void matrix_invert(const double m[2][2], double res[2][2])
28{
29    double det = m[0][0]*m[1][1]-m[0][1]*m[1][0];
30
31    assert(res != (void *) m);
32    res[0][0] = m[1][1]/det;
33    res[0][1] = -m[0][1]/det;
34    res[1][0] = -m[1][0]/det;
35    res[1][1] = m[0][0]/det;
36}
37
38
39void matrix_mult(double a[2][2], double b[2][2], double res[2][2])
40{
41    assert(res != a);
42    assert(res != b);
43    res[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0];
44    res[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1];
45    res[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0];
46    res[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1];
47}
48
49
50void matrix_multv(const double v[2], double m[2][2], double res[2])
51{
52    double tmp;
53
54    tmp = v[0]*m[0][0]+v[1]*m[0][1];
55    res[1] = v[0]*m[1][0]+v[1]*m[1][1];
56    res[0] = tmp;
57}
58
59
60void matrix_copy(double from[2][2], double to[2][2])
61{
62    to[0][0] = from[0][0];
63    to[0][1] = from[0][1];
64    to[1][0] = from[1][0];
65    to[1][1] = from[1][1];
66}
solidify/matrix.h
1/*
2 * matrix.h - 2D matrix operations
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#ifndef MATRIX_H
14#define MATRIX_H
15
16/*
17 * 2D transformation:
18 *
19 * x' = x*a[0][0]+y*a[0][1]+b[0]
20 * y' = x*a[1][0]+y*a[1][1]+b[1]
21 */
22
23
24struct matrix {
25    double a[2][2];
26    double b[2];
27};
28
29
30void matrix_identity(struct matrix *m);
31void matrix_invert(const double m[2][2], double res[2][2]);
32void matrix_mult(double a[2][2], double b[2][2], double res[2][2]);
33void matrix_multv(const double v[2], double m[2][2], double res[2]);
34void matrix_copy(double from[2][2], double to[2][2]);
35
36
37static inline void matrix_map(int x, int y, const struct matrix *m,
38    double *res_x, double *res_y)
39{
40    *res_x = x*m->a[0][0]+y*m->a[0][1]+m->b[0];
41    *res_y = x*m->a[1][0]+y*m->a[1][1]+m->b[1];
42
43}
44
45#endif /* !MATRIX_H */
solidify/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
30static int has_osd;
31
32
33static 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
39static 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
45static double r_center(const struct solid *s)
46{
47    return hypot(sx(s), sy(s))/OVERLAP_CENTER_DIV;
48}
49
50
51static double ramp(int z0, double w0, int z1, double w1)
52{
53    if (z0 != UNDEF && z1 != UNDEF)
54        return 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
65static 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 = xa+1;
72    ya = floor(y);
73    yb = ya+1;
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
95static void point(const struct solid *s, int x, int y, guchar *p,
96    const struct matrix *ma, const struct matrix *mb)
97{
98    double za, zb, z;
99    double xaf, xbf, yaf, ybf;
100
101    matrix_map(x, y, ma, &xaf, &yaf);
102    matrix_map(x, y, mb, &xbf, &ybf);
103
104    za = zmix(s->a, xaf, yaf);
105    zb = zmix(s->b, xbf, ybf);
106
107    if (za == UNDEF_F && zb == UNDEF_F)
108        return;
109
110    if (za == UNDEF_F) {
111        z = 128.0*(zb-s->b->a->min_z)/(s->b->a->max_z-s->b->a->min_z);
112        if (z < 0)
113            z = 0;
114        if (z > 255)
115            z = 255;
116        p[0] = 255;
117        p[1] = z;
118        p[2] = z;
119        return;
120    }
121    if (zb == UNDEF_F) {
122        z = 128.0*(za-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z);
123        if (z < 0)
124            z = 0;
125        if (z > 255)
126            z = 255;
127        p[0] = z;
128        p[1] = 255;
129        p[2] = z;
130        return;
131    }
132
133    z = za;
134    za -= face_z0(s->a, xaf, yaf);
135    zb -= face_z0(s->b, xbf, ybf);
136
137    if (za+zb < -s->dist) {
138        p[0] = 0;
139        p[1] = 0;
140        p[2] = 255;
141        return;
142    }
143
144    z = 256.0*(z-s->a->a->min_z)/(s->a->a->max_z-s->a->a->min_z);
145    if (z < 0)
146        z = 0;
147    if (z > 255)
148        z = 255;
149    p[0] = z;
150    p[1] = z;
151    p[2] = z;
152}
153
154
155static void merge_matrix(struct matrix *m, const struct solid *s,
156    const struct face *f)
157{
158    double tm[2][2], tm2[2][2];
159    double tv[2];
160    double f_x, f_y;
161
162    /*
163     * Finally, we convert to model matrix coordinates.
164     *
165     * v' = v+c
166     */
167
168    m->b[0] += f->cx;
169    m->b[1] += f->cy;
170
171    /*
172     * Apply shrinkage caused by rotation out of z0.
173     * We need to divide by x = cos a. We have f = tan a.
174     * With sin^2 a + cos^2 a = 1, we get
175     *
176     * f = sqrt(1-cos^2 a)/cos a
177     * = sqrt(1-x^2)/x
178     * f^2 = 1/x^2-1
179     * 1/(f^2+1) = x^2
180     * cos a = sqrt(1/(f^2+1))
181     */
182
183    f_x = sqrt(f->fx*f->fx+1);
184    f_y = sqrt(f->fy*f->fy+1);
185
186    m->a[0][0] *= f_x;
187    m->a[0][1] *= f_x;
188    m->b[0] *= f_x;
189    m->a[1][0] *= f_y;
190    m->a[1][1] *= f_y;
191    m->b[1] *= f_y;
192
193    /*
194     * The transformation matrix f->m describes a transformation of
195     * (centered) model coordinates. We therefore have to reverse it:
196     *
197     * v = v'A+b
198     * v-b = v'A
199     * (v-b)A^-1 = v'
200     * vA^-1-bA^-1 = v'
201     */
202
203    matrix_invert(f->m.a, tm);
204    matrix_multv(f->m.b, tm, tv);
205    tv[0] = -tv[0];
206    tv[1] = -tv[1];
207
208    /*
209     * Merge with the transformations we have so far:
210     *
211     * v' = vA1+b1 the transformation we have so far
212     * v'' = v'A2+b2 the transformation we apply
213     *
214     * v'' = (vA1+b1)A2+b2
215     * v'' = vA1A2+b1A2+b2
216     */
217
218    /*
219     * So far, the theory. To make it really work, we have to calculate
220     * v'' = vA1A2+b1+b2
221     * duh ?!?
222     */
223
224    matrix_mult(m->a, tm, tm2); /* A1A2 */
225    matrix_copy(tm2, m->a);
226// matrix_multv(m->b, tm, m->b); /* b1A2 */
227    m->b[0] += tv[0]; /* b2 */
228    m->b[1] += tv[1];
229
230    /*
231     * Our input is a screen coordinate, its origin is in a corner so we
232     * first have to make it center-based:
233     *
234     * v' = (v-s/2)A+b
235     * v' = vA+(b-s/2*A)
236     */
237
238    tv[0] = sx(s)/2;
239    tv[1] = sy(s)/2;
240    matrix_multv(tv, m->a, tv);
241    m->b[0] -= tv[0];
242    m->b[1] -= tv[1];
243}
244
245
246static void draw_map(GtkWidget *widget, struct solid *s)
247{
248    guchar *rgbbuf, *p;
249    int x, y;
250    struct matrix ma = {
251        .a = { { 1, 0 }, { 0, 1 } },
252        .b = { 0, 0 },
253    };
254    struct matrix mb = {
255        .a = { { -1, 0 }, { 0, 1 } },
256        .b = { 0, 0 },
257    };
258
259    rgbbuf = p = calloc(sx(s)*sy(s), 3);
260    if (!rgbbuf) {
261        perror("calloc");
262        exit(1);
263    }
264
265    merge_matrix(&ma, s, s->a);
266    merge_matrix(&mb, s, s->b);
267
268    for (y = sy(s)-1; y >= 0; y--)
269        for (x = 0; x != sx(s) ; x++) {
270            point(s, x, y, p, &ma, &mb);
271            p += 3;
272        }
273    gdk_draw_rgb_image(widget->window,
274        widget->style->fg_gc[GTK_STATE_NORMAL],
275        0, 0, sx(s), sy(s), GDK_RGB_DITHER_MAX, rgbbuf, sx(s)*3);
276    free(rgbbuf);
277}
278
279
280static void draw_image(GtkWidget *widget, struct solid *s, int osd)
281{
282    int cx = sx(s)/2;
283    int cy = sy(s)/2;
284    int p;
285
286    draw_map(widget, s);
287    has_osd = osd;
288    if (!osd)
289        return;
290    draw_circle(widget->window, gc_osd, cx, cy, r_center(s));
291    p = r_center(s)/sqrt(2);
292    gdk_draw_line(widget->window, gc_osd, cx-p, cy-p, cx+p, cy+p);
293    gdk_draw_line(widget->window, gc_osd, cx-p, cy+p, cx+p, cy-p);
294}
295
296
297/*
298 * Rotate such that a point at distance "r" moves one unit. Rotate
299 * counter-clockwise for r > 1, clockwise for r < 0.
300 */
301
302static void rotate(struct matrix *m, double r)
303{
304    struct matrix t;
305    double s, c;
306
307    s = 1/r;
308    c = sqrt(1-s*s);
309    t.a[0][0] = m->a[0][0]*c-m->a[1][0]*s;
310    t.a[0][1] = m->a[0][1]*c-m->a[1][1]*s;
311    t.a[1][0] = m->a[1][0]*c+m->a[0][0]*s;
312    t.a[1][1] = m->a[1][1]*c+m->a[0][1]*s;
313    t.b[0] = m->b[0]*c-m->b[1]*s;
314    t.b[1] = m->b[0]*s+m->b[1]*c;
315    *m = t;
316}
317
318
319static void do_shift(struct matrix *m, double dx, double dy)
320{
321    m->b[0] += dx;
322    m->b[1] += dy;
323}
324
325
326static void shift(struct matrix *m, int dx, int dy, double dist)
327{
328    /*
329     * Wheeling "up" in each quadrant shifts in the respective direction,
330     * wheeling "down" in the opposite direction.
331     *
332     * No rule without exception: we treat the "down" quadrant like the
333     * "up" quadrant, because it would be extremely counter-intuitive to
334     * wheel "up" to move "down".
335     */
336
337    if (dx > 0 && dy < dx && dy > -dx)
338        do_shift(m, dist, 0);
339    if (dx < 0 && dy < -dx && dy > dx)
340        do_shift(m, -dist, 0);
341    if (dy > 0 && dx < dy && dx > -dy)
342        do_shift(m, 0, dist);
343    if (dy < 0 && dx < -dy && dx > dy)
344        do_shift(m, 0, dist); /* exception ! */
345}
346
347
348static int osd_proximity(const struct solid *s, int dx, int dy)
349{
350    double r = hypot(dx, dy);
351    double rc = r_center(s);
352
353    if (fabs(r-rc) < OSD_PROXIMITY)
354        return 1;
355    if (r > rc)
356        return 0;
357    if (abs(abs(dx)-abs(dy)) < OSD_PROXIMITY)
358        return 1;
359    return 0;
360}
361
362
363static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
364    gpointer data)
365{
366    GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget));
367    struct solid *s = data;
368    int dx = event->x-sx(s)/2;
369    int dy = event->y-sy(s)/2;
370    double r = hypot(dx, dy);
371    double rc = r_center(s);
372    double rs, rot, dist;
373    int center = r < rc;
374    int osd = osd_proximity(s, dx, dy);
375
376    if (r < 1)
377        return TRUE;
378
379    /*
380     * rot goes exponentially from SLOWEST_ROT*rs to FASTEST_ROT for
381     * r = rc to rs, with rs being half the canvas diagonal.
382     *
383     * The values are picked such that we achieve sufficient precision at
384     * a reasonably large distance from the circle (for accidently entering
385     * the circle would change the mode) but can also spin quickly, e.g.,
386     * when a 180 degrees rotation is needed.
387     *
388     * First, normalize to 0 ... 1
389     * Then, we start at exp(0) and end at
390     * exp(ln(SLOWEST_ROT*rs/FASTEST_ROT)))
391     */
392    rs = hypot(sx(s), sy(s))/2;
393    rot = (r-rc)/(rs-rc);
394    rot = SLOWEST_ROT*rs*exp(-rot*log(SLOWEST_ROT*rs/FASTEST_ROT));
395
396    /*
397     * dist stays at 1 from 0...rc/DIST_STEPS, then linearly goes up to
398     * DIST_STEPS from rc/DIST_STEPS...rc
399     */
400    dist = r/rc*DIST_STEPS;
401    if (dist < 0)
402        dist = 1;
403
404    switch (event->direction) {
405    case GDK_SCROLL_UP:
406        if (center)
407            shift(&s->a->m, dx, dy, dist);
408        else
409            rotate(&s->a->m, dx > 0 ? rot : -rot);
410        draw_image(darea, s, osd);
411        break;
412    case GDK_SCROLL_DOWN:
413        if (center)
414            shift(&s->a->m, dx, dy, -dist);
415        else
416            rotate(&s->a->m, dx > 0 ? -rot : rot);
417        draw_image(darea, s, osd);
418        break;
419    default:
420        /* ignore */;
421    }
422    return TRUE;
423}
424
425
426static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
427    gpointer user_data)
428{
429    draw_image(widget, user_data, has_osd);
430    return TRUE;
431}
432
433
434static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
435    gpointer data)
436{
437    struct solid *s = data;
438    int dx = event->x-sx(s)/2;
439    int dy = event->y-sy(s)/2;
440    int osd = osd_proximity(s, dx, dy);
441
442    if (osd != has_osd)
443        draw_image(widget, s, osd);
444    return FALSE;
445}
446
447
448void overlap(GtkWidget *canvas, struct solid *s)
449{
450    GtkWidget *evbox, *darea;
451
452    evbox = gtk_event_box_new();
453    darea = gtk_drawing_area_new();
454
455    gtk_widget_set_events(darea,
456        GDK_EXPOSE | GDK_KEY_PRESS_MASK |
457        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
458        GDK_SCROLL |
459        GDK_POINTER_MOTION_MASK);
460
461    gtk_widget_set_size_request(darea, sx(s), sy(s));
462    gtk_container_add(GTK_CONTAINER(canvas), evbox);
463    gtk_container_add(GTK_CONTAINER(evbox), darea);
464
465    draw_image(darea, s, 0);
466
467    g_signal_connect(G_OBJECT(evbox), "scroll-event",
468        G_CALLBACK(scroll_event), s);
469    g_signal_connect(G_OBJECT(darea), "expose-event",
470        G_CALLBACK(expose_event), s);
471    g_signal_connect(G_OBJECT(darea), "motion-notify-event",
472        G_CALLBACK(motion_notify_event), s);
473
474if (0) {
475int i;
476long t0 = time(NULL);
477gtk_widget_show_all(canvas);
478for (i = 0; i != 1000; i++) {
479    rotate(&s->a->m, 100);
480    draw_image(darea, s, 0);
481    while (gtk_events_pending())
482        gtk_main_iteration();
483}
484fprintf(stderr, "%lu\n", time(NULL)-t0);
485}
486
487}
solidify/overlap.h
1/*
2 * overlap.h - 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#ifndef OVERLAP_H
14#define OVERLAP_H
15
16#include <gtk/gtk.h>
17
18#include "solid.h"
19
20
21void overlap(GtkWidget *canvas, struct solid *solid);
22
23#endif /* !OVERLAP_H */
solidify/povray.c
1/*
2 * povray.c - Generate POV-Ray output
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 <stdint.h>
15#include <stdlib.h>
16#include <stdio.h>
17
18#include "face.h"
19#include "solid.h"
20
21
22static void height_field(const char *name, const struct face *f,
23    const struct matrix *m)
24{
25    FILE *file;
26    int x, y;
27    int z;
28    uint16_t g;
29    uint8_t v[2];
30
31    file = fopen(name, "w");
32    if (!file) {
33        perror(name);
34        exit(1);
35    }
36    fprintf(file, "P5\n%d %d\n65535\n", f->sx, f->sy);
37    for (y = 0; y != f->sy; y++)
38        for (x = 0; x != f->sx; x++) {
39            z = get(f->a, x+f->a->min_x, y+f->a->min_y);
40            g = z == UNDEF ? 0 :
41                65535*(z-f->a->min_z)/(f->a->max_z-f->a->min_z);
42            v[0] = g >> 8;
43            v[1] = g;
44            fwrite(v, 2, 1, file);
45        }
46    fclose(file);
47}
48
49
50static void sanitize(const char *s, char *res)
51{
52    while (*s) {
53        if ((*s >= 'A' && *s <= 'Z') ||
54            (*s >= 'a' && *s <= 'z') ||
55            (*s >= '0' && *s <= '9') || *s == '_')
56            *res = *s;
57        else
58            *res = '_';
59        res++;
60        s++;
61    }
62    *res = 0;
63}
64
65
66/*
67 * For now, we put the part such that its x/y/z center is at the origin.
68 * Later, we will also have to consider the changes the user made and the
69 * relation with the opposing face.
70 */
71
72static void povray_face(const struct face *f, const char *side,
73    const char *prefix, int flip, double dist)
74{
75    int sz = f->a->max_z-f->a->min_z;
76
77    /*
78     * 1/65535 = 0.000015..., so we set the water level a bit lower, e.g.,
79     * to 0.0001
80     */
81    printf(
82"\theight_field {\n"
83"\t pgm \"%s-%s.pgm\"\n"
84"\t water_level 0.00001\n"
85"\t smooth\n"
86"\t scale <%g, %g, %g>\n"
87"\t rotate <90, 0, 0>\n"
88"\t translate <%g, %g, %g>\n"
89"\t translate <0, 0, %g>\n"
90"%s" /* flip bottom face */
91"\t translate <0, 0, %g>\n"
92"\t}\n", prefix, side,
93    f->sx*f->x_step, sz*f->z_step, f->sy*f->y_step,
94    -f->sx*f->x_step/2, f->sy*f->y_step/2, f->a->min_z*f->z_step,
95    -f->z_ref*f->z_step,
96    flip ? "\t rotate <180, 0, 0>\n" : "",
97    dist*f->z_step);
98}
99
100
101void povray(const char *name, const struct solid *s)
102{
103    struct matrix m;
104    char tmp[1000]; /* @@@ enough */
105
106    m.a[0][0] = m.a[1][1] = 1;
107    m.a[0][1] = m.a[1][0] = 0;
108    m.b[0] = m.b[1] = 0;
109
110    sprintf(tmp, "%s-top.pgm", name);
111    height_field(tmp, s->a, &m);
112    sprintf(tmp, "%s-bot.pgm", name);
113    height_field(tmp, s->b, &m);
114
115    sanitize(name, tmp);
116    printf("#declare Part_%s =\n intersection {\n", tmp);
117    povray_face(s->a, "top", name, 0, s->dist/2);
118    povray_face(s->b, "bot", name, 1, -s->dist/2);
119    printf(" }\n");
120}
solidify/project.c
1/*
2 * project.c - Load and save solidify project descriptions
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 <string.h>
17#include <errno.h>
18#include <math.h>
19
20#include "util.h"
21#include "face.h"
22#include "solid.h"
23#include "project.h"
24
25
26static struct project *make_project(const char *name,
27    const char *top, const char *bottom, double dist_mm)
28{
29    struct project *prj;
30
31    prj = alloc_type(struct project);
32    prj->name = stralloc(name);
33    prj->top = stralloc(top);
34    prj->bottom = stralloc(bottom);
35    prj->s.a = read_face(top);
36    prj->s.b = read_face(bottom);
37
38    if (prj->s.a->x_step != prj->s.a->x_step ||
39        prj->s.a->y_step != prj->s.a->y_step ||
40        prj->s.a->z_step != prj->s.a->z_step) {
41        fprintf(stderr, "both faces must have the same resolution\n");
42        exit(1);
43        }
44
45    prj->s.dist = dist_mm/prj->s.a->z_step;
46    return prj;
47}
48
49
50struct project *new_project(const char *name,
51    const char *top, const char *bottom, double dist_mm)
52{
53    FILE *file;
54
55    file = fopen(name, "r");
56    if (file) {
57        fprintf(stderr, "%s: already exists\n", name);
58        exit(1);
59    }
60    return make_project(name, top, bottom, dist_mm);
61}
62
63
64static void read_face_data(FILE *file, struct face *f)
65{
66    float v;
67
68    if (fscanf(file, "%f", &v) != 1)
69        return;
70    f->z_ref = v/f->z_step;
71
72    if (fscanf(file, "%f", &v) != 1)
73        return;
74    f->fx = tan(v/180*M_PI);
75
76    if (fscanf(file, "%f", &v) != 1)
77        return;
78    f->fy = tan(v/180*M_PI);
79
80    if (fscanf(file, "%f", &v) != 1)
81        return;
82    v = v/180*M_PI;
83    f->m.a[0][0] = cos(v);
84    f->m.a[0][1] = -sin(v);
85    f->m.a[1][0] = sin(v);
86    f->m.a[1][1] = cos(v);
87
88    if (fscanf(file, "%f", &v) != 1)
89        return;
90    f->m.b[0] = v/f->x_step;
91
92    if (fscanf(file, "%f", &v) != 1)
93        return;
94    f->m.b[1] = v/f->y_step;
95}
96
97
98static void read_optional(FILE *file, struct project *prj)
99{
100    float v;
101
102    if (fscanf(file, "%f", &v) != 1)
103        return;
104    prj->s.dist = v/prj->s.a->z_step;
105
106    read_face_data(file, prj->s.a);
107    read_face_data(file, prj->s.b);
108}
109
110
111struct project *load_project(const char *name)
112{
113    FILE *file;
114    char top[1000], bottom[1000]; /* @@@ enough */
115    struct project *prj;
116
117    file = fopen(name, "r");
118    if (!file) {
119        perror(name);
120        exit(1);
121    }
122
123    if (!fgets(top, sizeof(top), file)) {
124        fprintf(stderr, "%s: can't read name of top face\n", name);
125        exit(1);
126    }
127    if (strchr(top, '\n'))
128        *strchr(top, '\n') = 0;
129
130    if (!fgets(bottom, sizeof(bottom), file)) {
131        fprintf(stderr, "%s: can't read name of bottom face\n",
132            bottom);
133        exit(1);
134    }
135    if (strchr(bottom, '\n'))
136        *strchr(bottom, '\n') = 0;
137
138    prj = make_project(name, top, bottom, 0);
139
140    read_optional(file, prj);
141
142    return prj;
143}
144
145
146static void save_face_data(FILE *file, const char *name, const struct face *f)
147{
148    double a;
149
150    a = asin(-f->m.a[0][1])/M_PI*180;
151    if (f->m.a[0][0] < 0)
152        a = 180-a;
153    if (fprintf(file, "%g %g %g\n%g %g %g\n",
154        f->z_ref*f->z_step,
155        atan(f->fx)/M_PI*180, atan(f->fy)/M_PI*180,
156        a, f->m.b[0]*f->x_step, f->m.b[1]*f->y_step) < 0) {
157        perror(name);
158        exit(1);
159    }
160}
161
162
163void save_project(const struct project *prj)
164{
165    char tmp[1000]; /* @@@ enough */
166    FILE *file;
167
168    sprintf(tmp, "%s~", prj->name);
169    file = fopen(tmp, "w");
170    if (!file) {
171        perror(tmp);
172        exit(1);
173    }
174    if (fprintf(file,
175        "%s\n%s\n%g\n", prj->top, prj->bottom,
176        prj->s.dist*prj->s.a->z_step) < 0) {
177        perror(tmp);
178        exit(1);
179    }
180    save_face_data(file, tmp, prj->s.a);
181    save_face_data(file, tmp, prj->s.b);
182    if (fclose(file) < 0) {
183        perror(tmp);
184        exit(1);
185    }
186
187    if (rename(tmp, prj->name) < 0) {
188        fprintf(stderr, "rename %s to %s: %s\n", tmp, prj->name,
189            strerror(errno));
190        exit(1);
191    }
192}
solidify/project.h
1/*
2 * project.h - Load and save solidify project descriptions
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 * Project file structure:
15 *
16 * line 1: file name of top face (required)
17 * line 2: file name of bottom face (required)
18 * line 3 and beyond, separated by whitespace:
19 * - z distance between faces, in mm
20 * - z distance of the z0 plane from the midpoint of the top face, in mm
21 * - inclination of the x axis of the z0 plane of the top face, in degrees
22 * - inclination of the y axis of the z0 plane of the top face, in degrees
23 * - rotation of the top face, in degrees
24 * - x shift of the top face, in mm
25 * - y shift of the top face, in mm
26 * - the above 6 fields for the bottom face
27 */
28
29#ifndef PROJECT_H
30#define PROJECT_H
31
32#include "solid.h"
33
34
35struct project {
36    const char *name;
37    const char *top;
38    const char *bottom;
39    struct solid s;
40};
41
42
43struct project *new_project(const char *name,
44    const char *top, const char *bottom, double dist_mm);
45struct project *load_project(const char *name);
46void save_project(const struct project *prj);
47
48#endif /* !PROJECT_H */
solidify/solid.h
1/*
2 * solid.h - Data structure and handling of a solid made of two opposing 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#ifndef SOLID_H
14#define SOLID_H
15
16struct solid {
17    struct face *a, *b;
18    double dist;
19};
20
21
22void povray(const char *name, const struct solid *s);
23
24#endif /* !SOLID_H */
solidify/solidify.c
1/*
2 * solidify.c - Merge two opposing faces of a part into a solid
3 *
4 * Written 2010 by Werner Almesberger
5 * Copyright 2010 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 <unistd.h>
17#include <string.h>
18#include <locale.h>
19#include <gtk/gtk.h>
20
21#include "face.h"
22#include "solid.h"
23#include "project.h"
24#include "style.h"
25#include "level.h"
26#include "overlap.h"
27
28
29static struct project *prj;
30static const struct face *active; /* NULL if overlapping */
31static GtkWidget *canvas;
32
33
34static void clicked(GtkButton *button, gpointer user_data)
35{
36    struct face *face = user_data;
37
38    if (active == face)
39        return;
40
41    gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(canvas)));
42
43    if (face)
44        level(canvas, face);
45    else
46        overlap(canvas, &prj->s);
47    active = face;
48
49    gtk_widget_show_all(canvas);
50}
51
52
53static GtkWidget *gui_buttons(void)
54{
55    GtkWidget *vbox, *but;
56
57    vbox = gtk_vbox_new(FALSE, 0);
58
59    but = gtk_button_new_with_label("A");
60    gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0);
61    g_signal_connect(G_OBJECT(but), "clicked",
62        G_CALLBACK(clicked), prj->s.a);
63
64    but = gtk_button_new_with_label("B");
65    gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0);
66    g_signal_connect(G_OBJECT(but), "clicked",
67        G_CALLBACK(clicked), prj->s.b);
68
69    but = gtk_button_new_with_label("A+B");
70    gtk_box_pack_start(GTK_BOX(vbox), but, FALSE, FALSE, 0);
71    g_signal_connect(G_OBJECT(but), "clicked",
72        G_CALLBACK(clicked), NULL);
73
74    return vbox;
75}
76
77
78static gboolean key_press_event(GtkWidget *widget, GdkEventKey *event,
79    gpointer data)
80{
81    if (event->keyval == 'q')
82        gtk_main_quit();
83    return TRUE;
84}
85
86
87static void gui(void)
88{
89    GtkWidget *root, *hbox, *buttons;
90
91    root = gtk_window_new(GTK_WINDOW_TOPLEVEL);
92    gtk_window_set_position(GTK_WINDOW(root), GTK_WIN_POS_CENTER);
93
94    hbox = gtk_hbox_new(FALSE, 0);
95    gtk_container_add(GTK_CONTAINER(root), hbox);
96
97    canvas = gtk_event_box_new();
98    gtk_box_pack_start(GTK_BOX(hbox), canvas, FALSE, FALSE, 0);
99
100    /* initialize root->window */
101    gtk_widget_show_all(root);
102
103    buttons = gui_buttons();
104    gtk_box_pack_start(GTK_BOX(hbox), buttons, FALSE, FALSE, 0);
105
106    level(canvas, prj->s.a);
107    active = prj->s.a;
108
109    init_style(root->window);
110
111    gtk_widget_show_all(root);
112
113    g_signal_connect(G_OBJECT(root), "key-press-event",
114        G_CALLBACK(key_press_event), NULL);
115    g_signal_connect(G_OBJECT(root), "destroy",
116        G_CALLBACK(gtk_main_quit), NULL);
117
118    gtk_main();
119}
120
121
122static void usage(const char *name)
123{
124    fprintf(stderr, "usage: %s project [top bottom dist]\n", name);
125    exit(1);
126}
127
128
129int main(int argc, char **argv)
130{
131    double dist;
132
133    gtk_init(&argc, &argv);
134    setlocale(LC_ALL, "C"); /* damage control */
135
136    switch (argc) {
137    case 2:
138        prj = load_project(argv[1]);
139        break;
140    case 5:
141        dist = atof(argv[4]);
142        prj = new_project(argv[1], argv[2], argv[3], dist);
143        break;
144    default:
145        usage(*argv);
146    }
147
148    gui();
149
150    save_project(prj);
151
152    if (!isatty(1)) {
153        const char *slash = strrchr(prj->name, '/');
154        char tmp[1000]; /* @@@ enough */
155
156        strcpy(tmp, slash ? slash+1 : prj->name);
157        if (strchr(tmp, '.'))
158            *strchr(tmp, '.') = 0;
159        povray(tmp, &prj->s);
160    }
161
162    return 0;
163}
solidify/style.c
1/*
2 * style.c - GUI style parameters and items
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 <gtk/gtk.h>
16
17#include "style.h"
18
19
20GdkGC *gc_osd;
21
22
23static GdkColor get_color(GdkDrawable *da, const char *spec)
24{
25    GdkColormap *cmap;
26    GdkColor color;
27
28    cmap = gdk_drawable_get_colormap(da);
29    if (!gdk_color_parse(spec, &color))
30        abort();
31    if (!gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE))
32        abort();
33    return color;
34}
35
36
37static GdkGC *gc(GdkDrawable *da, const char *spec, int width)
38{
39    GdkGCValues gc_values = {
40        .background = get_color(da, "black"),
41        .foreground = get_color(da, spec),
42        .line_width = width,
43    };
44
45    return gdk_gc_new_with_values(da, &gc_values,
46        GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_LINE_WIDTH);
47}
48
49
50void init_style(GdkDrawable *da)
51{
52    gc_osd = gc(da, "#ffff00", 4);
53}
solidify/style.h
1/*
2 * style.h - GUI style parameters and items
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#ifndef STYLE_H
14#define STYLE_H
15
16#include <gtk/gtk.h>
17
18
19extern GdkGC *gc_osd;
20
21
22#define OSD_PROXIMITY 20 /* pixels */
23#define LEVEL_CENTER_DIV 5 /* fraction of diagonal */
24#define OVERLAP_BORDER 10 /* pixels around min. drawing area */
25#define OVERLAP_CENTER_DIV 5 /* fraction of diagonal */
26#define SLOWEST_ROT 3 /* thrice the half-diagonal */
27#define FASTEST_ROT 2 /* one pixel in distance of 2 pixels */
28#define DIST_STEPS 5 /* fastest shift is 5 px/wheel step */
29
30
31void init_style(GdkDrawable *da);
32
33#endif /* !STYLE_H */
solidify/util.c
1/*
2 * util.c - Common utility functions
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 <gtk/gtk.h>
15
16#include "util.h"
17
18
19void draw_circle(GdkDrawable *da, GdkGC *gc, int x, int y, int r)
20{
21    gdk_draw_arc(da, gc, FALSE, x-r, y-r, 2*r, 2*r, 0, 360*64);
22}
solidify/util.h
1/*
2 * util.h - Common utility functions
3 *
4 * Written 2009 by Werner Almesberger
5 * Copyright 2009 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#ifndef UTIL_H
14#define UTIL_H
15
16#include <stdlib.h>
17#include <string.h>
18#include <gtk/gtk.h>
19
20
21#define alloc_size(s) \
22    ({ void *alloc_size_tmp = malloc(s); \
23    if (!alloc_size_tmp) \
24        abort(); \
25    alloc_size_tmp; })
26
27#define alloc_type(t) ((t *) alloc_size(sizeof(t)))
28
29#define stralloc(s) \
30    ({ char *stralloc_tmp = strdup(s); \
31    if (!stralloc_tmp) \
32        abort(); \
33    stralloc_tmp; })
34
35
36void draw_circle(GdkDrawable *da, GdkGC *gc, int x, int y, int r);
37
38#endif /* !UTIL_H */

Archive Download the corresponding diff file

Branches:
master



interactive