Initial commit. For older history, see project ben-scans.

http://projects.qi-hardware.com/index.php/p/ben-scans/
This commit is contained in:
Werner Almesberger 2010-09-25 04:46:16 -03:00
commit e8d6837065
23 changed files with 2139 additions and 0 deletions

90
solidify/Makefile Normal file
View File

@ -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)

85
solidify/array.c Normal file
View File

@ -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 <stdlib.h>
#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;
}
}

52
solidify/array.h Normal file
View File

@ -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 <limits.h>
#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 */

204
solidify/face.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/wait.h>
#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;
}

39
solidify/face.h Normal file
View File

@ -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 */

52
solidify/histo.c Normal file
View File

@ -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;
}

29
solidify/histo.h Normal file
View File

@ -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 */

200
solidify/level.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gtk/gtk.h>
#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);
}

23
solidify/level.h Normal file
View File

@ -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 <gtk/gtk.h>
#include "face.h"
void level(GtkWidget *canvas, struct face *f);
#endif /* !LEVEL_H */

51
solidify/main.pov Normal file
View File

@ -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 }
}

66
solidify/matrix.c Normal file
View File

@ -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 <assert.h>
#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];
}

45
solidify/matrix.h Normal file
View File

@ -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 */

487
solidify/overlap.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <gtk/gtk.h>
#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);
}
}

23
solidify/overlap.h Normal file
View File

@ -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 <gtk/gtk.h>
#include "solid.h"
void overlap(GtkWidget *canvas, struct solid *solid);
#endif /* !OVERLAP_H */

120
solidify/povray.c Normal file
View File

@ -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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#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");
}

192
solidify/project.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#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);
}
}

48
solidify/project.h Normal file
View File

@ -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 */

24
solidify/solid.h Normal file
View File

@ -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 */

163
solidify/solidify.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <locale.h>
#include <gtk/gtk.h>
#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;
}

53
solidify/style.c Normal file
View File

@ -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 <stdlib.h>
#include <gtk/gtk.h>
#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);
}

33
solidify/style.h Normal file
View File

@ -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 <gtk/gtk.h>
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 */

22
solidify/util.c Normal file
View File

@ -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 <gtk/gtk.h>
#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);
}

38
solidify/util.h Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#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 */