ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvGraphicalTransformationTool.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 
9 
10 #include "MainWindow.h"
11 
12 // CV_DB_LIB
13 #include <CVLog.h>
14 #include <ecvDisplayTools.h>
15 #include <ecvFacet.h>
17 #include <ecvMesh.h>
18 #include <ecvPolyline.h>
19 
21  : ccOverlayDialog(parent),
22  Ui::GraphicalTransformationDlg(),
23  m_toTransform("transformed") {
24  setupUi(this);
25 
26  connect(pauseButton, &QAbstractButton::toggled, this,
28  connect(okButton, &QAbstractButton::clicked, this,
30  connect(razButton, &QAbstractButton::clicked, this,
32  connect(cancelButton, &QAbstractButton::clicked, this,
34 
35  // add shortcuts
36  addOverridenShortcut(Qt::Key_Space); // space bar for the "pause" button
37  addOverridenShortcut(Qt::Key_Escape); // escape key for the "cancel" button
38  addOverridenShortcut(Qt::Key_Return); // return key for the "ok" button
39  connect(this, &ccOverlayDialog::shortcutTriggered, this,
41  connect(this->rotComboBox,
42  static_cast<void (QComboBox::*)(int)>(
43  &QComboBox::currentIndexChanged),
45  connect(this->TxCheckBox, &QCheckBox::toggled, this,
47  connect(this->TyCheckBox, &QCheckBox::toggled, this,
49  connect(this->TzCheckBox, &QCheckBox::toggled, this,
51  connect(this->scaleCheckBox, &QCheckBox::toggled, this,
53  connect(this->shearCheckBox, &QCheckBox::toggled, this,
55 }
56 
58 
60  switch (key) {
61  case Qt::Key_Space:
62  pauseButton->toggle();
63  return;
64 
65  case Qt::Key_Return:
66  okButton->click();
67  return;
68 
69  case Qt::Key_Escape:
70  cancelButton->click();
71  return;
72 
73  default:
74  // nothing to do
75  break;
76  }
77 }
78 
80  if (!m_tool) {
81  return;
82  }
83 
84  m_tool->setScaleEnabled(this->scaleCheckBox->isChecked());
85 }
86 
88  if (!m_tool) {
89  return;
90  }
91 
92  m_tool->setShearEnabled(this->shearCheckBox->isChecked());
93 }
94 
96  if (!m_tool) {
97  return;
98  }
99 
100  switch (rotComboBox->currentIndex()) {
101  case 0: // XYZ
103  ecvGenericTransformTool::RotationMode::R_XYZ);
104  break;
105  case 1: // X
106  m_tool->setRotationMode(ecvGenericTransformTool::RotationMode::R_X);
107  break;
108  case 2: // Y
109  m_tool->setRotationMode(ecvGenericTransformTool::RotationMode::R_Y);
110  break;
111  case 3: // Z
112  m_tool->setRotationMode(ecvGenericTransformTool::RotationMode::R_Z);
113  break;
114  }
115 }
116 
118  if (!m_tool) {
119  return;
120  }
121 
122  int flag = 0;
123  flag += TxCheckBox->isChecked() * 1;
124  flag += TyCheckBox->isChecked() * 2;
125  flag += TzCheckBox->isChecked() * 3;
126  switch (flag) {
127  case 0:
129  ecvGenericTransformTool::TranslationMOde::T_NONE);
130  break;
131  case 1:
133  ecvGenericTransformTool::TranslationMOde::T_X);
134  break;
135  case 2:
137  ecvGenericTransformTool::TranslationMOde::T_Y);
138  break;
139  case 3: {
140  if (TzCheckBox->isChecked()) {
142  ecvGenericTransformTool::TranslationMOde::T_Z);
143  } else {
145  ecvGenericTransformTool::TranslationMOde::T_XY);
146  }
147  } break;
148  case 4:
150  ecvGenericTransformTool::TranslationMOde::T_XZ);
151  break;
152  case 5:
154  ecvGenericTransformTool::TranslationMOde::T_ZY);
155  break;
156  case 6:
158  ecvGenericTransformTool::TranslationMOde::T_XYZ);
159  break;
160 
161  default:
162  break;
163  }
164 }
165 
167  if (!ecvDisplayTools::GetCurrentScreen()) return;
168 
169  if (state) {
170  // ecvDisplayTools::SetInteractionMode(ecvDisplayTools::TRANSFORM_CAMERA());
172  "Transformation [PAUSED]",
176  "Unpause to transform again",
179  } else {
180  // ecvDisplayTools::SetInteractionMode(ecvDisplayTools::TRANSFORM_ENTITIES());
182  "[Rotation/Translation mode]",
185  }
186 
187  if (m_tool) {
188  m_tool->showInteractor(!state);
189  }
190 
191  // update mini-GUI
192  pauseButton->blockSignals(true);
193  pauseButton->setChecked(state);
194  pauseButton->blockSignals(false);
195 
197  ecvDisplayTools::RedrawDisplay(true, false);
198 }
199 
202 
203  if (m_tool) {
204  m_tool->clear();
205  }
206 
208  QString(),
209  ecvDisplayTools::UPPER_CENTER_MESSAGE); // clear the area
210 }
211 
213  assert(entity);
214  if (!entity) return false;
215 
216  // we can't transform locked entities
217  if (entity->isLocked()) {
218  CVLog::Warning(QString("[Graphical Transformation Tool] Can't "
219  "transform entity '%1' cause it's locked!")
220  .arg(entity->getName()));
221  return false;
222  }
223 
224  // we can't transform child meshes
225  if (entity->isA(CV_TYPES::MESH) && entity->getParent() &&
226  entity->getParent()->isKindOf(CV_TYPES::MESH)) {
227  CVLog::Warning(QString("[Graphical Transformation Tool] Entity '%1' "
228  "can't be modified as it is part of a mesh "
229  "group. You should 'clone' it first.")
230  .arg(entity->getName()));
231  return false;
232  }
233 
234  // eventually, we must check that there is no "parent + sibling" in the
235  // selection! otherwise, the sibling will be rotated twice!
236  unsigned n = m_toTransform.getChildrenNumber();
237  for (unsigned i = 0; i < n;) {
238  ccHObject* previous = m_toTransform.getChild(i);
239  if (previous->isAncestorOf(entity)) {
240  // we have found a parent, we won't add this entity
241  return false;
242  }
243  // if the inverse is true, then we get rid of the current element!
244  else if (entity->isAncestorOf(previous)) {
245  m_toTransform.detachChild(previous);
246  --n;
247  } else {
248  // proceed
249  ++i;
250  }
251  }
252 
253  // TODO+ add ccFacet, ccCoordinateSystem and ccSensor support!
254  if (entity->isA(CV_TYPES::FACET) || entity->isKindOf(CV_TYPES::SENSOR) ||
255  entity->isA(CV_TYPES::COORDINATESYSTEM)) {
257  QString("[Graphical Transformation Tool] Can't transform "
258  "entity '%1' cause it's not supported now!")
259  .arg(entity->getName()));
260  return false;
261  } else {
263  }
264 
265  return true;
266 }
267 
270 }
271 
273  m_rotationCenter = center;
274 }
275 
277  if (!ccOverlayDialog::linkWith(win)) {
278  return false;
279  }
280 
281  // assert(m_toTransform.getChildrenNumber() == 0);
282  return true;
283 }
284 
286  assert(!m_processing);
287  if (!ecvDisplayTools::GetCurrentScreen() || !m_tool) return false;
288 
289  unsigned childNum = m_toTransform.getChildrenNumber();
290  if (childNum == 0) return false;
291 
293  return false;
294  }
295 
296  if (!m_tool->start()) {
297  return false;
298  }
299 
300  // update m_tool
301  onScaleEnabled(true);
302  onShearEnabled(false);
305 
306  pause(this->pauseButton->isChecked());
307 
308  return ccOverlayDialog::start();
309 }
310 
312  if (m_tool) {
313  m_tool->stop();
314  m_tool->clear();
315  delete m_tool;
316  m_tool = nullptr;
317  }
318 
319  ccOverlayDialog::stop(state);
320 }
321 
323  ecvGenericTransformTool* tool) {
324  if (!tool) {
325  return false;
326  }
327 
328  m_tool = tool;
329  return true;
330 }
331 
333  if (!m_tool) {
335  QString("[ccGraphicalTransformationTool::reset] transformation "
336  "tool has not been initialized!"));
337  return;
338  }
339 
340  m_tool->reset();
341 }
342 
344  if (!m_tool || !MainWindow::TheInstance()) return;
345 
346  ccHObject::Container tranformedEntities;
347  m_tool->getOutput(tranformedEntities);
348  if (tranformedEntities.size() != getNumberOfValidEntities()) {
349  assert(false);
350  return;
351  }
352 
353  for (unsigned ci = 0; ci != getNumberOfValidEntities(); ++ci) {
354  ccHObject* obj = m_toTransform.getChild(ci);
355  if (!obj) {
356  assert(false);
357  continue;
358  }
359 
360  ccHObject* result = tranformedEntities[ci];
361  if (result) {
362  result->setEnabled(true);
363  obj->setEnabled(false);
364  result->setName(obj->getName() + QString(".ScaleShear"));
365  if (obj->getParent()) obj->getParent()->addChild(result);
367  } else {
368  CVLog::Warning(QString("ignore entity [ID:%1] transformation!")
369  .arg(obj->getUniqueID()));
370  }
371  }
372 
373  // reset interactors or model if necessary
374  reset();
375 }
376 
378  // we recompute global GL transformation matrix and display it in console
379  if (!m_tool) {
381  QString("[ccGraphicalTransformationTool::apply] transformation "
382  "tool has not been initialized!"));
383  stop(true);
384  clear();
385  return;
386  }
387 
388  // non rigid transform
389  if (this->scaleCheckBox->isChecked() || this->shearCheckBox->isChecked()) {
391  stop(true);
392  clear();
393  return;
394  }
395 
396  // rigid transform
397  ccGLMatrixd finalTrans;
398  finalTrans = m_tool->getFinalTransformation();
399 
400  ccGLMatrixd finalTransCorrected = finalTrans;
401 #define NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
402 #ifdef NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
403  {
404  // convert matrix back and forth so as to be sure to get a 'true'
405  // rotation matrix DGM: we use Euler angles, as the axis/angle method
406  // (formerly used) is not robust enough! Shifts could be perceived by
407  // the user.
408  double phi_rad, theta_rad, psi_rad;
409  CCVector3d t3D;
410  finalTrans.getParameters(phi_rad, theta_rad, psi_rad, t3D);
411  finalTransCorrected.initFromParameters(phi_rad, theta_rad, psi_rad,
412  t3D);
413 
414 #ifdef QT_DEBUG
415  CVLog::Print(
416  "[GraphicalTransformationTool] Final transformation (before "
417  "correction):");
418  CVLog::Print(finalTrans.toString(12, ' ')); // full precision
419  CVLog::Print(QString("Angles(%1,%2,%3) T(%5,%6,%7)")
420  .arg(phi_rad)
421  .arg(theta_rad)
422  .arg(psi_rad)
423  .arg(t3D.x)
424  .arg(t3D.y)
425  .arg(t3D.z));
426 #endif
427  }
428 #endif // NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
429 
430 #ifdef QT_DEBUG
431  // test: compute rotation "norm" (as it may not be exactly 1 due to
432  // numerical (in)accuracy!)
433  {
434  ccGLMatrixd finalRotation = finalTransCorrected;
435  finalRotation.setTranslation(CCVector3(0, 0, 0));
436  ccGLMatrixd finalRotationT = finalRotation.transposed();
437  ccGLMatrixd idTrans = finalRotation * finalRotationT;
438  double norm =
439  idTrans.data()[0] * idTrans.data()[5] * idTrans.data()[10];
440  CVLog::PrintDebug("[GraphicalTransformationTool] T*T-1:");
441  CVLog::PrintDebug(idTrans.toString(12, ' ')); // full precision
442  CVLog::PrintDebug(QString("Rotation norm = %1").arg(norm, 0, 'f', 12));
443  }
444 #endif
445 
446  // update GL transformation for all entities
447  ccGLMatrix correctedFinalTrans(finalTransCorrected.data());
448 
450  for (unsigned i = 0; i < m_toTransform.getChildrenNumber(); ++i) {
451  ccHObject* toTransform = m_toTransform.getChild(i);
452  toTransform->setGLTransformation(correctedFinalTrans);
453 
454  // DGM: warning, applyGLTransformation may delete the associated octree!
455  MainWindow::ccHObjectContext objContext =
457  toTransform);
458 
459  toTransform->applyGLTransformation_recursive();
460  // toTransform->prepareDisplayForRefresh_recursive();
462  objContext);
463 
464  toTransform->setRedrawFlagRecursive(true);
465 
466  // special case: if the object is a mesh vertices set, we may have to
467  // update the mesh normals!
468  if (toTransform->isA(CV_TYPES::POINT_CLOUD) &&
469  toTransform->getParent() &&
470  toTransform->getParent()->isKindOf(CV_TYPES::MESH)) {
471  ccMesh* mesh = static_cast<ccMesh*>(toTransform->getParent());
472  if (mesh->hasTriNormals() && !m_toTransform.isAncestorOf(mesh)) {
473  mesh->transformTriNormals(correctedFinalTrans);
474  }
475  }
476  }
477 
478  stop(true);
479 
480  clear();
481 
483 
484  // output resulting transformation matrix
485  CVLog::Print("[GraphicalTransformationTool] Applied transformation:");
486  CVLog::Print(correctedFinalTrans.toString(12, ' ')); // full precision
487 
488 #ifdef QT_DEBUG
489  {
490  float phi_rad, theta_rad, psi_rad;
491  CCVector3f t3D;
492  correctedFinalTrans.getParameters(phi_rad, theta_rad, psi_rad, t3D);
493  CVLog::Print(QString("Angles(%1,%2,%3) T(%5,%6,%7)")
494  .arg(phi_rad)
495  .arg(theta_rad)
496  .arg(psi_rad)
497  .arg(t3D.x)
498  .arg(t3D.y)
499  .arg(t3D.z));
500  }
501 #endif
502 }
503 
505  for (unsigned i = 0; i < m_toTransform.getChildrenNumber(); ++i) {
506  ccHObject* child = m_toTransform.getChild(i);
507  child->resetGLTransformation();
508  child->setRedraw(true);
509  }
510 
511  reset();
512 
513  stop(false);
514 
515  clear();
516 }
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
core::Tensor result
Definition: VtkUtils.cpp:76
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
Definition: CVLog.cpp:153
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
ccHObjectContext removeObjectTemporarilyFromDBTree(ccHObject *obj) override
Removes object temporarily from DB tree.
static MainWindow * TheInstance()
Returns the unique instance of this object.
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
void putObjectBackIntoDBTree(ccHObject *obj, const ccHObjectContext &context) override
Adds back object to DB tree.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
virtual void setRedraw(bool state)
Sets entity redraw mode.
virtual void resetGLTransformation()
Resets associated GL transformation.
virtual void setGLTransformation(const ccGLMatrix &trans)
Associates entity with a GL transformation (rotation + translation)
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
ccGLMatrixTpl< T > transposed() const
Returns transposed matrix.
T * data()
Returns a pointer to internal data.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
void initFromParameters(T alpha_rad, const Vector3Tpl< T > &axis3D, const Vector3Tpl< T > &t3D)
Inits transformation from a rotation axis, an angle and a translation.
void getParameters(T &alpha_rad, Vector3Tpl< T > &axis3D, Vector3Tpl< T > &t3D) const
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
virtual bool start() override
Starts process.
void onShortcutTriggered(int)
To capture overridden shortcuts (pause button, etc.)
void setRotationCenter(CCVector3d &center)
Sets the rotation center.
void pause(bool)
Pauses the transformation mode.
virtual ~ccGraphicalTransformationTool()
Default destructor.
void apply()
Applies transformation to selected entities.
bool addEntity(ccHObject *anObject)
Adds an entity to the 'selected' entities set.
CCVector3d m_rotationCenter
Rotation center.
void clear()
Clear all variables and 'unlink' dialog.
unsigned getNumberOfValidEntities() const
Returns the number of valid entities (see addEntity)
virtual bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
ccGraphicalTransformationTool(QWidget *parent)
Default constructor.
ccHObject m_toTransform
List of entities to be transformed.
virtual void stop(bool state) override
Stops process/dialog.
void cancel()
Cancels (no transformation is applied)
bool setTansformTool(ecvGenericTransformTool *tool)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void detachChild(ccHObject *child)
Detaches a specific child.
bool isAncestorOf(const ccHObject *anObject) const
Returns true if the current object is an ancestor of the specified one.
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
void setRedrawFlagRecursive(bool redraw=false)
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
void detachAllChildren()
Removes a specific child.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
Triangular mesh.
Definition: ecvMesh.h:35
void transformTriNormals(const ccGLMatrix &trans)
Transforms the mesh per-triangle normals.
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
virtual bool isLocked() const
Returns whether the object is locked or not.
Definition: ecvObject.h:112
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
virtual unsigned getUniqueID() const
Returns object unique ID.
Definition: ecvObject.h:86
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
Generic overlay dialog interface.
void shortcutTriggered(int key)
Signal emitted when an overridden key shortcut is pressed.
virtual void stop(bool accepted)
Stops process/dialog.
virtual bool start()
Starts process.
bool m_processing
Running/processing state.
virtual bool linkWith(QWidget *win)
Links the overlay dialog with a MDI window.
void addOverridenShortcut(Qt::Key key)
static QWidget * GetCurrentScreen()
static void SetRedrawRecursive(bool redraw=false)
static void DisplayNewMessage(const QString &message, MessagePosition pos, bool append=false, int displayMaxDelay_sec=2, MessageType type=CUSTOM_MESSAGE)
Displays a status message in the bottom-left corner.
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
Generic Annotation Tool interface.
virtual const ccGLMatrixd getFinalTransformation()=0
virtual void clear()=0
virtual void reset()=0
virtual void setTranlationMode(TranslationMOde mode)=0
virtual void setShearEnabled(bool state)=0
virtual void getOutput(std::vector< ccHObject * > &out)=0
virtual void showInteractor(bool state)=0
virtual void setScaleEnabled(bool state)=0
virtual void setRotationMode(RotationMode mode)=0
virtual bool start()=0
virtual void stop()=0
virtual bool setInputData(ccHObject *entity, int viewport=0)
@ SENSOR
Definition: CVTypes.h:116
@ MESH
Definition: CVTypes.h:105
@ COORDINATESYSTEM
Definition: CVTypes.h:145
@ POINT_CLOUD
Definition: CVTypes.h:104
@ FACET
Definition: CVTypes.h:109
Backup "context" for an object.