fabmodules/src/solver/fabvars.cpp

571 wiersze
17 KiB
C++

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <png.h>
#include "fabvars.hpp"
#include "region.hpp"
using namespace std;
FabVars::FabVars(output_mode o, int argc, char** argv)
: ni(-1), nj(-1), nk(-1),
min_volume(-1), min_area(-1),
xmin(0), ymin(0), zmin(0),
dx(0), dy(0), dz(0),
pixels_per_mm(10), mm_per_unit(-1), quality(8),
decimation_error(1),
mode(SOLVE_BOOL), output(o), projection(false),
infile_name(""), outfile_name(""),
red(NULL), green(NULL), blue(NULL), intensity(NULL),
volume(0)
{
infile_name = argv[0];
if (output != OUTPUT_STATS)
outfile_name = argv[1];
pixels_per_mm = 10;
// The different programs have different input argument orders.
if (output == OUTPUT_PNG) {
if (argc >= 3)
pixels_per_mm = atof(argv[2]);
if (argc >= 4)
nk = atoi(argv[3]);
}
else if (output == OUTPUT_STL) {
if (argc >= 3)
pixels_per_mm = atof(argv[2]);
if (argc >= 4)
quality = atoi(argv[3]);
}
else if (output == OUTPUT_SVG) {
if (argc >= 3)
pixels_per_mm = atof(argv[2]);
if (argc >= 4)
nk = atoi(argv[3]);
if (argc >= 5)
decimation_error = atof(argv[4]);
if (argc >= 6)
quality = atoi(argv[7]);
}
else if (output == OUTPUT_STATS) {
if (argc >= 2)
pixels_per_mm = atof(argv[1]);
}
// Load data from the input file
load();
}
FabVars::~FabVars()
{
if (mode == SOLVE_RGB && red && green && blue) {
for (int y = 0; y < nj; ++y) {
delete [] red[y];
delete [] green[y];
delete [] blue[y];
}
delete [] red;
red = NULL;
delete [] green;
green = NULL;
delete [] blue;
blue = NULL;
} else if ((mode == SOLVE_BOOL || mode == SOLVE_REAL) && intensity) {
for (int y = 0; y < nj; ++y)
delete [] intensity[y];
delete [] intensity;
intensity = NULL;
}
}
void FabVars::load()
{
if (infile_name == "") {
cerr << "No input file provided!" << endl;
exit(1);
}
fstream input;
// Open the input file
input.open(infile_name.c_str(), ios::in);
if (!input.good()) {
cerr << "Failed to open input file.\n";
exit(1);
}
// Read and parse the file
string line;
while (getline(input, line))
if (line.find("format:") != string::npos)
if (line.find("Boolean") != string::npos)
mode = SOLVE_BOOL;
else if (line.find("RGB") != string::npos)
mode = SOLVE_RGB;
else if (line.find("Real") != string::npos)
mode = SOLVE_REAL;
else {
cerr << "Input math string must be Boolean, RGB, or Real, not "
<< line.substr(8,string::npos) << "." << endl;
exit(4);
}
else if (line.find("mm per unit:") != string::npos)
sscanf(line.substr(12,string::npos).c_str(),"%lf",
&mm_per_unit);
else if (line.find("dx dy dz:") != string::npos)
sscanf(line.substr(9,string::npos).c_str(),"%lf %lf %lf",
&dx, &dy, &dz);
else if (line.find("xmin ymin zmin:") != string::npos)
sscanf(line.substr(15,string::npos).c_str(),"%lf %lf %lf",
&xmin, &ymin, &zmin);
else if (line.find("expression: ") != string::npos)
math_string = line.substr(12,string::npos);
ni = dx * mm_per_unit * pixels_per_mm;
nj = dy * mm_per_unit * pixels_per_mm;
// Default Z slices: 10 for SVG output, equivalent resolution
// for other output modes.
if (nk == -1) {
if (output == OUTPUT_SVG)
nk = dz ? 10 : 1;
else
nk = dz * mm_per_unit * pixels_per_mm;
}
// Make sure that these are all non-zero.
ni = ni ? ni : 1;
nj = nj ? nj : 1;
nk = nk ? nk : 1;
pb.full = uint64_t(ni)*uint64_t(nj)*uint64_t(nk);
// Pick a minimum volume below which we won't do
// octree recursion.
min_volume = 64;
min_area = 8;
// Pick a stroke size for drawing SVGs
stroke = min(ni, nj) / (pixels_per_mm * 1000.);
// Convert decimation error in mm^2 into something that can
// directly be compared with a simplified Heron's formula output.
decimation_error = pow(decimation_error*4, 2);
paths.decimation_error = decimation_error;
// Everything below this point is allocating memory for the images,
// so return early if we have a different output format.
if (output != OUTPUT_PNG)
return;
// Allocate memory for the image.
if (mode == SOLVE_RGB) {
red = new uint8_t*[nj];
green = new uint8_t*[nj];
blue = new uint8_t*[nj];
for (int y = 0; y < nj; ++y) {
red[y] = new uint8_t[ni];
green[y] = new uint8_t[ni];
blue[y] = new uint8_t[ni];
for (int x = 0; x < ni; ++x)
{
red[y][x] = 0;
green[y][x] = 0;
blue[y][x] = 0;
}
}
} else {
intensity = new uint16_t*[nj];
for (int y = 0; y < nj; ++y) {
intensity[y] = new uint16_t[ni];
for (int x = 0; x < ni; ++x)
intensity[y][x] = 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
float FabVars::x(float i) const
{
return xmin + dx*i / float(ni);
}
float FabVars::y(float j) const
{
return ymin + dy*j / float(nj);
}
float FabVars::z(float k) const
{
return zmin + dz*k / float(nk);
}
////////////////////////////////////////////////////////////////////////////////
FabInterval FabVars::x(float imin, float imax) const
{
return FabInterval(xmin + dx * imin / float(ni),
xmin + dx * imax / float(ni));
}
FabInterval FabVars::y(float jmin, float jmax) const
{
return FabInterval(ymin + dy * jmin / float(nj),
ymin + dy * jmax / float(nj));
}
FabInterval FabVars::z(float kmin, float kmax) const
{
return FabInterval(zmin + dz * kmin / float(nk),
zmin + dz * kmax / float(nk));
}
float FabVars::scale(unsigned int k) const
{
if (nk == 1) {
if (mode == SOLVE_BOOL || mode == SOLVE_REAL)
return 65535;
else
return 1;
}
return k/float(nk - 1) *
((mode == SOLVE_BOOL || mode == SOLVE_REAL) ? 65535 : 1);
}
////////////////////////////////////////////////////////////////////////////////
int FabVars::k(float z) const
{
return (z - zmin) * float(nk) / dz;
}
////////////////////////////////////////////////////////////////////////////////
void FabVars::fill(Region r)
{
float s = scale(r.kmax - 1);
for (int i = r.imin; i < r.imax; ++i)
for (int j = r.jmin; j < r.jmax; ++j)
if (s > intensity[nj - j - 1][i])
intensity[nj - j - 1][i] = s;
}
void FabVars::fill(Region r, unsigned char R, unsigned char G, unsigned char B)
{
float s = scale(r.kmax);
for (int j = r.jmin; j < r.jmax; ++j)
for (int i = r.imin; i < r.imax; ++i) {
if (R * s > red[nj - j - 1][i])
red[nj - j - 1][i] = R * s;
if (G * s > green[nj - j - 1][i])
green[nj - j - 1][i] = G * s;
if (B * s > blue[nj - j - 1][i])
blue[nj - j - 1][i] = B * s;
}
}
////////////////////////////////////////////////////////////////////////////////
void FabVars::add_triangles(std::list<Triangle> tris)
{
geometry_lock.lock();
triangles.splice(triangles.begin(), tris);
geometry_lock.unlock();
}
void FabVars::add_paths(const PathSet& p)
{
geometry_lock.lock();
list<Path>::const_iterator it;
for (it = p.begin(); it != p.end(); ++it)
paths += *it;
geometry_lock.unlock();
}
void FabVars::add_volume(const uint64_t v)
{
geometry_lock.lock();
volume += v;
geometry_lock.unlock();
}
////////////////////////////////////////////////////////////////////////////////
void FabVars::write_png()
{
if (mode == SOLVE_BOOL || mode == SOLVE_REAL)
fab_write_png_K16(this, outfile_name.c_str());
else if (mode == SOLVE_RGB)
fab_write_png_RGB24(this, outfile_name.c_str());
}
void FabVars::write_stl()
{
fstream stl_out;
stl_out.open(outfile_name.c_str(), fstream::trunc | fstream::out);
// The first 80 characters are undefined, so let's leave an informative message!
stl_out << "This is a binary STL file created by math_stl. Learn more at kokompe.cba.mit.edu";
stl_out << " ";
uint32_t stl_faces = 0;
list<Triangle>::iterator it;
for (it = triangles.begin(); it != triangles.end(); ++it)
{
// Write the normal as all zeros
for (int i = 0; i < 12; ++i)
stl_out << char(0);
// Extract vertices from the list
Vec3f v1 = it->v1;
Vec3f v2 = it->v2;
Vec3f v3 = it->v3;
// Convert into world coordinates
v1.x = x(v1.x);
v1.y = y(v1.y);
v1.z = z(v1.z);
// Write out each of the floats, byte by byte
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v1.x)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v1.y)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v1.z)[i];
v2.x = x(v2.x);
v2.y = y(v2.y);
v2.z = z(v2.z);
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v2.x)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v2.y)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v2.z)[i];
v3.x = x(v3.x);
v3.y = y(v3.y);
v3.z = z(v3.z);
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v3.x)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v3.y)[i];
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&v3.z)[i];
stl_out << char(0) << char(0);
stl_faces++;
}
stl_out.seekp(80);
for (int i = 0; i < 4; ++i)
stl_out << ((char*)&stl_faces)[i];
stl_out.close();
}
void FabVars::write_svg()
{
float scale = pixels_per_mm * 0.352778; // 72 dpi
fstream svg_out;
svg_out.open(outfile_name.c_str(), fstream::trunc | fstream::out);
svg_out << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>\n"
<< "<!-- Created with math_svg (kokompe.cba.mit.edu) -->\n"
<< "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"
<< " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
<< "<svg \n"
<< " xmlns=\"http://www.w3.org/2000/svg\"\n"
<< " width=\"" << ni / pixels_per_mm << "mm\"\n"
<< " height=\"" << nj / pixels_per_mm << "mm\"\n"
<< " units=\"mm\"\n"
// << " xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"\n"
// << " inkscape:document-units=\"mm\""
<< " viewBox=\"0 0 "
<< ni / scale << ' ' << nj / scale << "\">\n"
<< "<g style=\"stroke:rgb(0,0,0); "
<< "stroke-width:" << stroke << "; "
<< "fill:none\">\n";
list<Path>::iterator it;
for (it = paths.begin(); it != paths.end(); ++it) {
svg_out << " <path d=\"M" << it->front().x / scale
<< ' ' << (nj - it->front().y - 1) / scale;
Path::iterator p = it->begin();
Path::iterator path_end = it->end();
bool loop = (it->front() == it->back());
if (loop)
path_end--;
while (++p != path_end)
svg_out << " L" << p->x / scale
<< ' ' << (nj - p->y - 1) / scale;
if (loop)
svg_out << " Z";
svg_out << "\"/>\n";
}
svg_out << "</g>\n</svg>";
svg_out.close();
}
// Based on code from math_png
// with attribution
// Neil Gershenfeld
// CBA MIT 3/6/11
//
// (c) Massachusetts Institute of Technology 2010
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//
void fab_write_png_K16(FabVars *v, const char* output_file_name) {
//
// write 16-bit grayscale PNG from FabVars
//
FILE *output_file;
int x,y;
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");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, output_file);
//
// set vars
//
bit_depth = 16;
color_type = PNG_COLOR_TYPE_GRAY;
png_set_IHDR(png_ptr, info_ptr, v->ni, v->nj,
bit_depth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
res_x = 1000 * v->ni / (v->dx * v->mm_per_unit);
res_y = 1000 * v->nj / (v->dy * v->mm_per_unit);
png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
png_text text[2];
char zmin[10];
snprintf(zmin, 10, "%g", v->zmin*v->mm_per_unit);
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
text[0].key = (char*)"zmin";
text[0].text = zmin;
char zmax[10];
snprintf(zmax, 10, "%g", (v->zmin+v->dz)*v->mm_per_unit);
text[1].compression = PNG_TEXT_COMPRESSION_NONE;
text[1].key = (char*)"zmax";
text[1].text = zmax;
png_set_text(png_ptr, info_ptr, text, 2);
png_write_info(png_ptr, info_ptr);
//
// allocate pixels
//
png_bytep* row_pointers = new png_bytep[v->nj];
for (y = 0; y < v->nj; ++y)
row_pointers[y] = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
//
// set pixels
//
for (y = 0; y < v->nj; ++y)
for (x = 0; x < v->ni; ++x) {
ptr = &(row_pointers[y][x*2]);
ptr[0] = (v->intensity[y][x] >> 8) & 255;
ptr[1] = v->intensity[y][x] & 255;
}
//
// write, close, and return
//
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, NULL);
fclose(output_file);
png_destroy_write_struct(&png_ptr, &info_ptr);
cout << "write " << output_file_name
<< "\n x pixels: " << v->ni << ", y pixels: " << v->nj
<< "\n x pixels/m: " << res_x << ", y pixels/m: " << res_y
<< "\n dx: " << v->dx << " mm, dy: " << v->dy <<" mm" << endl;
for (y = 0; y < v->nj; ++y)
delete [] row_pointers[y];
delete [] row_pointers;
}
void fab_write_png_RGB24(FabVars *v, const char *output_file_name) {
//
// write 24-bit RGB PNG from FabVars
//
FILE *output_file;
int x,y;
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");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, output_file);
//
// set vars
//
bit_depth = 8;
color_type = PNG_COLOR_TYPE_RGB;
png_set_IHDR(png_ptr, info_ptr, v->ni, v->nj,
bit_depth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
res_x = 1000 * v->ni / (v->dx * v->mm_per_unit);
res_y = 1000 * v->nj / (v->dy * v->mm_per_unit);
png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
png_write_info(png_ptr, info_ptr);
//
// allocate pixels
//
png_bytep* row_pointers = new png_bytep[v->nj];
for (y = 0; y < v->nj; ++y)
row_pointers[y] = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
//
// set pixels
//
for (y = 0; y < v->nj; ++y)
for (x = 0; x < v->ni; ++x) {
ptr = &(row_pointers[y][x*3]);
ptr[0] = v->red[y][x];
ptr[1] = v->green[y][x];
ptr[2] = v->blue[y][x];
}
//
// write, close, and return
//
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, NULL);
fclose(output_file);
png_destroy_write_struct(&png_ptr, &info_ptr);
cout << "write " << output_file_name
<< "\n x pixels: " << v->ni << ", y pixels: " << v->nj
<< "\n x pixels/m: " << res_x << ", y pixels/m: " << res_y
<< "\n dx: " << v->dx << " mm, dy: " << v->dy <<" mm" << endl;
for (y = 0; y < v->nj; ++y)
delete [] row_pointers[y];
delete [] row_pointers;
}