fabmodules/src/core/png_path.c

672 wiersze
23 KiB
C

//
// png_path.c
// slice, offset, and vectorize PNG to path
//
// Neil Gershenfeld 10/14/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.
//
// todo:
#include "fab.h"
int main(int argc, char **argv) {
//
// local vars
//
struct fab_vars v;
init_vars(&v);
struct fab_vars vxz;
init_vars(&vxz);
struct fab_vars vyz;
init_vars(&vyz);
int offset_number,offset_count,
layer,layer_number,nz,xz,yz,xy,
ix,ixmin,ixmax,idx,
iy,iymin,iymax,idy,
iz,ithreshold,
*tool_dx,*tool_dy,*tool_iz,tool_pt,tool_n,tool_ir,tool_id,
*clearance_dx,*clearance_dy,*clearance_iz,clearance_pt,clearance_n,clearance_ir,clearance_id,
remaining_count,remaining_count_sum,tool_collision;
float error,offset_diameter,offset_overlap,clearance_length,clearance_diameter,
intensity_top,intensity_bottom,intensity_layer,
z_top,z_bottom,z_thickness,z_layer,*z_list,distance;
char tool_type;
//
// command line args
//
if ((argc < 3) || (argc > 18)) {
printf("command line: png_path in.png out.path [error [offset_diameter [offset_number [offset_overlap [intensity_top [intensity_bottom [z_top [z_bottom [z_thickness [xz [yz [xy [type [clearance_length clearance_diameter]]]]]]]]]]]]]]\n");
printf(" in.png = input PNG file\n");
printf(" out.path = output path file\n");
printf(" error = allowable vector fit deviation (optional, pixels, default 1.1)\n");
printf(" offset_diameter = diameter to offset (optional, mm, default 0)\n");
printf(" offset_number = number of contours to offset (optional, -1 to fill all, default 1)\n");
printf(" offset_overlap = tool offset overlap fraction (optional, 0 (no overlap) - 1 (complete overlap, default 0.5))\n");
printf(" intensity_top = top slice intensity (optional, 0-1, default 0.5)\n");
printf(" intensity_bottom = bottom slice intensity (optional, 0-1, default intensity_top)\n");
printf(" z_top = top slice z value (optional, mm, default 0)\n");
printf(" z_bottom = bottom slice z value (optional, mm, default z_top)\n");
printf(" z_thickness = slice z thickness (optional, mm, default z_top-z_bottom)\n");
printf(" xz = xz finish (optional, 1=yes, default 0\n");
printf(" yz = yz finish (optional, 1=yes, default 0\n");
printf(" xy = xy path (optional, 1=yes, default 1\n");
printf(" type = finish tool type (optional, f=flat end, b=ball end, default f\n");
printf(" clearance_length = finish tool clearance length (optional, mm, 0 = no limit, default 0\n");
printf(" clearance_diameter = finish tool clearance diameter (optional, mm, default offset_diameter\n");
exit(-1);
}
error = 1.1;
offset_diameter = 0;
offset_number = 1;
offset_overlap = 0.5;
intensity_top = 0.5;
intensity_bottom = intensity_top;
z_top = 0;
z_bottom = 0;
z_thickness = 0;
xz = 0;
yz = 0;
xy = 1;
tool_type = 'f';
if (argc >= 4) {
sscanf(argv[3],"%f",&error);
}
if (argc >= 5) {
sscanf(argv[4],"%f",&offset_diameter);
}
if (argc >= 6) {
sscanf(argv[5],"%d",&offset_number);
}
if (argc >= 7) {
sscanf(argv[6],"%f",&offset_overlap);
}
if (argc >= 8) {
sscanf(argv[7],"%f",&intensity_top);
intensity_bottom = intensity_top;
}
if (argc >= 9) {
sscanf(argv[8],"%f",&intensity_bottom);
}
if (argc >= 10) {
sscanf(argv[9],"%f",&z_top);
z_bottom = z_top;
}
if (argc >= 11) {
sscanf(argv[10],"%f",&z_bottom);
z_thickness = z_top - z_bottom;
}
if (argc >= 12) {
sscanf(argv[11],"%f",&z_thickness);
}
if (argc >= 13) {
sscanf(argv[12],"%d",&xz);
}
if (argc >= 14) {
sscanf(argv[13],"%d",&yz);
}
if (argc >= 15) {
sscanf(argv[14],"%d",&xy);
}
if (argc >= 16) {
sscanf(argv[15],"%c",&tool_type);
}
clearance_length = z_top - z_bottom;
clearance_diameter = offset_diameter;
if (argc >= 17) {
sscanf(argv[16],"%f",&clearance_length);
if (clearance_length == 0)
clearance_length = z_top - z_bottom;
}
if (argc >= 18) {
sscanf(argv[17],"%f",&clearance_diameter);
}
tool_collision = 0;
//
// read PNG
//
fab_read_png(&v,argv[1]);
//
// 2D path?
//
if (argc < 9) {
//
// start path
//
fab_path_start(&v,2);
printf("png_path: intensity %f\n",intensity_top);
//
// copy image to array
//
fab_png_array(&v);
//
// threshold array
//
printf(" threshold\n");
fab_threshold(&v,intensity_top);
//
// find edges
//
printf(" find edges\n");
remaining_count = fab_edges(&v);
if (remaining_count != 0) {
//
// find edge distances
//
printf(" find distances\n");
//test_fab_distances(&v);
fab_distances(&v);
//
// loop over contours
//
printf(" offset:\n");
offset_count = 0;
remaining_count_sum = 0;
do {
//
// offset
//
distance = offset_diameter/2.0 + offset_diameter*(1-offset_overlap)*offset_count;
remaining_count = fab_offset(&v,distance);
remaining_count_sum += remaining_count;
printf(" distance %f, %d exterior points remain\n",
distance,remaining_count);
if (remaining_count != 0) {
//
// set edge directions
//
fab_directions(&v);
//
// vectorize edge directions
//
fab_vectorize(&v,error,0);
}
++offset_count;
} while ((offset_count != offset_number) && (remaining_count != 0));
}
else {
printf("path.c: oops -- no layer edges\n");
exit(-1);
}
if (remaining_count_sum == 0) {
printf("png_path: no offset edges\n");
exit(1);
}
}
//
// 3D path
//
else {
//
// start path
//
fab_path_start(&v,3);
//
// set path z bounds
//
v.zmin = z_bottom;
v.dz = (z_top - z_bottom);
if (v.dz == 0)
v.nz = 1;
else
v.nz = v.dz * v.nx / v.dx;
//
// find z layer values
//
if (xy == 1) {
if (v.dz == 0)
nz = 1;
else {
nz = 2 + (z_top-z_bottom)/z_thickness;
z_list = (float*) malloc(nz*sizeof(float));
z_layer = z_top;
nz = 0;
while (1) {
z_list[nz] = z_layer;
nz += 1;
if (z_layer == z_bottom)
break;
z_layer -= z_thickness;
if (z_layer < z_bottom)
z_layer = z_bottom;
}
}
}
//
// copy image to array for finish cuts
//
fab_png_array(&v);
//
// create tool shape offset list
//
tool_ir = v.nx*offset_diameter/(2*v.dx);
tool_id = 2*tool_ir;
tool_dx = (int*) malloc(tool_id*tool_id*sizeof(int));
tool_dy = (int*) malloc(tool_id*tool_id*sizeof(int));
tool_iz = (int*) malloc(tool_id*tool_id*sizeof(int));
tool_pt = 0;
tool_n = 0;
for (ix = 0; ix < tool_id; ++ix)
for (iy = 0; iy < tool_id; ++iy)
if (sqrt((ix-tool_ir)*(ix-tool_ir)+(iy-tool_ir)*(iy-tool_ir)) < tool_ir) {
tool_dx[tool_pt] = ix-tool_ir;
tool_dy[tool_pt] = iy-tool_ir;
if (tool_type == 'f')
tool_iz[tool_pt] = 0;
else if (tool_type == 'b') {
tool_iz[tool_pt] = pow(2,v.bit_depth)*(tool_ir - sqrt(tool_ir*tool_ir
- ((ix-tool_ir)*(ix-tool_ir) + (iy-tool_ir)*(iy-tool_ir))))/v.nz;
}
else {
printf("path.c: oops -- unsupported tool type\n");
exit(-1);
}
tool_pt += 1;
tool_n = tool_pt;
}
//
// create tool clearance offset list
//
clearance_ir = v.nx*clearance_diameter/(2*v.dx);
clearance_id = 2*clearance_ir;
clearance_dx = (int*) malloc(clearance_id*clearance_id*sizeof(int));
clearance_dy = (int*) malloc(clearance_id*clearance_id*sizeof(int));
clearance_iz = (int*) malloc(clearance_id*clearance_id*sizeof(int));
clearance_pt = 0;
clearance_n = 0;
for (ix = 0; ix < clearance_id; ++ix)
for (iy = 0; iy < clearance_id; ++iy)
if (((sqrt((ix-clearance_ir)*(ix-clearance_ir)+(iy-clearance_ir)*(iy-clearance_ir))) >= tool_ir)
&& ((sqrt((ix-clearance_ir)*(ix-clearance_ir)+(iy-clearance_ir)*(iy-clearance_ir))) < clearance_ir)) {
clearance_dx[clearance_pt] = ix-clearance_ir;
clearance_dy[clearance_pt] = iy-clearance_ir;
clearance_iz[clearance_pt] = (pow(2,v.bit_depth)-1)*clearance_length/v.dz;
clearance_pt += 1;
clearance_n = clearance_pt;
}
//
// check for xz cut
//
if (xz == 1) {
//
// allocate xz finish cut array
//
vxz.nx = v.nx;
vxz.ny = v.nz;
vxz.nz = 1;
vxz.dx = v.dx;
vxz.dy = v.dz;
vxz.dz = 0;
vxz.xmin = v.xmin;
vxz.ymin = v.zmin;
vxz.zmin = 0;
vxz.array = malloc(v.nz*sizeof(uint32_t *));
for (iz = 0; iz < v.nz; ++iz)
vxz.array[iz] = malloc(v.nx*sizeof(uint32_t));
//
// start xz path
//
fab_path_start(&vxz,3);
//
// y loop
//
iymin = v.ny*clearance_diameter/(2.0*v.dy);
iymax = (v.ny-1) - v.ny*clearance_diameter/(2.0*v.dy);
idy = v.ny*offset_diameter*(1-offset_overlap)/v.dy;
printf(" xz finish:\n");
for (iy = iymin; iy <= iymax; (iy += idy)) {
printf(" y=%d/%d\n",iy,iymax);
//
// x loop
//
for (ix = 0; ix < vxz.nx; ++ix) {
//
// set z threshold
//
if ((ix <= clearance_ir) | (ix >= (vxz.nx-clearance_ir)))
//
// too close to edge, set max
//
ithreshold = pow(2,v.bit_depth);
else {
//
// offset for tool shape
//
ithreshold = 0;
for (tool_pt = 0; tool_pt < tool_n; ++tool_pt) {
if (v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] > (ithreshold+tool_iz[tool_pt]))
ithreshold = v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] - tool_iz[tool_pt];
}
//
// check clearance
//
for (clearance_pt = 0; clearance_pt < clearance_n; ++clearance_pt) {
if (v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] > (ithreshold+clearance_iz[clearance_pt])) {
ithreshold = v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] - clearance_iz[clearance_pt];
tool_collision = 1;
}
}
}
//
// convert from image to lattice units
//
ithreshold = 1+(v.nz-2.0)*ithreshold/(pow(2,v.bit_depth)-1.0); // +1 for bottom edge
//
// z (array y) loop
//
for (iz = 0; iz < vxz.ny; ++iz) {
if (iz <= ithreshold)
vxz.array[iz][ix] = v.interior;
else
vxz.array[iz][ix] = v.empty;
}
}
//
// set edge directions
//
fab_directions(&vxz);
//
// vectorize edge directions
//
fab_vectorize(&vxz,error,iy);
}
//
// copy xz path
//
vxz.path->segment = vxz.path->first;
while (1) {
//
// follow segment forward
//
vxz.path->segment->point = vxz.path->segment->first;
fab_path_segment(&v);
while (1) {
//
// follow points
//
fab_path_point(&v);
vxz.path->segment->point->axis = vxz.path->segment->point->first;
ix = vxz.path->segment->point->axis->value;
vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
iy = vxz.path->segment->point->axis->value;
vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
iz = vxz.path->segment->point->axis->value;
fab_path_axis(&v,ix);
fab_path_axis(&v,iz);
fab_path_axis(&v,iy);
if (vxz.path->segment->point->next == 0)
break;
vxz.path->segment->point = vxz.path->segment->point->next;
}
if (vxz.path->segment->next == 0)
break;
vxz.path->segment = vxz.path->segment->next;
//
// follow segment backwards
//
vxz.path->segment->point = vxz.path->segment->last;
fab_path_segment(&v);
while (1) {
//
// follow points
//
fab_path_point(&v);
vxz.path->segment->point->axis = vxz.path->segment->point->first;
ix = vxz.path->segment->point->axis->value;
vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
iy = vxz.path->segment->point->axis->value;
vxz.path->segment->point->axis = vxz.path->segment->point->axis->next;
iz = vxz.path->segment->point->axis->value;
fab_path_axis(&v,ix);
fab_path_axis(&v,iz);
fab_path_axis(&v,iy);
if (vxz.path->segment->point->previous == 0)
break;
vxz.path->segment->point = vxz.path->segment->point->previous;
}
if (vxz.path->segment->next == 0)
break;
vxz.path->segment = vxz.path->segment->next;
}
}
//
// check for yz cut
//
if (yz == 1) {
//
// allocate yz finish cut array
//
vyz.nx = v.ny;
vyz.ny = v.nz;
vyz.nz = 1;
vyz.dx = v.dy;
vyz.dy = v.dz;
vyz.dz = 0;
vyz.xmin = v.ymin;
vyz.ymin = v.zmin;
vyz.zmin = 0;
vyz.array = malloc(v.nz*sizeof(uint32_t *));
for (iz = 0; iz < v.nz; ++iz)
vyz.array[iz] = malloc(v.ny*sizeof(uint32_t));
//
// start yz path
//
fab_path_start(&vyz,3);
//
// x loop
//
ixmin = v.nx*clearance_diameter/(2.0*v.dx);
ixmax = (v.nx-1) - v.nx*clearance_diameter/(2.0*v.dx);
idx = v.nx*offset_diameter*(1-offset_overlap)/v.dx;
printf(" yz finish:\n");
for (ix = ixmax; ix >= ixmin; (ix -= idx)) {
printf(" x=%d/%d\n",ix,ixmax);
//
// y (array x) loop
//
for (iy = 0; iy < vyz.nx; ++iy) {
//
// set z threshold
//
if ((iy <= clearance_ir) | (iy >= (vyz.nx-clearance_ir)))
//
// too close to edge, set max
//
ithreshold = pow(2,v.bit_depth);
else {
//
// offset for tool shape
//
ithreshold = 0;
for (tool_pt = 0; tool_pt < tool_n; ++tool_pt) {
if (v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] > (ithreshold+tool_iz[tool_pt]))
ithreshold = v.array[iy+tool_dy[tool_pt]][ix+tool_dx[tool_pt]] - tool_iz[tool_pt];
}
//
// check clearance
//
for (clearance_pt = 0; clearance_pt < clearance_n; ++clearance_pt) {
if (v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] > (ithreshold+clearance_iz[clearance_pt])) {
ithreshold = v.array[iy+clearance_dy[clearance_pt]][ix+clearance_dx[clearance_pt]] - clearance_iz[clearance_pt];
tool_collision = 1;
}
}
}
//
// convert from image to lattice units
//
ithreshold = 1+(v.nz-2.0)*ithreshold/(pow(2,v.bit_depth)-1.0); // +1 for bottom edge
//
// z (array y) loop
//
for (iz = 0; iz < vyz.ny; ++iz) {
if (iz <= ithreshold)
vyz.array[iz][iy] = v.interior;
else
vyz.array[iz][iy] = v.empty;
}
}
//
// set edge directions
//
fab_directions(&vyz);
//
// vectorize edge directions
//
fab_vectorize(&vyz,error,ix);
}
//
// copy yz path
//
vyz.path->segment = vyz.path->first;
while (1) {
//
// follow segment forwards
//
vyz.path->segment->point = vyz.path->segment->first;
fab_path_segment(&v);
while (1) {
//
// follow points
//
fab_path_point(&v);
vyz.path->segment->point->axis = vyz.path->segment->point->first;
ix = vyz.path->segment->point->axis->value;
vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
iy = vyz.path->segment->point->axis->value;
vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
iz = vyz.path->segment->point->axis->value;
fab_path_axis(&v,iz);
fab_path_axis(&v,ix);
fab_path_axis(&v,iy);
if (vyz.path->segment->point->next == 0)
break;
vyz.path->segment->point = vyz.path->segment->point->next;
}
if (vyz.path->segment->next == 0)
break;
vyz.path->segment = vyz.path->segment->next;
//
// follow segment backwards
//
vyz.path->segment->point = vyz.path->segment->last;
fab_path_segment(&v);
while (1) {
//
// follow points
//
fab_path_point(&v);
vyz.path->segment->point->axis = vyz.path->segment->point->first;
ix = vyz.path->segment->point->axis->value;
vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
iy = vyz.path->segment->point->axis->value;
vyz.path->segment->point->axis = vyz.path->segment->point->axis->next;
iz = vyz.path->segment->point->axis->value;
fab_path_axis(&v,iz);
fab_path_axis(&v,ix);
fab_path_axis(&v,iy);
if (vyz.path->segment->point->previous == 0)
break;
vyz.path->segment->point = vyz.path->segment->point->previous;
}
if (vyz.path->segment->next == 0)
break;
vyz.path->segment = vyz.path->segment->next;
}
}
//
// check for xy cut
//
if (xy == 1) {
//
// z loop
//
for (layer = (nz-1); layer >= 0; --layer) {
if (v.dz == 0) {
z_layer = z_top;
intensity_layer = intensity_top;
layer_number = 0;
}
else {
z_layer = z_list[layer];
intensity_layer = intensity_bottom + (intensity_top-intensity_bottom)*(z_layer-z_bottom)/(z_top-z_bottom);
layer_number = v.nz*(z_layer-z_bottom)/(z_top-z_bottom);
}
printf("png_path: intensity %f, z %f\n",intensity_layer,z_layer);
//
// copy image to array
//
fab_png_array(&v);
//
// threshold array
//
printf(" threshold\n");
fab_threshold(&v,intensity_layer);
//
// find edges
//
printf(" find edges\n");
remaining_count = fab_edges(&v);
if (remaining_count == 0) {
printf(" no layer edges\n");
continue;
}
//
// find edge distances
//
printf(" find distances\n");
//test_fab_distances(&v);
fab_distances(&v);
//
// loop over contours
//
printf(" offset:\n");
offset_count = 0;
remaining_count_sum = 0;
do {
//
// offset
//
distance = offset_diameter/2.0 + offset_diameter*(1-offset_overlap)*offset_count;
remaining_count = fab_offset(&v,distance);
remaining_count_sum += remaining_count;
printf(" distance %f, %d exterior points remain\n",
distance,remaining_count);
if (remaining_count != 0) {
//
// set edge directions
//
fab_directions(&v);
//
// vectorize edge directions
//
fab_vectorize(&v,error,layer_number);
}
++offset_count;
} while ((offset_count != offset_number) && (remaining_count != 0));
}
if (remaining_count_sum == 0) {
printf("png_path: no offset edges\n");
exit(1);
}
}
}
if (tool_collision == 1)
printf(" tool collisions\n");
//
// write path
//
fab_write_path(&v,argv[2]);
//
// exit
//
exit(0);
}