mirror of https://github.com/CGAL/cgal
2082 lines
67 KiB
C++
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"
|