fabmodules/src/core/fab.c

2377 wiersze
67 KiB
C

//
// fab.c
// fab modules routines
//
// Neil Gershenfeld 9/8/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.
//
#include "fab.h"
//
// initialization
//
void init_vars(struct fab_vars *v) {
//
// fab_vars initialization
//
v->empty = 0;
v->interior = 1;
v->edge = (1 << 1);
v->north = (1 << 2);
v->west = (2 << 2);
v->east = (3 << 2);
v->south = (4 << 2);
v->stop = (5 << 2);
v->corner = (6 << 2);
v->corner2 = (7 << 2);
v->direction = (7 << 2);
v->g = 0;
v->h = 0;
v->dx = 0;
v->dy = 0;
v->dz = 0;
v->xmin = 0;
v->ymin = 0;
v->zmin = 0;
v->distances = 0;
v->starts= 0;
v->minimums= 0;
}
void fab_limits(struct fab_vars *v, float *vertex) {
//
// update limits for vertex
//
int i;
for (i = 0; i < 3; ++i) {
if (vertex[i] > v->mesh->max[i])
v->mesh->max[i] = vertex[i];
if (vertex[i] < v->mesh->min[i])
v->mesh->min[i] = vertex[i];
}
}
//
// input
//
void fab_read_stl(struct fab_vars *v, char *input_file_name) {
//
// read STL into triangle mesh
//
FILE *input_file;
char buf[80],*ptr;
uint32_t i,size,ret;
//
// read file
//
input_file = fopen(input_file_name, "rb");
if (input_file == 0) {
printf("fab.c: oops -- can't open %s\n",input_file_name);
exit(-1);
}
ret = fread(buf,80,1,input_file);
ptr = strstr(buf,"facet");
if (ptr != NULL) {
printf("fab.c: oops -- must be binary STL file\n");
exit(-1);
}
ret = fread(&size,4,1,input_file);
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;
ret = fread(&(v->mesh->triangle->normal),4,3,input_file);
ret = fread(&(v->mesh->triangle->v0),4,3,input_file);
ret = fread(&(v->mesh->triangle->v1),4,3,input_file);
ret = fread(&(v->mesh->triangle->v2),4,3,input_file);
ret = fread(&(v->mesh->triangle->attribute),2,1,input_file);
for (i = 1; i < size; ++i) {
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->previous = v->mesh->triangle->next = 0;
ret = fread(&(v->mesh->triangle->normal),4,3,input_file);
ret = fread(&(v->mesh->triangle->v0),4,3,input_file);
ret = fread(&(v->mesh->triangle->v1),4,3,input_file);
ret = fread(&(v->mesh->triangle->v2),4,3,input_file);
ret = fread(&(v->mesh->triangle->attribute),2,1,input_file);
}
fclose(input_file);
//
// check read
//
if (ret == 0) {
printf("fab.c: oops -- file read failed\n");
exit(-1);
}
v->mesh->min[0] = fab_big;
v->mesh->min[1] = fab_big;
v->mesh->min[2] = fab_big;
v->mesh->max[0] = -fab_big;
v->mesh->max[1] = -fab_big;
v->mesh->max[2] = -fab_big;
v->mesh->triangle = v->mesh->first;
while (v->mesh->triangle != 0) {
fab_limits(v, v->mesh->triangle->v0);
fab_limits(v, v->mesh->triangle->v1);
fab_limits(v, v->mesh->triangle->v2);
v->mesh->triangle = v->mesh->triangle->next;
}
//
// report and return
//
v->mesh->number = size;
printf("read %s\n",input_file_name);
printf(" number of triangles: %d\n",v->mesh->number);
printf(" xmin, xmax: %f %f\n",v->mesh->min[0],v->mesh->max[0]);
printf(" ymin, ymax: %f %f\n",v->mesh->min[1],v->mesh->max[1]);
printf(" zmin, zmax: %f %f\n",v->mesh->min[2],v->mesh->max[2]);
}
void fab_read_png(struct fab_vars *v, char *input_file_name) {
//
// read PNG into fab_vars
//
FILE *input_file;
int y;
png_uint_32 res_x,res_y;
int unit_type;
//
// read PNG file
//
input_file = fopen(input_file_name, "rb");
if (input_file == 0) {
printf("fab.c: oops -- can't open %s\n",input_file_name);
exit(-1);
}
v->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
v->info_ptr = png_create_info_struct(v->png_ptr);
png_init_io(v->png_ptr, input_file);
png_read_info(v->png_ptr, v->info_ptr);
//
// get size
//
v->nx = png_get_image_width(v->png_ptr, v->info_ptr);
v->ny = png_get_image_height(v->png_ptr, v->info_ptr);
v->bit_depth = png_get_bit_depth(v->png_ptr, v->info_ptr);
//
// get units
//
png_get_pHYs(v->png_ptr, v->info_ptr, &res_x, &res_y, &unit_type);
if (unit_type == PNG_RESOLUTION_METER) {
v->dx = 1000 * v->nx / ((float) res_x);
v->dy = 1000 * v->ny / ((float) res_y);
}
else {
printf("fab.c: don't recognize PNG units, assuming 72/inch\n");
res_x = (72*1000.0)/(25.4);
res_y = (72*1000.0)/(25.4);
v->dx = 1000 * v->nx / ((float) res_x);
v->dy = 1000 * v->ny / ((float) res_y);
}
//
// get texts
//
png_textp text_ptr;
int num_text;
png_get_text(v->png_ptr, v->info_ptr, &text_ptr, &num_text);
int t;
float xmin=0,ymin=0,zmin=0,zmax=0;
for (t = 0; t < num_text; ++t) {
if (0 == strcmp(text_ptr[t].key, "zmax"))
sscanf(text_ptr[t].text, "%g", &zmax);
else if (0 == strcmp(text_ptr[t].key, "zmin"))
sscanf(text_ptr[t].text, "%g", &zmin);
else if (0 == strcmp(text_ptr[t].key, "ymin"))
sscanf(text_ptr[t].text, "%g", &ymin);
else if (0 == strcmp(text_ptr[t].key, "xmin"))
sscanf(text_ptr[t].text, "%g", &xmin);
}
//
// set pixels
//
v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny);
for (y = 0; y < v->ny; ++y)
v->row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(v->png_ptr,
v->info_ptr));
png_read_image(v->png_ptr, v->row_pointers);
//
// set variables
//
v->nz = 1;
if (zmax != zmin)
v->dz = zmax - zmin;
else
v->dz = 0;
v->xmin = xmin;
v->ymin = ymin;
v->zmin = zmin;
//
// allocate array
//
v->array = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->array[y] = malloc(v->nx*sizeof(uint32_t));
//
// copy image to array
//
fab_png_array(v);
//
// close and return
//
fclose(input_file);
printf("read %s\n",input_file_name);
printf(" bit depth: %d\n",v->bit_depth);
printf(" x pixels: %d, y pixels: %d\n",v->nx,v->ny);
printf(" x pixels/m: %d, y pixels/m: %d\n",(int) res_x,(int) res_y);
if (v->dz != 0) {
printf(" dx: %f mm, dy: %f mm, dz: %f mm\n",v->dx,v->dy,v->dz);
}
else {
printf(" dx: %f mm, dy: %f mm\n",v->dx,v->dy);
}
}
void fab_read_array(struct fab_vars *v, char *input_file_name) {
//
// read array
//
FILE *input_file;
int x,y,ret;
input_file = fopen(input_file_name,"rb");
//
// read vars
//
ret = fread(&v->nx,4,1,input_file);
ret = fread(&v->ny,4,1,input_file);
ret = fread(&v->nz,4,1,input_file);
ret = fread(&v->dx,4,1,input_file);
ret = fread(&v->dy,4,1,input_file);
ret = fread(&v->dz,4,1,input_file);
ret = fread(&v->xmin,4,1,input_file);
ret = fread(&v->ymin,4,1,input_file);
ret = fread(&v->zmin,4,1,input_file);
//
// allocate array
//
v->array = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->array[y] = malloc(v->nx*sizeof(uint32_t));
//
// read array
//
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x)
ret = fread(&v->array[y][x],1,1,input_file);
//
// check read
//
if (ret == 0) {
printf("fab.c: oops -- file read failed\n");
exit(-1);
}
//
// print, close, and return
//
printf("read %s\n",input_file_name);
printf(" nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
printf(" dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
printf(" xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
fclose(input_file);
}
void fab_read_path(struct fab_vars *v, char *input_file_name) {
//
// read path from file
//
FILE *input_file;
int x,y,z,dof,nsegs=0,npts=0;
char line[255];
input_file = fopen(input_file_name,"r");
if (input_file == 0) {
printf("fab.c: oops -- can't open %s\n",input_file_name);
exit(-1);
}
printf("read %s\n",input_file_name);
//
// scan file
//
while (fgets(line,sizeof line,input_file) != NULL) {
if (0 == strncmp(line,"dof:",4)) {
sscanf(line,"dof: %d",&dof);
printf(" degrees of freedom: %d\n",dof);
fab_path_start(v,dof);
}
else if (0 == strncmp(line,"units:",6)) {
printf(" %s",line); // todo: parse and use units
}
else if (0 == strncmp(line,"nx ny:",6)) {
sscanf(line,"nx ny: %d %d",&v->nx,&v->ny);
printf(" nx: %d, ny: %d\n",v->nx,v->ny);
}
else if (0 == strncmp(line,"nx ny nz:",9)) {
sscanf(line,"nx ny nz: %d %d %d",&v->nx,&v->ny,&v->nz);
printf(" nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
}
else if (0 == strncmp(line,"dx dy:",6)) {
sscanf(line,"dx dy: %lf %lf",&v->dx,&v->dy);
printf(" dx: %f, dy: %f\n",v->dx,v->dy);
}
else if (0 == strncmp(line,"dx dy dz:",9)) {
sscanf(line,"dx dy dz: %lf %lf %lf",&v->dx,&v->dy,&v->dz);
printf(" dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
}
else if (0 == strncmp(line,"xmin ymin:",10)) {
sscanf(line,"xmin ymin: %lf %lf",&v->xmin,&v->ymin);
printf(" xmin: %f, ymin: %f\n",v->xmin,v->ymin);
}
else if (0 == strncmp(line,"xmin ymin zmin:",15)) {
sscanf(line,"xmin ymin zmin: %lf %lf %lf",&v->xmin,&v->ymin,&v->zmin);
printf(" xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
}
else if (0 == strncmp(line,"path start:",11)) {
printf(" path start:\n");
}
else if (0 == strncmp(line,"segment start:",14)) {
fab_path_segment(v);
nsegs += 1;
while (fgets(line,sizeof line,input_file) != NULL) {
if (0 == strncmp(line,"segment end:",12)) {
break;
}
else {
fab_path_point(v);
npts += 1;
if (dof == 3) {
sscanf(line,"%d %d %d",&x,&y,&z);
fab_path_axis(v,x);
fab_path_axis(v,y);
fab_path_axis(v,z);
}
else if (dof == 2) {
sscanf(line,"%d %d",&x,&y);
fab_path_axis(v,x);
fab_path_axis(v,y);
}
else {
printf("fab.c: oops -- can't handle %d dof\n",dof);
exit(-1);
}
}
}
}
else if (0 == strncmp(line,"path end:",9)) {
printf(" segments: %d, points: %d\n",nsegs,npts);
break;
}
else {
printf("fab.c: oops -- can't parse %s\n",line);
exit(-1);
}
}
fclose(input_file);
//
// create array
//
v->array = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->array[y] = malloc(v->nx*sizeof(uint32_t));
//
// return
//
return;
}
//
// output
//
void fab_write_png_K(struct fab_vars *v, char *output_file_name) {
//
// write grayscale PNG from fab_vars
//
FILE *output_file;
int x,y;
int num_text=0;
char xmins[100],ymins[100],zmins[100],zmaxs[100];
png_text text_ptr[4];
png_uint_32 res_x,res_y;
png_byte color_type;
png_byte bit_depth;
png_byte *ptr;
//
// open PNG file
//
output_file = fopen(output_file_name, "wb");
v->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
v->info_ptr = png_create_info_struct(v->png_ptr);
png_init_io(v->png_ptr, output_file);
//
// set vars
//
bit_depth = v->bit_depth;
color_type = PNG_COLOR_TYPE_GRAY;
png_set_IHDR(v->png_ptr, v->info_ptr, v->nx, v->ny,
bit_depth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
res_x = 1000 * v->nx / v->dx;
res_y = 1000 * v->ny / v->dy;
png_set_pHYs(v->png_ptr, v->info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
text_ptr[0].key = "xmin";
sprintf(xmins,"%f",v->xmin);
text_ptr[0].text = xmins;
text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
num_text += 1;
text_ptr[1].key = "ymin";
sprintf(ymins,"%f",v->ymin);
text_ptr[1].text = ymins;
text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
num_text += 1;
if (v->dz != 0) {
text_ptr[2].key = "zmin";
sprintf(zmins,"%f",v->zmin);
text_ptr[2].text = zmins;
text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
num_text += 1;
text_ptr[3].key = "zmax";
sprintf(zmaxs,"%f",(v->zmin + v->dz));
text_ptr[3].text = zmaxs;
text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
num_text += 1;
}
png_set_text(v->png_ptr, v->info_ptr, text_ptr, num_text);
png_write_info(v->png_ptr, v->info_ptr);
//
// allocate pixels
//
v->row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * v->ny);
for (y = 0; y < v->ny; ++y)
v->row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(v->png_ptr,
v->info_ptr));
//
// set pixels
//
if (bit_depth == 8) {
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x]);
ptr[0] = v->array[y][x];
}
}
else if (bit_depth == 16) {
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*2]);
ptr[0] = (v->array[y][x] >> 8) & 255;
ptr[1] = v->array[y][x] & 255;
}
}
else {
printf("fab.c: oops -- don't recognize bit depth %d\n",v->bit_depth);
exit(-1);
}
//
// write, close, and return
//
png_write_image(v->png_ptr, v->row_pointers);
png_write_end(v->png_ptr, NULL);
fclose(output_file);
printf("write %s\n",output_file_name);
printf(" x pixels: %d, y pixels: %d\n",v->nx,v->ny);
printf(" x pixels/m: %d, y pixels/m: %d\n",(int) res_x,(int) res_y);
if (v->dz != 0) {
printf(" dx: %f mm, dy: %f mm, dz: %f mm\n",v->dx,v->dy,v->dz);
}
else {
printf(" dx: %f mm, dy: %f mm\n",v->dx,v->dy);
}
}
void fab_write_array(struct fab_vars *v, char *output_file_name) {
//
// write array
//
FILE *output_file;
int x,y;
output_file = fopen(output_file_name,"wb");
fwrite(&v->nx,4,1,output_file);
fwrite(&v->ny,4,1,output_file);
fwrite(&v->nz,4,1,output_file);
fwrite(&v->dx,4,1,output_file);
fwrite(&v->dy,4,1,output_file);
fwrite(&v->dz,4,1,output_file);
fwrite(&v->xmin,4,1,output_file);
fwrite(&v->ymin,4,1,output_file);
fwrite(&v->zmin,4,1,output_file);
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x)
fwrite(&v->array[y][x],1,1,output_file);
fclose(output_file);
printf("write %s\n",output_file_name);
printf(" nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
printf(" dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
printf(" xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
}
void fab_write_path(struct fab_vars *v, char *output_file_name) {
//
// write path to file
//
FILE *output_file;
int i,nsegs=0,npts=0;
output_file = fopen(output_file_name,"w");
v->path->segment = v->path->first;
fprintf(output_file,"dof: %d\n",v->path->dof);
fprintf(output_file,"units: ");
for (i = 0; i < v->path->dof; ++i)
fprintf(output_file,"mm ");
fprintf(output_file,"\n");
if (v->path->dof == 2) {
fprintf(output_file,"nx ny: %d %d\n",v->nx,v->ny);
fprintf(output_file,"dx dy: %f %f\n",v->dx,v->dy);
fprintf(output_file,"xmin ymin: %f %f\n",v->xmin,v->ymin);
}
else if (v->path->dof > 2) {
fprintf(output_file,"nx ny nz: %d %d %d\n",v->nx,v->ny,v->nz);
fprintf(output_file,"dx dy dz: %f %f %f\n",v->dx,v->dy,v->dz);
fprintf(output_file,"xmin ymin zmin: %f %f %f\n",v->xmin,v->ymin,v->zmin);
}
fprintf(output_file,"path start:\n");
while (1) {
//
// follow segments
//
v->path->segment->point = v->path->segment->first;
fprintf(output_file,"segment start:\n");
nsegs += 1;
while (1) {
//
// follow points
//
v->path->segment->point->axis = v->path->segment->point->first;
while (1) {
//
// follow axes
//
fprintf(output_file,"%d ",v->path->segment->point->axis->value);
if (v->path->segment->point->axis->next == 0)
break;
v->path->segment->point->axis = v->path->segment->point->axis->next;
}
fprintf(output_file,"\n");
npts += 1;
if (v->path->segment->point->next == 0)
break;
v->path->segment->point = v->path->segment->point->next;
}
fprintf(output_file,"segment end:\n");
if (v->path->segment->next == 0)
break;
v->path->segment = v->path->segment->next;
}
fprintf(output_file,"path end:\n");
fclose(output_file);
printf("write %s\n",output_file_name);
printf(" degrees of freedom: %d\n",v->path->dof);
printf(" units: ");
for (i = 0; i < v->path->dof; ++i) {
printf("mm ");
}
printf("\n");
printf(" segments: %d, points: %d\n",nsegs,npts);
if (v->path->dof == 2) {
printf(" nx: %d, ny: %d\n",v->nx,v->ny);
printf(" dx: %f, dy: %f\n",v->dx,v->dy);
printf(" xmin: %f, ymin: %f\n",v->xmin,v->ymin);
}
else if (v->path->dof > 2) {
printf(" nx: %d, ny: %d, nz: %d\n",v->nx,v->ny,v->nz);
printf(" dx: %f, dy: %f, dz: %f\n",v->dx,v->dy,v->dz);
printf(" xmin: %f, ymin: %f, zmin: %f\n",v->xmin,v->ymin,v->zmin);
}
}
//
// shading
//
void fab_shade_states(struct fab_vars *v) {
//
// shade array states
//
int x,y;
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
if ((v->array[y][x] & v->direction) == v->north)
v->array[y][x] = 200;
else if ((v->array[y][x] & v->direction) == v->east)
v->array[y][x] = 150;
else if ((v->array[y][x] & v->direction) == v->south)
v->array[y][x] = 100;
else if ((v->array[y][x] & v->direction) == v->west)
v->array[y][x] = 50;
else if ((v->array[y][x] & v->direction) == v->stop)
v->array[y][x] = 25;
else if (v->array[y][x] == v->empty)
v->array[y][x] = 0;
else if (v->array[y][x] & v->edge)
v->array[y][x] = 128;
else if (v->array[y][x] & v->interior)
v->array[y][x] = 255;
}
}
void fab_shade_line(struct fab_vars *v, int x0, int y0, int x1, int y1, int intensity) {
//
// shade line into array
//
int x,y,dx,dy,step;
float slope;
dx = x1 - x0;
dy = y1 - y0;
x = x0;
y = y0;
if ((dx == 0) && (dy == 0)) {
if ((x0 > 0) && (x0 < v->nx) && (y0 > 0) && (y0 < v->ny))
v->array[y0][x0] = intensity;
}
else if (abs(dx) == 0) {
if (dy > 0)
step = 1;
else
step = -1;
do {
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
y += step;
} while (y != y1);
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
}
else if (abs(dy) == 0) {
if (dx > 0)
step = 1;
else
step = -1;
do {
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
x += step;
} while (x != x1);
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
}
else {
slope = ((float) dy)/((float) dx);
if (dx > 0)
step = 1;
else
step = -1;
do {
y = y0 + slope*(x-x0);
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
x += step;
} while (x != x1);
y = y0 + slope*(x-x0);
if ((x > 0) && (x < v->nx) && (y > 0) && (y < v->ny))
v->array[y][x] = intensity;
}
}
void fab_shade_path(struct fab_vars *v) {
//
// shade path into array
//
int x,y,x0,y0,x1,y1,intensity;
//
// set up and clear array
//
v->bit_depth = 8;
intensity = pow(2,8) - 1;
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x)
v->array[y][x] = 0;
v->path->segment = v->path->first;
while (1) {
//
// follow segments
//
v->path->segment->point = v->path->segment->first;
x0 = v->path->segment->point->first->value;
y0 = v->path->segment->point->first->next->value;
while (1) {
//
// follow points
//
if (v->path->segment->point->next == 0)
break;
v->path->segment->point = v->path->segment->point->next;
x1 = v->path->segment->point->first->value;
y1 = v->path->segment->point->first->next->value;
fab_shade_line(v,x0,y0,x1,y1,intensity);
x0 = x1;
y0 = y1;
}
if (v->path->segment->next == 0)
break;
v->path->segment = v->path->segment->next;
}
}
void fab_shade_path_displace(struct fab_vars *v) {
//
// shade path into array with z displacement
//
int x,y,x0,y0,z0,x1,y1,z1,nz,intensity;
float scale;
//
// check for thickness
//
if (v->dz == 0) {
//
// no, draw 2D
//
fab_shade_path(v);
return;
}
//
// yes, enlarge and clear array for displacements
//
nz = v->nx * v->dz / v->dx;
scale = ((float) nz) / v->nz;
v->nx += nz;
v->ny += nz;
v->dx += v->dz;
v->dy += v->dz;
v->bit_depth = 8;
v->array = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->array[y] = malloc(v->nx*sizeof(uint32_t));
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x)
v->array[y][x] = 0;
//
// follow path
//
v->path->segment = v->path->first;
while (1) {
//
// follow segments
//
v->path->segment->point = v->path->segment->first;
z0 = nz - scale * v->path->segment->point->first->next->next->value;
y0 = z0 + v->path->segment->point->first->next->value;
x0 = z0 + v->path->segment->point->first->value;
while (1) {
//
// follow points
//
if (v->path->segment->point->next == 0)
break;
v->path->segment->point = v->path->segment->point->next;
z1 = nz - scale * v->path->segment->point->first->next->next->value;
y1 = z1 + v->path->segment->point->first->next->value;
x1 = z1 + v->path->segment->point->first->value;
intensity = (z0 + z1)/2;
fab_shade_line(v,x0,y0,x1,y1,intensity);
x0 = x1;
y0 = y1;
z0 = z1;
}
if (v->path->segment->next == 0)
break;
v->path->segment = v->path->segment->next;
}
}
int fab_row(struct fab_vars *v, float units, int Y, float value) {
//
// return array row
//
value = 0.5f + (v->ny - 1) * units * (value - v->mesh->min[Y]) / v->dy;
return (value);
}
int fab_col(struct fab_vars *v, float units, int X, float value) {
//
// return array col
//
value = 0.5f + (v->nx - 1) * units * (value - v->mesh->min[X]) / v->dx;
return value;
}
int fab_height(struct fab_vars *v, int S, int Z, float value) {
//
// return array height
//
value = ((1-S)/2) * 65535 + S * 65535 * (value - v->mesh->min[Z]) / (v->mesh->max[Z] - v->mesh->min[Z]);
return value;
}
void fab_shade_triangle(struct fab_vars *v, float units, int S, int X, int Y, int Z) {
//
// shade triangle intro array
//
float slope;
int xmin,ymin,zmin,x1,y1,z1,x2,y2,z2;
int x20,z20,x21,z21,x10,z10,z;
int i,j;
int dir;
int temp;
#define fab_swap(a,b) temp=a; a=b; b=temp;
//
// find coords
//
xmin = fab_col(v, units, X, v->mesh->triangle->v0[X]);
ymin = fab_row(v, units, Y, v->mesh->triangle->v0[Y]);
zmin = fab_height(v, S, Z, v->mesh->triangle->v0[Z]);
x1 = fab_col(v, units, X, v->mesh->triangle->v1[X]);
y1 = fab_row(v, units, Y, v->mesh->triangle->v1[Y]);
z1 = fab_height(v, S, Z, v->mesh->triangle->v1[Z]);
x2 = fab_col(v, units, X, v->mesh->triangle->v2[X]);
y2 = fab_row(v, units, Y, v->mesh->triangle->v2[Y]);
z2 = fab_height(v, S, Z, v->mesh->triangle->v2[Z]);
//
// check normal if needs to be drawn
//
if ((S*((x1-xmin)*(y1-y2)-(x1-x2)*(y1-ymin))) >= 0)
return;
//
// sort heights
//
if (y1 > y2) {
fab_swap(x1,x2);
fab_swap(y1,y2);
fab_swap(z1,z2);
}
if (ymin > y1) {
fab_swap(xmin,x1);
fab_swap(ymin,y1);
fab_swap(zmin,z1);
}
if (y1 > y2) {
fab_swap(x1,x2);
fab_swap(y1,y2);
fab_swap(z1,z2);
}
//
// check orientation
//
if (x1 < (xmin+((x2-xmin)*(y1-ymin))/(y2-ymin)))
dir = 1;
else
dir = -1;
//
// draw
//
if (y2 != y1) {
for (i = y2; i >= y1; --i) {
x21 = x1 + ((x2-x1)*(i-y1))/(y2-y1);
z21 = z1 + ((z2-z1)*(i-y1))/(y2-y1);
x20 = xmin + ((x2-xmin)*(i-ymin))/(y2-ymin);
z20 = zmin + ((z2-zmin)*(i-ymin))/(y2-ymin);
if (x21 != x20)
slope = (z20-z21)/((float) (x20-x21));
else
slope = 0;
j = x21 - dir;
while (j != x20) {
j += dir;
z = z21 + slope*(j-x21);
if (z > v->array[v->ny-1-i][j])
v->array[v->ny-1-i][j] = z;
}
}
}
if (y1 != ymin) {
for (i = y1; i >= ymin; --i) {
x10 = xmin + ((x1-xmin)*(i-ymin))/(y1-ymin);
z10 = zmin + ((z1-zmin)*(i-ymin))/(y1-ymin);
x20 = xmin + ((x2-xmin)*(i-ymin))/(y2-ymin);
z20 = zmin + ((z2-zmin)*(i-ymin))/(y2-ymin);
if (x10 != x20)
slope = (z20-z10)/((float) (x20-x10));
else
slope = 0;
j = x10 - dir;
while (j != x20) {
j += dir;
z = z10 + slope*(j-x10);
if (z > v->array[v->ny-1-i][j])
v->array[v->ny-1-i][j] = z;
}
}
}
}
void fab_shade_mesh(struct fab_vars *v, float units, float resolution, char axis) {
//
// shade triangle mesh into 16-bit array
//
int X,Y,Z,S;
int i;
//
// allocate array
//
if (axis == 'x') {
X = 1; Y = 2; Z = 0; S = 1;
}
else if (axis == 'X') {
X = 1; Y = 2; Z = 0; S = -1;
}
if (axis == 'y') {
X = 2; Y = 0; Z = 1; S = 1;
}
else if (axis == 'Y') {
X = 2; Y = 0; Z = 1; S = -1;
}
if (axis == 'z') {
X = 0; Y = 1; Z = 2; S = 1;
}
else if (axis == 'Z') {
X = 0; Y = 1; Z = 2; S = -1;
}
v->dx = units * (v->mesh->max[X]-v->mesh->min[X]);
v->dy = units * (v->mesh->max[Y]-v->mesh->min[Y]);
v->dz = units * (v->mesh->max[Z]-v->mesh->min[Z]);
v->nx = resolution * v->dx;
v->ny = resolution * v->dy;
v->nz = 1;
v->xmin = units * v->mesh->min[X];
v->ymin = units * v->mesh->min[Y];
v->zmin = units * v->mesh->min[Z];
v->bit_depth = 16;
//
// alloc and init
//
v->array = malloc(v->ny*sizeof(uint32_t *));
for (i = 0; i < v->ny; ++i) {
v->array[i] = malloc(v->nx*sizeof(uint32_t));
memset(v->array[i],'\0',v->nx*sizeof(uint32_t));
}
//
// draw triangles into array
//
v->mesh->triangle = v->mesh->first;
while (v->mesh->triangle != 0) {
fab_shade_triangle(v, units, S, X, Y, Z);
v->mesh->triangle = v->mesh->triangle->next;
}
}
//
// array operations
//
void fab_png_array(struct fab_vars *v) {
//
// copy PNG image to array
//
int intensity,value,x,y;
int bit, byte;
png_byte *ptr;
png_colorp palette;
png_color color;
//png_uint_32 num_palette;
int num_palette;
if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 1) {
if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_PALETTE)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
byte = x / 8;
bit = 7 - x % 8;
ptr = &(v->row_pointers[y][byte]);
value = (ptr[0] >> bit) & 1;
png_get_PLTE(v->png_ptr, v->info_ptr, &palette, &num_palette);
color = palette[value];
intensity = (color.red + color.green + color.blue)/3.0;
v->array[y][x] = intensity;
}
else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
byte = x / 8;
bit = 7 - x % 8;
ptr = &(v->row_pointers[y][byte]);
value = (ptr[0] >> bit) & 1;
v->array[y][x] = value;
}
else {
printf("fab.c: oops 3 -- image type not supported\n");
exit(-1);
}
}
else if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 8) {
if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*1]);
v->array[y][x] = ptr[0];
}
else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*2]);
v->array[y][x] = (int) ((1.0-ptr[1]/255.0)*255 + (0.0+(ptr[1]/255.0))*ptr[0]);
}
else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_RGB)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*3]);
v->array[y][x] = (int) ((ptr[0] + ptr[1] + ptr[2])/3.0);
}
else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*4]);
v->array[y][x] = (int) ((1.0-ptr[3]/255.0)*255 + (0.0+(ptr[3]/255.0))*((ptr[0] + ptr[1] + ptr[2])/3.0));
}
else if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_PALETTE) {
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
value = v->row_pointers[y][x];
png_get_PLTE(v->png_ptr, v->info_ptr, &palette, &num_palette);
color = palette[value];
intensity = (color.red + color.green + color.blue)/3.0;
v->array[y][x] = intensity;
}
}
else {
printf("fab.c: oops -- image type not supported\n");
exit(-1);
}
}
else if (png_get_bit_depth(v->png_ptr, v->info_ptr) == 16) {
if (png_get_color_type(v->png_ptr, v->info_ptr) == PNG_COLOR_TYPE_GRAY)
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x) {
ptr = &(v->row_pointers[y][x*2]);
v->array[y][x] = (ptr[0] << 8) + ptr[1];
}
else {
printf("fab.c: oops -- image type not supported\n");
exit(-1);
}
}
else {
printf("fab.c: oops -- image type not supported\n");
exit(-1);
}
}
void fab_rescale(struct fab_vars *v, float min, float max) {
//
// rescale array intensity
//
int x,y,top;
float low,high;
//
// find limits
//
top = pow(2,v->bit_depth) - 1;
low = top;
high = 0;
for (y = 0; y < v->ny; ++y) {
for (x = 0; x < v->nx; ++x) {
if (v->array[y][x] > high)
high = v->array[y][x];
if (v->array[y][x] < low)
low = v->array[y][x];
}
}
//
// rescale
//
for (y = 0; y < v->ny; ++y) {
for (x = 0; x < v->nx; ++x) {
v->array[y][x] = top*min + top*(max-min) * (v->array[y][x] - low) / (high - low);
}
}
}
void add_edge(struct fab_vars *v, int* count, int y, int x) {
//
// set cell to interior edge
//
v->array[y][x] = v->interior | v->edge;
++(*count);
}
int fab_edges(struct fab_vars *v) {
//
// find edges of thresholded array
//
int x,y,sum,count;
count = 0;
for (x = 1; x < (v->nx-1); ++x)
if (v->array[0][x] & v->interior) {
sum = (v->array[0][x-1] & v->interior)
+ (v->array[0][x+1] & v->interior)
+ (v->array[1][x] & v->interior);
if (sum < 3)
add_edge(v, &count, 0,x);
}
else if (v->array[v->ny-1][x] & v->interior) {
sum = (v->array[v->ny-1][x-1] & v->interior)
+ (v->array[v->ny-1][x+1] & v->interior)
+ (v->array[v->ny-2][x] & v->interior);
if (sum < 3)
add_edge(v, &count, v->ny-1,x);
}
for (y = 1; y < (v->ny-1); ++y) {
if (v->array[y][0] & v->interior) {
sum = (v->array[y-1][0] & v->interior)
+ (v->array[y+1][0] & v->interior)
+ (v->array[y][1] & v->interior);
if (sum < 3)
add_edge(v, &count, y,0);
}
if (v->array[y][v->nx-1] & v->interior) {
sum = (v->array[y-1][v->nx-1] & v->interior)
+ (v->array[y+1][v->nx-1] & v->interior)
+ (v->array[y][v->nx-2] & v->interior);
if (sum < 3)
add_edge(v, &count, y,v->nx-1);
}
for (x = 1; x < (v->nx-1); ++x) {
if (v->array[y][x] & v->interior) {
sum = (v->array[y-1][x] & v->interior)
+ (v->array[y+1][x] & v->interior)
+ (v->array[y][x-1] & v->interior)
+ (v->array[y][x+1] & v->interior);
if (sum < 4)
add_edge(v, &count, y,x);
}
}
}
return count;
}
void fab_threshold(struct fab_vars *v, float intensity) {
//
// threshold array
//
int x,y;
float threshold;
threshold = (pow(2,v->bit_depth) - 1.0) * intensity;
for (y = 0; y < v->ny; ++y)
for (x = 0; x < v->nx; ++x)
if (v->array[y][x] >= threshold)
v->array[y][x] = v->interior;
else
v->array[y][x] = v->empty;
}
int fab_distances_distance(struct fab_vars *v, int x, int y, int i) {
return ((y-i)*(y-i) + (v->g[i][x])*(v->g[i][x]));
}
int fab_distances_intersection(struct fab_vars *v, int x, int y0, int y1) {
return ((0.0+(v->g[y0][x])*(v->g[y0][x])-(v->g[y1][x])*(v->g[y1][x])+y0*y0-y1*y1)/(2.0*(y0-y1)));
}
void fab_distances(struct fab_vars *v) {
//
// find Euclidean distance to interior in a thresholded array
//
int x,y,closest,segment,newstart;
double d;
//
// allocate arrays if needed
//
if (v->g == 0) {
v->g = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->g[y] = malloc(v->nx*sizeof(uint32_t));
}
if (v->h == 0) {
v->h = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->h[y] = malloc(v->nx*sizeof(uint32_t));
}
if (v->distances == 0) {
v->distances = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->distances[y] = malloc(v->nx*sizeof(uint32_t));
}
if (v->starts == 0)
v->starts = malloc(v->ny*sizeof(uint32_t *));
if (v->minimums == 0)
v->minimums = malloc(v->ny*sizeof(uint32_t *));
//
// column scan
//
for (y = 0; y < v->ny; ++y) {
//
// right pass
//
closest = -(v->nx);
for (x = 0; x < v->nx; ++x) {
if (v->array[y][x] == v->interior) {
v->g[y][x] = 0;
closest = x;
}
else
v->g[y][x] = (x-closest);
}
//
// left pass
//
closest = 2*(v->nx);
for (x = (v->nx - 1); x >= 0; --x) {
if (v->array[y][x] == v->interior)
closest = x;
else {
d = (closest-x);
if (d < v->g[y][x])
v->g[y][x] = d;
}
}
}
//
// row scan
//
for (x = 0; x < v->nx; ++x) {
printf("\b\b\b\b\b\b\b\b %d",x);
segment = 0;
v->starts[0] = 0;
v->minimums[0] = 0;
//
// down
//
for (y = 1; y < v->ny; ++y) {
while ((segment >= 0) &&
(fab_distances_distance(v, x, v->starts[segment],v->minimums[segment])
> fab_distances_distance(v, x, v->starts[segment],y)))
segment -= 1;
if (segment < 0) {
segment = 0;
v->minimums[0] = y;
}
else {
newstart = 1 + fab_distances_intersection(v, x, v->minimums[segment],y);
if (newstart < v->ny) {
segment += 1;
v->minimums[segment] = y;
v->starts[segment] = newstart;
}
}
}
//
// up
//
for (y = (v->ny - 1); y >= 0; --y) {
v->distances[y][x] = sqrt(fab_distances_distance(v, x, y, v->minimums[segment]));
if (y == v->starts[segment])
segment -= 1;
}
}
printf("\n");
}
void test_fab_distances(struct fab_vars *v) {
//
// find Euclidean distance to interior in a thresholded array
//
int x,y;
//
// allocate arrays if needed
//
if (v->ddx == 0) {
v->ddx = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->ddx[y] = malloc(v->nx*sizeof(uint32_t));
}
if (v->ddy == 0) {
v->ddy = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->ddy[y] = malloc(v->nx*sizeof(uint32_t));
}
if (v->distances == 0) {
v->distances = malloc(v->ny*sizeof(uint32_t *));
for (y = 0; y < v->ny; ++y)
v->distances[y] = malloc(v->nx*sizeof(uint32_t));
}
//
// column scan
//
for (y = 0; y < v->ny; ++y) {
printf("\b\b\b\b\b\b\b\b\b\b y %d",y);
//
// right pass
//
v->ddx[y][0] = -1;
v->ddy[y][0] = 0;
for (x = 1; x < v->nx; ++x) {
v->ddy[y][x] = 0;
if (v->array[y][x] == v->interior)
v->ddx[y][x] = 0;
else
v->ddx[y][x] = v->ddx[y][x-1] - 1;
}
//
// left pass
//
v->ddx[y][v->nx-1] = 1;
for (x = (v->nx - 2); x >= 0; --x) {
if (abs(v->ddx[y][x]) > (abs(v->ddx[y][x+1])) + 1)
v->ddx[y][x] = v->ddx[y][x+1] + 1;
//v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
}
}
printf("\n");
//
// row scan
//
for (x = 0; x < v->nx; ++x) {
printf("\b\b\b\b\b\b\b\b\b\b x %d",x);
//
// down
//
v->ddx[0][x] = 0;
v->ddy[0][x] = -1;
for (y = 1; y < v->ny; ++y) {
if ((v->ddx[y][x]*v->ddx[y][x]) > (v->ddx[y-1][x]*v->ddx[y-1][x] + (v->ddy[y-1][x]-1)*(v->ddy[y-1][x]-1))) {
v->ddx[y][x] = v->ddx[y-1][x];
v->ddy[y][x] = v->ddy[y-1][x]-1;
}
v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
}
//
// up
//
/*
v->ddx[v->ny-1][x] = 0;
v->ddy[v->ny-1][x] = 1;
for (y = (v->ny - 2); y >= 0; --y) {
if ((v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]) > (v->ddx[y+1][x]*v->ddx[y+1][x] + (abs(v->ddy[y+1][x])+1)*(abs(v->ddy[y+1][x])+1))) {
v->ddx[y][x] = v->ddx[y+1][x];
v->ddy[y][x] = v->ddy[y+1][x]+1;
}
//v->distances[y][x] = sqrt(v->ddx[y][x]*v->ddx[y][x]+v->ddy[y][x]*v->ddy[y][x]);
}
*/
}
printf("\n");
}
int fab_offset(struct fab_vars *v, float distance) {
//
// use distances to offset array
// real units, assumes square pixels
// returns remaining points
//
int x,y,remaining_count;
float image_distance;
image_distance = distance * v->nx/v->dx;
//
// loop over array
//
remaining_count = 0;
for (y = 0; y < v->ny; ++y) {
for (x = 0; x < v->nx; ++x) {
if (v->distances[y][x] <= image_distance)
v->array[y][x] = v->interior;
else {
v->array[y][x] = v->empty;
remaining_count += 1;
}
}
}
return remaining_count;
}
int state(struct fab_vars *v, int y, int x) {
int nw, nn, ne, ww, cc, ee, sw, ss, se, state;
//
// assemble state string
// 0 = empty
// 1 = interior
// 2 = boundary
//
if ((y == 0) | (x == 0))
nw = 2;
else
nw = v->array[y-1][x-1] & 1;
if (y == 0)
nn = 2;
else
nn = v->array[y-1][x] & 1;
if ((y == 0) | (x == (v->nx-1)))
ne = 2;
else
ne = v->array[y-1][x+1] & 1;
if (x == 0)
ww = 2;
else
ww = v->array[y][x-1] & 1;
cc = v->array[y][x] & 1;
if (x == (v->nx-1))
ee = 2;
else
ee = v->array[y][x+1] & 1;
if ((y == (v->ny-1)) | (x == 0))
sw = 2;
else
sw = v->array[y+1][x-1] & 1;
if (y == (v->ny-1))
ss = 2;
else
ss = v->array[y+1][x] & 1;
if ((y == (v->ny-1)) | (x == (v->nx-1)))
se = 2;
else
se = v->array[y+1][x+1] & 1;
state =
(nw << 0) + (nn << 2) + (ne << 4) +
(ww << 6) + (cc << 8) + (ee << 10) +
(sw << 12) + (ss << 14) + (se << 16);
return state;
}
void add_rule(struct fab_vars *v, int *table, int nw, int nn, int ne,
int ww, int cc, int ee,int sw,int ss, int se, int rule) {
//
// add a CA rule, with rotations
//
int state;
//
// add the rule
//
state =
(nw << 0) + (nn << 2) + (ne << 4) +
(ww << 6) + (cc << 8) + (ee << 10) +
(sw << 12) + (ss << 14) + (se << 16);
table[state] = rule;
//
// rotate 90 degrees
//
state =
(sw << 0) + (ww << 2) + (nw << 4) +
(ss << 6) + (cc << 8) + (nn << 10) +
(se << 12) + (ee << 14) + (ne << 16);
if (rule == v->east)
table[state] = v->south;
else if (rule == v->south)
table[state] = v->west;
else if (rule == v->west)
table[state] = v->north;
else if (rule == v->north)
table[state] = v->east;
else if (rule == v->corner)
table[state] = v->corner;
else if (rule == v->stop)
table[state] = v->stop;
//
// rotate 180 degrees
//
state =
(se << 0) + (ss << 2) + (sw << 4) +
(ee << 6) + (cc << 8) + (ww << 10) +
(ne << 12) + (nn << 14) + (nw << 16);
if (rule == v->east)
table[state] = v->west;
else if (rule == v->south)
table[state] = v->north;
else if (rule == v->west)
table[state] = v->east;
else if (rule == v->north)
table[state] = v->south;
else if (rule == v->corner)
table[state] = v->corner;
else if (rule == v->stop)
table[state] = v->stop;
//
// rotate 270 degrees
//
state =
(ne << 0) + (ee << 2) + (se << 4) +
(nn << 6) + (cc << 8) + (ss << 10) +
(nw << 12) + (ww << 14) + (sw << 16);
if (rule == v->east)
table[state] = v->north;
else if (rule == v->south)
table[state] = v->east;
else if (rule == v->west)
table[state] = v->south;
else if (rule == v->north)
table[state] = v->west;
else if (rule == v->corner)
table[state] = v->corner;
else if (rule == v->stop)
table[state] = v->stop;
}
void fab_directions(struct fab_vars *v) {
//
// find directions around interior edges (right-hand rule)
//
int x,y,rule,dir,*table;
table = calloc((pow(2,18)), sizeof(int));
//
// build rule table
//
// 0 = empty
// 1 = interior
// 2 = boundary
//
// 1 0:
//
// 011
// 111
// 111
add_rule(v, table,0,1,1,1,1,1,1,1,1,v->north);
// 101
// 111
// 111
add_rule(v, table,1,0,1,1,1,1,1,1,1,v->east);
//
// 2 0's:
//
// 001
// 111
// 111
add_rule(v, table,0,0,1,1,1,1,1,1,1,v->east);
// 100
// 111
// 111
add_rule(v, table,1,0,0,1,1,1,1,1,1,v->east);
// 010
// 111
// 111
add_rule(v, table,0,1,0,1,1,1,1,1,1,v->east);
// 011
// 110
// 111
add_rule(v, table,0,1,1,1,1,0,1,1,1,v->south);
// 110
// 011
// 111
add_rule(v, table,1,1,0,0,1,1,1,1,1,v->east);
// 101
// 011
// 111
add_rule(v, table,1,0,1,0,1,1,1,1,1,v->east);
// 101
// 110
// 111
add_rule(v, table,1,0,1,1,1,0,1,1,1,v->south);
// 011
// 111
// 110
add_rule(v, table,0,1,1,1,1,1,1,1,0,v->corner);
// 011
// 111
// 101
add_rule(v, table,0,1,1,1,1,1,1,0,1,v->north);
// 110
// 111
// 101
add_rule(v, table,1,1,0,1,1,1,1,0,1,v->west);
// 101
// 111
// 110
add_rule(v, table,1,0,1,1,1,1,1,1,0,v->south);
// 101
// 111
// 011
add_rule(v, table,1,0,1,1,1,1,0,1,1,v->east);
//
// 3 0's:
//
// 001
// 011
// 111
add_rule(v, table,0,0,1,0,1,1,1,1,1,v->east);
// 010
// 011
// 111
add_rule(v, table,0,1,0,0,1,1,1,1,1,v->east);
// 010
// 110
// 111
add_rule(v, table,0,1,0,1,1,0,1,1,1,v->south);
// 010
// 111
// 011
add_rule(v, table,0,1,0,1,1,1,0,1,1,v->east);
// 010
// 111
// 110
add_rule(v, table,0,1,0,1,1,1,1,1,0,v->south);
// 110
// 011
// 011
add_rule(v, table,1,1,0,0,1,1,0,1,1,v->east);
// 011
// 110
// 110
add_rule(v, table,0,1,1,1,1,0,1,1,0,v->south);
// 101
// 011
// 011
add_rule(v, table,1,0,1,0,1,1,0,1,1,v->east);
// 101
// 110
// 110
add_rule(v, table,1,0,1,1,1,0,1,1,0,v->south);
// 011
// 011
// 011;
add_rule(v, table,0,1,1,0,1,1,0,1,1,v->north);
//
// 4 0's:
//
// 001
// 011
// 011
add_rule(v, table,0,0,1,0,1,1,0,1,1,v->east);
// 100
// 110
// 110
add_rule(v, table,1,0,0,1,1,0,1,1,0,v->south);
// 010
// 011
// 011
add_rule(v, table,0,1,0,0,1,1,0,1,1,v->east);
// 010
// 110
// 110
add_rule(v, table,0,1,0,1,1,0,1,1,0,v->south);
// 001
// 110
// 110
add_rule(v, table,0,0,1,1,1,0,1,1,0,v->south);
// 100
// 011
// 011
add_rule(v, table,1,0,0,0,1,1,0,1,1,v->east);
//
// 5 0's:
//
// 000
// 011
// 011
add_rule(v, table,0,0,0,0,1,1,0,1,1,v->east);
//
// boundary states
//
// 200
// 211
// 211
add_rule(v, table,2,0,0,2,1,1,2,1,1,v->east);
// 201
// 211
// 211
add_rule(v, table,2,0,1,2,1,1,2,1,1,v->east);
// 210
// 211
// 211
add_rule(v, table,2,1,0,2,1,1,2,1,1,v->east);
// 002
// 112
// 112
add_rule(v, table,0,0,2,1,1,2,1,1,2,v->stop);
// 102
// 112
// 112
add_rule(v, table,1,0,2,1,1,2,1,1,2,v->stop);
// 002
// 112
// 102
add_rule(v, table,0,0,2,1,1,2,1,0,2,v->stop);
// 012
// 112
// 112
add_rule(v, table,0,1,2,1,1,2,1,1,2,v->stop);
// 012
// 112
// 102
add_rule(v, table,0,1,2,1,1,2,1,0,2,v->stop);
for (y = 0; y < v->ny; ++y) {
for (x = 0; x < v->nx; ++x) {
if (v->array[y][x] & v->interior) {
v->array[y][x] = v->interior;
rule = state(v,y,x);
dir = table[rule];
v->array[y][x] |= dir;
}
}
}
free(table);
}
//
// mesh operations
//
void fab_mesh_path(struct fab_vars *v, float units, float resolution) {
//
// convert mesh to path
//
//
// set up path
//
fab_path_start(v,3);
v->dx = units * (v->mesh->max[0]-v->mesh->min[0]);
v->dy = units * (v->mesh->max[1]-v->mesh->min[1]);
v->dz = units * (v->mesh->max[2]-v->mesh->min[2]);
v->nx = resolution * v->dx;
v->ny = resolution * v->dy;
v->nz = resolution * v->dz;
v->xmin = units * v->mesh->min[0];
v->ymin = units * v->mesh->min[1];
v->zmin = units * v->mesh->min[2];
v->bit_depth = 16;
//
// loop over mesh
//
v->mesh->triangle = v->mesh->first;
while (v->mesh->triangle != 0) {
fab_path_segment(v);
fab_path_point(v);
fab_path_axis(v,(v->nx)*(v->mesh->triangle->v0[0] - v->mesh->min[0])/(v->dx));
fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v0[1])/(v->dy));
fab_path_axis(v,(v->nz)*(v->mesh->triangle->v0[2] - v->mesh->min[2])/(v->dz));
fab_path_point(v);
fab_path_axis(v,(v->nx)*(v->mesh->triangle->v1[0] - v->mesh->min[0])/(v->dx));
fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v1[1])/(v->dy));
fab_path_axis(v,(v->nz)*(v->mesh->triangle->v1[2] - v->mesh->min[2])/(v->dz));
fab_path_point(v);
fab_path_axis(v,(v->nx)*(v->mesh->triangle->v2[0] - v->mesh->min[0])/(v->dx));
fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v2[1])/(v->dy));
fab_path_axis(v,(v->nz)*(v->mesh->triangle->v2[2] - v->mesh->min[2])/(v->dz));
fab_path_point(v);
fab_path_axis(v,(v->nx)*(v->mesh->triangle->v0[0] - v->mesh->min[0])/(v->dx));
fab_path_axis(v,(v->ny)*(v->mesh->max[1] - v->mesh->triangle->v0[1])/(v->dy));
fab_path_axis(v,(v->nz)*(v->mesh->triangle->v0[2] - v->mesh->min[2])/(v->dz));
v->mesh->triangle = v->mesh->triangle->next;
}
}
//
// path operations
//
void fab_path_start(struct fab_vars *v, int dof) {
//
// start a new path
//
v->path = malloc(sizeof(struct fab_path_type));
v->path->first = 0;
v->path->dof = dof;
}
void fab_path_segment(struct fab_vars *v) {
//
// add a segment to the current path
//
v->path->last = malloc(sizeof(struct fab_path_segment_type));
v->path->last->previous = 0;
v->path->last->next = 0;
v->path->last->first = 0;
if (v->path->first == 0) {
//
// first segment
//
v->path->first = v->path->last;
v->path->segment = v->path->last;
}
else {
//
// not first segment
//
v->path->last->previous = v->path->segment;
v->path->segment->next = v->path->last;
v->path->segment = v->path->last;
}
}
void fab_path_point(struct fab_vars *v) {
//
// add a point to the current path segment
//
v->path->segment->last =
malloc(sizeof(struct fab_path_point_type));
v->path->segment->last->previous = 0;
v->path->segment->last->next = 0;
v->path->segment->last->first = 0;
if (v->path->segment->first == 0) {
//
// first point
//
v->path->segment->first = v->path->segment->last;
v->path->segment->point = v->path->segment->last;
}
else {
//
// not first point
//
v->path->segment->last->previous = v->path->segment->point;
v->path->segment->point->next = v->path->segment->last;
v->path->segment->point = v->path->segment->last;
}
}
void fab_path_axis(struct fab_vars *v, int value) {
//
// add an axis to the current path segment point
//
v->path->segment->point->last =
malloc(sizeof(struct fab_path_axis_type));
v->path->segment->point->last->previous = 0;
v->path->segment->point->last->next = 0;
v->path->segment->point->last->value = value;
if (v->path->segment->point->first == 0) {
//
// first axis
//
v->path->segment->point->first = v->path->segment->point->last;
v->path->segment->point->axis = v->path->segment->point->last;
}
else {
//
// not first axis
//
v->path->segment->point->last->previous = v->path->segment->point->axis;
v->path->segment->point->axis->next = v->path->segment->point->last;
v->path->segment->point->axis = v->path->segment->point->last;
}
}
void fab_path_join(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout, float dx, float dy) {
//
// join paths, checking limits
//
float xf,yf,zf;
int xi,yi,zi;
float xmin,ymin,zmin,xmax,ymax,zmax,dpmm;
xmin = ((dx + vin1->xmin) < vin2->xmin) ? (dx + vin1->xmin) : vin2->xmin;
ymin = ((-dy + vin1->ymin) < vin2->ymin) ? (-dy + vin1->ymin) : vin2->ymin;
zmin = (vin1->zmin < vin2->zmin) ? vin1->zmin : vin2->zmin;
xmax = ((dx + vin1->xmin + vin1->dx) > (vin2->xmin + vin2->dx)) ? (dx + vin1->xmin + vin1->dx) : (vin2->xmin + vin2->dx);
ymax = ((-dy + vin1->ymin + vin1->dy) > (vin2->ymin + vin2->dy)) ? (-dy + vin1->ymin + vin1->dy) : (vin2->ymin + vin2->dy);
zmax = ((vin1->zmin + vin1->dz) > (vin2->zmin + vin2->dz)) ? (vin1->zmin + vin1->dz) : (vin2->zmin + vin2->dz);
dpmm = ((vin1->nx/vin1->dx) > (vin2->nx/vin2->dx)) ? (vin1->nx/vin1->dx) : (vin2->nx/vin2->dx);
vout->xmin = xmin;
vout->ymin = ymin;
vout->zmin = zmin;
vout->dx = (xmax-xmin);
vout->dy = (ymax-ymin);
vout->dz = (zmax-zmin);
vout->nx = dpmm * vout->dx;
vout->ny = dpmm * vout->dy;
vout->nz = dpmm * vout->dz;
fab_path_start(vout,vin1->path->dof);
//
// copy first path
//
vin1->path->segment = vin1->path->first;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin1->path->segment->point = vin1->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
xf = dx + vin1->xmin + vin1->dx * vin1->path->segment->point->first->value / (vin1->nx - 1.0);
xi = (vout->nx - 1) * (xf - vout->xmin) / vout->dx;
fab_path_axis(vout,xi);
yf = -dy + vin1->ymin + vin1->dy * vin1->path->segment->point->first->next->value / (vin1->ny - 1.0);
yi = (vout->ny - 1) * (yf - vout->ymin) / vout->dy;
fab_path_axis(vout,yi);
if (vin1->path->dof == 3) {
if (vin1->nz == 1)
zi = 0;
else {
zf = vin1->zmin + vin1->dz * vin1->path->segment->point->first->next->next->value / (vin1->nz - 1.0);
zi = (vout->nz - 1) * (zf - vout->zmin) / vout->dz;
}
fab_path_axis(vout,zi);
}
if (vin1->path->segment->point->next == 0)
break;
vin1->path->segment->point = vin1->path->segment->point->next;
}
if (vin1->path->segment->next == 0)
break;
vin1->path->segment = vin1->path->segment->next;
}
//
// copy second path
//
vin2->path->segment = vin2->path->first;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin2->path->segment->point = vin2->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
xf = vin2->xmin + vin2->dx * vin2->path->segment->point->first->value / (vin2->nx - 1.0);
xi = (vout->nx - 1) * (xf - vout->xmin) / vout->dx;
fab_path_axis(vout,xi);
yf = vin2->ymin + vin2->dy * vin2->path->segment->point->first->next->value / (vin2->ny - 1.0);
yi = (vout->ny - 1) * (yf - vout->ymin) / vout->dy;
fab_path_axis(vout,yi);
if (vin2->path->dof == 3) {
if (vin2->nz == 1)
zi = 0;
else {
zf = vin2->zmin + vin2->dz * vin2->path->segment->point->first->next->next->value / (vin2->nz - 1.0);
zi = (vout->nz - 1) * (zf - vout->zmin) / vout->dz;
}
fab_path_axis(vout,zi);
}
if (vin2->path->segment->point->next == 0)
break;
vin2->path->segment->point = vin2->path->segment->point->next;
}
if (vin2->path->segment->next == 0)
break;
vin2->path->segment = vin2->path->segment->next;
}
}
void fab_path_cat(struct fab_vars *vin1, struct fab_vars *vin2, struct fab_vars *vout) {
//
// concatenate paths, copying limits
//
vout->xmin = vin1->xmin;
vout->ymin = vin1->ymin;
vout->zmin = vin1->zmin;
vout->dx = vin1->dx;
vout->dy = vin1->dy;
vout->dz = vin1->dz;
vout->nx = vin1->nx;
vout->ny = vin1->ny;
vout->nz = vin1->nz;
fab_path_start(vout,vin1->path->dof);
//
// copy first path
//
vin1->path->segment = vin1->path->first;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin1->path->segment->point = vin1->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
fab_path_axis(vout,vin1->path->segment->point->first->value);
fab_path_axis(vout,vin1->path->segment->point->first->next->value);
if (vin1->path->dof > 2)
fab_path_axis(vout,vin1->path->segment->point->first->next->next->value);
if (vin1->path->segment->point->next == 0)
break;
vin1->path->segment->point = vin1->path->segment->point->next;
}
if (vin1->path->segment->next == 0)
break;
vin1->path->segment = vin1->path->segment->next;
}
//
// copy second path
//
vin2->path->segment = vin2->path->first;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin2->path->segment->point = vin2->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
fab_path_axis(vout,vin2->path->segment->point->first->value);
fab_path_axis(vout,vin2->path->segment->point->first->next->value);
if (vin2->path->dof > 2)
fab_path_axis(vout,vin2->path->segment->point->first->next->next->value);
if (vin2->path->segment->point->next == 0)
break;
vin2->path->segment->point = vin2->path->segment->point->next;
}
if (vin2->path->segment->next == 0)
break;
vin2->path->segment = vin2->path->segment->next;
}
}
void fab_path_append(struct fab_vars *vin, struct fab_vars *vout) {
//
// append vin to vout, copying limits
//
vin->path->segment = vin->path->first;
vout->path->segment = vout->path->last;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin->path->segment->point = vin->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
fab_path_axis(vout,vin->path->segment->point->first->value);
fab_path_axis(vout,vin->path->segment->point->first->next->value);
if (vin->path->dof > 2)
fab_path_axis(vout,vin->path->segment->point->first->next->next->value);
if (vin->path->segment->point->next == 0)
break;
vin->path->segment->point = vin->path->segment->point->next;
}
if (vin->path->segment->next == 0)
break;
vin->path->segment = vin->path->segment->next;
}
}
void fab_path_array(struct fab_vars *vin, struct fab_vars *vout, int nx, int ny, float dx, float dy) {
//
// array path
//
int i,j,x,y,z,dnx,dny;
dnx = dx * vin->nx / vin->dx;
dny = dy * vin->ny / vin->dy;
vout->dx = nx * vin->dx + (nx-1) * dx;
vout->dy = ny * vin->dy + (ny-1) * dy;
vout->dz = vin->dz;
vout->nx = nx * vin->nx + (nx-1) * dnx;
vout->ny = ny * vin->ny + (ny-1) * dny;
vout->nz = vin->nz;
vout->xmin = vin->xmin;
vout->ymin = vin->ymin;
vout->zmin = vin->zmin;
fab_path_start(vout,vin->path->dof);
for (i = 0; i < nx; ++i) {
for (j = 0; j < ny; ++j) {
vin->path->segment = vin->path->first;
while (1) {
//
// follow segments
//
fab_path_segment(vout);
vin->path->segment->point = vin->path->segment->first;
while (1) {
//
// follow points
//
fab_path_point(vout);
x = i*(vin->nx + dnx) + vin->path->segment->point->first->value;
fab_path_axis(vout,x);
y = j*(vin->ny + dny) + vin->path->segment->point->first->next->value;
fab_path_axis(vout,y);
if (vin->path->dof == 3) {
z = vin->path->segment->point->first->next->next->value;
fab_path_axis(vout,z);
}
if (vin->path->segment->point->next == 0)
break;
vin->path->segment->point = vin->path->segment->point->next;
}
if (vin->path->segment->next == 0)
break;
vin->path->segment = vin->path->segment->next;
}
}
}
}
void fab_follow(struct fab_vars *v, float error, int y, int x, int z) {
//
// create path by following edge directions at x,y
// error is allowable deviation before stroking (in pixel units)
// z is layer height (ignored for 2D paths)
//
int xnew=0,ynew=0;
float xmean,ymean;
float dx,dy,nx,ny,d;
int xcur,ycur,xstart,ystart,xseg,yseg,xsum,ysum,nsum;
char last_direction;
//
// don't start on a corner state
//
if (((v->array[y][x] & v->direction) == v->corner)
|| ((v->array[y][x] & v->direction) == v->corner2))
return;
//
// otherwise start segment
//
xstart = x;
ystart = y;
xcur = x;
ycur = y;
xseg = x;
yseg = y;
xsum = 0;
ysum = 0;
nsum = 0;
last_direction = '?';
fab_path_segment(v);
//
// add first point
//
fab_path_point(v);
fab_path_axis(v,xstart);
fab_path_axis(v,ystart);
if (v->path->dof > 2)
fab_path_axis(v,z);
while (1) {
//
// accumulate current point
//
xsum += xcur;
ysum += ycur;
nsum += 1;
xmean = xsum / ((float) nsum);
ymean = ysum / ((float) nsum);
dx = (xmean - xseg);
dy = (ymean - yseg);
d = sqrt(dx*dx + dy*dy);
nx = dy/d;
ny = -dx/d;
//
// follow current direction
//
if ((v->array[ycur][xcur] & v->direction) == v->north) {
xnew = xcur;
ynew = ycur - 1;
last_direction = 'n'; // remember direction
v->array[ycur][xcur] &= ~v->direction; // clear direction
}
else if ((v->array[ycur][xcur] & v->direction) == v->south) {
xnew = xcur;
ynew = ycur + 1;
last_direction = 's'; // remember direction
v->array[ycur][xcur] &= ~v->direction; // clear direction
}
else if ((v->array[ycur][xcur] & v->direction) == v->east) {
xnew = xcur + 1;
ynew = ycur;
last_direction = 'e'; // remember direction
v->array[ycur][xcur] &= ~v->direction; // clear direction
}
else if ((v->array[ycur][xcur] & v->direction) == v->west) {
xnew = xcur - 1;
ynew = ycur;
last_direction = 'w'; // remember direction
v->array[ycur][xcur] &= ~v->direction; // clear direction
}
else if ((v->array[ycur][xcur] & v->direction) == v->corner) {
if (last_direction == 'n') {
xnew = xcur - 1;
ynew = ycur;
last_direction = 'w';
}
else if (last_direction == 'e') {
xnew = xcur;
ynew = ycur - 1;
last_direction = 'n';
}
else if (last_direction == 's') {
xnew = xcur + 1;
ynew = ycur;
last_direction = 'e';
}
else if (last_direction == 'w') {
xnew = xcur;
ynew = ycur + 1;
last_direction = 's';
}
v->array[ycur][xcur] &= ~v->direction; // set direction to corner2
v->array[ycur][xcur] |= v->corner2;
}
else if ((v->array[ycur][xcur] & v->direction) == v->corner2) {
if (last_direction == 'n') {
xnew = xcur - 1;
ynew = ycur;
last_direction = 'w';
}
else if (last_direction == 'e') {
xnew = xcur;
ynew = ycur - 1;
last_direction = 'n';
}
else if (last_direction == 's') {
xnew = xcur + 1;
ynew = ycur;
last_direction = 'e';
}
else if (last_direction == 'w') {
xnew = xcur;
ynew = ycur + 1;
last_direction = 's';
}
v->array[ycur][xcur] &= ~v->direction; // clear direction
}
else if ((v->array[ycur][xcur] & v->direction) == 0) {
printf("fab.c: oops -- no direction\n");
exit(-1);
}
else {
printf("fab.c: oops -- unknown direction = %d\n",
v->array[ycur][xcur] & v->direction);
exit(-1);
}
//
// finish segment and return if back to start, or stop reached
//
if (((xnew == xstart) && (ynew == ystart)) ||
((v->array[ynew][xnew] & v->direction) == v->stop)) {
fab_path_point(v);
fab_path_axis(v,xnew);
fab_path_axis(v,ynew);
if (v->path->dof > 2)
fab_path_axis(v,z);
break;
}
//
// check segment at new point
//
d = fabs(nx*(xnew-xseg) + ny*(ynew-yseg));
if (d >= error) {
//
// exceeds error, add current point to path
//
fab_path_point(v);
fab_path_axis(v,xcur);
fab_path_axis(v,ycur);
if (v->path->dof > 2)
fab_path_axis(v,z);
xsum = 0;
ysum = 0;
nsum = 0;
xseg = xcur;
yseg = ycur;
}
//
// move to new point
//
xcur = xnew;
ycur = ynew;
}
}
void fab_vectorize(struct fab_vars *v, float error, int z) {
//
// vectorize xy edge directions
// error is transverse distance from segment mean
// in lattice units
// z is layer height (ignored for 2D paths)
//
int x, y;
//
// find edge starts
//
for (x = 0; x < v->nx; ++x)
if ((v->array[0][x] & v->direction) == v->south)
fab_follow(v, error, 0,x, z);
for (y = 0; y < v->ny; ++y)
if ((v->array[y][v->nx-1] & v->direction) == v->west)
fab_follow(v, error, y,v->nx-1, z);
for (x = (v->nx - 1); x >= 0 ; --x)
if ((v->array[v->ny-1][x] & v->direction) == v->north)
fab_follow(v, error, v->ny-1,x, z);
for (y = (v->ny - 1); y >= 0; --y)
if ((v->array[y][0] & v->direction) == v->east)
fab_follow(v, error, y,0, z);
//
// find remaining interior paths
//
for (y = 1; y < (v->ny - 1); ++y) {
if ((y % 2) == 1) {
for (x = 1; x < (v->nx - 1); ++x) {
if (v->array[y][x] & v->direction) {
fab_follow(v,error,y,x,z);
}
}
}
else {
for (x = (v->nx - 2); x > 0; --x) {
if (v->array[y][x] & v->direction) {
fab_follow(v,error,y,x,z);
}
}
}
}
}
void fab_halftone(struct fab_vars *v, float threshold, int points, float size, float spacing, float offset, int invert) {
//
// halftone array to path
//
int xmin,ymin,x,y,n,i,space,shift,top;
float pi,angle,r;
top = pow(2,v->bit_depth) - 1;
//
// start 2D path
//
fab_path_start(v,2);
//
// loop over spots
//
pi = 4.0*atan(1.0);
angle = 2.0*pi/(points-1.0);
n = v->nx * (0.5*size/v->dx);
space = spacing*2*n;
shift = 0;
for (ymin = n; ymin < (v->ny - n); ymin += space) {
for (xmin = n; xmin < (v->nx - n); xmin += space) {
r = n * (invert*top + (1-2*invert)* v->array[ymin][xmin]) / ((float) top);
//
// if spot radius larger than threshold, add to path
//
if (r >= threshold) {
fab_path_segment(v);
for (i = 0; i < points; ++i) {
x = shift + 0.5 + xmin + r*cos(i*angle);
y = 0.5 + ymin + r*sin(i*angle);
fab_path_point(v);
fab_path_axis(v,x);
fab_path_axis(v,y);
}
}
}
shift += offset * space;
if (shift >= space)
shift -= space;
}
}