fabmodules/src/core/vol_stl.c

668 wiersze
20 KiB
C

//
// vol_stl.c
// .vol to .stl
//
// Neil Gershenfeld 5/21/14
// (c) Massachusetts Institute of Technology 2014
//
// 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, float *w, float *array) {
switch(c) {
case 'a':
array[0] = x+(w[0]-t)/(w[0]-w[1]);
array[1] = y;
array[2] = z;
break;
case 'b':
array[0] = x+1;
array[1] = y+(w[1]-t)/(w[1]-w[3]);
array[2] = z;
break;
case 'c':
array[0] = x+(w[2]-t)/(w[2]-w[3]);
array[1] = y+1;
array[2] = z;
break;
case 'd':
array[0] = x;
array[1] = y+(w[0]-t)/(w[0]-w[2]);
array[2] = z;
break;
case 'e':
array[0] = x;
array[1] = y;
array[2] = z+(w[0]-t)/(w[0]-w[4]);
break;
case 'f':
array[0] = x+1;
array[1] = y;
array[2] = z+(w[1]-t)/(w[1]-w[5]);
break;
case 'g':
array[0] = x+1;
array[1] = y+1;
array[2] = z+(w[3]-t)/(w[3]-w[7]);
break;
case 'h':
array[0] = x;
array[1] = y+1;
array[2] = z+(w[2]-t)/(w[2]-w[6]);
break;
case 'i':
array[0] = x+(w[4]-t)/(w[4]-w[5]);
array[1] = y;
array[2] = z+1;
break;
case 'j':
array[0] = x+1;
array[1] = y+(w[5]-t)/(w[5]-w[7]);
array[2] = z+1;
break;
case 'k':
array[0] = x+(w[6]-t)/(w[6]-w[7]);
array[1] = y+1;
array[2] = z+1;
break;
case 'l':
array[0] = x;
array[1] = y+(w[4]-t)/(w[4]-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, float *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,float **lower_array,float **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
//
FILE *input_file;
uint64_t count;
uint16_t itype;
int x,y,z,i,j,k,n,p,imin,imax,nx,ny,nz;
int image_width,image_height,color_resolution,ret;
float w[8],**lower_array,**upper_array;
float threshold,voxel_size;
float ftype,fmin=1e10,fmax=-1e10;
char format,comment[256],rules[255][20];
struct fab_vars v;
init_vars(&v);
//
// command line args
//
if (!((argc == 6) || (argc == 7) || (argc == 8) || (argc == 9) || (argc == 10))) {
printf("command line: vol_stl in.vol out.stl nx ny nz [format [threshold [size [points [angle]]]]]\n");
printf(" in.vol = input VOL file\n");
printf(" out.stl = output STL file\n");
printf(" nx,ny,nz = x,y,z input voxel number\n");
printf(" format = 'f' for float 32, 'i' for uint16_t (default 'f')\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);
}
format = 'f';
p = 0;
threshold = 0.5;
voxel_size = -1;
image_width = -1;
image_height = -1;
sscanf(argv[3],"%d",&nx);
sscanf(argv[4],"%d",&ny);
sscanf(argv[5],"%d",&nz);
if (argc >= 7) {
sscanf(argv[6],"%c",&format);
if (!((format == 'f') || (format == 'i'))) {
printf("vol_gif: oops -- format must be 'f' or 'i'\n");
exit(-1);
}
}
if (argc >= 8)
sscanf(argv[7],"%f",&threshold);
if (argc >= 9)
sscanf(argv[8],"%f",&voxel_size);
if (argc >= 10)
sscanf(argv[9],"%d",&p);
//
// initialize the rule table
//
init_rules(rules);
//
// check and find limits
//
input_file = fopen(argv[1],"rb");
if (input_file == NULL) {
printf("vol_stl: oops -- can not open %s\n",argv[1]);
exit(-1);
}
printf("read %s\n",argv[1]);
if (format == 'f') {
count = 0;
while (fread(&ftype,sizeof(ftype),1,input_file) != 0) {
if (ftype > fmax) fmax = ftype;
if (ftype < fmin) fmin = ftype;
count += 1;
}
}
else if (format == 'i') {
count = 0;
while (fread(&itype,sizeof(itype),1,input_file) != 0) {
if (itype > fmax) fmax = itype;
if (itype < fmin) fmin = itype;
count += 1;
}
}
if (nx*ny*nz != count) {
printf("vol_stl: oops -- file size doesn't match pixel number\n");
exit(-1);
}
printf(" %" PRIu64 " points, min %f, max %f\n",count,fmin,fmax);
printf(" nx ny nz: %d %d %d\n",nx,ny,nz);
rewind(input_file);
//
// scan the file
//
printf("read %s\n",argv[1]);
//
// set threshold
//
threshold = fmin + threshold*(fmax-fmin);
//
// add empty border
//
image_width = nx+2;
image_height = ny+2;
//
// allocate arrays
//
lower_array = malloc(image_height*sizeof(float *));
for (y = 0; y < image_height; ++y) {
lower_array[y] = malloc(image_width*sizeof(float));
for (x = 0; x < image_width; ++x)
lower_array[y][x] = 0;
}
upper_array = malloc(image_height*sizeof(float *));
for (y = 0; y < image_height; ++y) {
upper_array[y] = malloc(image_width*sizeof(float));
for (x = 0; x < image_width; ++x)
upper_array[y][x] = 0;
}
//
// read the file
//
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;
for (z = 0; z < nz; ++z) {
//
// read 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 < ny; ++y) {
for (x = 0; x < nx; ++x) {
lower_array[y+1][x+1] = upper_array[y+1][x+1];
if (format == 'f') {
fread(&ftype,sizeof(ftype),1,input_file);
upper_array[y+1][x+1] = ftype;
}
else if (format == 'i') {
fread(&itype,sizeof(itype),1,input_file);
upper_array[y+1][x+1] = itype;
}
}
}
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);
}
}
}
}
}
}
}
//
// 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
//
fclose(input_file);
exit(0);
}