fabmodules/src/core/gif_stl.c

718 wiersze
22 KiB
C

//
// gif_stl.c
// .gif to .stl
//
// Neil Gershenfeld 12/11/13
// (c) Massachusetts Institute of Technology 2013
//
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose, but must
// acknowledge the fab modules project. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all
// liability.
//
#define MAX_LINE 10000
#include "fab.h"
/*
vertices:
---
6 7
4 5
---
2 3
0 1
---
edges:
---
k
l j
i
---
h g
e f
---
c
d b
a
---
*/
//
// rotate_x
// rotate rule around x and add
//
void rotate_x(char rules[255][20], int *index) {
int i,b[8];
int old_index = *index;
for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
*index =
(b[4] << 0)
+(b[5] << 1)
+(b[0] << 2)
+(b[1] << 3)
+(b[6] << 4)
+(b[7] << 5)
+(b[2] << 6)
+(b[3] << 7);
for (i
= 0; i < strlen(rules[old_index]); ++i) {
switch (rules[old_index][i]) {
case 'a': rules[*index][i] = 'c'; break;
case 'b': rules[*index][i] = 'g'; break;
case 'c': rules[*index][i] = 'k'; break;
case 'd': rules[*index][i] = 'h'; break;
case 'e': rules[*index][i] = 'd'; break;
case 'f': rules[*index][i] = 'b'; break;
case 'g': rules[*index][i] = 'j'; break;
case 'h': rules[*index][i] = 'l'; break;
case 'i': rules[*index][i] = 'a'; break;
case 'j': rules[*index][i] = 'f'; break;
case 'k': rules[*index][i] = 'i'; break;
case 'l': rules[*index][i] = 'e'; break;
case ' ': rules[*index][i] = ' '; break;
}
}
}
//
// rotate_y
// rotate rule around y and add
//
void rotate_y(char rules[255][20], int *index) {
int i,b[8];
int old_index = *index;
for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
*index =
(b[1] << 0)
+(b[5] << 1)
+(b[3] << 2)
+(b[7] << 3)
+(b[0] << 4)
+(b[4] << 5)
+(b[2] << 6)
+(b[6] << 7);
for (i = 0; i < strlen(rules[old_index]); ++i) {
switch (rules[old_index][i]) {
case 'a': rules[*index][i] = 'e'; break;
case 'b': rules[*index][i] = 'd'; break;
case 'c': rules[*index][i] = 'h'; break;
case 'd': rules[*index][i] = 'l'; break;
case 'e': rules[*index][i] = 'i'; break;
case 'f': rules[*index][i] = 'a'; break;
case 'g': rules[*index][i] = 'c'; break;
case 'h': rules[*index][i] = 'k'; break;
case 'i': rules[*index][i] = 'f'; break;
case 'j': rules[*index][i] = 'b'; break;
case 'k': rules[*index][i] = 'g'; break;
case 'l': rules[*index][i] = 'j'; break;
case ' ': rules[*index][i] = ' '; break;
}
}
}
//
// rotate_z
// rotate rule around z and add
//
void rotate_z(char rules[255][20], int *index) {
int i,b[8];
int old_index = *index;
for (i = 0; i < 8; ++i) b[i] = (*index & (1 << i)) >> i;
*index =
(b[2] << 0)
+(b[0] << 1)
+(b[3] << 2)
+(b[1] << 3)
+(b[6] << 4)
+(b[4] << 5)
+(b[7] << 6)
+(b[5] << 7);
for (i = 0; i < strlen(rules[old_index]); ++i) {
switch (rules[old_index][i]) {
case 'a': rules[*index][i] = 'b'; break;
case 'b': rules[*index][i] = 'c'; break;
case 'c': rules[*index][i] = 'd'; break;
case 'd': rules[*index][i] = 'a'; break;
case 'e': rules[*index][i] = 'f'; break;
case 'f': rules[*index][i] = 'g'; break;
case 'g': rules[*index][i] = 'h'; break;
case 'h': rules[*index][i] = 'e'; break;
case 'i': rules[*index][i] = 'j'; break;
case 'j': rules[*index][i] = 'k'; break;
case 'k': rules[*index][i] = 'l'; break;
case 'l': rules[*index][i] = 'i'; break;
case ' ': rules[*index][i] = ' '; break;
}
}
}
//
// print_rules
// print the rule table
//
void print_rules(char rules[255][20]) {
int i,b;
printf("76543210\n");
for (i = 0; i < 256; ++i) {
for (b = 7; b >= 0; --b) {
printf("%d",(i & (1 << b)) >> b);
}
printf(" %d %s\n",i,rules[i]);
}
}
// add_rules
// add a rule and its variants to the table
//
void add_rules(char rules[255][20], int index, char *edges) {
int i,j,k,l;
strcpy(rules[index],edges);
for (i=0; i<4; ++i) {
for (j=0; j<4; ++j) {
for (k=0; k<4; ++k) {
rotate_x(rules,&index);
}
rotate_y(rules,&index);
}
rotate_z(rules,&index);
}
}
//
// init_rules
// create the rule table
//
void init_rules(char rules[255][20]) {
int i,j;
for (i = 0; i < 256; ++i)
for (j = 0; j < 20; ++j)
rules[i][j] = 0;
add_rules(rules,0b00000000,""); // 0
add_rules(rules,0b11111111,""); // ~0
add_rules(rules,0b00000001,"eda"); // 1
add_rules(rules,0b11111110,"ade"); // ~1
add_rules(rules,0b00000011,"fed dbf"); // 2
add_rules(rules,0b11111100,"def fbd"); // ~2
add_rules(rules,0b00100001,"eda jif"); // 3
add_rules(rules,0b11011110,"ade fij"); // ~3
add_rules(rules,0b10000001,"eda gkj"); // 4
add_rules(rules,0b01111110,"ade jkg"); // ~4
add_rules(rules,0b00001110,"fhg fdh fad"); // 5
add_rules(rules,0b11110001,"ghf hdf daf"); // ~5
add_rules(rules,0b10000011,"fed fdb gkj"); // 6
add_rules(rules,0b01111100,"def bdf jkg"); // ~6
add_rules(rules,0b10010010,"bfa ile gkj"); // 7
add_rules(rules,0b01101101,"afb eli jkg"); // ~7
add_rules(rules,0b00001111,"ehg feg"); // 8
add_rules(rules,0b11110000,"ghe gef"); // ~8
add_rules(rules,0b01001101,"elk eka akg agb"); // 9
add_rules(rules,0b10110010,"kle ake gka bga"); // ~9
add_rules(rules,0b10011001,"ild ida ckj cjb"); // 10
add_rules(rules,0b01100110,"dli adi jkc bjc"); // ~10
add_rules(rules,0b10001101,"hkj hja hae ajb"); // 11
add_rules(rules,0b01110010,"jkh ajh eah bja"); // ~11
add_rules(rules,0b00011110,"ile hgf hfd dfa"); // 12
add_rules(rules,0b11100001,"eli fgh dfh afd"); // ~12
add_rules(rules,0b01101001,"eda bcg lkh jif"); // 13
add_rules(rules,0b10010110,"ade gcb hkl fij"); // ~13
add_rules(rules,0b01001110,"lkg lga lad agf"); // 14
add_rules(rules,0b10110001,"gkl agl dal fga"); // ~14
}
//
// vertex
// add a triangle vertex
//
void vertex(char c, int x, int y, int z, float voxel_size, float t, uint32_t *w, float *array) {
switch(c) {
case 'a':
array[0] = x+(w[0]-t)/(w[0]-((float) w[1]));
array[1] = y;
array[2] = z;
break;
case 'b':
array[0] = x+1;
array[1] = y+(w[1]-t)/(w[1]-((float) w[3]));
array[2] = z;
break;
case 'c':
array[0] = x+(w[2]-t)/(w[2]-((float) w[3]));
array[1] = y+1;
array[2] = z;
break;
case 'd':
array[0] = x;
array[1] = y+(w[0]-t)/(w[0]-((float) w[2]));
array[2] = z;
break;
case 'e':
array[0] = x;
array[1] = y;
array[2] = z+(w[0]-t)/(w[0]-((float) w[4]));
break;
case 'f':
array[0] = x+1;
array[1] = y;
array[2] = z+(w[1]-t)/(w[1]-((float) w[5]));
break;
case 'g':
array[0] = x+1;
array[1] = y+1;
array[2] = z+(w[3]-t)/(w[3]-((float) w[7]));
break;
case 'h':
array[0] = x;
array[1] = y+1;
array[2] = z+(w[2]-t)/(w[2]-((float) w[6]));
break;
case 'i':
array[0] = x+(w[4]-t)/(w[4]-((float) w[5]));
array[1] = y;
array[2] = z+1;
break;
case 'j':
array[0] = x+1;
array[1] = y+(w[5]-t)/(w[5]-((float) w[7]));
array[2] = z+1;
break;
case 'k':
array[0] = x+(w[6]-t)/(w[6]-((float) w[7]));
array[1] = y+1;
array[2] = z+1;
break;
case 'l':
array[0] = x;
array[1] = y+(w[4]-t)/(w[4]-((float) w[6]));
array[2] = z+1;
break;
}
array[0] = voxel_size*array[0];
array[1] = voxel_size*array[1];
array[2] = voxel_size*array[2];
}
//
// triangulate
// triangulate voxel at lattice site x,y,z with vertex weights w
//
void triangulate(int x, int y, int z, float voxel_size, float t, uint32_t *w, char rules[255][20], struct fab_vars *v) {
int i,index;
char c;
//
// set index code
//
index = ((w[0] >= t) ? 0 : 1)
+ ((w[1] >= t) ? 0 : 2)
+ ((w[2] >= t) ? 0 : 4)
+ ((w[3] >= t) ? 0 : 8)
+ ((w[4] >= t) ? 0 : 16)
+ ((w[5] >= t) ? 0 : 32)
+ ((w[6] >= t) ? 0 : 64)
+ ((w[7] >= t) ? 0 : 128);
//
// add triangles for rule
//
i = 0;
while (1) {
//
// loop over rule chars
//
c = rules[index][i];
if (c == 0)
//
// end of rule
//
break;
else if (c == ' ') {
//
// space between rules
//
i += 1;
continue;
}
else {
//
// create triangle for rule
//
v->mesh->last = malloc(sizeof(struct fab_mesh_triangle_type));
v->mesh->last->previous = v->mesh->triangle;
v->mesh->triangle->next = v->mesh->last;
v->mesh->triangle = v->mesh->last;
v->mesh->triangle->next = 0;
v->mesh->triangle->attribute = 0;
v->mesh->triangle->normal[0] = v->mesh->triangle->normal[1] = v->mesh->triangle->normal[2] = 0;
//
// add vertices for rule
//
c = rules[index][i];
vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v0);
i += 1;
c = rules[index][i];
vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v1);
i += 1;
c = rules[index][i];
vertex(c,x,y,z,voxel_size,t,w,v->mesh->triangle->v2);
i += 1;
}
}
}
void fab_write_stl(struct fab_vars *v, char *output_file_name) {
//
// write mesh as STL
//
FILE *output_file;
char buf[80];
uint32_t count;
//
// open output file
//
output_file = fopen(output_file_name, "wb");
if (output_file == 0) {
printf("fab.c: oops -- can't open %s\n",output_file_name);
exit(-1);
}
//
// write header
//
fwrite(buf,80,1,output_file);
//
// write count
//
count = 0;
v->mesh->triangle = v->mesh->first;
while (v->mesh->triangle->next != 0) {
v->mesh->triangle = v->mesh->triangle->next;
count += 1;
}
fwrite(&count,4,1,output_file);
//
// write triangles
//
v->mesh->triangle = v->mesh->first;
while (v->mesh->triangle->next != 0) {
v->mesh->triangle = v->mesh->triangle->next;
fwrite(v->mesh->triangle->normal,4,3,output_file);
fwrite(v->mesh->triangle->v0,4,3,output_file);
fwrite(v->mesh->triangle->v1,4,3,output_file);
fwrite(v->mesh->triangle->v2,4,3,output_file);
fwrite(&(v->mesh->triangle->attribute),2,1,output_file);
}
//
// return
//
printf("wrote %d triangles to %s\n",count,output_file_name);
fclose(output_file);
}
float interp(int x,int y,int i,int j,int k,uint32_t **lower_array,uint32_t **upper_array,int p) {
//
// trilinear interpolation within a voxel
//
return (lower_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+ lower_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+ lower_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+ lower_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((p+1.0-k)/(p+1.0))
+ upper_array[y][x]*((p+1.0-i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+ upper_array[y][x+1]*((i)/(p+1.0))*((p+1.0-j)/(p+1.0))*((k)/(p+1.0))
+ upper_array[y+1][x]*((p+1.0-i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0))
+ upper_array[y+1][x+1]*((i)/(p+1.0))*((j)/(p+1.0))*((k)/(p+1.0)));
}
int main(int argc, char **argv) {
//
// local vars
//
GifFileType *GIFfile;
GifRecordType GIFtype;
GifByteType *GIFextension;
GifPixelType *GIFline;
uint32_t w[8],**lower_array,**upper_array;
int x,y,z,i,j,k,n,p,imin,imax;
int image_width,image_height,image_count,color_resolution,GIFcode,ret;
float threshold,voxel_size;
char comment[256],rules[255][20];
struct fab_vars v;
init_vars(&v);
//
// command line args
//
if (!((argc == 3) || (argc == 4) || (argc == 5) || (argc == 6))) {
printf("command line: gif_stl in.gif out.stl [threshold [size [points [angle]]]]\n");
printf(" in.gif = input GIF section file\n");
printf(" out.stl = output STL file\n");
printf(" threshold: surface intensity threshold (0 = min, 1 = max, default 0.5))\n");
printf(" size = voxel size (mm, default from file))\n");
printf(" points = points to interpolate per point (default 0)\n");
printf(" to be implemented: angle = minimum relative face angle to decimate vertices (default 0)\n");
exit(-1);
}
p = 0;
threshold = 0.5;
voxel_size = -1;
image_width = -1;
image_height = -1;
image_count = -1;
if (argc >= 4)
sscanf(argv[3],"%f",&threshold);
if (argc >= 5)
sscanf(argv[4],"%f",&voxel_size);
if (argc >= 6)
sscanf(argv[5],"%d",&p);
//
// initialize the rule table
//
init_rules(rules);
//
// scan the file
//
printf("read %s\n",argv[1]);
color_resolution = -1;
GIFfile = DGifOpenFileName(argv[1]);
if (GIFfile == NULL) {
printf("gif_stl: oops -- can not open %s\n",argv[1]);
exit(-1);
}
GIFline = malloc(MAX_LINE*sizeof(GifPixelType));
imin = 256;
imax = 0;
do {
DGifGetRecordType(GIFfile,&GIFtype);
switch (GIFtype) {
case IMAGE_DESC_RECORD_TYPE:
DGifGetImageDesc(GIFfile);
image_width = GIFfile->SWidth;
image_height = GIFfile->SHeight;
image_count = GIFfile->ImageCount;
color_resolution = GIFfile->SColorResolution;
for (y = 0; y < GIFfile->SHeight; ++y) {
ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
if (ret != GIF_OK) {
printf("gif_stl: oops -- error reading line\n");
exit(-1);
}
for (x = 0; x < GIFfile->SWidth; ++x) {
if (GIFline[x] < imin) imin = GIFline[x];
if (GIFline[x] > imax) imax = GIFline[x];
}
}
break;
case EXTENSION_RECORD_TYPE:
DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
if (GIFcode == COMMENT_EXT_FUNC_CODE) {
n = GIFextension[0];
for (i = 1; i <= n; ++i)
comment[i-1] = GIFextension[i];
comment[n] = 0;
if (voxel_size == -1)
sscanf(comment,"mm per pixel: %f;",&voxel_size);
}
while (GIFextension != NULL)
DGifGetExtensionNext(GIFfile,&GIFextension);
break;
case SCREEN_DESC_RECORD_TYPE:
DGifGetScreenDesc(GIFfile);
break;
case TERMINATE_RECORD_TYPE:
break;
case UNDEFINED_RECORD_TYPE:
printf("gif_stl: oops -- undefined GIF record type\n");
exit(-1);
break;
}
} while (GIFtype != TERMINATE_RECORD_TYPE);
if (GIFfile == NULL) {
printf("gif_stl: oops -- can not open %s\n",argv[1]);
exit(-1);
}
if (voxel_size == -1) {
voxel_size = 1.0;
printf(" no pixel size found, assuming 1 mm\n");
}
printf(" voxel size (mm): %f, color resolution (bits): %d\n",voxel_size,color_resolution);
printf(" intensity min: %d max: %d\n",imin,imax);
printf(" number of images: %d, image width %d, image height %d\n",image_count,image_width,image_height);
//
// set threshold
//
threshold = imin + threshold*(imax-imin);
//
// add empty border
//
image_width += 2;
image_height += 2;
image_count += 2;
//
// allocate arrays
//
lower_array = malloc(image_height*sizeof(uint32_t *));
for (y = 0; y < image_height; ++y) {
lower_array[y] = malloc(image_width*sizeof(uint32_t));
for (x = 0; x < image_width; ++x)
lower_array[y][x] = 0;
}
upper_array = malloc(image_height*sizeof(uint32_t *));
for (y = 0; y < image_height; ++y) {
upper_array[y] = malloc(image_width*sizeof(uint32_t));
for (x = 0; x < image_width; ++x)
upper_array[y][x] = 0;
}
//
// read the file
//
DGifCloseFile(GIFfile);
GIFfile = DGifOpenFileName(argv[1]);
if (GIFfile == NULL) {
printf("gif_stl: oops -- can not open %s\n",argv[1]);
exit(-1);
}
z = 0;
v.mesh = malloc(sizeof(struct fab_mesh_type));
v.mesh->triangle = malloc(sizeof(struct fab_mesh_triangle_type));
v.mesh->first = v.mesh->triangle;
v.mesh->last = v.mesh->triangle;
v.mesh->triangle->previous = v.mesh->triangle->next = 0;
do {
DGifGetRecordType(GIFfile,&GIFtype);
switch (GIFtype) {
case IMAGE_DESC_RECORD_TYPE:
//
// read image
//
DGifGetImageDesc(GIFfile);
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b layer = %d",z);
//
// read layer
//
for (y = 0; y < (image_height-2); ++y) {
ret = DGifGetLine(GIFfile,GIFline,GIFfile->SWidth);
if (ret != GIF_OK) {
printf("gif_stl: oops -- error reading line\n");
exit(-1);
}
for (x = 0; x < (image_width-2); ++x) {
lower_array[y+1][x+1] = upper_array[y+1][x+1];
upper_array[y+1][x+1] = GIFline[x];
}
}
if (p == 0) {
//
// no interpolation, loop over layer voxels
//
for (x = 0; x < (image_width-1); ++x) {
for (y = 0; y < (image_height-1); ++y) {
w[0] = lower_array[y][x];
w[1] = lower_array[y][x+1];
w[2] = lower_array[y+1][x];
w[3] = lower_array[y+1][x+1];
w[4] = upper_array[y][x];
w[5] = upper_array[y][x+1];
w[6] = upper_array[y+1][x];
w[7] = upper_array[y+1][x+1];
triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
}
}
}
else {
//
// yes interpolation, loop over layer sub-voxels
//
for (x = 0; x < (image_width-1); ++x) {
for (y = 0; y < (image_height-1); ++y) {
for (i = 0; i <= p; ++i) {
for (j = 0; j <= p; ++j) {
for (k = 0; k <= p; ++k) {
w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
}
}
}
}
}
}
z += 1;
break;
case EXTENSION_RECORD_TYPE:
DGifGetExtension(GIFfile,&GIFcode,&GIFextension);
while (GIFextension != NULL)
DGifGetExtensionNext(GIFfile,&GIFextension);
break;
case SCREEN_DESC_RECORD_TYPE:
DGifGetScreenDesc(GIFfile);
break;
case TERMINATE_RECORD_TYPE:
break;
case UNDEFINED_RECORD_TYPE:
printf("gif_stl: oops -- undefined GIF record type\n");
exit(-1);
break;
}
} while (GIFtype != TERMINATE_RECORD_TYPE);
//
// add empty top layer
//
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b layer = %d",z);
for (y = 0; y < (image_height-2); ++y) {
for (x = 0; x < (image_width-2); ++x) {
lower_array[y+1][x+1] = upper_array[y+1][x+1];
upper_array[y+1][x+1] = 0;
}
}
if (p == 0) {
//
// no interpolation, loop over layer voxels
//
for (x = 0; x < (image_width-1); ++x) {
for (y = 0; y < (image_height-1); ++y) {
w[0] = lower_array[y][x];
w[1] = lower_array[y][x+1];
w[2] = lower_array[y+1][x];
w[3] = lower_array[y+1][x+1];
w[4] = upper_array[y][x];
w[5] = upper_array[y][x+1];
w[6] = upper_array[y+1][x];
w[7] = upper_array[y+1][x+1];
triangulate(x,y,z,voxel_size,threshold,w,rules,&v);
}
}
}
else {
//
// yes interpolation, loop over layer sub-voxels
//
for (x = 0; x < (image_width-1); ++x) {
for (y = 0; y < (image_height-1); ++y) {
for (i = 0; i <= p; ++i) {
for (j = 0; j <= p; ++j) {
for (k = 0; k <= p; ++k) {
w[0] = interp(x,y,i,j,k,lower_array,upper_array,p);
w[1] = interp(x,y,i+1,j,k,lower_array,upper_array,p);
w[2] = interp(x,y,i,j+1,k,lower_array,upper_array,p);
w[3] = interp(x,y,i+1,j+1,k,lower_array,upper_array,p);
w[4] = interp(x,y,i,j,k+1,lower_array,upper_array,p);
w[5] = interp(x,y,i+1,j,k+1,lower_array,upper_array,p);
w[6] = interp(x,y,i,j+1,k+1,lower_array,upper_array,p);
w[7] = interp(x,y,i+1,j+1,k+1,lower_array,upper_array,p);
triangulate((1+p)*x+i,(1+p)*y+j,(1+p)*z+k,voxel_size,threshold,w,rules,&v);
}
}
}
}
}
}
printf("\n");
//
// write STL
//
fab_write_stl(&v,argv[2]);
//
// exit
//
DGifCloseFile(GIFfile);
exit(0);
}