ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qCork.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "qCork.h"
9 
10 // CV_DB_LIB
11 #include <ecvMesh.h>
12 #include <ecvPointCloud.h>
13 
14 // dialog
15 #include "ccCorkDlg.h"
16 
17 // Qt
18 #include <QApplication>
19 #include <QMainWindow>
20 #include <QProgressDialog>
21 #include <QtConcurrentRun>
22 #include <QtGui>
23 
24 // Cork
25 #include <mesh/corkMesh.h>
26 
27 // system
28 #if defined(CV_WINDOWS)
29 #include "windows.h"
30 #else
31 #include <time.h>
32 #include <unistd.h>
33 #endif
34 
35 qCork::qCork(QObject* parent /*=0*/)
36  : QObject(parent),
37  ccStdPluginInterface(":/CC/plugin/qCork/info.json"),
38  m_action(nullptr) {}
39 
40 QList<QAction*> qCork::getActions() {
41  // default action
42  if (!m_action) {
43  m_action = new QAction(getName(), this);
44  m_action->setToolTip(getDescription());
45  m_action->setIcon(getIcon());
46  // connect signal
47  connect(m_action, &QAction::triggered, this, &qCork::doAction);
48  }
49 
50  return QList<QAction*>{m_action};
51 }
52 
53 void qCork::onNewSelection(const ccHObject::Container& selectedEntities) {
54  if (m_action) {
55  // we need two and only two meshes!
56  m_action->setEnabled(selectedEntities.size() == 2 &&
57  selectedEntities[0]->isKindOf(CV_TYPES::MESH) &&
58  selectedEntities[1]->isKindOf(CV_TYPES::MESH));
59  }
60 }
61 
62 bool ToCorkMesh(const ccMesh* in, CorkMesh& out, ecvMainAppInterface* app = 0) {
63  if (!in || !in->getAssociatedCloud()) {
64  if (app)
65  app->dispToConsole("[Cork] Invalid input mesh!",
67  assert(false);
68  return false;
69  }
70 
71  ccGenericPointCloud* vertices = in->getAssociatedCloud();
72  assert(vertices);
73 
74  unsigned triCount = in->size();
75  unsigned vertCount = vertices ? vertices->size() : 0;
76 
77  std::vector<CorkMesh::Tri>& outTris = out.getTris();
78  std::vector<CorkVertex>& outVerts = out.getVerts();
79  try {
80  outVerts.resize(vertCount);
81  outTris.resize(triCount);
82  } catch (const std::bad_alloc&) {
83  if (app)
84  app->dispToConsole("[Cork] Not enough memory!",
86  return false;
87  }
88 
89  if (outVerts.empty() || outTris.empty()) {
90  if (app)
91  app->dispToConsole(QString("[Cork] Input mesh '%1' is empty?!")
92  .arg(in->getName()),
94  return false;
95  }
96 
97  // import triangle indexes
98  {
99  for (unsigned i = 0; i < triCount; i++) {
100  const cloudViewer::VerticesIndexes* tsi =
101  in->getTriangleVertIndexes(i);
102  CorkTriangle corkTri;
103  corkTri.a = tsi->i1;
104  corkTri.b = tsi->i2;
105  corkTri.c = tsi->i3;
106  outTris[i].data = corkTri;
107  // DGM: it seems that Cork doubles this information?!
108  outTris[i].a = tsi->i1;
109  outTris[i].b = tsi->i2;
110  outTris[i].c = tsi->i3;
111  }
112  }
113 
114  // import vertices
115  {
116  for (unsigned i = 0; i < vertCount; i++) {
117  const CCVector3* P = vertices->getPoint(i);
118  outVerts[i].pos.x = static_cast<double>(P->x);
119  outVerts[i].pos.y = static_cast<double>(P->y);
120  outVerts[i].pos.z = static_cast<double>(P->z);
121  }
122  }
123 
124  return true;
125 }
126 
127 ccMesh* FromCorkMesh(const CorkMesh& in, ecvMainAppInterface* app = 0) {
128  const std::vector<CorkMesh::Tri>& inTris = in.getTris();
129  const std::vector<CorkVertex>& inVerts = in.getVerts();
130 
131  if (inTris.empty() || inVerts.empty()) {
132  if (app)
133  app->dispToConsole("[Cork] Output mesh is empty?!",
135  return 0;
136  }
137 
138  unsigned triCount = static_cast<unsigned>(inTris.size());
139  unsigned vertCount = static_cast<unsigned>(inVerts.size());
140 
141  ccPointCloud* vertices = new ccPointCloud("vertices");
142  if (!vertices->reserve(vertCount)) {
143  if (app)
144  app->dispToConsole("[Cork] Not enough memory!",
146  delete vertices;
147  return 0;
148  }
149 
150  ccMesh* mesh = new ccMesh(vertices);
151  mesh->addChild(vertices);
152  if (!mesh->reserve(triCount)) {
153  if (app)
154  app->dispToConsole("[Cork] Not enough memory!",
156  delete mesh;
157  return 0;
158  }
159 
160  // import vertices
161  {
162  for (unsigned i = 0; i < vertCount; i++) {
163  const CorkVertex& P = inVerts[i];
164  CCVector3 Pout(static_cast<PointCoordinateType>(P.pos.x),
165  static_cast<PointCoordinateType>(P.pos.y),
166  static_cast<PointCoordinateType>(P.pos.z));
167  vertices->addPoint(Pout);
168  }
169  }
170 
171  // import triangle indexes
172  {
173  for (unsigned i = 0; i < triCount; i++) {
174  const CorkMesh::Tri& tri = inTris[i];
175  mesh->addTriangle(tri.a, tri.b, tri.c);
176  }
177  }
178 
179  mesh->setVisible(true);
180  vertices->setEnabled(false);
181 
182  return mesh;
183 }
184 
188  : operation(ccCorkDlg::UNION),
189  corkA(0),
190  corkB(0),
191  app(0),
192  meshesAreOk(false) {}
193 
195  CorkMesh* corkA;
196  CorkMesh* corkB;
197  QString nameA;
198  QString nameB;
201 };
203 
205  // invalid parameters
206  if (!s_params.corkA || !s_params.corkB) return false;
207 
208  try {
209  // check meshes
210  s_params.meshesAreOk = true;
211  if (false) {
212  if (s_params.corkA->isSelfIntersecting()) {
213  if (s_params.app)
215  QString("[Cork] Mesh '%1' is self-intersecting! "
216  "Result may be jeopardized!")
217  .arg(s_params.nameA),
219  s_params.meshesAreOk = false;
220  } else if (!s_params.corkA->isClosed()) {
221  if (s_params.app)
223  QString("[Cork] Mesh '%1' is not closed! Result "
224  "may be jeopardized!")
225  .arg(s_params.nameA),
227  s_params.meshesAreOk = false;
228  }
229  if (s_params.corkB->isSelfIntersecting()) {
230  if (s_params.app)
232  QString("[Cork] Mesh '%1' is self-intersecting! "
233  "Result may be jeopardized!")
234  .arg(s_params.nameB),
236  s_params.meshesAreOk = false;
237  } else if (!s_params.corkB->isClosed()) {
238  if (s_params.app)
240  QString("[Cork] Mesh '%1' is not closed! Result "
241  "may be jeopardized!")
242  .arg(s_params.nameB),
244  s_params.meshesAreOk = false;
245  }
246  }
247 
248  // perform the boolean operation
249  switch (s_params.operation) {
250  case ccCorkDlg::UNION:
251  s_params.corkA->boolUnion(*s_params.corkB);
252  break;
253 
255  s_params.corkA->boolIsct(*s_params.corkB);
256  break;
257 
258  case ccCorkDlg::DIFF:
259  s_params.corkA->boolDiff(*s_params.corkB);
260  break;
261 
262  case ccCorkDlg::SYM_DIFF:
263  s_params.corkA->boolXor(*s_params.corkB);
264  break;
265 
266  default:
267  assert(false);
268  if (s_params.app)
270  "Unhandled operation?!",
272  WRN_CONSOLE_MESSAGE); // DGM: can't issue
273  // an error message
274  // (i.e. with dialog)
275  // in another thread!
276  break;
277  }
278  } catch (const std::exception& e) {
279  if (s_params.app)
281  QString("Exception caught: %1").arg(e.what()),
283  return false;
284  }
285 
286  return true;
287 }
288 
290  assert(m_app);
291  if (!m_app) return;
292 
293  const ccHObject::Container& selectedEntities = m_app->getSelectedEntities();
294  size_t selNum = selectedEntities.size();
295  if (selNum != 2 || !selectedEntities[0]->isKindOf(CV_TYPES::MESH) ||
296  !selectedEntities[1]->isKindOf(CV_TYPES::MESH)) {
297  assert(false);
298  m_app->dispToConsole("Select two and only two meshes!",
300  return;
301  }
302 
303  ccMesh* meshA = static_cast<ccMesh*>(selectedEntities[0]);
304  ccMesh* meshB = static_cast<ccMesh*>(selectedEntities[1]);
305 
306  // show dialog to let the user choose the operation to perform
307  ccCorkDlg cDlg(m_app->getMainWindow());
308  cDlg.setNames(meshA->getName(), meshB->getName());
309  if (!cDlg.exec()) return;
310  if (cDlg.isSwapped()) std::swap(meshA, meshB);
311 
312  // try to convert both meshes to CorkMesh structures
313  CorkMesh corkA;
314  if (!ToCorkMesh(meshA, corkA, m_app)) return;
315  CorkMesh corkB;
316  if (!ToCorkMesh(meshB, corkB, m_app)) return;
317 
318  // launch process
319  {
320  // run in a separate thread
321  QProgressDialog pDlg("Operation in progress", QString(), 0, 0,
322  m_app->getMainWindow());
323  pDlg.setWindowTitle("Cork");
324  pDlg.show();
325  QApplication::processEvents();
326 
327  s_params.app = m_app;
328  s_params.corkA = &corkA;
329  s_params.corkB = &corkB;
330  s_params.nameA = meshA->getName();
331  s_params.nameB = meshB->getName();
333 
334  QFuture<bool> future = QtConcurrent::run(doPerformBooleanOp);
335 
336  // wait until process is finished!
337  while (!future.isFinished()) {
338 #if defined(CV_WINDOWS)
339  ::Sleep(500);
340 #else
341  usleep(500 * 1000);
342 #endif
343 
344  pDlg.setValue(pDlg.value() + 1);
345  QApplication::processEvents();
346  }
347 
348  // just to be sure
349  s_params.app = 0;
351 
352  pDlg.hide();
353  QApplication::processEvents();
354 
355  if (!future.result()) {
356  if (m_app)
359  ? "Computation failed!"
360  : "Computation failed! (check console)",
362  // an error occurred
363  return;
364  }
365  }
366 
367  // convert the updated mesh (A) to a new ccMesh structure
368  ccMesh* result = FromCorkMesh(corkA);
369 
370  if (result) {
371  meshA->setEnabled(false);
372  meshB->setEnabled(false);
373  // if (meshB->getDisplay() == meshA->getDisplay())
374  // meshB->setEnabled(false);
375 
376  // set name
377  QString opName;
378  switch (cDlg.getSelectedOperation()) {
379  case ccCorkDlg::UNION:
380  opName = "union";
381  break;
383  opName = "isect";
384  break;
385  case ccCorkDlg::DIFF:
386  opName = "diff";
387  break;
388  case ccCorkDlg::SYM_DIFF:
389  opName = "sym_diff";
390  break;
391  default:
392  assert(false);
393  break;
394  }
395  result->setName(QString("(%1).%2.(%3)")
396  .arg(meshA->getName())
397  .arg(opName)
398  .arg(meshB->getName()));
399 
400  // normals
401  bool hasNormals = false;
402  if (meshA->hasTriNormals())
403  hasNormals = result->computePerTriangleNormals();
404  else if (meshA->hasNormals())
405  hasNormals = result->computePerVertexNormals();
406  meshA->showNormals(hasNormals && meshA->normalsShown());
407 
408  // result->setDisplay(meshA->getDisplay());
409  m_app->addToDB(result);
410  // result->redrawDisplay();
411  }
412 
413  // currently selected entities appearance may have changed!
414  // m_app->refreshAll();
415 }
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
core::Tensor result
Definition: VtkUtils.cpp:76
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Dialog for qCork plugin.
Definition: ccCorkDlg.h:13
CSG_OPERATION
Supported CSG operations.
Definition: ccCorkDlg.h:21
@ INTERSECT
Definition: ccCorkDlg.h:21
@ SYM_DIFF
Definition: ccCorkDlg.h:21
CSG_OPERATION getSelectedOperation() const
Returns the selected operation.
Definition: ccCorkDlg.h:27
void setNames(QString A, QString B)
Set meshes names.
Definition: ccCorkDlg.cpp:26
bool isSwapped() const
Returns whether mesh order has been swappped or not.
Definition: ccCorkDlg.h:30
virtual QString getName() const override
Returns (short) name (for menu entry, etc.)
virtual QString getDescription() const override
Returns long name/description (for tooltip, etc.)
virtual QIcon getIcon() const override
Returns icon.
virtual void setVisible(bool state)
Sets entity visibility.
void showNormals(bool state) override
Sets normals visibility.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
Triangular mesh.
Definition: ecvMesh.h:35
bool normalsShown() const override
Returns whether normals are shown or not.
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
bool hasNormals() const override
Returns whether normals are enabled or not.
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
Standard ECV plugin interface.
virtual void onNewSelection(const ccHObject::Container &selectedEntities)
ecvMainAppInterface * m_app
Main application interface.
virtual unsigned size() const =0
Returns the number of points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
Main application interface (for plugins)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual const ccHObject::Container & getSelectedEntities() const =0
Returns currently selected entities ("read only")
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
QAction * m_action
Associated action.
Definition: qCork.h:43
void doAction()
Starts main action.
Definition: qCork.cpp:289
@ MESH
Definition: CVTypes.h:105
void Sleep(int milliseconds)
Definition: Helper.cpp:278
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
static BoolOpParameters s_params
Definition: qCork.cpp:202
bool doPerformBooleanOp()
Definition: qCork.cpp:204
bool ToCorkMesh(const ccMesh *in, CorkMesh &out, ecvMainAppInterface *app=0)
Definition: qCork.cpp:62
ccMesh * FromCorkMesh(const CorkMesh &in, ecvMainAppInterface *app=0)
Definition: qCork.cpp:127
Boolean operation parameters (for concurrent run)
Definition: qCork.cpp:186
ccCorkDlg::CSG_OPERATION operation
Definition: qCork.cpp:194
bool meshesAreOk
Definition: qCork.cpp:200
CorkMesh * corkB
Definition: qCork.cpp:196
CorkMesh * corkA
Definition: qCork.cpp:195
ecvMainAppInterface * app
Definition: qCork.cpp:199
QString nameB
Definition: qCork.cpp:198
QString nameA
Definition: qCork.cpp:197
Triangle described by the indexes of its 3 vertices.