ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvApplyTransformationDlg.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 // Local
11 #include "MainWindow.h"
13 #include "ecvPersistentSettings.h"
14 #include "ui_dipDirTransformationDlg.h"
15 
16 // CV_CORE_LIB
17 #include <CVConst.h>
18 
19 // CV_DB_LIB
20 #include <ecvFileUtils.h>
21 #include <ecvNormalVectors.h>
22 
23 // Qt
24 #include <QClipboard>
25 #include <QFileDialog>
26 #include <QFileInfo>
27 #include <QMessageBox>
28 #include <QSettings>
29 
30 // Qt5/Qt6 Compatibility
31 #include <QtCompat.h>
32 
33 static QString s_lastMatrix(
34  "1.00000000 0.00000000 0.00000000 0.00000000\n0.00000000 1.00000000 "
35  "0.00000000 0.00000000\n0.00000000 0.00000000 1.00000000 "
36  "0.00000000\n0.00000000 0.00000000 0.00000000 1.00000000");
37 static bool s_inverseMatrix = false;
38 static int s_currentFormIndex = 0;
39 
41 class DipDirTransformationDialog : public QDialog,
42  public Ui::DipDirTransformationDialog {
43  Q_OBJECT
44 
45 public:
46  DipDirTransformationDialog(QWidget* parent = 0) : QDialog(parent) {
47  setupUi(this);
48  }
49 };
50 
52  : QDialog(parent), Ui::ApplyTransformationDialog() {
53  setupUi(this);
54 
55  helpTextEdit->setVisible(false);
56 
57  // restore last state
58  matrixTextEdit->setPlainText(s_lastMatrix);
59  inverseCheckBox->setChecked(s_inverseMatrix);
60  onMatrixTextChange(); // provoke the update of the other forms
61  tabWidget->setCurrentIndex(s_currentFormIndex);
62 
63  connect(buttonBox, &QDialogButtonBox::accepted, this,
65  connect(buttonBox, &QDialogButtonBox::clicked, this,
67 
68  connect(matrixTextEdit, &QPlainTextEdit::textChanged, this,
70  connect(fromFileToolButton, &QToolButton::clicked, this,
72  connect(fromClipboardToolButton, &QToolButton::clicked, this,
74  connect(fromDipDipDirToolButton, &QToolButton::clicked, this,
76 
77  connect(rxAxisDoubleSpinBox,
78  static_cast<void (QDoubleSpinBox::*)(double)>(
79  &QDoubleSpinBox::valueChanged),
81  connect(ryAxisDoubleSpinBox,
82  static_cast<void (QDoubleSpinBox::*)(double)>(
83  &QDoubleSpinBox::valueChanged),
85  connect(rzAxisDoubleSpinBox,
86  static_cast<void (QDoubleSpinBox::*)(double)>(
87  &QDoubleSpinBox::valueChanged),
89  connect(rAngleDoubleSpinBox,
90  static_cast<void (QDoubleSpinBox::*)(double)>(
91  &QDoubleSpinBox::valueChanged),
93  connect(txAxisDoubleSpinBox,
94  static_cast<void (QDoubleSpinBox::*)(double)>(
95  &QDoubleSpinBox::valueChanged),
97  connect(tyAxisDoubleSpinBox,
98  static_cast<void (QDoubleSpinBox::*)(double)>(
99  &QDoubleSpinBox::valueChanged),
101  connect(tzAxisDoubleSpinBox,
102  static_cast<void (QDoubleSpinBox::*)(double)>(
103  &QDoubleSpinBox::valueChanged),
105 
106  connect(ePhiDoubleSpinBox,
107  static_cast<void (QDoubleSpinBox::*)(double)>(
108  &QDoubleSpinBox::valueChanged),
110  connect(eThetaDoubleSpinBox,
111  static_cast<void (QDoubleSpinBox::*)(double)>(
112  &QDoubleSpinBox::valueChanged),
114  connect(ePsiDoubleSpinBox,
115  static_cast<void (QDoubleSpinBox::*)(double)>(
116  &QDoubleSpinBox::valueChanged),
118  connect(etxAxisDoubleSpinBox,
119  static_cast<void (QDoubleSpinBox::*)(double)>(
120  &QDoubleSpinBox::valueChanged),
122  connect(etyAxisDoubleSpinBox,
123  static_cast<void (QDoubleSpinBox::*)(double)>(
124  &QDoubleSpinBox::valueChanged),
126  connect(etzAxisDoubleSpinBox,
127  static_cast<void (QDoubleSpinBox::*)(double)>(
128  &QDoubleSpinBox::valueChanged),
130 }
131 
133  QString text = matrixTextEdit->toPlainText();
134  if (text.contains("[")) {
135  // automatically remove anything between square brackets
136  // Use QtCompat for Qt5/Qt6 compatibility
137  static const QtCompatRegExp squareBracketsFilter("\\[([^]]+)\\]");
138  text.replace(squareBracketsFilter, "");
139  matrixTextEdit->blockSignals(true);
140  matrixTextEdit->setPlainText(text);
141  matrixTextEdit->blockSignals(false);
142  }
143 
144  bool valid = false;
145  ccGLMatrix mat = ccGLMatrix::FromString(text, valid);
146  if (valid)
147  updateAll(mat, false, true, true); // no need to update the current
148  // form
149 }
150 
152  PointCoordinateType alpha = 0;
153  CCVector3 axis, t;
154 
155  axis.x = static_cast<PointCoordinateType>(rxAxisDoubleSpinBox->value());
156  axis.y = static_cast<PointCoordinateType>(ryAxisDoubleSpinBox->value());
157  axis.z = static_cast<PointCoordinateType>(rzAxisDoubleSpinBox->value());
158  alpha = static_cast<PointCoordinateType>(
159  cloudViewer::DegreesToRadians(rAngleDoubleSpinBox->value()));
160  t.x = static_cast<PointCoordinateType>(txAxisDoubleSpinBox->value());
161  t.y = static_cast<PointCoordinateType>(tyAxisDoubleSpinBox->value());
162  t.z = static_cast<PointCoordinateType>(tzAxisDoubleSpinBox->value());
163 
164  ccGLMatrix mat;
165  mat.initFromParameters(alpha, axis, t);
166 
167  updateAll(mat, true, false, true); // no need to update the current form
168 }
169 
171  PointCoordinateType phi, theta, psi = 0;
172  CCVector3 t;
173 
174  phi = static_cast<PointCoordinateType>(
175  cloudViewer::DegreesToRadians(ePhiDoubleSpinBox->value()));
176  theta = static_cast<PointCoordinateType>(
177  cloudViewer::DegreesToRadians(eThetaDoubleSpinBox->value()));
178  psi = static_cast<PointCoordinateType>(
179  cloudViewer::DegreesToRadians(ePsiDoubleSpinBox->value()));
180  t.x = static_cast<PointCoordinateType>(etxAxisDoubleSpinBox->value());
181  t.y = static_cast<PointCoordinateType>(etyAxisDoubleSpinBox->value());
182  t.z = static_cast<PointCoordinateType>(etzAxisDoubleSpinBox->value());
183 
184  ccGLMatrix mat;
185  mat.initFromParameters(phi, theta, psi, t);
186 
187  updateAll(mat, true, true, false); // no need to update the current form
188 }
189 
191  bool textForm /*=true*/,
192  bool axisAngleForm /*=true*/,
193  bool eulerForm /*=true*/) {
194  if (textForm) {
195  QString matText = mat.toString();
196  matrixTextEdit->blockSignals(true);
197  matrixTextEdit->setPlainText(matText);
198  matrixTextEdit->blockSignals(false);
199  }
200 
201  if (axisAngleForm) {
202  rxAxisDoubleSpinBox->blockSignals(true);
203  ryAxisDoubleSpinBox->blockSignals(true);
204  rzAxisDoubleSpinBox->blockSignals(true);
205  rAngleDoubleSpinBox->blockSignals(true);
206  txAxisDoubleSpinBox->blockSignals(true);
207  tyAxisDoubleSpinBox->blockSignals(true);
208  tzAxisDoubleSpinBox->blockSignals(true);
209 
210  PointCoordinateType alpha = 0;
211  CCVector3 axis, t;
212  mat.getParameters(alpha, axis, t);
213 
214  rxAxisDoubleSpinBox->setValue(axis.x);
215  ryAxisDoubleSpinBox->setValue(axis.y);
216  rzAxisDoubleSpinBox->setValue(axis.z);
217  rAngleDoubleSpinBox->setValue(cloudViewer::DegreesToRadians(alpha));
218  txAxisDoubleSpinBox->setValue(t.x);
219  tyAxisDoubleSpinBox->setValue(t.y);
220  tzAxisDoubleSpinBox->setValue(t.z);
221 
222  rxAxisDoubleSpinBox->blockSignals(false);
223  ryAxisDoubleSpinBox->blockSignals(false);
224  rzAxisDoubleSpinBox->blockSignals(false);
225  rAngleDoubleSpinBox->blockSignals(false);
226  txAxisDoubleSpinBox->blockSignals(false);
227  tyAxisDoubleSpinBox->blockSignals(false);
228  tzAxisDoubleSpinBox->blockSignals(false);
229  }
230 
231  if (eulerForm) {
232  ePhiDoubleSpinBox->blockSignals(true);
233  eThetaDoubleSpinBox->blockSignals(true);
234  ePsiDoubleSpinBox->blockSignals(true);
235  etxAxisDoubleSpinBox->blockSignals(true);
236  etyAxisDoubleSpinBox->blockSignals(true);
237  etzAxisDoubleSpinBox->blockSignals(true);
238 
239  PointCoordinateType phi, theta, psi = 0;
240  CCVector3 t;
241  mat.getParameters(phi, theta, psi, t);
242 
243  ePhiDoubleSpinBox->setValue(cloudViewer::DegreesToRadians(phi));
244  eThetaDoubleSpinBox->setValue(cloudViewer::DegreesToRadians(theta));
245  ePsiDoubleSpinBox->setValue(cloudViewer::DegreesToRadians(psi));
246  etxAxisDoubleSpinBox->setValue(t.x);
247  etyAxisDoubleSpinBox->setValue(t.y);
248  etzAxisDoubleSpinBox->setValue(t.z);
249 
250  ePhiDoubleSpinBox->blockSignals(false);
251  eThetaDoubleSpinBox->blockSignals(false);
252  ePsiDoubleSpinBox->blockSignals(false);
253  etxAxisDoubleSpinBox->blockSignals(false);
254  etyAxisDoubleSpinBox->blockSignals(false);
255  etzAxisDoubleSpinBox->blockSignals(false);
256  }
257 }
258 
260  // get current input matrix text
261  QString matText = matrixTextEdit->toPlainText();
262  // convert it to a ccGLMatrix
263  bool valid = false;
264  ccGLMatrixd mat = ccGLMatrixd::FromString(matText, valid);
265  assert(valid);
266  // eventually invert it if necessary
267  if (inverseCheckBox->isChecked()) {
268  mat.invert();
269  }
270 
271  return mat;
272 }
273 
275  // get current input matrix text
276  QString matText = matrixTextEdit->toPlainText();
277  // convert it to a ccGLMatrix
278  bool valid = false;
279  ccGLMatrix mat = ccGLMatrix::FromString(matText, valid);
280 
281  if (!valid) {
282  QMessageBox::warning(this, "Invalid matrix",
283  "Matrix is invalid. Make sure to only use white "
284  "spaces or tabulations between the 16 elements");
285  return;
286  }
287 
288  accept();
289 
290  s_lastMatrix = matrixTextEdit->toPlainText();
291  s_inverseMatrix = inverseCheckBox->isChecked();
292  s_currentFormIndex = tabWidget->currentIndex();
293 }
294 
296  // persistent settings
297  QSettings settings;
298  settings.beginGroup(ecvPS::LoadFile());
299  QString currentPath =
301  .toString();
302 
303  QString inputFilename = QFileDialog::getOpenFileName(
304  this, "Select input file", currentPath, "*.txt");
305  if (inputFilename.isEmpty()) return;
306 
307  ccGLMatrixd mat;
308  if (mat.fromAsciiFile(inputFilename)) {
309  matrixTextEdit->setPlainText(mat.toString());
310  } else {
311  CVLog::Error(QString("Failed to load file '%1'").arg(inputFilename));
312  }
313 
314  // save last loading location
315  settings.setValue(ecvPS::CurrentPath(),
316  QFileInfo(inputFilename).absolutePath());
317  settings.endGroup();
318 }
319 
321  QClipboard* clipboard = QApplication::clipboard();
322  if (clipboard) {
323  QString clipText = clipboard->text();
324  if (!clipText.isEmpty())
325  matrixTextEdit->setPlainText(clipText);
326  else
327  CVLog::Warning("[ccApplyTransformationDlg] Clipboard is empty");
328  }
329 }
330 
332  static double s_dip_deg = 0.0;
333  static double s_dipDir_deg = 0.0;
334  static bool s_rotateAboutCenter = false;
335  DipDirTransformationDialog dddDlg(this);
336  dddDlg.dipDoubleSpinBox->setValue(s_dip_deg);
337  dddDlg.dipDirDoubleSpinBox->setValue(s_dipDir_deg);
338  dddDlg.rotateAboutCenterCheckBox->setChecked(s_rotateAboutCenter);
339 
340  if (!dddDlg.exec()) {
341  return;
342  }
343 
344  s_dip_deg = dddDlg.dipDoubleSpinBox->value();
345  s_dipDir_deg = dddDlg.dipDirDoubleSpinBox->value();
346  s_rotateAboutCenter = dddDlg.rotateAboutCenterCheckBox->isChecked();
347 
348  // resulting normal vector
350  static_cast<PointCoordinateType>(s_dip_deg),
351  static_cast<PointCoordinateType>(s_dipDir_deg));
352  // corresponding rotation (assuming we start from (0, 0, 1))
353 
354  ccGLMatrix trans = ccGLMatrix::FromToRotation(CCVector3(0, 0, 1), Nd);
355 
356  if (s_rotateAboutCenter && MainWindow::TheInstance()) {
357  const ccHObject::Container& selectedEntities =
359  ccBBox box;
360  for (ccHObject* obj : selectedEntities) {
361  box += obj->getBB_recursive();
362  }
363 
364  if (box.isValid()) {
365  CCVector3 C = box.getCenter();
366  ccGLMatrix shiftToCenter;
367  shiftToCenter.setTranslation(-C);
368  ccGLMatrix backToOrigin;
369  backToOrigin.setTranslation(C);
370  trans = backToOrigin * trans * shiftToCenter;
371  }
372  }
373 
374  updateAll(trans, true, true, true);
375 }
376 
377 void ccApplyTransformationDlg::buttonClicked(QAbstractButton* button) {
378  if (buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole) {
379  updateAll(ccGLMatrix(), true, true, true);
380  inverseCheckBox->setChecked(false);
381  }
382 }
383 
384 #include "ecvApplyTransformationDlg.moc"
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
QRegularExpression QtCompatRegExp
Definition: QtCompat.h:170
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Dialog to define a dip / dip dir. transformation.
const ccHObject::Container & getSelectedEntities() const override
Returns currently selected entities ("read only")
Definition: MainWindow.h:238
static MainWindow * TheInstance()
Returns the unique instance of this object.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void onEulerValueChanged(double)
Updates dialog when a component of the euleur form changes.
void updateAll(const ccGLMatrix &mat, bool textForm=true, bool axisAngleForm=true, bool eulerForm=true)
Updates all forms with a given matrix.
void onRotAngleValueChanged(double)
Updates dialog when a component of the rotation axis/angle form changes.
ccApplyTransformationDlg(QWidget *parent=0)
Default constructor.
ccGLMatrixd getTransformation() const
Returns input matrix.
void loadFromASCIIFile()
Loads matrix from ASCII file.
void buttonClicked(QAbstractButton *)
Signal called when a button is clicked.
void checkMatrixValidityAndAccept()
Checks matrix validity and 'accept' dialog if ok.
void loadFromClipboard()
Loads matrix from clipboard ("paste")
void initFromDipAndDipDir()
Inits matrix from dip / dip direction values.
Bounding box structure.
Definition: ecvBBox.h:25
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
static ccGLMatrixTpl< float > FromToRotation(const Vector3Tpl< float > &from, const Vector3Tpl< float > &to)
Creates a transformation matrix that rotates a vector to another.
void invert()
Inverts transformation.
virtual bool fromAsciiFile(QString filename)
Loads matrix from an ASCII file.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
static ccGLMatrixTpl< float > FromString(const QString &matText, bool &success)
Converts a 'text' matrix to a ccGLMatrix.
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
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual ccBBox getBB_recursive(bool withGLFeatures=false, bool onlyEnabledChildren=true)
Returns the bounding-box of this entity and it's children.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
static CCVector3 ConvertDipAndDipDirToNormal(PointCoordinateType dip_deg, PointCoordinateType dipDir_deg, bool upward=true)
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
static const QString CurrentPath()
static const QString LoadFile()
static bool s_inverseMatrix
static QString s_lastMatrix("1.00000000 0.00000000 0.00000000 0.00000000\n0.00000000 1.00000000 " "0.00000000 0.00000000\n0.00000000 0.00000000 1.00000000 " "0.00000000\n0.00000000 0.00000000 0.00000000 1.00000000")
static int s_currentFormIndex
float DegreesToRadians(int degrees)
Convert degrees to radians.
Definition: CVMath.h:98
QString defaultDocPath()
Shortcut for getting the documents location path.
Definition: ecvFileUtils.h:30