mirror of https://github.com/CGAL/cgal
616 lines
19 KiB
C++
616 lines
19 KiB
C++
#include <CGAL/Three/Viewer_interface.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/Buffer_for_vao.h>
|
|
|
|
#include "Color_map.h"
|
|
#include "Scene_lcc_item.h"
|
|
|
|
//todo : create a struct for facets containing useful infos for drawing and their volume, and fill it during computeElements().
|
|
using namespace CGAL::Three;
|
|
typedef Triangle_container Tri;
|
|
typedef Edge_container Ec;
|
|
typedef Point_container Pc;
|
|
typedef Viewer_interface Vi;
|
|
|
|
typedef Scene_lcc_item::LCC::Dart_const_descriptor Dart_const_descriptor;
|
|
typedef Scene_lcc_item::LCC::Dart_descriptor Dart_descriptor;
|
|
typedef Scene_lcc_item::LCC::Point Point;
|
|
|
|
struct Facet{
|
|
Facet():normal(Scene_lcc_item::LCC::Vector(0,0,0)){}
|
|
Dart_const_descriptor f_handle;
|
|
std::vector<Point> points;
|
|
Scene_lcc_item::LCC::Vector normal;
|
|
std::size_t volume_id;
|
|
std::size_t size() { return points.size(); }
|
|
};
|
|
|
|
|
|
struct lcc_priv{
|
|
|
|
Scene_lcc_item::LCC lcc;
|
|
std::vector<float> faces;
|
|
std::vector<float> lines;
|
|
std::vector<float> vertices;
|
|
std::vector<float> colors;
|
|
|
|
std::vector<Facet> facets;
|
|
std::size_t nb_volumes;
|
|
bool is_mono_color;
|
|
|
|
std::size_t nb_lines, nb_vertices, nb_faces;
|
|
|
|
lcc_priv(const Scene_lcc_item::LCC& lcc)
|
|
:lcc(lcc), is_mono_color(true){}
|
|
|
|
bool compute_face(Dart_const_descriptor dh, Facet& f)
|
|
{
|
|
f.f_handle = dh;
|
|
const CGAL::qglviewer::Vec offset = static_cast<CGAL::Three::Viewer_interface*>(CGAL::QGLViewer::QGLViewerPool().first())->offset();
|
|
// We fill only closed faces.
|
|
Dart_const_descriptor cur=dh;
|
|
Dart_const_descriptor min=dh;
|
|
do
|
|
{
|
|
if (!lcc.is_next_exist(cur)) return false; // open face=>not filled
|
|
if (cur<min) min=cur;
|
|
cur=lcc.next(cur);
|
|
}
|
|
while(cur!=dh);
|
|
|
|
cur=dh;
|
|
do
|
|
{
|
|
f.points.push_back(lcc.point(cur));
|
|
cur=lcc.next(cur);
|
|
}
|
|
while(cur!=dh);
|
|
for (std::size_t i = 0; i < f.size() ; ++ i){
|
|
const Point& pa = f.points[i];
|
|
const Point& pb = f.points[(i+1)%f.size()];
|
|
double x = f.normal.x() + (pa.y()-pb.y())*(pa.z()+pb.z());
|
|
double y = f.normal.y() + (pa.z()-pb.z())*(pa.x()+pb.x());
|
|
double z = f.normal.z() + (pa.x()-pb.x())*(pa.y()+pb.y());
|
|
f.normal = Scene_lcc_item::LCC::Vector(x,y,z);
|
|
}
|
|
|
|
if (f.size()==3)
|
|
{
|
|
for(auto pt = f.points.begin();
|
|
pt != f.points.end();
|
|
++pt)
|
|
{
|
|
faces.push_back(pt->x() + offset.x);
|
|
faces.push_back(pt->y() + offset.y);
|
|
faces.push_back(pt->z() + offset.z);
|
|
}
|
|
}
|
|
else if (CGAL::Buffer_for_vao::is_facet_convex(f.points,f.normal))
|
|
{
|
|
if (f.size()==4)
|
|
{
|
|
Point p = f.points[0];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
p = f.points[1];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
p = f.points[2];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
|
|
p = f.points[0];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
p = f.points[2];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
p = f.points[3];
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
}
|
|
else
|
|
{
|
|
for(std::size_t i=1; i<f.size()-1; ++i)
|
|
{
|
|
Point& p0 = f.points[0];
|
|
Point& p1 = f.points[i];
|
|
Point& p2 = f.points[i+1];
|
|
|
|
// (1) add points
|
|
faces.push_back(p0.x() + offset.x);
|
|
faces.push_back(p0.y() + offset.y);
|
|
faces.push_back(p0.z() + offset.z);
|
|
faces.push_back(p1.x() + offset.x);
|
|
faces.push_back(p1.y() + offset.y);
|
|
faces.push_back(p1.z() + offset.z);
|
|
faces.push_back(p2.x() + offset.x);
|
|
faces.push_back(p2.y() + offset.y);
|
|
faces.push_back(p2.z() + offset.z);
|
|
}
|
|
} // Convex face with > 4 vertices
|
|
}
|
|
else
|
|
{
|
|
struct Vertex_info
|
|
{
|
|
Scene_lcc_item::LCC::Vector v;
|
|
std::size_t index;
|
|
};
|
|
|
|
struct Face_info
|
|
{
|
|
bool exist_edge[3];
|
|
bool is_external;
|
|
bool is_process;
|
|
};
|
|
|
|
typedef CGAL::Projection_traits_3<CGAL::Exact_predicates_inexact_constructions_kernel> P_traits;
|
|
typedef CGAL::Triangulation_vertex_base_with_info_2<Vertex_info, P_traits> Vb;
|
|
typedef CGAL::Triangulation_face_base_with_info_2<Face_info, P_traits> Fb1;
|
|
typedef CGAL::Constrained_triangulation_face_base_2<P_traits, Fb1> Fb;
|
|
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
|
|
typedef CGAL::Exact_predicates_tag Itag;
|
|
typedef CGAL::Constrained_Delaunay_triangulation_2<P_traits, TDS, Itag> CDT;
|
|
|
|
P_traits cdt_traits(f.normal);
|
|
CDT cdt(cdt_traits);
|
|
// (1) We insert all the edges as constraint in the CDT.
|
|
typename CDT::Vertex_handle previous=nullptr, first=nullptr;
|
|
for (unsigned int i=0; i<f.size(); ++i)
|
|
{
|
|
typename CDT::Vertex_handle vh = cdt.insert(f.points[i]);
|
|
if(first==nullptr)
|
|
{ first=vh; }
|
|
vh->info().v=f.normal;
|
|
|
|
if(previous!=nullptr && previous!=vh)
|
|
{ cdt.insert_constraint(previous, vh); }
|
|
previous=vh;
|
|
}
|
|
|
|
if (previous!=nullptr && previous!=first)
|
|
{ cdt.insert_constraint(previous, first); }
|
|
|
|
// (2) We mark all external triangles
|
|
// (2.1) We initialize is_external and is_process values
|
|
for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(),
|
|
fitend = cdt.all_faces_end(); fit!=fitend; ++fit)
|
|
{
|
|
fit->info().is_external = true;
|
|
fit->info().is_process = false;
|
|
}
|
|
// (2.2) We check if the facet is external or internal
|
|
std::queue<typename CDT::Face_handle> face_queue;
|
|
typename CDT::Face_handle face_internal = nullptr;
|
|
if (cdt.infinite_vertex()->face()!=nullptr)
|
|
{ face_queue.push(cdt.infinite_vertex()->face()); }
|
|
while(!face_queue.empty())
|
|
{
|
|
typename CDT::Face_handle fh = face_queue.front();
|
|
face_queue.pop();
|
|
if(!fh->info().is_process)
|
|
{
|
|
fh->info().is_process = true;
|
|
for(int i=0; i<3; ++i)
|
|
{
|
|
if(!cdt.is_constrained(std::make_pair(fh, i)))
|
|
{
|
|
if (fh->neighbor(i)!=nullptr)
|
|
{ face_queue.push(fh->neighbor(i)); }
|
|
}
|
|
else if (face_internal==nullptr)
|
|
{
|
|
face_internal = fh->neighbor(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( face_internal!=nullptr )
|
|
{ face_queue.push(face_internal); }
|
|
|
|
while(!face_queue.empty())
|
|
{
|
|
typename CDT::Face_handle fh = face_queue.front();
|
|
face_queue.pop();
|
|
if(!fh->info().is_process)
|
|
{
|
|
fh->info().is_process = true;
|
|
fh->info().is_external = false;
|
|
for(unsigned int i=0; i<3; ++i)
|
|
{
|
|
if(!cdt.is_constrained(std::make_pair(fh, i)))
|
|
{
|
|
if (fh->neighbor(i)!=nullptr)
|
|
{ face_queue.push(fh->neighbor(i)); }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// (3) Now we iterates on the internal faces to add the vertices
|
|
// and the normals to the appropriate vectors
|
|
for(typename CDT::Finite_faces_iterator ffit=cdt.finite_faces_begin(),
|
|
ffitend = cdt.finite_faces_end(); ffit!=ffitend; ++ffit)
|
|
{
|
|
if(!ffit->info().is_external)
|
|
{
|
|
for(unsigned int i=0; i<3; ++i)
|
|
{
|
|
Point p = ffit->vertex(i)->point();
|
|
faces.push_back(p.x() + offset.x);
|
|
faces.push_back(p.y() + offset.y);
|
|
faces.push_back(p.z() + offset.z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
Scene_lcc_item::Scene_lcc_item(const LCC& lcc)
|
|
:d(new lcc_priv(lcc))
|
|
{
|
|
d->nb_faces = 0;
|
|
d->nb_lines = 0;
|
|
d->nb_vertices = 0;
|
|
d->nb_volumes = 0;
|
|
setTriangleContainer(0,
|
|
new Tri(Three::mainViewer()->isOpenGL_4_3() ? Vi::PROGRAM_FLAT
|
|
: Vi::PROGRAM_OLD_FLAT, false));
|
|
|
|
setEdgeContainer(0,
|
|
new Ec(Three::mainViewer()->isOpenGL_4_3() ? Vi::PROGRAM_SOLID_WIREFRAME
|
|
: Vi::PROGRAM_NO_SELECTION
|
|
, false));
|
|
setPointContainer(0,
|
|
new Pc(Vi::PROGRAM_NO_SELECTION, false));
|
|
}
|
|
|
|
Scene_lcc_item::~Scene_lcc_item()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
Scene_lcc_item* Scene_lcc_item::clone() const
|
|
{
|
|
Scene_lcc_item* item = new Scene_lcc_item(d->lcc);
|
|
return item;
|
|
}
|
|
|
|
bool Scene_lcc_item::supportsRenderingMode(RenderingMode m) const
|
|
{
|
|
return m==FlatPlusEdges;
|
|
}
|
|
|
|
QString Scene_lcc_item::toolTip() const
|
|
{
|
|
return QString();
|
|
}
|
|
|
|
void Scene_lcc_item::compute_bbox() const
|
|
{
|
|
Scene_item::Bbox bb;
|
|
for (LCC::Dart_range::const_iterator it=d->lcc.darts().begin(),
|
|
itend=d->lcc.darts().end(); it!=itend; ++it )
|
|
{
|
|
bb+=d->lcc.point(it).bbox();
|
|
}
|
|
this->setBbox(bb);
|
|
}
|
|
|
|
|
|
void Scene_lcc_item::draw(CGAL::Three::Viewer_interface* viewer) const
|
|
{
|
|
if(!isInit(viewer))
|
|
initGL(viewer);
|
|
if ( getBuffersFilled() &&
|
|
! getBuffersInit(viewer))
|
|
{
|
|
initializeBuffers(viewer);
|
|
setBuffersInit(viewer, true);
|
|
}
|
|
if(!getBuffersFilled())
|
|
{
|
|
computeElements();
|
|
initializeBuffers(viewer);
|
|
}
|
|
if(d->is_mono_color)
|
|
getTriangleContainer(0)->setColor(this->color());
|
|
getTriangleContainer(0)->draw(viewer, d->is_mono_color);
|
|
}
|
|
|
|
void Scene_lcc_item::drawEdges(CGAL::Three::Viewer_interface* viewer) const
|
|
{
|
|
if(!isInit(viewer))
|
|
initGL(viewer);
|
|
if ( getBuffersFilled() &&
|
|
! getBuffersInit(viewer))
|
|
{
|
|
initializeBuffers(viewer);
|
|
setBuffersInit(viewer, true);
|
|
}
|
|
if(!getBuffersFilled())
|
|
{
|
|
computeElements();
|
|
initializeBuffers(viewer);
|
|
}
|
|
GLfloat offset_factor;
|
|
GLfloat offset_units;
|
|
viewer->glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &offset_factor);
|
|
viewer->glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offset_units);
|
|
viewer->glEnable(GL_POLYGON_OFFSET_LINE);
|
|
viewer->glPolygonOffset(0.3f, 0.3f);
|
|
if(viewer->isOpenGL_4_3())
|
|
{
|
|
QVector2D vp(viewer->width(), viewer->height());
|
|
|
|
getEdgeContainer(0)->setViewport(vp);
|
|
getEdgeContainer(0)->setWidth(2);
|
|
}
|
|
getEdgeContainer(0)->setColor(QColor(Qt::black));
|
|
getEdgeContainer(0)->draw(viewer, true);
|
|
drawPoints(viewer);
|
|
viewer->glDisable(GL_POLYGON_OFFSET_LINE);
|
|
viewer->glPolygonOffset(offset_factor, offset_units);
|
|
}
|
|
|
|
void Scene_lcc_item::drawPoints(CGAL::Three::Viewer_interface* viewer) const
|
|
{
|
|
if(!visible())
|
|
return;
|
|
if(!isInit(viewer))
|
|
initGL(viewer);
|
|
if ( getBuffersFilled() &&
|
|
! getBuffersInit(viewer))
|
|
{
|
|
initializeBuffers(viewer);
|
|
setBuffersInit(viewer, true);
|
|
}
|
|
GLfloat point_size;
|
|
viewer->glGetFloatv(GL_POINT_SIZE, &point_size);
|
|
viewer->setGlPointSize(GLfloat(5));
|
|
if(!getBuffersFilled())
|
|
{
|
|
computeElements();
|
|
initializeBuffers(viewer);
|
|
}
|
|
|
|
getPointContainer(0)->setColor(QColor(Qt::black));
|
|
getPointContainer(0)->draw(viewer, true);
|
|
viewer->setGlPointSize(point_size);
|
|
}
|
|
|
|
void Scene_lcc_item::computeElements() const
|
|
{
|
|
CGAL::Three::Three::CursorScopeGuard guard{QCursor(Qt::WaitCursor)};
|
|
d->facets.clear();
|
|
const CGAL::qglviewer::Vec offset = CGAL::Three::Three::mainViewer()->offset();
|
|
typename LCC::size_type markvolumes = d->lcc.get_new_mark();
|
|
typename LCC::size_type markfaces = d->lcc.get_new_mark();
|
|
typename LCC::size_type markedges = d->lcc.get_new_mark();
|
|
typename LCC::size_type markvertices = d->lcc.get_new_mark();
|
|
std::size_t volume_id = 0;
|
|
for (typename LCC::Dart_range::const_iterator it=d->lcc.darts().begin(),
|
|
itend=d->lcc.darts().end(); it!=itend; ++it )
|
|
{
|
|
if (!d->lcc.is_marked(it, markvolumes))
|
|
{
|
|
for (typename LCC::template Dart_of_cell_basic_range<3>::
|
|
const_iterator itv=d->lcc.template darts_of_cell_basic<3>(it, markvolumes).begin(),
|
|
itvend=d->lcc.template darts_of_cell_basic<3>(it, markvolumes).end();
|
|
itv!=itvend; ++itv)
|
|
{
|
|
d->lcc.mark(itv, markvolumes); // To be sure that all darts of the basic iterator will be marked
|
|
if (!d->lcc.is_marked(itv, markfaces))
|
|
{
|
|
Facet f;
|
|
if(d->compute_face(itv, f))
|
|
d->facets.push_back(f);
|
|
d->facets.back().volume_id = volume_id;
|
|
for (typename LCC::template Dart_of_cell_basic_range<2>::
|
|
const_iterator itf=d->lcc.template darts_of_cell_basic<2>(itv, markfaces).begin(),
|
|
itfend=d->lcc.template darts_of_cell_basic<2>(itv, markfaces).end();
|
|
itf!=itfend; ++itf)
|
|
{
|
|
d->lcc.mark(itf, markfaces); // To be sure that all darts of the basic iterator will be marked
|
|
if ( !d->lcc.is_marked(itf, markedges))
|
|
{
|
|
Point p1 = d->lcc.point(itf);
|
|
LCC::Dart_const_descriptor d2 = d->lcc.other_extremity(itf);
|
|
Point p2 = d->lcc.point(d2);
|
|
d->lines.push_back(p1.x() + offset.x);
|
|
d->lines.push_back(p1.y() + offset.y);
|
|
d->lines.push_back(p1.z() + offset.z);
|
|
|
|
d->lines.push_back(p2.x() + offset.x);
|
|
d->lines.push_back(p2.y() + offset.y);
|
|
d->lines.push_back(p2.z() + offset.z);
|
|
|
|
for (typename LCC::template Dart_of_cell_basic_range<1>::
|
|
const_iterator ite=d->lcc.template darts_of_cell_basic<1>(itf, markedges).begin(),
|
|
iteend=d->lcc.template darts_of_cell_basic<1>(itf, markedges).end();
|
|
ite!=iteend; ++ite)
|
|
{
|
|
d->lcc.mark(ite, markedges); // To be sure that all darts of the basic iterator will be marked
|
|
if ( !d->lcc.is_marked(ite, markvertices))
|
|
{
|
|
Point p1 = d->lcc.point(ite);
|
|
d->vertices.push_back(p1.x() + offset.x);
|
|
d->vertices.push_back(p1.y() + offset.y);
|
|
d->vertices.push_back(p1.z() + offset.z);
|
|
CGAL::mark_cell<LCC, 0>(d->lcc, ite, markvertices);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
++volume_id;
|
|
}
|
|
}
|
|
d->nb_volumes = volume_id;
|
|
for (typename LCC::Dart_range::const_iterator it=d->lcc.darts().begin(),
|
|
itend=d->lcc.darts().end(); it!=itend; ++it )
|
|
{
|
|
d->lcc.unmark(it, markvertices);
|
|
d->lcc.unmark(it, markedges);
|
|
d->lcc.unmark(it, markfaces);
|
|
d->lcc.unmark(it, markvolumes);
|
|
|
|
}
|
|
|
|
d->lcc.free_mark(markvolumes);
|
|
d->lcc.free_mark(markfaces);
|
|
d->lcc.free_mark(markedges);
|
|
d->lcc.free_mark(markvertices);
|
|
|
|
getTriangleContainer(0)->allocate(
|
|
Tri::Flat_vertices, d->faces.data(),
|
|
static_cast<int>(d->faces.size()*sizeof(float)));
|
|
if(!d->is_mono_color)
|
|
{
|
|
getTriangleContainer(0)->allocate(Tri::FColors, d->colors.data(),
|
|
static_cast<int>(d->colors.size()*sizeof(float)));
|
|
}
|
|
else
|
|
getTriangleContainer(0)->allocate(Tri::FColors, nullptr, 0);
|
|
|
|
getEdgeContainer(0)->allocate(
|
|
Ec::Vertices, d->lines.data(),
|
|
static_cast<int>(d->lines.size()*sizeof(float)));
|
|
|
|
getPointContainer(0)->allocate(
|
|
Pc::Vertices, d->vertices.data(),
|
|
static_cast<int>(d->vertices.size()*sizeof(float)));
|
|
|
|
setBuffersFilled(true);
|
|
d->nb_faces = d->faces.size();
|
|
d->nb_lines = d->lines.size();
|
|
d->nb_vertices= d->vertices.size();
|
|
}
|
|
void Scene_lcc_item::initializeBuffers(CGAL::Three::Viewer_interface *viewer) const
|
|
{
|
|
getTriangleContainer(0)->initializeBuffers(viewer);
|
|
getTriangleContainer(0)->setFlatDataSize(d->nb_faces);
|
|
getEdgeContainer(0)->initializeBuffers(viewer);
|
|
getEdgeContainer(0)->setFlatDataSize(d->nb_lines);
|
|
getPointContainer(0)->initializeBuffers(viewer);
|
|
getPointContainer(0)->setFlatDataSize(d->nb_vertices);
|
|
|
|
d->faces.clear();
|
|
d->faces.shrink_to_fit();
|
|
d->lines.clear();
|
|
d->lines.shrink_to_fit();
|
|
d->vertices.clear();
|
|
d->vertices.shrink_to_fit();
|
|
}
|
|
|
|
void Scene_lcc_item::invalidateOpenGLBuffers()
|
|
{
|
|
setBuffersFilled(false);
|
|
getTriangleContainer(0)->reset_vbos(ALL);
|
|
getEdgeContainer(0)->reset_vbos(ALL);
|
|
getPointContainer(0)->reset_vbos(ALL);
|
|
compute_bbox();
|
|
}
|
|
|
|
bool Scene_lcc_item::isEmpty() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Scene_lcc_item::randomFaceColors()
|
|
{
|
|
d->is_mono_color = false;
|
|
d->colors.resize(d->nb_faces);
|
|
for(std::size_t i=0; i< d->colors.size()-3; i+=3)
|
|
{
|
|
QColor col = generate_random_color();
|
|
d->colors[i] = col.redF();
|
|
d->colors[i+1] = col.greenF();
|
|
d->colors[i+2] = col.blueF();
|
|
}
|
|
|
|
invalidateOpenGLBuffers();
|
|
redraw();
|
|
}
|
|
|
|
void Scene_lcc_item::randomVolumeColors()
|
|
{
|
|
d->is_mono_color = false;
|
|
d->colors.resize(d->nb_faces);
|
|
std::vector<QColor> colors(d->nb_volumes);
|
|
for(std::size_t i = 0; i<d->nb_volumes; ++i)
|
|
{
|
|
colors[i] = generate_random_color();
|
|
}
|
|
std::size_t color_id = 0;
|
|
for(auto f : d->facets)//filled in the same order as GL faces
|
|
{
|
|
QColor col = colors[f.volume_id];
|
|
//3 points per face.
|
|
for(std::size_t j = 0; j < 3; ++j)
|
|
{
|
|
d->colors[color_id+j*3] = col.redF();
|
|
d->colors[color_id+j*3+1] = col.greenF();
|
|
d->colors[color_id+j*3+2] = col.blueF();
|
|
}
|
|
color_id += 9;
|
|
}
|
|
invalidateOpenGLBuffers();
|
|
redraw();
|
|
}
|
|
|
|
void Scene_lcc_item::resetColors()
|
|
{
|
|
d->is_mono_color = true;
|
|
invalidateOpenGLBuffers();
|
|
redraw();
|
|
}
|
|
QMenu* Scene_lcc_item::contextMenu()
|
|
{
|
|
const char* prop_name = "Menu modified by Scene_lcc_item.";
|
|
|
|
QMenu* menu = Scene_item::contextMenu();
|
|
|
|
// Use dynamic properties:
|
|
// https://doc.qt.io/qt-5/qobject.html#property
|
|
bool menuChanged = menu->property(prop_name).toBool();
|
|
|
|
if(!menuChanged) {
|
|
menu->addSeparator();
|
|
QAction* action = menu->addAction(tr("Set Random Colors for Faces."));
|
|
action->setObjectName("actionRandomFaceColors");
|
|
connect(action, &QAction::triggered,
|
|
this, &Scene_lcc_item::randomFaceColors);
|
|
action = menu->addAction(tr("Set Random Colors for Volumes."));
|
|
action->setObjectName("actionRandomVolumeColors");
|
|
connect(action, &QAction::triggered,
|
|
this, &Scene_lcc_item::randomVolumeColors);
|
|
menu->setProperty(prop_name, true);
|
|
}
|
|
QAction* action = menu->findChild<QAction*>("actionResetColors");
|
|
if(!action)
|
|
{
|
|
action = menu->addAction(tr("Reset Colors."));
|
|
action->setObjectName("actionResetColors");
|
|
connect(action, &QAction::triggered,
|
|
this, &Scene_lcc_item::resetColors);
|
|
}
|
|
action->setVisible(!d->is_mono_color);
|
|
|
|
return menu;
|
|
}
|