cgal/Lab/demo/Lab/Scene_surface_mesh_item.cpp

2658 lines
85 KiB
C++

#include "Scene_surface_mesh_item.h"
#include "Color_map.h"
#ifndef Q_MOC_RUN
#include <boost/graph/properties.hpp>
#include <boost/graph/graph_traits.hpp>
#endif
#include <QOpenGLShaderProgram>
#include <QInputDialog>
#include <QOpenGLBuffer>
#include <QApplication>
#include <QVariant>
#include <QMessageBox>
#include <QMenu>
#include <QWidgetAction>
#include <QSlider>
#include <QOpenGLFramebufferObject>
#ifndef Q_MOC_RUN
#include <CGAL/Surface_mesh.h>
#include <CGAL/Surface_mesh/IO.h>
#include <CGAL/intersections.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits_3.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include "triangulate_primitive.h"
#include <CGAL/IO/OBJ.h>
#include <CGAL/exceptions.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/statistics_helpers.h>
#include <CGAL/Three/Buffer_objects.h>
#include <CGAL/Three/Triangle_container.h>
#include <CGAL/Three/Edge_container.h>
#include <CGAL/Three/Point_container.h>
#include <CGAL/Three/Three.h>
#include <CGAL/boost/graph/io.h>
#include <CGAL/Buffer_for_vao.h>
#include <QMenu>
#include "id_printing.h"
#include <unordered_map>
#include <functional>
#include <utility>
#endif
typedef CGAL::Three::Triangle_container Tri;
typedef CGAL::Three::Edge_container Ed;
typedef CGAL::Three::Point_container Pt;
typedef CGAL::Three::Viewer_interface VI;
//Used to triangulate the AABB_Tree
class Primitive
{
public:
// types
typedef face_descriptor Id; // Id type
typedef Point_3 Point; // point type
typedef EPICK::Triangle_3 Datum; // datum type
private:
// member data
Id m_it; // iterator
Datum m_datum; // 3D triangle
// constructor
public:
Primitive() {}
Primitive(Datum triangle, Id it)
: m_it(it), m_datum(triangle)
{
}
public:
Id& id() { return m_it; }
const Id& id() const { return m_it; }
Datum& datum() { return m_datum; }
const Datum& datum() const { return m_datum; }
/// Returns a point on the primitive
Point reference_point() const { return m_datum.vertex(0); }
};
typedef CGAL::AABB_traits_3<EPICK, Primitive> AABB_traits;
typedef CGAL::AABB_tree<AABB_traits> Input_facets_AABB_tree;
struct KeyHash
{
std::size_t operator()(const std::pair<std::size_t, std::size_t>& k) const
{
return std::hash<std::size_t>()(k.first) ^ (std::hash<std::size_t>()(k.second) << 1);
}
};
struct KeyEqual {
bool operator()(const std::pair<std::size_t, std::size_t>& lhs,
const std::pair<std::size_t, std::size_t>& rhs) const
{
return lhs.first == rhs.first && lhs.second == rhs.second;
}
};
struct Scene_surface_mesh_item_priv{
typedef EPICK::Point_3 Point;
typedef CGAL::Surface_mesh<Point> SMesh;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
typedef std::vector<QColor> Color_vector;
Scene_surface_mesh_item_priv(const Scene_surface_mesh_item& other, Scene_surface_mesh_item* parent):
smesh_(new SMesh(*other.d->smesh_)),
idx_data_(other.d->idx_data_),
idx_edge_data_(other.d->idx_edge_data_),
fpatch_id_map(other.d->fpatch_id_map),
min_patch_id(other.d->min_patch_id),
colors_(other.d->colors_)
{
item = parent;
item->setTriangleContainer(1, new Triangle_container(VI::PROGRAM_WITH_LIGHT,
false));
item->setTriangleContainer(0, new Triangle_container(VI::PROGRAM_WITH_LIGHT,
true));
item->setEdgeContainer(1, new Edge_container(VI::PROGRAM_NO_SELECTION,
true));
item->setEdgeContainer(0, new Edge_container(VI::PROGRAM_WITHOUT_LIGHT,
true));
item->setPointContainer(0, new Point_container(VI::PROGRAM_NO_SELECTION,
false));
item->getEdgeContainer(0)->setFrameMatrix(QMatrix4x4());
has_feature_edges = false;
invalidate_stats();
vertices_displayed= false;
edges_displayed = false;
faces_displayed = false;
all_displayed = false;
alphaSlider = nullptr;
has_vcolors = false;
has_fcolors = false;
supported_rendering_modes << FlatPlusEdges
<< Wireframe
<< Flat
<< Gouraud
<< GouraudPlusEdges
<< Points;
item->setProperty("classname", QString("surface_mesh"));
ids_need_update = false;
}
Scene_surface_mesh_item_priv(SMesh* sm, Scene_surface_mesh_item *parent):
smesh_(sm)
{
item = parent;
item->setTriangleContainer(1, new Triangle_container(VI::PROGRAM_WITH_LIGHT,
false));
item->setTriangleContainer(0, new Triangle_container(VI::PROGRAM_WITH_LIGHT,
true));
item->setEdgeContainer(1, new Edge_container(VI::PROGRAM_NO_SELECTION,
true));
item->setEdgeContainer(0, new Edge_container(VI::PROGRAM_WITHOUT_LIGHT,
true));
item->setPointContainer(0, new Point_container(VI::PROGRAM_WITHOUT_LIGHT,
false));
has_feature_edges = false;
invalidate_stats();
vertices_displayed = false;
edges_displayed = false;
faces_displayed = false;
all_displayed = false;
alphaSlider = nullptr;
has_vcolors = false;
has_fcolors = false;
supported_rendering_modes << FlatPlusEdges
<< Wireframe
<< Flat
<< Gouraud
<< GouraudPlusEdges
<< Points;
item->setProperty("classname", QString("surface_mesh"));\
ids_need_update = false;
flat_vertex_map_ready = false;
}
~Scene_surface_mesh_item_priv()
{
if(alphaSlider)
delete alphaSlider;
if(smesh_)
{
delete smesh_;
smesh_ = nullptr;
}
}
void killIds();
void fillTargetedIds(const face_descriptor& selected_fh,
const EPICK::Point_3 &point_under,
CGAL::Three::Viewer_interface *viewer,
const CGAL::qglviewer::Vec &offset);
void initialize_colors() const;
void invalidate_stats();
void initializeBuffers(CGAL::Three::Viewer_interface *) const;
void addFlatData(Point, EPICK::Vector_3, CGAL::IO::Color *, Scene_item_rendering_helper::Gl_data_names name) const;
void* get_aabb_tree();
QList<EPICK::Triangle_3> triangulate_primitive(face_descriptor fit,
EPICK::Vector_3 normal);
//! \brief triangulate_facet Triangulates a facet.
//! \param fd a face_descriptor of the facet that needs to be triangulated.
//! \param fnormals a property_map containing the normals of the mesh.
//! \param fcolors a property_map containing the colors of the mesh
//! \param im a property_map containing the indices of the vertices of the mesh
//! \param index if true, the function will fill the index vector. If false, the function will
//! fill the flat data vectors.
void
triangulate_facet(face_descriptor fd,
SMesh::Property_map<face_descriptor, EPICK::Vector_3> *fnormals,
SMesh::Property_map<face_descriptor, CGAL::IO::Color> *fcolors,
boost::property_map< SMesh, boost::vertex_index_t >::type *im,
Scene_item_rendering_helper::Gl_data_names name,
bool index) const;
void triangulate_convex_facet(face_descriptor fd,
SMesh::Property_map<face_descriptor, EPICK::Vector_3> *fnormals,
SMesh::Property_map<face_descriptor, CGAL::IO::Color> *fcolors,
boost::property_map< SMesh, boost::vertex_index_t >::type *im,
Scene_item_rendering_helper::Gl_data_names name,
bool index) const;
void compute_elements(Scene_item_rendering_helper::Gl_data_names name) const;
void checkFloat() const;
TextListItem* textVItems;
TextListItem* textEItems;
TextListItem* textFItems;
mutable bool vertices_displayed;
mutable bool edges_displayed;
mutable bool faces_displayed;
mutable bool all_displayed;
mutable std::vector<TextItem*> targeted_id;
std::string comments;
mutable bool has_fpatch_id;
mutable bool has_feature_edges;
mutable bool floated;
mutable bool has_vcolors;
mutable bool has_fcolors;
SMesh* smesh_;
mutable bool is_filled;
mutable bool isinit;
mutable std::vector<unsigned int> idx_data_;
mutable std::size_t idx_data_size;
mutable std::vector<unsigned int> idx_edge_data_;
mutable std::size_t idx_edge_data_size;
mutable std::vector<unsigned int> idx_feature_edge_data_;
mutable std::size_t idx_feature_edge_data_size;
mutable std::vector<cgal_gl_data> smooth_vertices;
mutable std::vector<cgal_gl_data> smooth_normals;
mutable std::vector<cgal_gl_data> flat_vertices;
mutable std::vector<std::vector<std::size_t> > flat_vertices_map;
mutable std::vector<std::size_t> cumul_id;
mutable std::size_t flat_vertices_size;
mutable std::vector<cgal_gl_data> flat_normals;
mutable std::vector<cgal_gl_data> f_colors;
mutable std::vector<cgal_gl_data> v_colors;
mutable QOpenGLShaderProgram *program;
Scene_surface_mesh_item *item;
mutable SMesh::Property_map<face_descriptor,int> fpatch_id_map;
mutable int min_patch_id;
mutable SMesh::Property_map<vertex_descriptor,int> v_selection_map;
mutable SMesh::Property_map<face_descriptor,int> f_selection_map;
mutable SMesh::Property_map<boost::graph_traits<SMesh>::edge_descriptor, bool> e_is_feature_map;
mutable Color_vector colors_;
double volume, area;
unsigned int number_of_null_length_edges;
unsigned int number_of_degenerated_faces;
bool has_nm_vertices;
int genus;
bool self_intersect;
bool ids_need_update;
bool flat_vertex_map_ready;
mutable QSlider* alphaSlider;
QList<RenderingMode> supported_rendering_modes;
};
const char* aabb_property_name = "Scene_surface_mesh_item aabb tree";
void Scene_surface_mesh_item::initialize_priv()
{
CGAL_precondition(d != nullptr);
d->floated = false;
setRenderingMode(CGAL::Three::Three::defaultSurfaceMeshRenderingMode());
d->checkFloat();
d->textVItems = new TextListItem(this);
d->textEItems = new TextListItem(this);
d->textFItems = new TextListItem(this);
are_buffers_filled = false;
invalidate(ALL);
}
Scene_surface_mesh_item::Scene_surface_mesh_item()
{
d = new Scene_surface_mesh_item_priv(new SMesh(), this);
initialize_priv();
}
Scene_surface_mesh_item::Scene_surface_mesh_item(SMesh* sm)
{
d = new Scene_surface_mesh_item_priv(sm, this);
initialize_priv();
std::size_t isolated_v = 0;
for(vertex_descriptor v : vertices(*sm))
{
if(sm->is_isolated(v))
{
++isolated_v;
}
}
setNbIsolatedvertices(isolated_v);
}
Scene_surface_mesh_item::Scene_surface_mesh_item(const SMesh& sm)
: Scene_surface_mesh_item(new SMesh(sm))
{ }
Scene_surface_mesh_item::Scene_surface_mesh_item(SMesh&& sm)
: Scene_surface_mesh_item(new SMesh(std::move(sm)))
{ }
Scene_surface_mesh_item::Scene_surface_mesh_item(const Scene_surface_mesh_item& other)
{
d = new Scene_surface_mesh_item_priv(other, this);
initialize_priv();
}
Scene_surface_mesh_item*
Scene_surface_mesh_item::clone() const
{ return new Scene_surface_mesh_item(*this); }
Scene_surface_mesh_item::Vertex_selection_map
Scene_surface_mesh_item::vertex_selection_map()
{
if(! d->v_selection_map){
d->v_selection_map = d->smesh_->add_property_map<vertex_descriptor,int>("v:selection").first;
}
return d->v_selection_map;
}
Scene_surface_mesh_item::Face_selection_map
Scene_surface_mesh_item::face_selection_map()
{
if(! d->f_selection_map){
d->f_selection_map = d->smesh_->add_property_map<face_descriptor,int>("f:selection").first;
}
return d->f_selection_map;
}
std::vector<QColor>&
Scene_surface_mesh_item::color_vector()
{
return d->colors_;
}
void Scene_surface_mesh_item_priv::addFlatData(Point p, EPICK::Vector_3 n, CGAL::IO::Color *c, Scene_item_rendering_helper::Gl_data_names name) const
{
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
flat_vertices.push_back(static_cast<cgal_gl_data>(p.x()+offset[0]));
flat_vertices.push_back(static_cast<cgal_gl_data>(p.y()+offset[1]));
flat_vertices.push_back(static_cast<cgal_gl_data>(p.z()+offset[2]));
}
if(name.testFlag(Scene_item_rendering_helper::NORMALS))
{
flat_normals.push_back(static_cast<cgal_gl_data>(n.x()));
flat_normals.push_back(static_cast<cgal_gl_data>(n.y()));
flat_normals.push_back(static_cast<cgal_gl_data>(n.z()));
}
if(c != nullptr && name.testFlag(Scene_item_rendering_helper::COLORS))
{
f_colors.push_back(static_cast<float>(c->red())/255);
f_colors.push_back(static_cast<float>(c->green())/255);
f_colors.push_back(static_cast<float>(c->blue())/255);
}
}
void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper::Gl_data_names name)const
{
QApplication::setOverrideCursor(Qt::WaitCursor);
if(!alphaSlider)
{
alphaSlider = new QSlider(::Qt::Horizontal);
alphaSlider->setMinimum(0);
alphaSlider->setMaximum(255);
alphaSlider->setValue(255);
}
smooth_vertices.clear();
smooth_normals.clear();
flat_vertices.clear();
flat_normals.clear();
f_colors.clear();
v_colors.clear();
idx_data_.clear();
idx_data_.shrink_to_fit();
SMesh::Property_map<vertex_descriptor, EPICK::Vector_3 > vnormals =
smesh_->add_property_map<vertex_descriptor, EPICK::Vector_3 >("v:normal").first;
SMesh::Property_map<face_descriptor, EPICK::Vector_3 > fnormals =
smesh_->add_property_map<face_descriptor, EPICK::Vector_3 >("f:normal").first;
CGAL::Polygon_mesh_processing::compute_face_normals(*smesh_,fnormals);
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
CGAL::Polygon_mesh_processing::compute_vertex_normals(*smesh_,vnormals);
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 =
smesh_->points();
auto vcolors = smesh_->property_map<vertex_descriptor, CGAL::IO::Color >("v:color");
auto fcolors = smesh_->property_map<face_descriptor, CGAL::IO::Color >("f:color");
has_fcolors = fcolors.has_value();
has_vcolors = vcolors.has_value();
boost::property_map< SMesh, boost::vertex_index_t >::type
im = get(boost::vertex_index, *smesh_);
idx_data_.reserve(num_faces(*smesh_) * 3);
typedef CGAL::Buffer_for_vao CPF;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
typedef boost::graph_traits<SMesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<SMesh>::edge_descriptor edge_descriptor;
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
for(face_descriptor fd : faces(*smesh_))
{
if(is_triangle(halfedge(fd,*smesh_),*smesh_))
{
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *smesh_),*smesh_))
{
idx_data_.push_back(source(hd, *smesh_));
}
}
else
{
std::vector<Point> facet_points;
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *smesh_),*smesh_))
{
facet_points.push_back(positions[target(hd, *smesh_)]);
}
bool is_convex = CPF::is_facet_convex(facet_points, fnormals[fd]);
if(is_convex && is_quad(halfedge(fd,*smesh_),*smesh_) )
{
halfedge_descriptor hd = halfedge(fd,*smesh_);
//1st half
idx_data_.push_back(source(hd, *smesh_));
idx_data_.push_back(source(next(hd, *smesh_), *smesh_));
idx_data_.push_back(source(next(next(hd, *smesh_), *smesh_), *smesh_));
//2nd half
idx_data_.push_back(source(hd, *smesh_));
idx_data_.push_back(source(next(next(hd, *smesh_), *smesh_), *smesh_));
idx_data_.push_back(source(prev(hd, *smesh_), *smesh_));
}
else if(is_convex)
{
triangulate_convex_facet(fd, &fnormals, nullptr, &im, name, true);
}
else
{
triangulate_facet(fd, &fnormals, nullptr, &im, name, true);
}
}
}
}
if(name.testFlag(Scene_item_rendering_helper::COLORS))
{
has_fpatch_id = smesh_->property_map<face_descriptor, int >("f:patch_id").has_value();
}
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
idx_edge_data_.clear();
idx_edge_data_.shrink_to_fit();
idx_edge_data_.reserve(num_edges(*smesh_) * 2);
for(edge_descriptor ed : edges(*smesh_))
{
idx_edge_data_.push_back(source(ed, *smesh_));
idx_edge_data_.push_back(target(ed, *smesh_));
if(has_feature_edges &&
get(e_is_feature_map, ed))
{
idx_feature_edge_data_.push_back(source(ed, *smesh_));
idx_feature_edge_data_.push_back(target(ed, *smesh_));
}
}
idx_edge_data_.shrink_to_fit();
}
if(name.testFlag(Scene_item_rendering_helper::COLORS) &&
has_fpatch_id){
initialize_colors();
}
//compute the Flat data
flat_vertices.clear();
flat_normals.clear();
f_colors.clear();
for(face_descriptor fd : faces(*smesh_))
{
if(is_triangle(halfedge(fd,*smesh_),*smesh_))
{
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *smesh_),*smesh_))
{
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
vertex_descriptor vd = source(hd, *smesh_);
Point p = positions[vd] + offset;
CPF::add_point_in_buffer(p, flat_vertices);
}
if(name.testFlag(Scene_item_rendering_helper::NORMALS))
{
const EPICK::Vector_3& n = fnormals[fd];
CPF::add_normal_in_buffer(n, flat_normals);
}
if(name.testFlag(Scene_item_rendering_helper::COLORS))
{
if(has_fpatch_id)
{
//The sharp features detection produces patch ids >=1, this
//is meant to ensure the wanted id is in the range [min,max]
QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id];
CGAL::IO::Color color(c.red(),c.green(),c.blue());
CPF::add_color_in_buffer(color, f_colors);
}
else if(has_fcolors)
{
CGAL::IO::Color c = fcolors.value()[fd];
CPF::add_color_in_buffer(c, f_colors);
}
}
}
}
else
{
std::vector<Point> facet_points;
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *smesh_),*smesh_))
{
facet_points.push_back(positions[target(hd, *smesh_)]);
}
bool is_convex = CPF::is_facet_convex(facet_points, fnormals[fd]);
if(is_convex && is_quad(halfedge(fd,*smesh_),*smesh_) )
{
//1st half
halfedge_descriptor hd = halfedge(fd, *smesh_);
Point p = positions[source(hd, *smesh_)];
EPICK::Vector_3 n = fnormals[fd];
CGAL::IO::Color *c;
if(has_fpatch_id)
{
QColor color = item->color_vector()[fpatch_id_map[fd] - min_patch_id];
c = new CGAL::IO::Color(color.red(),color.green(),color.blue());
}
else if(has_fcolors)
c= &(fcolors.value()[fd]);
else
c = nullptr;
addFlatData(p,n,c, name);
hd = next(halfedge(fd, *smesh_),*smesh_);
addFlatData(positions[source(hd, *smesh_)]
,fnormals[fd]
,c
,name);
hd = next(next(halfedge(fd, *smesh_),*smesh_), *smesh_);
addFlatData(positions[source(hd, *smesh_)]
,fnormals[fd]
,c
,name);
//2nd half
hd = halfedge(fd, *smesh_);
addFlatData(positions[source(hd, *smesh_)]
,fnormals[fd]
,c
,name);
hd = next(next(halfedge(fd, *smesh_),*smesh_), *smesh_);
addFlatData(positions[source(hd, *smesh_)]
,fnormals[fd]
,c
,name);
hd = prev(halfedge(fd, *smesh_), *smesh_);
addFlatData(positions[source(hd, *smesh_)]
,fnormals[fd]
, c
, name);
if(has_fpatch_id)
delete c;
}
else if(is_convex)
{
if(has_fcolors)
triangulate_convex_facet(fd, &fnormals, &fcolors.value(), nullptr, name, false);
else
triangulate_convex_facet(fd, &fnormals, nullptr, nullptr, name, false);
}
else
{
if(has_fcolors)
triangulate_facet(fd, &fnormals, &fcolors.value(), nullptr, name, false);
else
triangulate_facet(fd, &fnormals, nullptr, nullptr, name, false);
}
}
}
if(has_vcolors && name.testFlag(Scene_item_rendering_helper::COLORS))
{
for(vertex_descriptor vd : vertices(*smesh_))
{
CGAL::IO::Color c = vcolors.value()[vd];
v_colors.push_back(static_cast<float>(c.red())/255);
v_colors.push_back(static_cast<float>(c.green())/255);
v_colors.push_back(static_cast<float>(c.blue())/255);
}
}
if(floated &&
(name.testFlag(Scene_item_rendering_helper::GEOMETRY)|| name.testFlag(Scene_item_rendering_helper::NORMALS)))
{
for(vertex_descriptor vd : vertices(*smesh_))
{
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
Point p = positions[vd] + offset;
CPF::add_point_in_buffer(p, smooth_vertices);
}
if(name.testFlag(Scene_item_rendering_helper::NORMALS))
{
EPICK::Vector_3 n = vnormals[vd];
CPF::add_normal_in_buffer(n, smooth_normals);
}
}
}
if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
idx_edge_data_size = idx_edge_data_.size();
idx_feature_edge_data_size = idx_feature_edge_data_.size();
idx_data_size = idx_data_.size();
flat_vertices_size = flat_vertices.size();
item->getPointContainer(0)->allocate(Pt::Vertices, smooth_vertices.data(),
static_cast<int>(num_vertices(*smesh_)*3*sizeof(cgal_gl_data)));
item->getEdgeContainer(0)->allocate(Ed::Indices, idx_edge_data_.data(),
static_cast<int>(idx_edge_data_.size()*sizeof(unsigned int)));
item->getEdgeContainer(0)->allocate(Ed::Vertices, smooth_vertices.data(),
static_cast<int>(num_vertices(*smesh_)*3*sizeof(cgal_gl_data)));
item->getEdgeContainer(1)->allocate(Ed::Indices, idx_feature_edge_data_.data(),
static_cast<int>(idx_feature_edge_data_.size()*sizeof(unsigned int)));
item->getEdgeContainer(1)->allocate(Ed::Vertices, smooth_vertices.data(),
static_cast<int>(num_vertices(*smesh_)*3*sizeof(cgal_gl_data)));
item->getTriangleContainer(0)->allocate(Tri::Vertex_indices, idx_data_.data(),
static_cast<int>(idx_data_.size()*sizeof(unsigned int)));
item->getTriangleContainer(1)->allocate(Tri::Flat_vertices, flat_vertices.data(),
static_cast<int>(flat_vertices.size()*sizeof(cgal_gl_data)));
item->getTriangleContainer(0)->allocate(Tri::Smooth_vertices, smooth_vertices.data(),
static_cast<int>(num_vertices(*smesh_)*3*sizeof(cgal_gl_data)));
}
if(name.testFlag(Scene_item_rendering_helper::NORMALS))
{
item->getTriangleContainer(1)->allocate(Tri::Flat_normals, flat_normals.data(),
static_cast<int>(flat_normals.size()*sizeof(cgal_gl_data)));
item->getTriangleContainer(0)->allocate(Tri::Smooth_normals, smooth_normals.data(),
static_cast<int>(num_vertices(*smesh_)*3*sizeof(cgal_gl_data)));
}
if(name.testFlag(Scene_item_rendering_helper::COLORS))
{
if(!f_colors.empty())
{
item->getTriangleContainer(1)->allocate(Tri::FColors, f_colors.data(),
static_cast<int>(f_colors.size()*sizeof(cgal_gl_data)));
}
else
item->getTriangleContainer(1)->allocate(Tri::FColors, nullptr, 0);
if(!v_colors.empty())
{
item->getTriangleContainer(0)->allocate(Tri::VColors, v_colors.data(),
static_cast<int>(v_colors.size()*sizeof(cgal_gl_data)));
}
else
item->getTriangleContainer(0)->allocate(Tri::VColors, nullptr, 0);
}
QApplication::restoreOverrideCursor();
}
void Scene_surface_mesh_item_priv::initialize_colors() const
{
// Fill indices map and get max subdomain value
int max = 0;
min_patch_id = (std::numeric_limits<int>::max)();
for(face_descriptor fd : faces(*smesh_)){
max = (std::max)(max, fpatch_id_map[fd]);
min_patch_id = (std::min)(min_patch_id, fpatch_id_map[fd]);
}
if(item->property("recompute_colors").toBool())
{
colors_.clear();
compute_deterministic_color_map(item->color(), (std::max)(1, max + 1 - min_patch_id),
std::back_inserter(colors_));
qDebug()<<colors_.size()<<" colors in item";
}
}
void Scene_surface_mesh_item_priv::initializeBuffers(CGAL::Three::Viewer_interface* viewer)const
{
item->getTriangleContainer(1)->initializeBuffers(viewer);
item->getTriangleContainer(0)->initializeBuffers(viewer);
item->getEdgeContainer(1)->initializeBuffers(viewer);
item->getEdgeContainer(0)->initializeBuffers(viewer);
item->getPointContainer(0)->initializeBuffers(viewer);
////Clean-up
item->getPointContainer(0)->setFlatDataSize(vertices(*smesh_).size()*3);
item->getTriangleContainer(1)->setFlatDataSize(flat_vertices_size);
item->getTriangleContainer(0)->setIdxSize(idx_data_size);
item->getEdgeContainer(1)->setIdxSize( idx_feature_edge_data_size);
item->getEdgeContainer(0)->setIdxSize( idx_edge_data_size);
smooth_vertices .resize(0);
smooth_normals .resize(0);
flat_vertices .resize(0);
flat_normals .resize(0);
f_colors .resize(0);
v_colors .resize(0);
idx_data_ .resize(0);
idx_edge_data_ .resize(0);
idx_feature_edge_data_.resize(0);
smooth_vertices .shrink_to_fit();
smooth_normals .shrink_to_fit();
flat_vertices .shrink_to_fit();
flat_normals .shrink_to_fit();
f_colors .shrink_to_fit();
v_colors .shrink_to_fit();
idx_data_ .shrink_to_fit();
idx_edge_data_ .shrink_to_fit();
idx_feature_edge_data_.shrink_to_fit();
}
void Scene_surface_mesh_item::draw(CGAL::Three::Viewer_interface *viewer) const
{
if(!isInit(viewer) && viewer->context()->isValid())
initGL(viewer);
if (getBuffersFilled() )
if(!getBuffersInit(viewer))
{
d->initializeBuffers(viewer);
setBuffersInit(viewer, true);
}
if(renderingMode() == Gouraud ||
renderingMode() == GouraudPlusEdges)
{
getTriangleContainer(0)->setColor(color());
getTriangleContainer(0)->setSelected(is_selected);
getTriangleContainer(0)->setAlpha(alpha());
getTriangleContainer(0)->draw( viewer, !d->has_vcolors);
}
else
{
getTriangleContainer(1)->setColor(color());
getTriangleContainer(1)->setSelected(is_selected);
getTriangleContainer(1)->setAlpha(alpha());
getTriangleContainer(1)->draw( viewer, !d->has_fcolors);
}
}
void Scene_surface_mesh_item::drawEdges(CGAL::Three::Viewer_interface *viewer) const
{
if(!isInit(viewer))
initGL(viewer);
if ( getBuffersFilled() &&
! getBuffersInit(viewer))
{
d->initializeBuffers(viewer);
setBuffersInit(viewer, true);
}
getEdgeContainer(0)->setSelected(is_selected);
getEdgeContainer(0)->setColor(QColor(Qt::black));
getEdgeContainer(0)->draw( viewer, true);
if(d->has_feature_edges)
{
getEdgeContainer(1)->setSelected(false);
getEdgeContainer(1)->setColor(QColor(Qt::red));
getEdgeContainer(1)->draw(viewer, true);
}
}
void Scene_surface_mesh_item::drawPoints(CGAL::Three::Viewer_interface *viewer) const
{
if(!isInit(viewer))
initGL(viewer);
if ( getBuffersFilled() &&
! getBuffersInit(viewer))
{
d->initializeBuffers(viewer);
setBuffersInit(viewer, true);
}
getPointContainer(0)->setSelected(is_selected);
getPointContainer(0)->setColor(color());
getPointContainer(0)->draw( viewer, true);
}
void
Scene_surface_mesh_item::selection_changed(bool p_is_selected)
{
if(p_is_selected != is_selected)
{
is_selected = p_is_selected;
}
}
bool
Scene_surface_mesh_item::supportsRenderingMode(RenderingMode m) const
{ return d->supported_rendering_modes.contains(m); }
CGAL::Three::Scene_item::Bbox Scene_surface_mesh_item::bbox() const
{
if(!is_bbox_computed)
compute_bbox();
return _bbox;
}
bool
Scene_surface_mesh_item::isEmpty() const
{
return num_vertices(*d->smesh_)==0;
}
QString Scene_surface_mesh_item::toolTip() const
{
QString str = QObject::tr("<p>Surface_mesh <b>%1</b> (mode: %5, color: %6)</p>"
"<p>Number of vertices: %2<br />"
"Number of edges: %3<br />"
"Number of faces: %4</p>")
.arg(this->name())
.arg(num_vertices(*d->smesh_))
.arg(num_edges(*d->smesh_))
.arg(num_faces(*d->smesh_))
.arg(this->renderingModeName())
.arg(this->color().name());
return str;
}
void Scene_surface_mesh_item_priv::checkFloat()const
{
#if CGAL_IS_FLOAT == 1
floated = true;
#endif
}
void Scene_surface_mesh_item_priv::triangulate_convex_facet(face_descriptor fd,
SMesh::Property_map<face_descriptor, EPICK::Vector_3> *fnormals,
SMesh::Property_map<face_descriptor, CGAL::IO::Color> *fcolors,
boost::property_map< SMesh, boost::vertex_index_t >::type *im,
Scene_item_rendering_helper::Gl_data_names name,
bool index) const
{
Point p0,p1,p2;
SMesh::Halfedge_around_face_circulator he(halfedge(fd, *smesh_), *smesh_);
SMesh::Halfedge_around_face_circulator he_end = he;
while(next(*he, *smesh_) != prev(*he_end, *smesh_))
{
++he;
vertex_descriptor v0(target(*he_end, *smesh_)),
v1(target(*he, *smesh_)),
v2(target(next(*he, *smesh_), *smesh_));
p0 = smesh_->point(v0);
p1 = smesh_->point(v1);
p2 = smesh_->point(v2);
if(!index)
{
CGAL::IO::Color* color;
if(has_fpatch_id)
{
QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id];
color = new CGAL::IO::Color(c.red(),c.green(),c.blue());
}
else if(has_fcolors)
color = &(*fcolors)[fd];
else
color = nullptr;
addFlatData(p0,
(*fnormals)[fd],
color,
name);
addFlatData(p1,
(*fnormals)[fd],
color,
name);
addFlatData(p2,
(*fnormals)[fd],
color,
name);
if(has_fpatch_id)
delete color;
}
else if(name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
idx_data_.push_back((*im)[v0]);
idx_data_.push_back((*im)[v1]);
idx_data_.push_back((*im)[v2]);
}
}
}
void
Scene_surface_mesh_item_priv::triangulate_facet(face_descriptor fd,
SMesh::Property_map<face_descriptor, EPICK::Vector_3> *fnormals,
SMesh::Property_map<face_descriptor, CGAL::IO::Color> *fcolors,
boost::property_map< SMesh, boost::vertex_index_t >::type *im,
Scene_item_rendering_helper::Gl_data_names name,
bool index) const
{
//Computes the normal of the facet
EPICK::Vector_3 normal = get(*fnormals, fd);
if(normal == CGAL::NULL_VECTOR)
{
boost::graph_traits<SMesh>::halfedge_descriptor start = prev(halfedge(fd, *smesh_), *smesh_);
boost::graph_traits<SMesh>::halfedge_descriptor hd = halfedge(fd, *smesh_);
boost::graph_traits<SMesh>::halfedge_descriptor next_=next(hd, *smesh_);
do
{
const Point_3& pa = smesh_->point(target(hd, *smesh_));
const Point_3& pb = smesh_->point(target(next_, *smesh_));
const Point_3& pc = smesh_->point(target(prev(hd, *smesh_), *smesh_));
if (!CGAL::collinear (pa, pb, pc))
{
normal = CGAL::cross_product(pb-pa, pc -pa);
break;
}
next_ =next(next_, *smesh_);
}while(next_ != start);
if (normal == CGAL::NULL_VECTOR) // No normal could be computed, return
{
qDebug()<<"Warning : normal is not valid. Facet not displayed";
return;
}
}
//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;
}
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);
//iterates on the internal faces
auto f = [&](auto& ffit, auto& v2v) {
if (ffit.info().is_external)
return;
//add the vertices to the positions
//adds the vertices, normals and colors to the appropriate vectors
if (!index)
{
CGAL::IO::Color* color;
if (has_fpatch_id)
{
QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id];
color = new CGAL::IO::Color(c.red(), c.green(), c.blue());
}
else if (has_fcolors)
color = &(*fcolors)[fd];
else
color = nullptr;
addFlatData(ffit.vertex(0)->point() - offset,
(*fnormals)[fd],
color,
name);
addFlatData(ffit.vertex(1)->point() - offset,
(*fnormals)[fd],
color,
name);
addFlatData(ffit.vertex(2)->point() - offset,
(*fnormals)[fd],
color,
name);
if (has_fpatch_id)
delete color;
}
//adds the indices to the appropriate vector
else
{
if (name.testFlag(Scene_item_rendering_helper::GEOMETRY))
{
idx_data_.push_back((*im)[v2v[ffit.vertex(0)]]);
idx_data_.push_back((*im)[v2v[ffit.vertex(1)]]);
idx_data_.push_back((*im)[v2v[ffit.vertex(2)]]);
}
}
};
try {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor> triangulation(fd, normal, smesh_, offset);
triangulation.per_face(f);
}
catch (...) {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor, CGAL::Exact_intersections_tag> triangulation(fd, normal, smesh_, offset);
triangulation.per_face(f);
}
}
void delete_aabb_tree(Scene_surface_mesh_item* item)
{
QVariant aabb_tree_property = item->property(aabb_property_name);
if(aabb_tree_property.isValid()) {
void* ptr = aabb_tree_property.value<void*>();
Input_facets_AABB_tree* tree = static_cast<Input_facets_AABB_tree*>(ptr);
if(tree) {
delete tree;
tree = nullptr;
}
item->setProperty(aabb_property_name, QVariant());
}
}
Scene_surface_mesh_item::~Scene_surface_mesh_item()
{
delete_aabb_tree(this);
CGAL::QGLViewer* viewer = *CGAL::QGLViewer::QGLViewerPool().begin();
if(viewer)
{
CGAL::Three::Viewer_interface* v = qobject_cast<CGAL::Three::Viewer_interface*>(viewer);
//Clears the targeted Id
if(d)
{
for(TextItem* item : d->targeted_id)
v->textRenderer()->removeText(item);
}
//Remove vertices textitems
if(d->textVItems)
{
v->textRenderer()->removeTextList(d->textVItems);
delete d->textVItems;
d->textVItems=nullptr;
}
//Remove edges textitems
if(d->textEItems)
{
v->textRenderer()->removeTextList(d->textEItems);
delete d->textEItems;
d->textEItems=nullptr;
}
//Remove faces textitems
if(d->textFItems)
{
v->textRenderer()->removeTextList(d->textFItems);
delete d->textFItems;
d->textFItems=nullptr;
}
}
delete d;
}
SMesh* Scene_surface_mesh_item::polyhedron() { return d->smesh_; }
const SMesh* Scene_surface_mesh_item::polyhedron() const { return d->smesh_; }
std::string& Scene_surface_mesh_item::comments() { return d->comments; }
const std::string& Scene_surface_mesh_item::comments() const { return d->comments; }
void Scene_surface_mesh_item::compute_bbox()const
{
SMesh::Property_map<vertex_descriptor, Point_3> pprop = d->smesh_->points();
CGAL::Bbox_3 bbox;
for(vertex_descriptor vd :vertices(*d->smesh_))
{
bbox = bbox + pprop[vd].bbox();
}
_bbox = Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(),
bbox.xmax(),bbox.ymax(),bbox.zmax());
is_bbox_computed = true;
}
void Scene_surface_mesh_item::itemAboutToBeDestroyed(Scene_item *item)
{
Scene_item::itemAboutToBeDestroyed(item);
if(d && d->smesh_ && item == this)
{
delete d->smesh_;
d->smesh_ = nullptr;
}
}
void* Scene_surface_mesh_item_priv::get_aabb_tree()
{
QVariant aabb_tree_property = item->property(aabb_property_name);
if(aabb_tree_property.isValid()) {
void* ptr = aabb_tree_property.value<void*>();
return static_cast<Input_facets_AABB_tree*>(ptr);
}
else {
QApplication::setOverrideCursor(Qt::WaitCursor);
SMesh* sm = item->polyhedron();
if(sm) {
sm->collect_garbage();
Input_facets_AABB_tree* tree =
new Input_facets_AABB_tree();
for(face_descriptor f : faces(*sm))
{
//if face is degenerate, skip it
if (CGAL::is_triangle(halfedge(f, *sm), *sm)
&& CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *sm))
continue;
//if face not triangle, triangulate corresponding primitive before adding it to the tree
if(!CGAL::is_triangle(halfedge(f, *sm), *sm))
{
EPICK::Vector_3 normal = CGAL::Polygon_mesh_processing::compute_face_normal(f, *sm);
for(EPICK::Triangle_3 triangle : triangulate_primitive(f,normal))
{
Primitive primitive(triangle, f);
tree->insert(primitive);
}
}
else
{
EPICK::Triangle_3 triangle(
sm->point(target(halfedge(f, *sm), *sm)),
sm->point(target(next(halfedge(f, *sm), *sm), *sm)),
sm->point(target(next(next(halfedge(f, *sm), *sm), *sm), *sm))
);
Primitive primitive(triangle, f);
tree->insert(primitive);
}
}
item->setProperty(aabb_property_name,
QVariant::fromValue<void*>(tree));
QApplication::restoreOverrideCursor();
return tree;
}
else return nullptr;
}
}
void
Scene_surface_mesh_item::select(double orig_x,
double orig_y,
double orig_z,
double dir_x,
double dir_y,
double dir_z)
{
SMesh *sm = d->smesh_;
std::size_t vertex_to_emit = 0;
typedef Input_facets_AABB_tree Tree;
typedef Tree::Intersection_and_primitive_id<EPICK::Ray_3>::Type Object_and_primitive_id;
Tree* aabb_tree = static_cast<Tree*>(d->get_aabb_tree());
if(aabb_tree)
{
const EPICK::Point_3 ray_origin(orig_x, orig_y, orig_z);
const EPICK::Vector_3 ray_dir(dir_x, dir_y, dir_z);
const EPICK::Ray_3 ray(ray_origin, ray_dir);
typedef std::list<Object_and_primitive_id> Intersections;
Intersections intersections;
aabb_tree->all_intersections(ray, std::back_inserter(intersections));
Intersections::iterator closest = intersections.begin();
if(closest != intersections.end())
{
const EPICK::Point_3* closest_point =
std::get_if<EPICK::Point_3>(&(closest->first));
for(Intersections::iterator
it = std::next(intersections.begin()),
end = intersections.end();
it != end; ++it)
{
if(! closest_point) {
closest = it;
}
else {
const EPICK::Point_3* it_point =
std::get_if<EPICK::Point_3>(&it->first);
if(it_point &&
(ray_dir * (*it_point - *closest_point)) < 0)
{
closest = it;
closest_point = it_point;
}
}
}
if(closest_point) {
face_descriptor selected_face = closest->second;
// The computation of the nearest vertex may be costly. Only
// do it if some objects are connected to the signal
// 'selected_vertex'.
if(QObject::receivers(SIGNAL(selected_vertex(void*))) > 0)
{
SMesh::Halfedge_around_face_circulator he_it(sm->halfedge(selected_face),*sm), around_end(he_it);
vertex_descriptor v = sm->target(*he_it), nearest_v = v;
EPICK::FT sq_dist = CGAL::squared_distance(*closest_point,
sm->point(v));
while(++he_it != around_end) {
v = sm->target(*he_it);
EPICK::FT new_sq_dist = CGAL::squared_distance(*closest_point,
sm->point(v));
if(new_sq_dist < sq_dist) {
sq_dist = new_sq_dist;
nearest_v = v;
}
}
//bottleneck
vertex_to_emit = static_cast<std::size_t>(nearest_v);
}
if(QObject::receivers(SIGNAL(selected_edge(void*))) > 0
|| QObject::receivers(SIGNAL(selected_halfedge(void*))) > 0)
{
SMesh::Halfedge_around_face_circulator he_it(sm->halfedge(selected_face),*sm), around_end(he_it);
halfedge_descriptor nearest_h = *he_it;
EPICK::FT sq_dist =
CGAL::squared_distance(*closest_point,
EPICK::Segment_3(sm->point(sm->target(*he_it)),
sm->point(
sm->target(
sm->opposite(*he_it)))));
while(++he_it != around_end)
{
EPICK::FT new_sq_dist =
CGAL::squared_distance(*closest_point,
EPICK::Segment_3(sm->point(sm->target(*he_it)),
sm->point(
sm->target(
sm->opposite(*he_it)))));
if(new_sq_dist < sq_dist) {
sq_dist = new_sq_dist;
nearest_h = *he_it;
}
}
std::size_t s_nearest_h = static_cast<std::size_t>(nearest_h);
std::size_t s_nearest_e = static_cast<std::size_t>(nearest_h)/2;
Q_EMIT selected_halfedge(reinterpret_cast<void*>(s_nearest_h));
Q_EMIT selected_edge(reinterpret_cast<void*>(s_nearest_e));
}
Q_EMIT selected_vertex(reinterpret_cast<void*>(vertex_to_emit));
std::size_t s_selected_f = static_cast<std::size_t>(selected_face);
Q_EMIT selected_facet(reinterpret_cast<void*>(s_selected_f));
}
}
}
Scene_item::select(orig_x, orig_y, orig_z, dir_x, dir_y, dir_z);
Q_EMIT selection_done();
}
void Scene_surface_mesh_item::invalidateOpenGLBuffers()
{
invalidate(ALL);
}
void Scene_surface_mesh_item::invalidate(Gl_data_names name)
{
Q_EMIT item_is_about_to_be_changed();
if(name.testFlag(GEOMETRY))
{
is_bbox_computed = false;
delete_aabb_tree(this);
d->smesh_->collect_garbage();
d->invalidate_stats();
d->flat_vertex_map_ready = false;
}
setBuffersFilled(false);
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool())
{
CGAL::Three::Viewer_interface* viewer = static_cast<CGAL::Three::Viewer_interface*>(v);
if(viewer == nullptr)
continue;
setBuffersInit(viewer, false);
viewer->update();
}
getTriangleContainer(1)->reset_vbos(name);
getTriangleContainer(0)->reset_vbos(name);
getEdgeContainer(1)->reset_vbos(name);
getEdgeContainer(0)->reset_vbos(name);
getPointContainer(0)->reset_vbos(name);
bool has_been_init = false;
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool())
{
CGAL::Three::Viewer_interface* viewer = static_cast<CGAL::Three::Viewer_interface*>(v);
if(!isInit(viewer))
{
initGL(viewer);
has_been_init = true;
}
}
if(!has_been_init)
processData(name);
if(!d->all_displayed)
d->killIds();
else
{
d->killIds();
if(d->vertices_displayed)
{
printVertexIds();
}
if(d->edges_displayed)
{
printEdgeIds();
}
if(d->faces_displayed)
{
printFaceIds();
}
}
}
QList<EPICK::Triangle_3> Scene_surface_mesh_item_priv::triangulate_primitive(face_descriptor fit,
EPICK::Vector_3 normal)
{
//The output list
QList<EPICK::Triangle_3> res;
//check if normal contains NaN values
if (normal.x() != normal.x() || normal.y() != normal.y() || normal.z() != normal.z())
{
qDebug()<<"Warning in triangulation of the selection item: normal contains NaN values and is not valid.";
return QList<EPICK::Triangle_3>();
}
//iterates on the internal faces to add the vertices to the positions
//and the normals to the appropriate vectors
auto f = [&](auto &ffit, auto&) {
if (ffit.info().is_external)
return;
res << EPICK::Triangle_3(ffit.vertex(0)->point(),
ffit.vertex(1)->point(),
ffit.vertex(2)->point());
};
try {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor> triangulation(fit, normal, smesh_);
triangulation.per_face(f);
}
catch (...) {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor, CGAL::Exact_intersections_tag> triangulation(fit, normal, smesh_);
triangulation.per_face(f);
}
return res;
}
void Scene_surface_mesh_item::invalidate_aabb_tree()
{
delete_aabb_tree(this);
}
bool Scene_surface_mesh_item::intersect_face(double orig_x,
double orig_y,
double orig_z,
double dir_x,
double dir_y,
double dir_z,
const face_descriptor &f)
{
typedef Input_facets_AABB_tree Tree;
typedef Tree::Object_and_primitive_id Object_and_primitive_id;
Tree* aabb_tree = static_cast<Tree*>(d->get_aabb_tree());
if(aabb_tree)
{
const EPICK::Point_3 ray_origin(orig_x, orig_y, orig_z);
const EPICK::Vector_3 ray_dir(dir_x, dir_y, dir_z);
const EPICK::Ray_3 ray(ray_origin, ray_dir);
typedef std::list<Object_and_primitive_id> Intersections;
Intersections intersections;
aabb_tree->all_intersections(ray, std::back_inserter(intersections));
Intersections::iterator closest = intersections.begin();
if(closest != intersections.end())
{
const EPICK::Point_3* closest_point =
CGAL::object_cast<EPICK::Point_3>(&closest->first);
for(Intersections::iterator
it = std::next(intersections.begin()),
end = intersections.end();
it != end; ++it)
{
if(! closest_point) {
closest = it;
}
else {
const EPICK::Point_3* it_point =
CGAL::object_cast<EPICK::Point_3>(&it->first);
if(it_point &&
(ray_dir * (*it_point - *closest_point)) < 0)
{
closest = it;
closest_point = it_point;
}
}
}
if(closest_point)
{
face_descriptor intersected_face = closest->second;
return intersected_face == f;
}
}
}
return false;
}
void Scene_surface_mesh_item::setItemIsMulticolor(bool b)
{
if(b)
{
d->fpatch_id_map = d->smesh_->add_property_map<face_descriptor,int>("f:patch_id", 1).first;
d->has_fcolors = true;
}
else
{
std::optional<SMesh::Property_map<face_descriptor, int>> fpatch_map = d->smesh_->property_map<face_descriptor, int>("f:patch_id");
if(fpatch_map.has_value())
{
d->fpatch_id_map = fpatch_map.value();
d->smesh_->remove_property_map(d->fpatch_id_map);
d->has_fcolors = false;
}
std::optional<SMesh::Property_map<face_descriptor, CGAL::IO::Color>> fpmap = d->smesh_->property_map<face_descriptor, CGAL::IO::Color >("f:color");
if(fpmap.has_value())
{
d->smesh_->remove_property_map(fpmap.value());
d->has_fcolors = false;
}
std::optional<SMesh::Property_map<vertex_descriptor, CGAL::IO::Color>> vpmap = d->smesh_->property_map<vertex_descriptor, CGAL::IO::Color >("v:color");
if(vpmap.has_value())
{
d->smesh_->remove_property_map(vpmap.value());
d->has_vcolors = false;
}
this->setProperty("NbPatchIds", 0); //for the joinandsplit_plugin
}
}
void Scene_surface_mesh_item::show_feature_edges(bool b)
{
d->has_feature_edges = b;
if(b)
{
d->e_is_feature_map = d->smesh_->add_property_map<boost::graph_traits<SMesh>::edge_descriptor,bool>("e:is_feature").first;
invalidate(COLORS);
itemChanged();
}
}
bool Scene_surface_mesh_item::isItemMulticolor()
{
return d->has_fcolors || d->has_vcolors;
}
bool Scene_surface_mesh_item::hasPatchIds()
{
return d->has_fpatch_id;
}
bool
Scene_surface_mesh_item::save(std::ostream& out) const
{
std::vector<std::string> internal_properties;
std::vector<std::string> vprop = d->smesh_->properties<vertex_descriptor>();
std::vector<std::string> fprop = d->smesh_->properties<face_descriptor>();
QString message = tr("Do you want to save the following properties ? \n");
for(auto s : vprop)
{
if (s.compare("v:normal") == 0)
{
message.append(tr(" - Vertex Normals\n"));
}
else if(s.compare("v:texcoord") == 0 )
{
message.append(tr(" - Vertex UV Coordinates\n"));
}
else if(s.compare("v:color") == 0)
{
message.append(tr(" - Vertex Colors\n"));
}
}
for(auto s : fprop)
{
if(s.compare("f:color") == 0)
{
message.append(tr(" - Face Colors\n"));
}
}
QMessageBox::StandardButton save_internal_properties =
QMessageBox::question(CGAL::Three::Three::mainWindow(), tr("Save Properties"), message);
QApplication::setOverrideCursor(Qt::WaitCursor);
out.precision(17);
if(save_internal_properties == QMessageBox::Yes)
{
out << *(d->smesh_);
}
else
{
CGAL::IO::internal::write_OFF_BGL(out,*d->smesh_, CGAL::parameters::default_values());
}
QApplication::restoreOverrideCursor();
return (bool) out;
}
bool
Scene_surface_mesh_item::load_obj(std::istream& in)
{
typedef SMesh::Point Point;
bool failed = !CGAL::IO::read_OBJ(in, *(d->smesh_));
if(failed)
{
in.clear();
in.seekg(0);
std::vector<Point> points;
std::vector<std::vector<std::size_t> > faces;
failed = !CGAL::IO::read_OBJ(in, points, faces);
if(!failed)
{
CGAL::Polygon_mesh_processing::repair_polygon_soup(points, faces);
CGAL::Polygon_mesh_processing::orient_polygon_soup(points, faces);
clear(*(d->smesh_));
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, faces, *(d->smesh_));
}
}
if((!failed) && !isEmpty())
{
invalidate(ALL);
return true;
}
return false;
}
bool
Scene_surface_mesh_item::save_obj(std::ostream& out) const
{
std::optional<SMesh::template Property_map<SMesh::Vertex_index, EPICK::Vector_3>> vnormals
= d->smesh_->template property_map<SMesh::Vertex_index, EPICK::Vector_3>("v:normal");
if(vnormals.has_value())
return CGAL::IO::write_OBJ(out, *(d->smesh_), CGAL::parameters::vertex_normal_map(vnormals.value()));
else
return CGAL::IO::write_OBJ(out, *(d->smesh_));
}
void
Scene_surface_mesh_item_priv::
invalidate_stats()
{
number_of_degenerated_faces = static_cast<unsigned int>(-1);
number_of_null_length_edges = static_cast<unsigned int>(-1);
has_nm_vertices = false;
volume = -std::numeric_limits<double>::infinity();
area = -std::numeric_limits<double>::infinity();
self_intersect = false;
genus = -1;
}
QString Scene_surface_mesh_item::computeStats(int type)
{
double minl, maxl, meanl, midl;
switch (type)
{
case MIN_LENGTH:
case MAX_LENGTH:
case MED_LENGTH:
case MEAN_LENGTH:
case NB_DEGENERATE_EDGES:
edges_length(d->smesh_, minl, maxl, meanl, midl, d->number_of_null_length_edges);
}
double mini(0), maxi(0), ave(0);
switch (type)
{
case MIN_ANGLE:
case MAX_ANGLE:
case MEAN_ANGLE:
angles(d->smesh_, mini, maxi, ave);
}
double min_area, max_area, med_area, mean_area;
switch (type)
{
case MIN_AREA:
case MAX_AREA:
case MEAN_AREA:
case MED_AREA:
if(!is_triangle_mesh(*d->smesh_))
{
return QString("n/a");
}
faces_area(d->smesh_, min_area, max_area, mean_area, med_area);
}
double min_altitude, min_ar, max_ar, mean_ar;
switch (type)
{
case MIN_ALTITUDE:
case MIN_ASPECT_RATIO:
case MAX_ASPECT_RATIO:
case MEAN_ASPECT_RATIO:
if(!is_triangle_mesh(*d->smesh_))
{
return QString("n/a");
}
faces_aspect_ratio(d->smesh_, min_altitude, min_ar, max_ar, mean_ar);
}
if(type == HAS_NM_VERTICES)
{
d->has_nm_vertices = false;
typedef boost::function_output_iterator<CGAL::internal::Throw_at_output> OutputIterator;
try{
CGAL::Polygon_mesh_processing::non_manifold_vertices(*d->smesh_, OutputIterator());
}
catch( CGAL::internal::Throw_at_output_exception& )
{
d->has_nm_vertices = true;
}
}
switch(type)
{
case NB_VERTICES:
return QString::number(num_vertices(*d->smesh_));
case NB_ISOLATED_VERTICES:
return QString::number(this->getNbIsolatedvertices());
case HAS_NM_VERTICES:
{
if(d->has_nm_vertices)
return QString("Yes");
return QString("No");
}
case NB_FACETS:
return QString::number(num_faces(*d->smesh_));
case NB_CONNECTED_COMPOS:
{
boost::vector_property_map<int,
boost::property_map<SMesh, boost::face_index_t>::type>
fccmap(static_cast<unsigned>(num_faces(*(d->smesh_))), get(boost::face_index, *(d->smesh_)));
return QString::number(CGAL::Polygon_mesh_processing::connected_components(*(d->smesh_), fccmap));
}
case NB_BORDER_EDGES:
{
int i=0;
for(halfedge_descriptor hd : halfedges(*d->smesh_))
{
if(is_border(hd, *d->smesh_))
++i;
}
return QString::number(i);
}
case NB_EDGES:
return QString::number(num_halfedges(*d->smesh_) / 2);
case NB_DEGENERATE_FACES:
{
if(is_triangle_mesh(*d->smesh_))
{
if (d->number_of_degenerated_faces == static_cast<unsigned int>(-1))
d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_);
return QString::number(d->number_of_degenerated_faces);
}
else
return QString("n/a");
}
case AREA:
{
if(is_triangle_mesh(*d->smesh_))
{
if(d->area == -std::numeric_limits<double>::infinity())
d->area = CGAL::Polygon_mesh_processing::area(*(d->smesh_));
return QString::number(d->area);
}
else
return QString("n/a");
}
case VOLUME:
{
if(is_triangle_mesh(*d->smesh_) && is_closed(*d->smesh_))
{
if (d->volume == -std::numeric_limits<double>::infinity())
d->volume = CGAL::Polygon_mesh_processing::volume(*(d->smesh_));
return QString::number(d->volume);
}
else
return QString("n/a");
}
case SELFINTER:
{
//todo : add a test about cache validity
if(is_triangle_mesh(*d->smesh_))
d->self_intersect = CGAL::Polygon_mesh_processing::does_self_intersect<CGAL::Parallel_if_available_tag>(*(d->smesh_));
if (d->self_intersect)
return QString("Yes");
else if(is_triangle_mesh(*d->smesh_))
return QString("No");
else
return QString("n/a");
}
case GENUS:
{
if(!is_closed(*d->smesh_))
{
return QString("n/a");
}
else if(d->genus == -1)
{
std::ptrdiff_t s(num_vertices(*d->smesh_)),
a(num_halfedges(*d->smesh_)/2),
f(num_faces(*d->smesh_));
d->genus = 1.0 - double(s-a+f)/2.0;
}
if(d->genus < 0)
{
return QString("n/a");
}
else
{
return QString::number(d->genus);
}
}
case MIN_LENGTH:
return QString::number(minl);
case MAX_LENGTH:
return QString::number(maxl);
case MED_LENGTH:
return QString::number(midl);
case MEAN_LENGTH:
return QString::number(meanl);
case NB_DEGENERATE_EDGES:
return QString::number(d->number_of_null_length_edges);
case MIN_ANGLE:
return QString::number(mini);
case MAX_ANGLE:
return QString::number(maxi);
case MEAN_ANGLE:
return QString::number(ave);
case NB_HOLES:
return QString::number(nb_holes(d->smesh_));
case MIN_AREA:
return QString::number(min_area);
case MAX_AREA:
return QString::number(max_area);
case MED_AREA:
return QString::number(med_area);
case MEAN_AREA:
return QString::number(mean_area);
case MIN_ALTITUDE:
return QString::number(min_altitude);
case MIN_ASPECT_RATIO:
return QString::number(min_ar);
case MAX_ASPECT_RATIO:
return QString::number(max_ar);
case MEAN_ASPECT_RATIO:
return QString::number(mean_ar);
case IS_PURE_TRIANGLE:
if(is_triangle_mesh(*d->smesh_))
return QString("yes");
else
return QString("no");
case IS_PURE_QUAD:
if (is_quad_mesh(*d->smesh_))
return QString("yes");
else
return QString("no");
}
return QString();
}
CGAL::Three::Scene_item::Header_data Scene_surface_mesh_item::header() const
{
CGAL::Three::Scene_item::Header_data data;
//categories
data.categories.append(std::pair<QString,int>(QString("Properties"),9));
data.categories.append(std::pair<QString,int>(QString("Vertices"),2));
data.categories.append(std::pair<QString,int>(QString("Faces"),10));
data.categories.append(std::pair<QString,int>(QString("Edges"),7));
data.categories.append(std::pair<QString,int>(QString("Angles"),3));
//titles
data.titles.append(QString("#Connected Components"));
data.titles.append(QString("#Connected Components of the Boundary"));
data.titles.append(QString("Genus"));
data.titles.append(QString("Pure Triangle"));
data.titles.append(QString("Pure Quad"));
data.titles.append(QString("Area"));
data.titles.append(QString("Volume"));
data.titles.append(QString("Self-Intersecting"));
data.titles.append(QString("Has Non-manifold Vertices"));
data.titles.append(QString("#Vertices"));
data.titles.append(QString("#Isolated Vertices"));
data.titles.append(QString("#Faces"));
data.titles.append(QString("#Degenerate Faces"));
data.titles.append(QString("Min Area"));
data.titles.append(QString("Max Area"));
data.titles.append(QString("Median Area"));
data.titles.append(QString("Mean Area"));
data.titles.append(QString("Min Altitude"));
data.titles.append(QString("Min Aspect-Ratio"));
data.titles.append(QString("Max Aspect-Ratio"));
data.titles.append(QString("Mean Aspect-Ratio"));
data.titles.append(QString("#Edges"));
data.titles.append(QString("#Border Edges"));
data.titles.append(QString("#Degenerate Edges"));
data.titles.append(QString("Minimum Length"));
data.titles.append(QString("Maximum Length"));
data.titles.append(QString("Median Length"));
data.titles.append(QString("Mean Length"));
data.titles.append(QString("Minimum"));
data.titles.append(QString("Maximum"));
data.titles.append(QString("Average"));
return data;
}
void Scene_surface_mesh_item::zoomToPosition(const QPoint &point, CGAL::Three::Viewer_interface *viewer) const
{
typedef Input_facets_AABB_tree Tree;
typedef Tree::Intersection_and_primitive_id<EPICK::Ray_3>::Type Intersection_and_primitive_id;
Tree* aabb_tree = static_cast<Input_facets_AABB_tree*>(d->get_aabb_tree());
if(aabb_tree) {
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
//find clicked facet
bool found = false;
CGAL::qglviewer::Vec point_under = viewer->camera()->pointUnderPixel(point,found);
EPICK::Point_3 ray_origin;
CGAL::qglviewer::Vec dir;
if(viewer->camera()->type() == CGAL::qglviewer::Camera::PERSPECTIVE)
{
ray_origin = EPICK::Point_3(viewer->camera()->position().x - offset.x,
viewer->camera()->position().y - offset.y,
viewer->camera()->position().z - offset.z);
dir = point_under - viewer->camera()->position();
}
else
{
dir = viewer->camera()->viewDirection();
ray_origin = EPICK::Point_3(point_under.x - dir.x,
point_under.y - dir.y,
point_under.z - dir.z);
}
const EPICK::Vector_3 ray_dir(dir.x, dir.y, dir.z);
const EPICK::Ray_3 ray(ray_origin, ray_dir);
typedef std::list<Intersection_and_primitive_id> Intersections;
Intersections intersections;
aabb_tree->all_intersections(ray, std::back_inserter(intersections));
if(!intersections.empty()) {
Intersections::iterator closest = intersections.begin();
const EPICK::Point_3* closest_point =
std::get_if<EPICK::Point_3>(&closest->first);
for(Intersections::iterator
it = std::next(intersections.begin()),
end = intersections.end();
it != end; ++it)
{
if(! closest_point) {
closest = it;
}
else {
const EPICK::Point_3* it_point =
std::get_if<EPICK::Point_3>(&it->first);
if(it_point &&
(ray_dir * (*it_point - *closest_point)) < 0)
{
closest = it;
closest_point = it_point;
}
}
}
if(closest_point) {
SMesh::Property_map<vertex_descriptor, SMesh::Point> positions =
d->smesh_->points();
face_descriptor selected_fh = closest->second;
//compute new position and orientation
EPICK::Vector_3 face_normal = CGAL::Polygon_mesh_processing::
compute_face_normal(selected_fh,
*d->smesh_);
double x(0), y(0), z(0),
xmin(std::numeric_limits<double>::infinity()), ymin(std::numeric_limits<double>::infinity()), zmin(std::numeric_limits<double>::infinity()),
xmax(-std::numeric_limits<double>::infinity()), ymax(-std::numeric_limits<double>::infinity()), zmax(-std::numeric_limits<double>::infinity());
int total(0);
for(vertex_descriptor vh : vertices_around_face(halfedge(selected_fh, *d->smesh_), *d->smesh_))
{
x+=positions[vh].x();
y+=positions[vh].y();
z+=positions[vh].z();
if(positions[vh].x() < xmin)
xmin = positions[vh].x();
if(positions[vh].y() < ymin)
ymin = positions[vh].y();
if(positions[vh].z() < zmin)
zmin = positions[vh].z();
if(positions[vh].x() > xmax)
xmax = positions[vh].x();
if(positions[vh].y() > ymax)
ymax = positions[vh].y();
if(positions[vh].z() > zmax)
zmax = positions[vh].z();
++total;
}
EPICK::Point_3 centroid(x/total + offset.x,
y/total + offset.y,
z/total + offset.z);
CGAL::qglviewer::Quaternion new_orientation(CGAL::qglviewer::Vec(0,0,-1),
CGAL::qglviewer::Vec(-face_normal.x(), -face_normal.y(), -face_normal.z()));
double max_side = (std::max)((std::max)(xmax-xmin, ymax-ymin),
zmax-zmin);
//put the camera in way we are sure the longest side is entirely visible on the screen
//See openGL's frustum definition
double factor = CGAL::abs(max_side/(tan(viewer->camera()->aspectRatio()/
(viewer->camera()->fieldOfView()/2))));
EPICK::Point_3 new_pos = centroid + factor*face_normal ;
viewer->camera()->setSceneCenter(CGAL::qglviewer::Vec(centroid.x(),
centroid.y(),
centroid.z()));
viewer->moveCameraToCoordinates(QString("%1 %2 %3 %4 %5 %6 %7").arg(new_pos.x())
.arg(new_pos.y())
.arg(new_pos.z())
.arg(new_orientation[0])
.arg(new_orientation[1])
.arg(new_orientation[2])
.arg(new_orientation[3]));
}
}
}
}
void Scene_surface_mesh_item::resetColors()
{
setItemIsMulticolor(false);
if(d->has_feature_edges){
for(boost::graph_traits<SMesh>::edge_descriptor e : edges(*d->smesh_)){
put(d->e_is_feature_map, e, false);
}
d->has_feature_edges = false;
}
invalidate(COLORS);
itemChanged(); // @fixme really shouldn't call something that strong
}
QMenu* Scene_surface_mesh_item::contextMenu()
{
QMenu* menu = Scene_item::contextMenu();
QAction* actionResetColor=
menu->findChild<QAction*>(tr("actionResetColor"));
if(isItemMulticolor() || d->has_fpatch_id)
{
if(!actionResetColor)
{
actionResetColor = menu->addAction(tr("Reset Colors"));
actionResetColor->setObjectName("actionResetColor");
}
connect(actionResetColor, SIGNAL(triggered()),
this, SLOT(resetColors()));
}
else if(actionResetColor)
{
menu->removeAction(actionResetColor);
actionResetColor->deleteLater();
}
const char* prop_name = "Menu modified by Scene_surface_mesh_item.";
bool menuChanged = menu->property(prop_name).toBool();
if(!menuChanged) {
QMenu *container = new QMenu(tr("Alpha value"));
container->menuAction()->setProperty("is_groupable", true);
QWidgetAction *sliderAction = new QWidgetAction(nullptr);
sliderAction->setDefaultWidget(d->alphaSlider);
connect(d->alphaSlider, &QSlider::valueChanged,
[this](){redraw();});
container->addAction(sliderAction);
menu->addMenu(container);
menu->addSeparator();
QAction* actionPrintVertices=
menu->addAction(tr("Display Vertices Ids"));
actionPrintVertices->setCheckable(true);
actionPrintVertices->setObjectName("actionPrintVertices");
connect(actionPrintVertices, SIGNAL(triggered(bool)),
this, SLOT(showVertices(bool)));
QAction* actionPrintEdges=
menu->addAction(tr("Display Edges Ids"));
actionPrintEdges->setCheckable(true);
actionPrintEdges->setObjectName("actionPrintEdges");
connect(actionPrintEdges, SIGNAL(triggered(bool)),
this, SLOT(showEdges(bool)));
QAction* actionPrintFaces=
menu->addAction(tr("Display Faces Ids"));
actionPrintFaces->setCheckable(true);
actionPrintFaces->setObjectName("actionPrintFaces");
connect(actionPrintFaces, SIGNAL(triggered(bool)),
this, SLOT(showFaces(bool)));
QAction* actionZoomToId=
menu->addAction(tr("Zoom to Index"));
actionZoomToId->setObjectName("actionZoomToId");
connect(actionZoomToId, &QAction::triggered,
this, &Scene_surface_mesh_item::zoomToId);
setProperty("menu_changed", true);
menu->setProperty(prop_name, true);
}
QAction* action = menu->findChild<QAction*>("actionPrintVertices");
if(action) action->setChecked(d->vertices_displayed);
action = menu->findChild<QAction*>("actionPrintEdges");
if(action) action->setChecked(d->edges_displayed);
action = menu->findChild<QAction*>("actionPrintFaces");
if(action) action->setChecked(d->faces_displayed);
return menu;
}
void Scene_surface_mesh_item::printPrimitiveId(QPoint point, CGAL::Three::Viewer_interface *viewer)
{
typedef Input_facets_AABB_tree Tree;
Tree* aabb_tree = static_cast<Input_facets_AABB_tree*>(d->get_aabb_tree());
if(!aabb_tree)
return;
face_descriptor selected_fh;
EPICK::Point_3 pt_under;
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
if(find_primitive_id(point, aabb_tree, viewer, selected_fh, pt_under))
d->fillTargetedIds(selected_fh, pt_under, viewer, offset);
}
void Scene_surface_mesh_item_priv::fillTargetedIds(const face_descriptor &selected_fh,
const EPICK::Point_3& pt_under,
CGAL::Three::Viewer_interface *viewer,
const CGAL::qglviewer::Vec& offset)
{
all_displayed = false;
compute_displayed_ids(*smesh_,
viewer,
selected_fh,
pt_under,
offset,
textVItems,
textEItems,
textFItems,
&targeted_id);
if(vertices_displayed
&& !textVItems->isEmpty())
item->showVertices(true);
if(edges_displayed
&& !textEItems->isEmpty())
item->showEdges(true);
if(faces_displayed
&& !textFItems->isEmpty())
item->showFaces(true);
}
bool Scene_surface_mesh_item::printVertexIds() const
{
if(d->vertices_displayed)
{
d->all_displayed = true;
return ::printVertexIds(*d->smesh_,
d->textVItems);
}
return true;
}
bool Scene_surface_mesh_item::printEdgeIds() const
{
if(d->edges_displayed)
{
d->all_displayed = true;
return ::printEdgeIds(*d->smesh_,
d->textEItems);
}
return true;
}
bool Scene_surface_mesh_item::printFaceIds() const
{
if(d->faces_displayed)
{
d->all_displayed = true;
return ::printFaceIds(*d->smesh_,
d->textFItems);
}
return true;
}
void Scene_surface_mesh_item_priv::killIds()
{
CGAL::Three::Viewer_interface* viewer =
qobject_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first());
deleteIds(viewer,
textVItems,
textEItems,
textFItems,
&targeted_id);
all_displayed = false;
}
void Scene_surface_mesh_item::printAllIds()
{
static bool all_ids_displayed = false;
all_ids_displayed = !all_ids_displayed;
if(all_ids_displayed )
{
bool s1(printVertexIds()),
s2(printEdgeIds()),
s3(printFaceIds());
if((s1 && s2 && s3))
{
for(CGAL::QGLViewer* viewer : CGAL::QGLViewer::QGLViewerPool()){
viewer->update();
}
return;
}
}
d->killIds();
}
bool Scene_surface_mesh_item::testDisplayId(double x, double y, double z, CGAL::Three::Viewer_interface* viewer)const
{
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
EPICK::Point_3 src(x - offset.x,
y - offset.y,
z - offset.z);
CGAL::qglviewer::Camera* cam = viewer->camera();
const QVector3D& scaler = viewer->scaler();
EPICK::Point_3 dest( cam->position().x/scaler.x() - offset.x,
cam->position().y/scaler.y() - offset.y,
cam->position().z/scaler.z() - offset.z);
EPICK::Vector_3 v(src,dest);
EPICK::Vector_3 dir(cam->viewDirection().x,
cam->viewDirection().y,
cam->viewDirection().z);
if(-CGAL::scalar_product(v, dir) < cam->zNear()) //if src is behind the near plane, don't display.
return false;
v = 0.01*v;
EPICK::Point_3 point = src;
point = point + v;
EPICK::Segment_3 query(point, dest);
return !static_cast<Input_facets_AABB_tree*>(d->get_aabb_tree())->do_intersect(query);
}
void Scene_surface_mesh_item::showVertices(bool b)
{
if(b)
if(d->textVItems->isEmpty())
{
d->vertices_displayed = b;
printVertexIds();
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->addTextList(d->textVItems);
viewer->update();
}
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->removeTextList(d->textVItems);
viewer->update();
}
}
d->vertices_displayed = b;
}
void Scene_surface_mesh_item::showEdges(bool b)
{
if(b)
{
if(d->textEItems->isEmpty())
{
d->edges_displayed = b;
printEdgeIds();
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->addTextList(d->textEItems);
viewer->update();
}
}
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->removeTextList(d->textEItems);
viewer->update();
}
}
d->edges_displayed = b;
}
void Scene_surface_mesh_item::showFaces(bool b)
{
if(b)
{
if(d->textFItems->isEmpty())
{
d->faces_displayed = b;
printFaceIds();
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->addTextList(d->textFItems);
viewer->update();
}
}
}
else
{
for(CGAL::QGLViewer* v : CGAL::QGLViewer::QGLViewerPool()){
CGAL::Three::Viewer_interface* viewer = dynamic_cast<CGAL::Three::Viewer_interface*>(v);
TextRenderer *renderer = viewer->textRenderer();
renderer->removeTextList(d->textFItems);
viewer->update();
}
}
d->faces_displayed = b;
}
void Scene_surface_mesh_item::showPrimitives(bool)
{
printAllIds();
}
void Scene_surface_mesh_item::zoomToId()
{
face_descriptor selected_fh;
bool ok;
QString text = QInputDialog::getText(QApplication::activeWindow(), tr("Zoom to Index"),
tr("Simplex"), QLineEdit::Normal,
tr("v0"), &ok);
if(!ok)
return;
CGAL::Three::Viewer_interface* viewer = CGAL::Three::Three::activeViewer();
Point_3 p;
QString id = text.right(text.length()-1);
int return_value = ::zoomToId(*d->smesh_, text, viewer, selected_fh, p);
switch(return_value)
{
case 1:
QMessageBox::warning(QApplication::activeWindow(),
"ERROR",
tr("Input must be of the form [v/e/f][int]")
);
return;
case 2:
QMessageBox::warning(QApplication::activeWindow(),
"ERROR",
tr("No vertex with id %1").arg(id)
);
return;
case 3:
QMessageBox::warning(QApplication::activeWindow(),
"ERROR",
tr("No edge with id %1").arg(id)
);
return;
case 4:
QMessageBox::warning(QApplication::activeWindow(),
"ERROR",
tr("No face with id %1").arg(id)
);
return;
default: //case 0
d->fillTargetedIds(selected_fh, p, viewer, viewer->offset());
break;
}
}
bool Scene_surface_mesh_item::shouldDisplayIds(CGAL::Three::Scene_item *current_item) const
{
return this == current_item;
}
float Scene_surface_mesh_item::alpha() const
{
if(!d->alphaSlider)
return 1.0f;
return static_cast<float>(d->alphaSlider->value()) / 255.0f;
}
void Scene_surface_mesh_item::setAlpha(int alpha)
{
if(!d->alphaSlider)
d->compute_elements(Scene_item_rendering_helper::ALL);
d->alphaSlider->setValue(alpha);
redraw();
}
QSlider* Scene_surface_mesh_item::alphaSlider() { return d->alphaSlider; }
void Scene_surface_mesh_item::computeElements() const
{
d->compute_elements(ALL);
setBuffersFilled(true);
}
void
Scene_surface_mesh_item::initializeBuffers(CGAL::Three::Viewer_interface* viewer) const
{
const_cast<Scene_surface_mesh_item*>(this)->//temporary, until the drawing pipeline is not const anymore.
d->initializeBuffers(viewer);
}
void Scene_surface_mesh_item::copyProperties(Scene_item *item)
{
Scene_surface_mesh_item* sm_item = qobject_cast<Scene_surface_mesh_item*>(item);
if(!sm_item)
return;
int value = sm_item->alphaSlider()->value();
alphaSlider()->setValue(value);
}
void Scene_surface_mesh_item::computeItemColorVectorAutomatically(bool b)
{
this->setProperty("recompute_colors",b);
}
void write_in_vbo(Vbo* vbo,
cgal_gl_data* data,
std::size_t size)
{
vbo->bind();
vbo->vbo.write(static_cast<int>((3*size)*sizeof(cgal_gl_data)),
data,
static_cast<int>(3*sizeof(cgal_gl_data)));
vbo->release();
}
//only works on indexed data
void Scene_surface_mesh_item::updateVertex(vertex_descriptor vh)
{
if(!d->flat_vertex_map_ready)
fill_flat_vertex_map();
const CGAL::qglviewer::Vec offset =
static_cast<CGAL::Three::Viewer_interface*>(
CGAL::QGLViewer::QGLViewerPool().first())->offset();
std::size_t id = vh;
cgal_gl_data new_point[3];
Point_3 p = face_graph()->point(vh);
for(int i=0; i<3; ++i)
new_point[i]=p[i]+offset[i];
write_in_vbo(getTriangleContainer(0)->getVbo(Tri::Smooth_vertices),
new_point,
id);
write_in_vbo(
getPointContainer(0)->getVbo(Pt::Vertices),
new_point,id);
write_in_vbo(
getEdgeContainer(0)->getVbo(Ed::Vertices),
new_point,id);
for(const auto v_it : CGAL::vertices_around_target(vh, *face_graph()))
{
EPICK::Vector_3 n = CGAL::Polygon_mesh_processing::compute_vertex_normal(v_it, *face_graph());
cgal_gl_data new_n[3];
for(int i=0; i<3; ++i)
new_n[i]=n[i];
id = v_it;
write_in_vbo(
getTriangleContainer(0)->getVbo(Tri::Smooth_normals),
new_n,id);
}
//flat data now
for(const auto& id : d->flat_vertices_map[vh])
{
write_in_vbo(getTriangleContainer(1)->getVbo(Tri::Flat_vertices),
new_point,
id);
}
for(const auto f_it : CGAL::faces_around_target( halfedge(vh, *face_graph()), *face_graph()))
{
if (f_it == boost::graph_traits<SMesh>::null_face()) continue;
EPICK::Vector_3 n = CGAL::Polygon_mesh_processing::compute_face_normal(f_it, *face_graph());
cgal_gl_data new_n[3];
for(int i=0; i<3; ++i)
new_n[i]=n[i];
for(std::size_t id = d->cumul_id[f_it]; id < d->cumul_id[f_it+1]; ++id)
{
write_in_vbo(
getTriangleContainer(1)->getVbo(Tri::Flat_normals),
new_n, id);
}
}
d->ids_need_update = true;
redraw();
}
void Scene_surface_mesh_item::updateIds(vertex_descriptor vh)
{
if(d->ids_need_update &&
(d->faces_displayed || d->vertices_displayed || d->edges_displayed))
{
invalidate_aabb_tree();
if(d->all_displayed)
{
d->killIds();
d->all_displayed = true;
::printVertexIds(*d->smesh_, d->textVItems);
}
else
{
d->fillTargetedIds(face(halfedge(vh, *d->smesh_), *d->smesh_),
face_graph()->point(vh), CGAL::Three::Three::mainViewer(), CGAL::Three::Three::mainViewer()->offset());
}
d->ids_need_update = false;
}
}
void Scene_surface_mesh_item::fill_flat_vertex_map()
{
typedef EPICK::Point_3 Point;
typedef CGAL::Surface_mesh<Point> SMesh;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
typedef CGAL::Buffer_for_vao CPF;
if(d->flat_vertex_map_ready)
return;
SMesh::Property_map<face_descriptor, EPICK::Vector_3 > fnormals =
face_graph()->add_property_map<face_descriptor, EPICK::Vector_3 >("f:normal").first;
d->flat_vertices_map.clear();
d->flat_vertices_map.resize(face_graph()->number_of_vertices());
d->cumul_id.clear();
std::size_t counter = 0;
for(face_descriptor fd : faces(*face_graph()))
{
d->cumul_id.push_back(counter);
if(is_triangle(halfedge(fd,*face_graph()),*face_graph()))
{
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *face_graph()),*face_graph()))
{
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
}
}
else
{
std::vector<Point> facet_points;
for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, *face_graph()),*face_graph()))
{
facet_points.push_back(face_graph()->points()[target(hd, *face_graph())]);
}
bool is_convex = CPF::is_facet_convex(facet_points, fnormals[fd]);
if(is_convex && is_quad(halfedge(fd,*face_graph()),*face_graph()) )
{
//1st half
halfedge_descriptor hd = halfedge(fd, *face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
hd = next(halfedge(fd, *face_graph()),*face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
hd = next(next(halfedge(fd, *face_graph()),*face_graph()), *face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
//2nd half
hd = halfedge(fd, *face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
hd = next(next(halfedge(fd, *face_graph()),*face_graph()), *face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
hd = prev(halfedge(fd, *face_graph()), *face_graph());
d->flat_vertices_map[source(hd, *face_graph())].push_back(counter++);
}
else if(is_convex)
{
SMesh::Halfedge_around_face_circulator he(halfedge(fd, *face_graph()), *face_graph());
SMesh::Halfedge_around_face_circulator he_end = he;
while(next(*he, *face_graph()) != prev(*he_end, *face_graph()))
{
++he;
vertex_descriptor v0(target(*he_end, *face_graph())),
v1(target(*he, *face_graph())),
v2(target(next(*he, *face_graph()), *face_graph()));
d->flat_vertices_map[v0].push_back(counter++);
d->flat_vertices_map[v1].push_back(counter++);
d->flat_vertices_map[v2].push_back(counter++);
}
}
else
{
//Computes the normal of the facet
EPICK::Vector_3 normal = fnormals[fd];
if(normal == CGAL::NULL_VECTOR)
{
boost::graph_traits<SMesh>::halfedge_descriptor start = prev(halfedge(fd, *face_graph()), *face_graph());
boost::graph_traits<SMesh>::halfedge_descriptor hd = halfedge(fd, *face_graph());
boost::graph_traits<SMesh>::halfedge_descriptor next_=next(hd, *face_graph());
do
{
const Point_3& pa = face_graph()->point(target(hd, *face_graph()));
const Point_3& pb = face_graph()->point(target(next_, *face_graph()));
const Point_3& pc = face_graph()->point(target(prev(hd, *face_graph()), *face_graph()));
if (!CGAL::collinear (pa, pb, pc))
{
normal = CGAL::cross_product(pb-pa, pc -pa);
break;
}
next_ =next(next_, *face_graph());
}while(next_ != start);
if (normal == CGAL::NULL_VECTOR) // No normal could be computed, return
{
qDebug()<<"Warning : normal is not valid. Facet not displayed";
return;
}
}
//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;
}
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);
auto f = [&](auto ffit, auto &v2v) {
if (ffit.info().is_external)
return;
d->flat_vertices_map[v2v[ffit.vertex(0)]].push_back(counter++);
d->flat_vertices_map[v2v[ffit.vertex(1)]].push_back(counter++);
d->flat_vertices_map[v2v[ffit.vertex(2)]].push_back(counter++);
};
try {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor> triangulation(fd, normal, face_graph(), offset);
triangulation.per_face(f);
}
catch (...) {
FacetTriangulator<SMesh, EPICK, boost::graph_traits<SMesh>::vertex_descriptor, CGAL::Exact_intersections_tag> triangulation(fd, normal, face_graph(), offset);
triangulation.per_face(f);
}
}
}
}
d->cumul_id.push_back(counter);
d->flat_vertex_map_ready = true;
}