cgal/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp

2082 lines
67 KiB
C++

#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <CGAL/Three/Three.h>
#include <QApplication>
#include <QObject>
#include <QAction>
#include <QMainWindow>
#include <QInputDialog>
#include <QColorDialog>
#include <QPalette>
#include <QColor>
#include <QStyleFactory>
#include <QMessageBox>
#include <QAbstractItemView>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h>
#include "Scene_points_with_normal_item.h"
#include "Messages_interface.h"
#include "Scene_surface_mesh_item.h"
#include "Color_ramp.h"
#include "Color_map.h"
#include "ui_Display_property.h"
#include "id_printing.h"
#include "Scene.h"
#include "triangulate_primitive.h"
#include <CGAL/Buffer_for_vao.h>
#include <CGAL/Three/Triangle_container.h>
#include <CGAL/Dynamic_property_map.h>
#include <type_traits>
#include <unordered_map>
#define ARBITRARY_DBL_MIN 1.0E-30
#define ARBITRARY_DBL_MAX 1.0E+30
//Item for heat values
typedef CGAL::Three::Triangle_container Tri;
typedef CGAL::Three::Viewer_interface VI;
class Scene_heat_item
: public CGAL::Three::Scene_item_rendering_helper
{
Q_OBJECT
public:
Scene_heat_item(Scene_surface_mesh_item* item)
:sm(item->face_graph()), parent(item)
{
setTriangleContainer(0, new Triangle_container(VI::PROGRAM_HEAT_INTENSITY,
true));
setRenderingMode(Gouraud);
}
Scene_item* clone() const Q_DECL_OVERRIDE {return nullptr;}
QString toolTip() const Q_DECL_OVERRIDE{return QString(); }
void select(double orig_x,
double orig_y,
double orig_z,
double dir_x,
double dir_y,
double dir_z) Q_DECL_OVERRIDE
{
parent->select( orig_x, orig_y, orig_z,
dir_x, dir_y, dir_z);
}
void initializeBuffers(CGAL::Three::Viewer_interface *viewer) const Q_DECL_OVERRIDE
{
getTriangleContainer(0)->initializeBuffers(viewer);
getTriangleContainer(0)->setIdxSize(nb_idx);
verts.resize(0);
normals .resize(0);
colors.resize(0);
idx.clear();
idx.shrink_to_fit();
colors.shrink_to_fit();
verts.shrink_to_fit();
normals.shrink_to_fit();
}
void draw(CGAL::Three::Viewer_interface *viewer) const Q_DECL_OVERRIDE
{
if(!visible())
return;
if(!isInit(viewer))
initGL(viewer);
if ( getBuffersFilled() &&
! getBuffersInit(viewer))
{
initializeBuffers(viewer);
setBuffersInit(viewer, true);
}
if(!getBuffersFilled())
{
computeElements();
initializeBuffers(viewer);
}
getTriangleContainer(0)->setAlpha(1.0f);
getTriangleContainer(0)->draw(viewer, false);
}
void compute_bbox() const Q_DECL_OVERRIDE
{
SMesh::Property_map<vertex_descriptor, Point_3> pprop = sm->points();
CGAL::Bbox_3 bbox ;
for(vertex_descriptor vd :vertices(*sm))
{
bbox = bbox + pprop[vd].bbox();
}
_bbox = Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(),
bbox.xmax(),bbox.ymax(),bbox.zmax());
is_bbox_computed = true;
}
Scene_item::Bbox bbox() const Q_DECL_OVERRIDE {
if(!is_bbox_computed)
compute_bbox();
is_bbox_computed = true;
return _bbox;
}
~Scene_heat_item(){}
virtual bool supportsRenderingMode(RenderingMode m) const Q_DECL_OVERRIDE { return m==Gouraud; }
virtual void invalidateOpenGLBuffers() Q_DECL_OVERRIDE
{
setBuffersFilled(false);
compute_bbox();
getTriangleContainer(0)->reset_vbos(NOT_INSTANCED);
is_bbox_computed = false;
}
void triangulate_convex_facet(face_descriptor fd,
boost::property_map< SMesh, boost::vertex_index_t >::type *im) const
{
const CGAL::qglviewer::Vec v_offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
EPICK::Vector_3 offset = EPICK::Vector_3(v_offset.x, v_offset.y, v_offset.z);
EPICK::Point_3 p0,p1,p2;
SMesh::Halfedge_around_face_circulator he(halfedge(fd, *sm), *sm);
SMesh::Halfedge_around_face_circulator he_end = he;
while(next(*he, *sm) != prev(*he_end, *sm))
{
++he;
vertex_descriptor v0(target(*he_end, *sm)),
v1(target(*he, *sm)),
v2(target(next(*he, *sm), *sm));
p0 = sm->point(v0) + offset;
p1 = sm->point(v1) + offset;
p2 = sm->point(v2) + offset;
idx.push_back((*im)[v0]);
idx.push_back((*im)[v1]);
idx.push_back((*im)[v2]);
}
}
void triangulate_facet(face_descriptor fd,
SMesh::Property_map<face_descriptor, EPICK::Vector_3> *fnormals,
boost::property_map< SMesh, boost::vertex_index_t >::type *im) const
{
//Computes the normal of the facet
EPICK::Vector_3 normal = get(*fnormals, fd);
//check if normal contains NaN values
if (normal.x() != normal.x() || normal.y() != normal.y() || normal.z() != normal.z())
{
qDebug()<<"Warning : normal is not valid. Facet not displayed";
return;
}
typedef FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor> FT;
const CGAL::qglviewer::Vec off = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
EPICK::Vector_3 offset(off.x,off.y,off.z);
FT triangulation(fd,normal,sm, offset);
//iterates on the internal faces
for(FT::CDT::Finite_faces_iterator
ffit = triangulation.cdt->finite_faces_begin(),
end = triangulation.cdt->finite_faces_end();
ffit != end; ++ffit)
{
if(ffit->info().is_external)
continue;
//add the vertices to the positions
//adds the vertices, normals and colors to the appropriate vectors
//adds the indices to the appropriate vector
idx.push_back((*im)[triangulation.v2v[ffit->vertex(0)]]);
idx.push_back((*im)[triangulation.v2v[ffit->vertex(1)]]);
idx.push_back((*im)[triangulation.v2v[ffit->vertex(2)]]);
}
}
void computeElements() const Q_DECL_OVERRIDE
{
typedef EPICK::Point_3 Point;
QApplication::setOverrideCursor(Qt::WaitCursor);
const CGAL::qglviewer::Vec o = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
EPICK::Vector_3 offset(o.x, o.y, o.z);
SMesh::Property_map<vertex_descriptor, SMesh::Point> positions =
sm->points();
SMesh::Property_map<vertex_descriptor, EPICK::Vector_3 > vnormals =
sm->property_map<vertex_descriptor, EPICK::Vector_3 >("v:normal").first;
SMesh::Property_map<face_descriptor, EPICK::Vector_3 > fnormals =
sm->property_map<face_descriptor, EPICK::Vector_3 >("f:normal").first;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
typedef boost::graph_traits<SMesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
SMesh::Property_map<vertex_descriptor, CGAL::IO::Color> vcolors =
sm->property_map<vertex_descriptor, CGAL::IO::Color >("v:color").first;
SMesh::Property_map<vertex_descriptor, float> vdist=
sm->property_map<vertex_descriptor, float >("v:dist").first;
typedef CGAL::Buffer_for_vao<float, unsigned int> CPF;
verts.clear();
normals.clear();
idx.clear();
colors.clear();
boost::property_map< SMesh, boost::vertex_index_t >::type
im = get(boost::vertex_index, *sm);
idx.reserve(num_faces(*sm) * 3);
for(face_descriptor fd : faces(*sm))
{
if(is_triangle(halfedge(fd,*sm),*sm))
{
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *sm),*sm))
{
idx.push_back(source(hd, *sm));
}
}
else
{
std::vector<Point> facet_points;
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *sm),*sm))
{
facet_points.push_back(positions[target(hd, *sm)]);
}
bool is_convex = CPF::is_facet_convex(facet_points, fnormals[fd]);
if(is_convex && is_quad(halfedge(fd,*sm),*sm) )
{
halfedge_descriptor hd = halfedge(fd,*sm);
//1st half
idx.push_back(source(hd, *sm));
idx.push_back(source(next(hd, *sm), *sm));
idx.push_back(source(next(next(hd, *sm), *sm), *sm));
//2nd half
idx.push_back(source(hd, *sm));
idx.push_back(source(next(next(hd, *sm), *sm), *sm));
idx.push_back(source(prev(hd, *sm), *sm));
}
else if(is_convex)
{
triangulate_convex_facet(fd, &im);
}
else
{
triangulate_facet(fd, &fnormals, &im);
}
}
}
for(vertex_descriptor vd : vertices(*sm))
{
CGAL::IO::Color c = vcolors[vd];
colors.push_back((float)c.red()/255);
colors.push_back((float)c.green()/255);
colors.push_back((float)c.blue()/255);
Point p = positions[vd] + offset;
CPF::add_point_in_buffer(p, verts);
EPICK::Vector_3 n = vnormals[vd];
CPF::add_normal_in_buffer(n, normals);
heat_values.push_back(vdist[vd]);
}
nb_idx = idx.size();
getTriangleContainer(0)->allocate(Tri::Vertex_indices, idx.data(),
static_cast<int>(idx.size()*sizeof(unsigned int)));
getTriangleContainer(0)->allocate(Tri::Smooth_vertices, verts.data(),
static_cast<int>(num_vertices(*sm)*3*sizeof(float)));
getTriangleContainer(0)->allocate(Tri::Smooth_normals, normals.data(),
static_cast<int>(num_vertices(*sm)*3*sizeof(float)));
getTriangleContainer(0)->allocate(Tri::VColors, colors.data(),
static_cast<int>(colors.size()*sizeof(float)));
getTriangleContainer(0)->allocate(Tri::Distances, heat_values.data(),
static_cast<int>(heat_values.size()*sizeof(float)));
compute_bbox();
setBuffersFilled(true);
QApplication::restoreOverrideCursor();
}
bool isEmpty() const Q_DECL_OVERRIDE {return false;}
SMesh *face_graph() { return sm;}
Scene_surface_mesh_item* getParent() { return parent; }
private:
SMesh* sm;
Scene_surface_mesh_item* parent;
mutable std::vector<float> normals;
mutable std::vector<unsigned int> idx;
mutable std::vector<float> verts;
mutable std::vector<float> colors;
mutable std::vector<float> heat_values;
mutable std::size_t nb_idx;
}; // end class Scene_heat_item
class DockWidget :
public QDockWidget,
public Ui::DisplayPropertyWidget
{
public:
DockWidget(QString name, QWidget *parent)
:QDockWidget(name,parent)
{
setupUi(this);
}
};
typedef boost::graph_traits<SMesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
CGAL::Three::Viewer_interface* (&getActiveViewer)() = CGAL::Three::Three::activeViewer;
class DisplayPropertyPlugin :
public QObject,
public CGAL::Three::Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
typedef SMesh::Property_map<boost::graph_traits<SMesh>::vertex_descriptor, double> Vertex_distance_map;
typedef CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3<SMesh> Heat_method;
typedef CGAL::Heat_method_3::Surface_mesh_geodesic_distances_3<SMesh, CGAL::Heat_method_3::Intrinsic_Delaunay> Heat_method_idt;
typedef CGAL::dynamic_vertex_property_t<bool> Vertex_source_tag;
typedef boost::property_map<SMesh, Vertex_source_tag>::type Vertex_source_map;
public:
bool applicable(QAction* action) const Q_DECL_OVERRIDE
{
CGAL::Three::Scene_item* item = scene->item(scene->mainSelectionIndex());
if(action == _actions.back())
return qobject_cast<Scene_surface_mesh_item*>(item);
else
return qobject_cast<Scene_surface_mesh_item*>(item)
|| qobject_cast<Scene_points_with_normal_item*>(item);
}
QList<QAction*> actions() const Q_DECL_OVERRIDE
{
return _actions;
}
QColor textColor(const QColor& color)
{
QColor text_color (255, 255, 255);
if (color.red() * 0.299 + color.green() * 0.587 + color.blue() * 0.114 > 128)
text_color = QColor (0, 0, 0);
return text_color;
}
void init(QMainWindow* mw, CGAL::Three::Scene_interface* sc, Messages_interface*) Q_DECL_OVERRIDE
{
this->scene = sc;
this->mw = mw;
this->current_item = nullptr;
QAction *actionDisplayProperties= new QAction(QString("Display Properties"), mw);
QAction *actionHeatMethod= new QAction(QString("Heat Method"), mw);
actionHeatMethod->setProperty("submenuName", "Color");
rm = 1.0;
rM = 0.0;
gm = 0.0;
gM = 1.0;
bm = 0.0;
bM = 0.0;
actionDisplayProperties->setProperty("submenuName", "Color");
if(actionDisplayProperties) {
connect(actionDisplayProperties, SIGNAL(triggered()),
this, SLOT(openDialog()));
if(actionHeatMethod)
{
connect(actionHeatMethod, &QAction::triggered,
this, [this](){
this->dock_widget->propertyBox->setCurrentIndex(2);
this->dock_widget->show();
});
}
_actions << actionDisplayProperties;
_actions << actionHeatMethod;
}
dock_widget = new DockWidget("Property Displaying", mw);
dock_widget->setVisible(false);
addDockWidget(dock_widget);
QPalette palette(Qt::red);
dock_widget->minColorButton->setPalette(palette);
dock_widget->minColorButton->setStyle(QStyleFactory::create("Fusion"));
dock_widget->minColorButton->update();
palette = QPalette(Qt::green);
dock_widget->maxColorButton->setPalette(palette);
dock_widget->maxColorButton->setStyle(QStyleFactory::create("Fusion"));
dock_widget->maxColorButton->update();
palette = QPalette(Qt::green);
dock_widget->initColorButton->setPalette(palette);
dock_widget->initColorButton->setStyle(QStyleFactory::create("Fusion"));
dock_widget->initColorButton->update();
connect(dock_widget->colorizeButton, SIGNAL(clicked(bool)),
this, SLOT(colorize()));
connect(dock_widget->propertyBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(on_propertyBox_currentIndexChanged(int)));
connect(dock_widget->zoomToMinButton, &QPushButton::pressed,
this, &DisplayPropertyPlugin::on_zoomToMinButton_pressed);
connect(dock_widget->zoomToMaxButton, &QPushButton::pressed,
this, &DisplayPropertyPlugin::on_zoomToMaxButton_pressed);
connect(dock_widget->minColorButton, &QPushButton::pressed,
this, [this]()
{
QColor minColor = QColorDialog::getColor();
if (!minColor.isValid())
{
return;
}
rm = minColor.redF();
gm = minColor.greenF();
bm = minColor.blueF();
QPalette palette(minColor);
dock_widget->minColorButton->setPalette(palette);
dock_widget->minColorButton->update();
replaceRamp();
});
connect(dock_widget->maxColorButton, &QPushButton::pressed,
this, [this]()
{
QColor maxColor = QColorDialog::getColor();
if(!maxColor.isValid())
return;
QPalette palette(maxColor);
rM = maxColor.redF();
gM = maxColor.greenF();
bM = maxColor.blueF();
dock_widget->maxColorButton->setPalette(palette);
dock_widget->maxColorButton->update();
replaceRamp();
});
connect(dock_widget->initColorButton, &QPushButton::pressed,
this, [this]()
{
QColor initColor = QColorDialog::getColor();
if (!initColor.isValid())
{
return;
}
QPalette palette(initColor);
rI = initColor.redF();
gI = initColor.greenF();
bI = initColor.blueF();
dock_widget->initColorButton->setPalette(palette);
dock_widget->initColorButton->update();
});
connect(dock_widget->sourcePointsButton, SIGNAL(toggled(bool)),
this, SLOT(on_sourcePointsButton_toggled(bool)));
connect(dock_widget->deleteButton, &QPushButton::clicked,
this, &DisplayPropertyPlugin::delete_group);
dock_widget->zoomToMaxButton->setEnabled(false);
dock_widget->zoomToMinButton->setEnabled(false);
Scene* scene_obj =static_cast<Scene*>(scene);
connect(scene_obj, SIGNAL(itemIndexSelected(int)),
this,SLOT(enableButtons(int)));
connect(scene_obj, SIGNAL(itemIndexSelected(int)),
this,SLOT(detectScalarProperties(int)));
on_propertyBox_currentIndexChanged(0);
}
private:
void detectSMScalarProperties(SMesh* smesh)
{
std::vector<std::string> vprop = smesh->properties<vertex_descriptor>();
std::vector<std::string> fprop = smesh->properties<face_descriptor>();
for(auto s : vprop)
if(is_property_scalar<vertex_descriptor>(s, smesh))
{
dock_widget->propertyBox->addItem(s.c_str());
}
for(auto s : fprop)
if(is_property_scalar<face_descriptor>(s, smesh))
{
dock_widget->propertyBox->addItem(s.c_str());
}
}
void detectPSScalarProperties(Point_set* ps)
{
for(auto s : ps->properties())
if(is_property_scalar(s, ps))
{
dock_widget->propertyBox->addItem(s.c_str());
}
}
private Q_SLOTS:
void detectScalarProperties(int i)
{
for(int j = dock_widget->propertyBox->count(); j>=0; --j)
dock_widget->propertyBox->removeItem(j);
Scene_surface_mesh_item* sm_item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(i));
Scene_points_with_normal_item* p_item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(i));
if(sm_item)
{
dock_widget->propertyBox->addItem("Smallest Angle Per Face");
dock_widget->propertyBox->addItem("Scaled Jacobian");
dock_widget->propertyBox->addItem("Heat Intensity");
dock_widget->propertyBox->addItem("Heat Intensity (Intrinsic Delaunay)");
detectSMScalarProperties(sm_item->face_graph());
}
else if(p_item)
{
detectPSScalarProperties(p_item->point_set());
}
int width = dock_widget->propertyBox->minimumSizeHint().width();
dock_widget->propertyBox->view()->setMinimumWidth(width);
}
void openDialog()
{
if(dock_widget->isVisible()) { dock_widget->hide(); }
else{
dock_widget->show();
dock_widget->raise(); }
}
void colorizePS(Scene_points_with_normal_item* ps_item)
{
ps_item->point_set()->add_colors();
if(!treat_point_property(dock_widget->propertyBox->currentText().toStdString(), ps_item->point_set()))
{
QApplication::restoreOverrideCursor();
return;
}
ps_item->setPointsMode();
ps_item->invalidateOpenGLBuffers();
ps_item->itemChanged();
}
void colorize()
{
Scene_points_with_normal_item* p_item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(scene->mainSelectionIndex()));
if(p_item)
{
colorizePS(p_item);
return;
}
Scene_heat_item* h_item = nullptr;
Scene_surface_mesh_item* sm_item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
if(!sm_item)
{
h_item = qobject_cast<Scene_heat_item*>(scene->item(scene->mainSelectionIndex()));
if(!h_item)
return;
sm_item = h_item->getParent();
}
QApplication::setOverrideCursor(Qt::WaitCursor);
sm_item->face_graph()->collect_garbage();
switch(dock_widget->propertyBox->currentIndex()){
case 0:
displayAngles(sm_item);
break;
case 1:
displayScaledJacobian(sm_item);
break;
case 2:
dock_widget->colorChoiceWidget->setCurrentIndex(0);
if(!displayHeatIntensity(sm_item)){
QApplication::restoreOverrideCursor();
return;
}
sm_item->setRenderingMode(Gouraud);
break;
case 3:// Heat Method (Intrinsic Delaunay)
dock_widget->colorChoiceWidget->setCurrentIndex(0);
if(!displayHeatIntensity(sm_item, true))
return;
sm_item->setRenderingMode(Gouraud);
break;
default:
if(dock_widget->propertyBox->currentText().contains("v:"))
{
if(!treat_sm_property<vertex_descriptor>(dock_widget->propertyBox->currentText().toStdString(), sm_item->face_graph()))
{
QApplication::restoreOverrideCursor();
return;
}
sm_item->setRenderingMode(Gouraud);
}
else if(dock_widget->propertyBox->currentText().contains("f:"))
{
if(!treat_sm_property<face_descriptor>(dock_widget->propertyBox->currentText().toStdString(), sm_item->face_graph()))
{
QApplication::restoreOverrideCursor();
return;
}
sm_item->setRenderingMode(Flat);
}
break;
}
connect(sm_item, &Scene_surface_mesh_item::itemChanged,
this, [sm_item](){
bool does_exist;
SMesh::Property_map<face_descriptor, double> pmap;
std::tie(pmap, does_exist) =
sm_item->face_graph()->property_map<face_descriptor,double>("f:jacobian");
if(does_exist)
sm_item->face_graph()->remove_property_map(pmap);
std::tie(pmap, does_exist) =
sm_item->face_graph()->property_map<face_descriptor,double>("f:angle");
if(does_exist)
sm_item->face_graph()->remove_property_map(pmap);
});
QApplication::restoreOverrideCursor();
sm_item->invalidateOpenGLBuffers();
sm_item->redraw();
if(dock_widget->propertyBox->currentIndex() != 2){
dock_widget->zoomToMinButton->setEnabled(true);
dock_widget->zoomToMaxButton->setEnabled(true);}
}
void enableButtons(int i)
{
Scene_surface_mesh_item* sm_item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(i));
Scene_points_with_normal_item* ps_item =
qobject_cast<Scene_points_with_normal_item*>(scene->item(i));
if(! sm_item && ! ps_item)
{
dock_widget->zoomToMinButton->setEnabled(false);
dock_widget->zoomToMaxButton->setEnabled(false);
}
else if(ps_item)
{
dock_widget->zoomToMinButton->setEnabled(false);
dock_widget->zoomToMaxButton->setEnabled(false);
}
else if(sm_item){
switch(dock_widget->propertyBox->currentIndex())
{
case 0:
dock_widget->zoomToMinButton->setEnabled(angles_max.count(sm_item)>0 );
dock_widget->zoomToMaxButton->setEnabled(angles_max.count(sm_item)>0 );
break;
case 1:
dock_widget->zoomToMinButton->setEnabled(jacobian_max.count(sm_item)>0);
dock_widget->zoomToMaxButton->setEnabled(jacobian_max.count(sm_item)>0);
break;
default:
break;
}
}
}
void resetProperty()
{
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(sender());
if(!item)
return;
SMesh& smesh = *item->face_graph();
SMesh::Property_map<face_descriptor, double> jacobians;
bool found;
std::tie(jacobians, found) = smesh.property_map<face_descriptor,double>("f:jacobian");
if(found)
{
smesh.remove_property_map(jacobians);
}
SMesh::Property_map<face_descriptor, double> angles;
std::tie(angles, found) = smesh.property_map<face_descriptor,double>("f:angle");
if(found)
{
smesh.remove_property_map(angles);
}
}
void displayScaledJacobian(Scene_surface_mesh_item* item)
{
SMesh& smesh = *item->face_graph();
//compute and store the jacobian per face
bool non_init;
SMesh::Property_map<face_descriptor, double> fjacobian;
std::tie(fjacobian, non_init) = smesh.add_property_map<face_descriptor, double>("f:jacobian", 0);
if(non_init)
{
double res_min = ARBITRARY_DBL_MAX,
res_max = -ARBITRARY_DBL_MAX;
SMesh::Face_index min_index, max_index;
for(boost::graph_traits<SMesh>::face_iterator fit = faces(smesh).begin();
fit != faces(smesh).end();
++fit)
{
fjacobian[*fit] = scaled_jacobian(*fit, smesh);
if(fjacobian[*fit] > res_max)
{
res_max = fjacobian[*fit];
max_index = *fit;
}
if(fjacobian[*fit] < res_min)
{
res_min = fjacobian[*fit];
min_index = *fit;
}
}
jacobian_min.erase(item);
jacobian_min.insert(std::make_pair(item, std::make_pair(res_min, min_index)));
jacobian_max.erase(item);
jacobian_max.insert(std::make_pair(item, std::make_pair(res_max, max_index)));
connect(item, &Scene_surface_mesh_item::itemChanged,
this, &DisplayPropertyPlugin::resetProperty);
}
treat_sm_property<face_descriptor>("f:jacobian", item->face_graph());
}
bool resetScaledJacobian(Scene_surface_mesh_item* item)
{
SMesh& smesh = *item->face_graph();
if(!smesh.property_map<face_descriptor, double>("f:jacobian").second)
{
return false;
}
dock_widget->minBox->setValue(jacobian_min[item].first-0.01);
dock_widget->maxBox->setValue(jacobian_max[item].first);
return true;
}
void displayAngles(Scene_surface_mesh_item* item)
{
SMesh& smesh = *item->face_graph();
typedef boost::property_map<SMesh, boost::vertex_point_t>::type PMap;
PMap pmap = get(boost::vertex_point, smesh);
//compute and store smallest angle per face
bool non_init;
SMesh::Property_map<face_descriptor, double> fangle;
std::tie(fangle, non_init) = smesh.add_property_map<face_descriptor, double>("f:angle", 0);
if(non_init)
{
double res_min = ARBITRARY_DBL_MAX,
res_max = -ARBITRARY_DBL_MAX;
SMesh::Face_index index_min, index_max;
for(boost::graph_traits<SMesh>::face_iterator fit = faces(smesh).begin();
fit != faces(smesh).end();
++fit)
{
bool is_face_triangle = is_triangle(halfedge(*fit, smesh), smesh);
bool normal_is_ok = true;
EPICK::Vector_3 normal(0,0,0);
EPICK::Orientation orientation = CGAL::POSITIVE;
if(!is_face_triangle)
{
face_descriptor f = *fit;
CGAL::Halfedge_around_face_circulator<SMesh>
he(halfedge(f, smesh), smesh),
he_end(he);
do{
normal_is_ok = true;
//Initializes the facet orientation
EPICK::Point_3 S,T;
T = get(pmap, source(*he, smesh));
S = get(pmap, target(*he, smesh));
EPICK::Vector_3 V1((T-S).x(), (T-S).y(), (T-S).z());
S = get(pmap,source(next(*he,smesh), smesh));
T = get(pmap, target(next(*he,smesh), smesh));
EPICK::Vector_3 V2((T-S).x(), (T-S).y(), (T-S).z());
if(normal == EPICK::Vector_3(0,0,0))
normal_is_ok = false;
{
normal = CGAL::cross_product(V1, V2);
}
if(normal_is_ok)
{
orientation = EPICK::Orientation_3()(V1, V2, normal);
if( orientation == CGAL::COPLANAR )
normal_is_ok = false;
}
}while( ++he != he_end && !normal_is_ok);
}
std::vector<float> local_angles;
local_angles.reserve(degree(*fit, smesh));
for(halfedge_descriptor hd :
halfedges_around_face(halfedge(*fit, smesh),smesh))
{
halfedge_descriptor hdn = next(hd, smesh);
EPICK::Vector_3 v1(get(pmap, source(hd, smesh)), get(pmap, target(hd, smesh))),
v2(get(pmap, target(hdn, smesh)), get(pmap, source(hdn, smesh)));
float norm1(CGAL::approximate_sqrt(v1.squared_length())), norm2(CGAL::approximate_sqrt(v2.squared_length()));
float dot_prod = v1*v2;
float angle = std::acos(dot_prod/(norm1*norm2));
if(is_face_triangle || !normal_is_ok)
local_angles.push_back(angle * 180/CGAL_PI);
else
{
bool is_convex = true;
EPICK::Orientation res = EPICK::Orientation_3()(v1, v2, normal) ;
if(res!= orientation && res != CGAL::ZERO)
is_convex = false;
local_angles.push_back(is_convex ? angle * 180/CGAL_PI : 360 - angle * 180/CGAL_PI );
}
}
std::sort(local_angles.begin(), local_angles.end());
fangle[*fit]=local_angles.front();
if(fangle[*fit] > res_max)
{
res_max = fangle[*fit];
index_max = *fit;
}
if(fangle[*fit] < res_min)
{
res_min = fangle[*fit];
index_min = *fit;
}
}
angles_min.erase(item);
angles_min.insert(std::make_pair(item, std::make_pair(res_min, index_min)));
angles_max.erase(item);
angles_max.insert(std::make_pair(item, std::make_pair(res_max, index_max)));
connect(item, &Scene_surface_mesh_item::itemChanged,
this, &DisplayPropertyPlugin::resetProperty);
}
treat_sm_property<face_descriptor>("f:angle", item->face_graph());
}
bool resetAngles(Scene_surface_mesh_item* item)
{
SMesh& smesh = *item->face_graph();
if(!smesh.property_map<face_descriptor, double>("f:angle").second)
{
return false;
}
dock_widget->minBox->setValue(angles_min[item].first);
dock_widget->maxBox->setValue(angles_max[item].first);
return true;
}
// AF: This function gets called when we click on the button "Colorize"
bool displayHeatIntensity(Scene_surface_mesh_item* item, bool iDT = false)
{
SMesh& mesh = *item->face_graph();
bool found = is_source.find(item) != is_source.end();
if(!found
|| ! source_points
|| source_points->point_set()->is_empty())
{
QApplication::restoreOverrideCursor();
QMessageBox::warning(mw, "Warning","Source vertices are needed for this property.");
return false;
}
if(!is_triangle_mesh(mesh))
{
QApplication::restoreOverrideCursor();
QMessageBox::warning(mw,"Error","The mesh must be triangulated.");
return false;
}
Heat_method * hm = nullptr;
Heat_method_idt * hm_idt = nullptr;
SMesh::Property_map<vertex_descriptor, double> heat_intensity =
mesh.add_property_map<vertex_descriptor, double>("v:heat_intensity", 0).first;
if(! iDT){
if(mesh_heat_method_map.find(item) != mesh_heat_method_map.end()){
hm = mesh_heat_method_map[item];
}else {
hm = new Heat_method(mesh);
mesh_heat_method_map[item] = hm;
}
connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed,
[this,item](){
auto it = mesh_heat_method_map.find(item);
if(it == mesh_heat_method_map.end())
return;
delete it->second;
mesh_heat_method_map.erase(it);
}
);
} else {
if(mesh_heat_method_idt_map.find(item) != mesh_heat_method_idt_map.end()){
hm_idt = mesh_heat_method_idt_map[item];
}else {
hm_idt = new Heat_method_idt(mesh);
mesh_heat_method_idt_map[item] = hm_idt;
}
connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed,
[this,item](){
auto it = mesh_heat_method_idt_map.find(item);
if(it == mesh_heat_method_idt_map.end())
return;
Heat_method_idt *hm_idt = it->second;
delete hm_idt;
mesh_heat_method_idt_map.erase(it);
}
);
}
for(vertex_descriptor vd : vertices(mesh)){
if(get(is_source[item], vd)){
if(iDT){
hm_idt->add_source(vd);
} else
hm->add_source(vd);
}
else
{
if(iDT){
hm_idt->remove_source(vd);
} else
hm->remove_source(vd);
}
}
if(iDT){
hm_idt->estimate_geodesic_distances(heat_intensity);
}else{
hm->estimate_geodesic_distances(heat_intensity);
}
double max = 0;
double min = (std::numeric_limits<double>::max)();
for(vertex_descriptor vd : vertices(mesh)){
double hi = heat_intensity[vd];
if(hi < min)
min = hi;
if(hi > max)
max = hi;
}
color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM);
dock_widget->minBox->setValue(min);
dock_widget->maxBox->setValue(max);
displayLegend();
//}
SMesh::Property_map<vertex_descriptor, CGAL::IO::Color> vcolors =
mesh.add_property_map<vertex_descriptor, CGAL::IO::Color >("v:color", CGAL::IO::Color()).first;
SMesh::Property_map<vertex_descriptor, float> vdist=
mesh.add_property_map<vertex_descriptor, float >("v:dist", 0.0).first;
for(boost::graph_traits<SMesh>::vertex_iterator vit = vertices(mesh).begin();
vit != vertices(mesh).end();
++vit)
{
double h =(heat_intensity[*vit]-min)/(max-min);
CGAL::IO::Color color(
255*color_ramp.r(h),
255*color_ramp.g(h),
255*color_ramp.b(h));
vcolors[*vit] = color;
vdist[*vit]=h;
}
Scene_group_item* group;
if(mesh_heat_item_map.find(item) != mesh_heat_item_map.end())
{
group = mesh_heat_item_map[item]->parentGroup();
group->unlockChild(mesh_heat_item_map[item]);
scene->erase(scene->item_id(mesh_heat_item_map[item]));
}
else
{
group = new Scene_group_item("Heat Visualization");
group->setProperty("heat_group", true);
scene->addItem(group);
scene->changeGroup(item, group);
scene->changeGroup(source_points, group);
group->lockChild(item);
group->lockChild(source_points);
dock_widget->deleteButton->setEnabled(true);
connect(group, &Scene_group_item::aboutToBeDestroyed,
this, [this](){
this->dock_widget->deleteButton->setEnabled(false);
});
}
mesh_heat_item_map[item] = new Scene_heat_item(item);
mesh_heat_item_map[item]->setName(tr("%1 heat").arg(item->name()));
scene->addItem(mesh_heat_item_map[item]);
scene->changeGroup(mesh_heat_item_map[item], group);
group->lockChild(mesh_heat_item_map[item]);
item->setVisible(false);
displayLegend();
if(dock_widget->sourcePointsButton->isChecked())
dock_widget->sourcePointsButton->toggle();
return true;
}
void replaceRamp()
{
if(dock_widget->colorChoiceWidget->currentIndex() == 0)
{
color_ramp = Color_ramp(rm, rM, gm, gM, bm, bM);
displayLegend();
minBox = dock_widget->minBox->value();
maxBox = dock_widget->maxBox->value();
}
}
void on_propertyBox_currentIndexChanged(int)
{
dock_widget->sourcePointsButton->setEnabled(false);
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
if(! item )
dock_widget->maxBox->setValue(180);
else{
switch(dock_widget->propertyBox->currentIndex())
{
case 0:
{
dock_widget->groupBox-> setEnabled(true);
dock_widget->groupBox_3->setEnabled(true);
dock_widget->minBox->setMinimum(0);
dock_widget->minBox->setMaximum(360);
dock_widget->minBox->setValue(0);
dock_widget->maxBox->setMinimum(0);
dock_widget->maxBox->setMaximum(360);
if(is_triangle_mesh(*item->face_graph()))
dock_widget->maxBox->setValue(60);
else if(is_quad_mesh(*item->face_graph()))
dock_widget->maxBox->setValue(90);
replaceRamp();
break;
}
case 1:
dock_widget->groupBox-> setEnabled(true);
dock_widget->groupBox_3->setEnabled(true);
dock_widget->minBox->setMinimum(-1000);
dock_widget->minBox->setMaximum(1000);
dock_widget->minBox->setValue(0);
dock_widget->maxBox->setMinimum(-1000);
dock_widget->maxBox->setMaximum(1000);
dock_widget->maxBox->setValue(2);
break;
case 2:
case 3:
dock_widget->sourcePointsButton->setEnabled(true);
CGAL_FALLTHROUGH;
default:
dock_widget->maxBox->setMinimum(-99999999);
dock_widget->maxBox->setMaximum(99999999);
dock_widget->minBox->setMinimum(-99999999);
dock_widget->minBox->setMaximum(99999999);
dock_widget->groupBox-> setEnabled(false);
dock_widget->groupBox_3->setEnabled(false);
}
}
}
void closure()Q_DECL_OVERRIDE
{
dock_widget->hide();
}
void on_zoomToMinButton_pressed()
{
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
if(!item)
return;
face_descriptor dummy_fd;
Point_3 dummy_p;
switch(dock_widget->propertyBox->currentIndex())
{
case 0:
{
::zoomToId(*item->face_graph(),
QString("f%1").arg(angles_min[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
case 1:
{
::zoomToId(*item->face_graph(),
QString("f%1").arg(jacobian_min[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
default:
break;
}
}
void on_zoomToMaxButton_pressed()
{
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
if(!item)
return;
face_descriptor dummy_fd;
Point_3 dummy_p;
switch(dock_widget->propertyBox->currentIndex())
{
case 0:
{
::zoomToId(*item->face_graph(),
QString("f%1").arg(angles_max[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
case 1:
{
::zoomToId(*item->face_graph(),
QString("f%1").arg(jacobian_max[item].second),
getActiveViewer(),
dummy_fd,
dummy_p);
}
break;
default:
break;
}
}
void delete_group()
{
if(scene->selectionIndices().empty())
return;
Scene_item* item = scene->item(scene->selectionIndices().first());
Scene_group_item* group = qobject_cast<Scene_group_item*>(item);
if(!group || !group->property("heat_group").toBool())
return;
for(auto child_id : group->getChildren())
{
if(Scene_surface_mesh_item* child = qobject_cast<Scene_surface_mesh_item*>(scene->item(child_id))){
auto it = mesh_heat_method_map.find(child);
if(it != mesh_heat_method_map.end())
mesh_heat_method_map.erase(it);
auto it2 = mesh_heat_item_map.find(child);
if(it2 != mesh_heat_item_map.end())
mesh_heat_item_map.erase(it2);
auto it3 = mesh_heat_method_idt_map.find(child);
if(it3 != mesh_heat_method_idt_map.end())
mesh_heat_method_idt_map.erase(it3);
group->unlockChild(child);
group->removeChild(child);
scene->addChild(child);
child->setVisible(true);
child->resetColors();
break;
}
}
scene->erase(scene->item_id(group));
source_points = nullptr;
}
void on_sourcePointsButton_toggled(bool b)
{
if(b)
{
Scene_heat_item* h_item = nullptr;
Scene_surface_mesh_item* item =
qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->mainSelectionIndex()));
if(!item)
{
h_item = qobject_cast<Scene_heat_item*>(scene->item(scene->mainSelectionIndex()));
if(h_item)
item = h_item->getParent();
}
if(!item)
{
QMessageBox::warning(mw, "Warning", "You must select a Surface_mesh_item to make this work. Aborting.");
dock_widget->sourcePointsButton->setChecked(false);
return;
}
current_item = item;
connect(current_item, &Scene_surface_mesh_item::aboutToBeDestroyed,
this, [this]()
{
dock_widget->sourcePointsButton->setChecked(false);
});
if(mesh_sources_map.find(item) == mesh_sources_map.end())
{
source_points = new Scene_points_with_normal_item();
source_points->setName(QString("Source vertices for %1").arg(current_item->name()));
source_points->setColor(QColor(Qt::red));
source_points->setPointSize(5);
scene->addItem(source_points);
connect(source_points, &Scene_points_with_normal_item::aboutToBeDestroyed,
[this](){
std::unordered_map<Scene_surface_mesh_item*, Scene_points_with_normal_item*>::iterator it;
for(it = mesh_sources_map.begin();
it != mesh_sources_map.end();
++it)
{
if(it->second == source_points)
{
mesh_sources_map.erase(it);
break;
}
}
});
mesh_sources_map[current_item] = source_points;
}
else
{
source_points=mesh_sources_map[current_item];
}
connect(item, SIGNAL(selected_vertex(void*)), this, SLOT(on_vertex_selected(void*)));
bool non_init = is_source.find(item) == is_source.end();
if(non_init)
{
Vertex_source_map map = get(Vertex_source_tag(), *item->face_graph());
is_source.insert(std::make_pair(item, map));
connect(item, &Scene_surface_mesh_item::itemChanged,
this, &DisplayPropertyPlugin::resetProperty);
connect(item, &Scene_surface_mesh_item::aboutToBeDestroyed,
[this, item](){
if(is_source.find(item) != is_source.end())
{
is_source.erase(item);
}
});
}
}
else
{
if(!current_item)
return;
disconnect(current_item, SIGNAL(selected_vertex(void*)), this, SLOT(on_vertex_selected(void*)));
current_item = nullptr;
}
}
void on_vertex_selected(void* void_ptr)
{
typedef boost::graph_traits<SMesh>::vertices_size_type size_type;
size_type h = static_cast<size_type>(reinterpret_cast<std::size_t>(void_ptr));
vertex_descriptor vd = static_cast<vertex_descriptor>(h) ;
bool found = is_source.find(current_item) != is_source.end();
if(found)
{
if(!get(is_source[current_item], vd))
{
put(is_source[current_item], vd, true);
source_points->point_set()->insert(current_item->face_graph()->point(vd));
}
else
{
put(is_source[current_item], vd, false);
Point_set::iterator it;
for(it = source_points->point_set()->begin(); it != source_points->point_set()->end(); ++it)
if(source_points->point_set()->point(*it) == current_item->face_graph()->point(vd))
{
source_points->point_set()->remove(it);
source_points->point_set()->collect_garbage();
break;
}
}
}
source_points->invalidateOpenGLBuffers();
source_points->itemChanged();
}
private:
template<typename PM>
bool displayPSProperty(Point_set* ps, PM pm);
template<typename PM>
bool displaySMProperty(SMesh& smesh, PM pm, vertex_descriptor);
template<typename PM>
bool displaySMProperty(SMesh& smesh, PM pm, face_descriptor);
template<typename TAG>
bool treat_sm_property(std::string name, SMesh* sm);
//cannot be treated as a sm_property because the property_map<>() function takes only 1 template arg.
bool treat_point_property(std::string name, Point_set* sm);
template<typename Simplex>
bool is_property_scalar(std::string name, const SMesh* sm);
//same problem of number of templates
bool is_property_scalar(std::string name, const Point_set* ps);
template<typename Value_type>
void displayMapLegend(const std::vector<Value_type>& values)
{
// Create a legend_ and display it
const std::size_t size = (std::min)(color_map.size(), (std::size_t)256);
const int text_height = 20;
const int height = text_height*static_cast<int>(size) + text_height;
const int width = 140;
const int cell_width = width/3;
const int top_margin = 15;
const int left_margin = 5;
const int drawing_height = height - text_height + top_margin;
legend_ = QPixmap(width, height );
legend_.fill(QColor(200, 200, 200));
QPainter painter(&legend_);
painter.setPen(Qt::black);
painter.setBrush(QColor(200, 200, 200));
int j = 0;
int tick_height = text_height;
for (std::size_t i = 0; i< size; ++i, j+=tick_height)
{
QColor color(color_map[i].red(),
color_map[i].green(),
color_map[i].blue());
painter.fillRect(left_margin,
drawing_height - top_margin - j,
cell_width,
tick_height,
color);
QRect text_rect(left_margin + cell_width+10, drawing_height - top_margin - j,
50, text_height);
painter.drawText(text_rect, Qt::AlignCenter, tr("%1").arg(values[i], 0, 'f', 3, QLatin1Char(' ')));
}
if(color_map.size() > size){
QRect text_rect(left_margin + cell_width+10, 0,
50, text_height);
painter.drawText(text_rect, Qt::AlignCenter, tr("[...]"));
}
// draw right vertical line
painter.setPen(Qt::blue);
painter.drawLine(QPoint(left_margin + cell_width+10, drawing_height - top_margin +tick_height),
QPoint(left_margin + cell_width+10,
drawing_height - top_margin - static_cast<int>(size)*tick_height+tick_height));
dock_widget->legendLabel->setPixmap(legend_);
}
void displayLegend()
{
// Create a legend_ and display it
const int height = 256;
const int width = 140;
const int cell_width = width/3;
const int top_margin = 5;
const int left_margin = 5;
const int drawing_height = height - top_margin * 2;
const int text_height = 20;
legend_ = QPixmap(width, height + text_height);
legend_.fill(QColor(200, 200, 200));
QPainter painter(&legend_);
painter.setPen(Qt::black);
painter.setBrush(QColor(200, 200, 200));
double min_value(dock_widget->minBox->value()),
max_value(dock_widget->maxBox->value());
// Build legend_ data
std::vector<double> graduations(100);
for(int i=0; i<100; ++i)
graduations[i] = i/100.0;
int i=0;
for (std::vector<double>::iterator it = graduations.begin(), end = graduations.end();
it != end; ++it, i+=2)
{
QColor color(255*color_ramp.r(*it),
255*color_ramp.g(*it),
255*color_ramp.b(*it));
painter.fillRect(left_margin,
drawing_height - top_margin - i,
cell_width,
2,
color);
}
// draw right vertical line
painter.setPen(Qt::blue);
painter.drawLine(QPoint(left_margin + cell_width+10, drawing_height - top_margin + 2),
QPoint(left_margin + cell_width+10,
drawing_height - top_margin - static_cast<int>(graduations.size())*2 + 2));
// draw min value and max value
painter.setPen(Qt::blue);
QRect min_text_rect(left_margin + cell_width+10,drawing_height - top_margin,
100, text_height);
painter.drawText(min_text_rect, Qt::AlignCenter, tr("%1").arg(min_value, 0, 'f', 1));
QRect max_text_rect(left_margin + cell_width+10, drawing_height - top_margin - 200,
100, text_height);
painter.drawText(max_text_rect, Qt::AlignCenter, tr("%1").arg(max_value, 0, 'f', 1));
dock_widget->legendLabel->setPixmap(legend_);
}
double scaled_jacobian(const face_descriptor& f , const SMesh &mesh);
QList<QAction*> _actions;
Color_ramp color_ramp;
std::vector<QColor> color_map;
DockWidget* dock_widget;
double rm;
double rM;
double rI;
double gm;
double gM;
double gI;
double bm;
double bM;
double bI;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > jacobian_min;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > jacobian_max;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > angles_min;
std::unordered_map<Scene_surface_mesh_item*, std::pair<double, SMesh::Face_index> > angles_max;
std::unordered_map<Scene_surface_mesh_item*, Vertex_source_map> is_source;
double minBox;
double maxBox;
QPixmap legend_;
Scene_surface_mesh_item* current_item;
Scene_points_with_normal_item* source_points;
std::unordered_map<Scene_surface_mesh_item*, Scene_points_with_normal_item*> mesh_sources_map;
std::unordered_map<Scene_surface_mesh_item*, Scene_heat_item*> mesh_heat_item_map;
std::unordered_map<Scene_surface_mesh_item*, Heat_method*> mesh_heat_method_map;
std::unordered_map<Scene_surface_mesh_item*, Heat_method_idt*> mesh_heat_method_idt_map;
template<typename, typename, typename> friend class PropertyDisplayer;
//CRTP used to display properties of surface meshes(vertex and face) and point set.
template <class DataSet, class PM, class CRTP_derived_class>
struct PropertyDisplayer
{
typedef typename PM::value_type Value_type;
PropertyDisplayer(DataSet& ds, PM pm, DisplayPropertyPlugin* parent)
:dataset(ds), property_map(pm), parent(parent)
{}
virtual void fill_values(){}
virtual void set_colors_map(std::unordered_map<Value_type, std::size_t> &){}
virtual void set_colors_ramp(){}
bool operator()()
{
parent->minBox = ARBITRARY_DBL_MAX;
parent->maxBox = -ARBITRARY_DBL_MAX;
static_cast<CRTP_derived_class*>(this)->fill_values();
std::sort(values.begin(), values.end());
auto end = std::unique(values.begin(), values.end());
parent->minBox = *values.begin();
parent->maxBox = *(end-1);
parent->dock_widget->minBox->setValue(parent->minBox);
parent->dock_widget->maxBox->setValue(parent->maxBox);
//fill color pmap
if(parent->dock_widget->colorChoiceWidget->currentIndex() == 1)
{
std::unordered_map<Value_type, std::size_t> value_index_map;
//fill map
std::size_t counter = 0;
for(auto it = values.begin(); it != end; ++it)
{
value_index_map[*it] = counter++;
}
parent->color_map.clear();
compute_color_map(QColor(parent->rI, parent->gI, parent->bI),std::distance(values.begin(), end),
std::back_inserter(parent->color_map));
static_cast<CRTP_derived_class*>(this)->set_colors_map(value_index_map);
parent->displayMapLegend(values);
}
else
{
//scale a color ramp between min and max
parent->replaceRamp();
static_cast<CRTP_derived_class*>(this)->set_colors_ramp();
}
return true;
}
DataSet& dataset;
PM property_map;
std::vector<Value_type> values;
DisplayPropertyPlugin* parent;
};
template <class PM>
struct PSDisplayer
: public PropertyDisplayer<Point_set, PM, PSDisplayer<PM> >
{
typedef typename PM::value_type Value_type;
typedef PropertyDisplayer<Point_set, PM, PSDisplayer<PM> > Base;
PSDisplayer(Point_set& ds, PM pm, DisplayPropertyPlugin* parent)
:Base(ds, pm, parent)
{}
void fill_values()
{
for(auto p : this->dataset)
{
this->values.push_back(this->property_map[p]);
}
}
void set_colors_map(std::unordered_map<Value_type, std::size_t> &value_index_map)
{
for(Point_set::iterator pit = this->dataset.begin();
pit != this->dataset.end();
++pit)
{
CGAL::IO::Color color(
this->parent->color_map[value_index_map[this->property_map[*pit]]].red(),
this->parent->color_map[value_index_map[this->property_map[*pit]]].green(),
this->parent->color_map[value_index_map[this->property_map[*pit]]].blue());
this->dataset.set_color(*pit, color.red(), color.green(), color.blue());
}
}
void set_colors_ramp()
{
float max = this->parent->maxBox;
float min = this->parent->minBox;
for(Point_set::iterator pit = this->dataset.begin();
pit != this->dataset.end();
++pit)
{
if(min == max)
--min;
float f = (static_cast<float>(this->property_map[*pit])-min)/(max-min);
if(f<0)
f = 0;
if(f>1)
f = 1;
CGAL::IO::Color color(
255*this->parent->color_ramp.r(f),
255*this->parent->color_ramp.g(f),
255*this->parent->color_ramp.b(f));
this->dataset.set_color(*pit, color.red(), color.green(), color.blue());
}
}
};
template <class PM>
struct SMVertexDisplayer
: public PropertyDisplayer<SMesh, PM, SMVertexDisplayer<PM> >
{
typedef typename PM::value_type Value_type;
typedef PropertyDisplayer<SMesh, PM, SMVertexDisplayer<PM> > Base;
SMVertexDisplayer(SMesh& ds, PM pm, DisplayPropertyPlugin* parent)
:Base(ds, pm, parent)
{}
void fill_values()
{
for(auto v : vertices(this->dataset))
{
this->values.push_back(this->property_map[v]);
}
}
void set_colors_map(std::unordered_map<Value_type, std::size_t> &value_index_map)
{
SMesh::Property_map<vertex_descriptor, CGAL::IO::Color> vcolors =
this->dataset.template add_property_map<vertex_descriptor, CGAL::IO::Color >("v:color", CGAL::IO::Color()).first;
for(boost::graph_traits<SMesh>::vertex_iterator vit = vertices(this->dataset).begin();
vit != vertices(this->dataset).end();
++vit)
{
CGAL::IO::Color color(
this->parent->color_map[value_index_map[this->property_map[*vit]]].red(),
this->parent->color_map[value_index_map[this->property_map[*vit]]].green(),
this->parent->color_map[value_index_map[this->property_map[*vit]]].blue());
vcolors[*vit] = color;
}
}
void set_colors_ramp()
{
SMesh::Property_map<vertex_descriptor, CGAL::IO::Color> vcolors =
this->dataset.template add_property_map<vertex_descriptor, CGAL::IO::Color >("v:color", CGAL::IO::Color()).first;
float max = this->parent->maxBox;
float min = this->parent->minBox;
for(boost::graph_traits<SMesh>::vertex_iterator vit = vertices(this->dataset).begin();
vit != vertices(this->dataset).end();
++vit)
{
if(min == max)
--min;
float f = (static_cast<float>(this->property_map[*vit])-min)/(max-min);
if(f<0)
f = 0;
if(f>1)
f = 1;
CGAL::IO::Color color(
255*this->parent->color_ramp.r(f),
255*this->parent->color_ramp.g(f),
255*this->parent->color_ramp.b(f));
vcolors[*vit] = color;
}
}
};
template <class PM>
struct SMFaceDisplayer
: public PropertyDisplayer<SMesh, PM, SMFaceDisplayer<PM> >
{
typedef PropertyDisplayer<SMesh, PM, SMFaceDisplayer<PM> > Base;
typedef typename PM::value_type Value_type;
SMFaceDisplayer(SMesh& ds, PM pm, DisplayPropertyPlugin* parent)
:Base(ds, pm, parent)
{}
void fill_values()
{
for(auto f : faces(this->dataset))
{
this->values.push_back(this->property_map[f]);
}
}
void set_colors_map(std::unordered_map<Value_type, std::size_t> &value_index_map)
{
SMesh::Property_map<face_descriptor, CGAL::IO::Color> fcolors =
this->dataset.template add_property_map<face_descriptor, CGAL::IO::Color >("f:color", CGAL::IO::Color()).first;
for(boost::graph_traits<SMesh>::face_iterator fit = faces(this->dataset).begin();
fit != faces(this->dataset).end();
++fit)
{
CGAL::IO::Color color(
this->parent->color_map[value_index_map[this->property_map[*fit]]].red(),
this->parent->color_map[value_index_map[this->property_map[*fit]]].green(),
this->parent->color_map[value_index_map[this->property_map[*fit]]].blue());
fcolors[*fit] = color;
}
}
void set_colors_ramp()
{
SMesh::Property_map<face_descriptor, CGAL::IO::Color> fcolors =
this->dataset.template add_property_map<face_descriptor, CGAL::IO::Color >("f:color", CGAL::IO::Color()).first;
float max = this->parent->maxBox;
float min = this->parent->minBox;
for(boost::graph_traits<SMesh>::face_iterator fit = faces(this->dataset).begin();
fit != faces(this->dataset).end();
++fit)
{
if(min == max)
--min;
float f = (static_cast<float>(this->property_map[*fit])-min)/(max-min);
if(f<0)
f = 0;
if(f>1)
f = 1;
CGAL::IO::Color color(
255*this->parent->color_ramp.r(f),
255*this->parent->color_ramp.g(f),
255*this->parent->color_ramp.b(f));
fcolors[*fit] = color;
}
}
};
};
/// Code based on the verdict module of vtk
/*=========================================================================
Copyright (c) 2006 Sandia Corporation.
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
double DisplayPropertyPlugin::scaled_jacobian( const face_descriptor& f , const SMesh& mesh)
{
boost::property_map<SMesh, boost::vertex_point_t>::type
pmap = get(boost::vertex_point, mesh);
std::vector<double> corner_areas(degree(f, mesh));
std::vector<EPICK::Vector_3> edges;
for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(f, mesh), mesh))
{
edges.push_back(EPICK::Vector_3(get(pmap, source(hd, mesh)), get(pmap, target(hd, mesh))));
}
std::vector<EPICK::Vector_3> corner_normals;
for(std::size_t i = 0; i < edges.size(); ++i)
{
corner_normals.push_back(CGAL::cross_product(edges[i], edges[(i+1)%(edges.size())]));
}
EPICK::Vector_3 unit_center_normal = CGAL::Polygon_mesh_processing::compute_face_normal(f, mesh);
unit_center_normal *= 1.0/CGAL::approximate_sqrt(unit_center_normal.squared_length());
for(std::size_t i = 0; i < corner_areas.size(); ++i)
{
corner_areas[i] = unit_center_normal*corner_normals[i];
}
std::vector<double> length;
for(std::size_t i=0; i<edges.size(); ++i)
{
length.push_back(CGAL::approximate_sqrt(edges[i].squared_length()));
if( length[i] < ARBITRARY_DBL_MIN)
return 0.0;
}
double min_scaled_jac = ARBITRARY_DBL_MAX;
for(std::size_t i=0; i<edges.size(); ++i)
{
double scaled_jac = corner_areas[i] / (length[i] * length[(i+edges.size()-1)%(edges.size())]);
min_scaled_jac = (std::min)( scaled_jac, min_scaled_jac );
}
if( min_scaled_jac > 0 )
return (double) (std::min)( min_scaled_jac, ARBITRARY_DBL_MAX );
return (double) (std::max)( min_scaled_jac, -ARBITRARY_DBL_MAX );
}
bool DisplayPropertyPlugin::is_property_scalar(std::string name, const Point_set* ps)
{
if(name == "red"
|| name == "green"
|| name == "blue")
{
return false;
}
if(ps->template property_map<boost::int8_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::uint8_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::int16_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::uint16_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::int32_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::uint32_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::int64_t>(name).second)
{
return true;
}
if(ps->template property_map<boost::uint64_t>(name).second)
{
return true;
}
if(ps->template property_map<float>(name).second)
{
return true;
}
if(ps->template property_map<double>(name).second)
{
return true;
}
return false;
}
template<typename Simplex>
bool DisplayPropertyPlugin::is_property_scalar(std::string name, const SMesh* sm)
{
if(sm->template property_map<Simplex,boost::int8_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::uint8_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::int16_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::uint16_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::int32_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::uint32_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::int64_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,boost::uint64_t>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,float>(name).second)
{
return true;
}
if(sm->template property_map<Simplex,double>(name).second)
{
return true;
}
return false;
}
bool DisplayPropertyPlugin::treat_point_property(std::string name, Point_set* ps)
{
typedef typename Point_set::template Property_map<boost::int8_t> Int8_map;
typedef typename Point_set::template Property_map<boost::uint8_t> Uint8_map;
typedef typename Point_set::template Property_map<boost::int16_t> Int16_map;
typedef typename Point_set::template Property_map<boost::uint16_t> Uint16_map;
typedef typename Point_set::template Property_map<boost::int32_t> Int32_map;
typedef typename Point_set::template Property_map<boost::uint32_t> Uint32_map;
typedef typename Point_set::template Property_map<boost::int64_t> Int64_map;
typedef typename Point_set::template Property_map<boost::uint64_t> Uint64_map;
typedef typename Point_set::template Property_map<float> Float_map;
typedef typename Point_set::template Property_map<double> Double_map;
bool okay = false;
{
Int8_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::int8_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Uint8_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::uint8_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Int16_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::int16_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Uint16_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::uint16_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Int32_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::int32_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Uint32_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::uint32_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Int64_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::int64_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Uint64_map pmap;
std::tie(pmap, okay) = ps->property_map<boost::uint64_t>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Float_map pmap;
std::tie(pmap, okay) = ps->property_map<float>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
{
Double_map pmap;
std::tie(pmap, okay) = ps->property_map<double>(name);
if(okay)
{
return displayPSProperty(ps, pmap);
}
}
return false;
}
template<typename TAG>
bool DisplayPropertyPlugin::treat_sm_property(std::string name, SMesh* sm)
{
typedef typename SMesh::template Property_map<TAG, boost::int8_t> Int8_map;
typedef typename SMesh::template Property_map<TAG, boost::uint8_t> Uint8_map;
typedef typename SMesh::template Property_map<TAG, boost::int16_t> Int16_map;
typedef typename SMesh::template Property_map<TAG, boost::uint16_t> Uint16_map;
typedef typename SMesh::template Property_map<TAG, boost::int32_t> Int32_map;
typedef typename SMesh::template Property_map<TAG, boost::uint32_t> Uint32_map;
typedef typename SMesh::template Property_map<TAG, boost::int64_t> Int64_map;
typedef typename SMesh::template Property_map<TAG, boost::uint64_t> Uint64_map;
typedef typename SMesh::template Property_map<TAG, float> Float_map;
typedef typename SMesh::template Property_map<TAG, double> Double_map;
bool okay = false;
{
Int8_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::int8_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Uint8_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::uint8_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Int16_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::int16_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Uint16_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::uint16_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Int32_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::int32_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Uint32_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::uint32_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Int64_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::int64_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Uint64_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,boost::uint64_t>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Float_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,float>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
{
Double_map pmap;
std::tie(pmap, okay) = sm->property_map<TAG,double>(name);
if(okay)
{
return displaySMProperty(*sm, pmap, TAG());
}
}
return false;
}
template<typename PM>
bool DisplayPropertyPlugin::displayPSProperty(Point_set* ps, PM pm)
{
PSDisplayer<PM> display_property(*ps, pm, this);
return display_property();
}
template<typename PM>
bool DisplayPropertyPlugin::displaySMProperty(SMesh& smesh, PM pm, vertex_descriptor)
{
SMVertexDisplayer<PM> display_property(smesh, pm, this);
return display_property();
}
template<typename PM>
bool DisplayPropertyPlugin::displaySMProperty(SMesh& smesh, PM pm, face_descriptor)
{
SMFaceDisplayer<PM> display_property(smesh, pm, this);
return display_property();
}
#include "Display_property_plugin.moc"