cgal/Mesh_3/applications/display_distribution.cpp

475 lines
12 KiB
C++

#include <CGAL/Simple_cartesian.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <CGAL/radius_ratio.h>
#include <CGAL/min_dihedral_angle.h>
#ifndef CGAL_USE_QT
int main()
{
std::cerr << "This tool requires Qt.\n";
exit(1);
}
#else
#include <CGAL/IO/Qt_widget.h>
#include <qpainter.h>
#include <qapplication.h>
struct K : public CGAL::Simple_cartesian<double> {};
typedef K::Point_3 Point_3;
typedef K::Tetrahedron_3 Tetrahedron_3;
typedef K::Point_2 Point_2;
typedef K::Segment_2 Segment_2;
typedef K::Iso_rectangle_2 Rectangle_2;
/// Global variables
typedef std::map<std::string, std::string> String_options;
typedef std::map<std::string, double> Double_options;
String_options string_options;
Double_options double_options;
bool use_angle;
template <typename K>
typename K::FT
criterion(const typename K::Point_3& p0,
const typename K::Point_3& p1,
const typename K::Point_3& p2,
const typename K::Point_3& p3,
K k = K())
{
if(use_angle)
return CGAL::minimum_dihedral_angle(p0, p1, p2, p3, k);
else
return CGAL::radius_ratio(p0, p1, p2, p3, k);
}
void init_options()
{
string_options["tets"] = "";
string_options["noboite"] = "";
string_options["mesh"] = "";
string_options["cgal"] = "";
string_options["criterion"] = "RATIO";
double_options["scale"] = 1;
}
void usage(char *argv0, std::string error = "")
{
if( error != "" )
std:: cerr << "Error: " << error << std::endl;
std::cerr << "Usage:\n "
<< argv0
<< " [--scale x] (--tets|--noboite|--mesh) FILE\n"
<< "Options:\n"
<< " --scale SCALE "
<< "SCALE is a real number, which will be the\n"
<< " "
<< "the vertical scaling of the histogram.\n"
<< " --criterion ANGLE\n"
<< " --criterion RATIO "
<< "Choose between an min dihedral angle distribution\n"
<< " "
<< "or an aspect ratio distribution.\n"
<< " "
<< "Default: RATIO.\n"
<< " --tets FILE\n"
<< " --noboite FILE\n"
<< " --mesh FILE\n"
<< " --cgal FILE\n"
<< " "
<< "Read input file FILE.\n"
<< " "
<< "FILE must a .tets file, or a .noboite file,\n"
<< " a cgal file, or a .mesh file."
<< std::endl;
exit(1);
}
void parse_argv(int argc, char** argv, int extra_args = 0)
{
if (argc >=(2 + extra_args))
{
std::string arg = argv[1+extra_args];
if( arg.substr(0, 2) == "--" )
{
Double_options::iterator opt_it =
double_options.find(arg.substr(2, arg.length()-2));
if( opt_it != double_options.end() )
{
if( argc < (3 + extra_args) )
usage(argv[0],
(arg + " must be followed by a double!").c_str());
std::stringstream s;
double val;
s << argv[extra_args + 2];
s >> val;
if( !s )
usage(argv[0], ("Bad double after " + arg + "!").c_str());
opt_it->second = val;
parse_argv(argc, argv, extra_args + 2);
}
else
{
String_options::iterator opt_it =
string_options.find(arg.substr(2, arg.length()-2));
if( opt_it != string_options.end() )
{
if( argc < (3 + extra_args) )
usage(argv[0],
(arg + " must be followed by a string!").c_str());
std::string s = argv[extra_args + 2];
opt_it->second = s;
parse_argv(argc, argv, extra_args + 2);
}
else
usage(argv[0], ("Invalid option: " + arg + "!").c_str());
}
}
}
} // end parse_argv
void output_distribution_to_png(std::vector<double>& elements,
double max,
const int number_of_classes,
std::string filename)
{
const int number_of_cells = elements.size();
std::vector<int> distribution(number_of_classes);
for(int j=0;j<number_of_cells;j++)
{ // This block is (c) Pierre Alliez 2005
int index = number_of_classes-1;
// saturate highest value to last bin
if(elements[j] < max)
{
double dratio = elements[j]/max;
index = static_cast<int>(dratio*(double)number_of_classes);
}
distribution[index]++;
}
// const int max_occurrence = *std::max_element(distribution.begin(),
// distribution.end());
CGAL::Qt_widget *widget = new CGAL::Qt_widget();
qApp->setMainWidget(widget);
widget->resize(400, 400);
widget->set_window(0, 1, 0, 1, true); // x_min, x_max,
// y_min, y_max.
widget->show();
widget->lock();
*widget << CGAL::FillColor(CGAL::Color(200, 200, 200))
<< CGAL::Color(200, 200, 200)
<< Rectangle_2(Point_2(0, 0), Point_2(1,1));
if( number_of_classes == 0 ) return;
const double width = 1.0 / number_of_classes;
const double scale = double_options["scale"];
*widget << CGAL::FillColor(CGAL::BLACK);
// *widget << Segment_2(Point_2(0., 0.), Point_2(1., 0.));
for(int k=0;k<number_of_classes;k++)
if(distribution[k]>0)
{
double height;
if(scale>0)
height = ( (distribution[k]+0.)/number_of_cells ) * scale;
else
height = ( std::log(distribution[k]+0.)/std::log(number_of_cells) ) * (-scale);
*widget << CGAL::BLACK
<< Rectangle_2(Point_2(k*width, 0),
Point_2((k+1)*width, height));
}
else
*widget << CGAL::RED << Segment_2(Point_2(k*width, 0),
Point_2((k+1)*width, 0));
widget->unlock();
if( widget->get_pixmap().save( QString(filename.c_str()),
"PNG") )
std::cerr << "Distribution saved to file " << filename
<< std::endl;
else
{
std::cerr << "Error: cannot save distribution to file "
<< filename << std::endl;
exit(1);
}
qApp->exec();
}
bool failed(const char* error)
{
std::cerr << error << std::endl;
return false;
}
bool read_tets(std::vector<double>& elements, std::istream& in)
{
// read header
int nb_vertices = 0;
int nb_tets = 0;
std::string head;
in >> nb_vertices >> head;
if( !in || head != "vertices" )
return false;
in >> nb_tets >> head;
if( !in || head != "tets" )
return false;
std::vector<Point_3> points;
points.reserve(nb_vertices);
// read points
for(int i=0;i<nb_vertices;i++)
{
float x,y,z;
in >> x >> y >> z;
if( !in )
return false;
points.push_back(Point_3(x,y,z));
}
// read tets
for(int i=0;i<nb_tets;i++)
{
int dummy, i0,i1,i2,i3;
in >> dummy >> i0 >> i1 >> i2 >> i3;
if( dummy != 4 || !in )
return false;
elements.push_back(criterion(points[i0],
points[i1],
points[i2],
points[i3],
K()));
}
return true;
}
bool read_mesh(std::vector<double>& elements, std::istream& in)
{
// Header.
std::string head;
int version;
in >> head >> version;
if( head != "MeshVersionFormatted" ||
version != 1 ||
! in)
return failed("read_mesh: bad version");
int dimension;
in >> head >> dimension;
if( head != "Dimension" ||
dimension!= 3 ||
! in)
return failed("read_mesh: bad dimension");
// Vertices
int number_of_vertices;
in >> head >> number_of_vertices;
if( head != "Vertices" || ! in )
return failed("read_mesh: bad file (missing Vertices)");
std::vector<Point_3> points;
points.reserve(number_of_vertices);
for(int i = 0; i < number_of_vertices; ++i)
{
int dummy_i;
in >> points[i] >> dummy_i;
if( !in )
return failed("read_mesh: bad file (reading of vertices)");
}
// Facets
int number_of_facets_on_surface;
in >> head >> number_of_facets_on_surface;
if( !in || head != "Triangles" )
return failed("read_mesh: bad file (missing Triangles)");
for(int i = 0; i < 4 * number_of_facets_on_surface; ++i)
{
double dummy;
in >> dummy;
}
// Tetrahedra
int number_of_cells;
in >> head >> number_of_cells;
if( !in || head != "Tetrahedra")
return failed("read_mesh: bad file (missing Tetrahedra)");
for(int i = 0; i < number_of_cells; ++i)
{
int i0, i1, i2, i3, dummy;
in >> i0 >> i1 >> i2 >> i3 >> dummy;
if( !in )
return failed("read_mesh: bad file (reading of cells)");
elements.push_back(criterion(points[i0-1],
points[i1-1],
points[i2-1],
points[i3-1],
K()));
}
in >> head;
if ( !in || head != "End")
return failed("read_mesh: bad file (missing End)");
else
return true;
}
bool read_noboite(std::vector<double>& elements, std::istream& in)
{
int nb_vertices = 0;
int nb_tets = 0;
int nb_input_points;
int dummy;
in >> nb_tets >> nb_vertices >> nb_input_points
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy
>> dummy;
if( !in )
return false;
std::vector<Point_3> points;
points.reserve(nb_vertices);
elements.clear();
elements.reserve(nb_tets);
// read tets
std::vector<int> tets;
tets.reserve(4 * nb_tets);
for(int i=0;i<nb_tets;i++)
{
int i0,i1,i2,i3;
in >> i0 >> i1 >> i2 >> i3;
if( !in )
return false;
tets.push_back(i0-1);
tets.push_back(i1-1);
tets.push_back(i2-1);
tets.push_back(i3-1);
}
// read points
for(int i=0;i<nb_vertices;i++)
{
double x,y,z;
in >> x >> y >> z;
if( !in )
return false;
points.push_back(Point_3(x,y,z));
}
// compute elements
for(int i = 0; i < 4 * nb_tets; i += 4)
{
elements.push_back(criterion(points[tets[i]],
points[tets[i+1]],
points[tets[i+2]],
points[tets[i+3]],
K()));
}
return true;
}
int main(int argc, char** argv)
{
QApplication app(argc, argv);
init_options();
parse_argv(argc, argv, 0);
if(string_options["criterion"] == "ANGLE")
use_angle = true;
else if(string_options["criterion"] == "RATIO")
use_angle = false;
else
usage(argv[0], "--criterion must be followed by ANGLE or RATIO.");
bool ghs = false;
bool tets = false;
bool mesh = false;
bool cgal = false;
std::string filename = "";
std::string ghs_filename = string_options["noboite"];
std::string tets_filename = string_options["tets"];
std::string mesh_filename = string_options["mesh"];
std::string cgal_filename = string_options["cgal"];
if(ghs_filename != "")
{
ghs = true;
filename = ghs_filename;
}
if(mesh_filename != "")
{
mesh = true;
filename = mesh_filename;
}
if(cgal_filename != "")
{
cgal = true;
filename = cgal_filename;
}
if(tets_filename != "")
{
tets = true;
filename = tets_filename;
}
std::vector<double> elements;
std::ifstream in_file(filename.c_str());
if(tets)
tets = read_tets(elements, in_file);
if(mesh)
mesh = read_mesh(elements, in_file);
if(cgal)
mesh = read_mesh(elements, in_file);
if(ghs)
ghs = read_noboite(elements, in_file);
std::stringstream png_name;
png_name << filename << "-scale-" << double_options["scale"];
if(use_angle)
png_name << "-angles";
else
png_name << "-ratios";
png_name << ".png";
std::cout << "min: " << *std::min_element(elements.begin(), elements.end())
<< "\nmax: " << *std::max_element(elements.begin(), elements.end())
<< "\n";
if(tets || mesh || ghs)
{
if(use_angle)
output_distribution_to_png(elements, 90., 100, png_name.str());
else
output_distribution_to_png(elements, 1., 100, png_name.str());
}
else
usage(argv[0], "cannot read file " + filename + "!");
}
#endif // CGAL_USE_QT