ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvBoundingBoxEditorDlg.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 // SYSTEM
11 #include <limits>
12 
13 // Qt
14 #include <QClipboard>
15 
16 // Box state at last dialog execution
18 
20  : QDialog(parent, Qt::Tool),
21  Ui::BoundingBoxEditorDialog(),
22  m_baseBoxIsMinimal(false),
23  m_showInclusionWarning(true) {
24  setupUi(this);
25 
26  showBoxAxes(false);
27 
28  xDoubleSpinBox->setMinimum(-1.0e9);
29  yDoubleSpinBox->setMinimum(-1.0e9);
30  zDoubleSpinBox->setMinimum(-1.0e9);
31  xDoubleSpinBox->setMaximum(1.0e9);
32  yDoubleSpinBox->setMaximum(1.0e9);
33  zDoubleSpinBox->setMaximum(1.0e9);
34 
35  dxDoubleSpinBox->setMinimum(0.0);
36  dyDoubleSpinBox->setMinimum(0.0);
37  dzDoubleSpinBox->setMinimum(0.0);
38  dxDoubleSpinBox->setMaximum(1.0e9);
39  dyDoubleSpinBox->setMaximum(1.0e9);
40  dzDoubleSpinBox->setMaximum(1.0e9);
41 
42  connect(keepSquareCheckBox, &QCheckBox::toggled, this,
44  connect(okPushButton, &QPushButton::clicked, this,
46  connect(cancelPushButton, &QPushButton::clicked, this,
48  connect(defaultPushButton, &QPushButton::clicked, this,
50  connect(lastPushButton, &QPushButton::clicked, this,
52  connect(fromClipboardPushButton, &QPushButton::clicked, this,
54  connect(toClipboardPushButton, &QPushButton::clicked, this,
56 
57  connect(pointTypeComboBox,
58  static_cast<void (QComboBox::*)(int)>(
59  &QComboBox::currentIndexChanged),
61 
62  connect(xDoubleSpinBox,
63  static_cast<void (QDoubleSpinBox::*)(double)>(
64  &QDoubleSpinBox::valueChanged),
66  connect(yDoubleSpinBox,
67  static_cast<void (QDoubleSpinBox::*)(double)>(
68  &QDoubleSpinBox::valueChanged),
70  connect(zDoubleSpinBox,
71  static_cast<void (QDoubleSpinBox::*)(double)>(
72  &QDoubleSpinBox::valueChanged),
74 
75  connect(dxDoubleSpinBox,
76  static_cast<void (QDoubleSpinBox::*)(double)>(
77  &QDoubleSpinBox::valueChanged),
79  connect(dyDoubleSpinBox,
80  static_cast<void (QDoubleSpinBox::*)(double)>(
81  &QDoubleSpinBox::valueChanged),
83  connect(dzDoubleSpinBox,
84  static_cast<void (QDoubleSpinBox::*)(double)>(
85  &QDoubleSpinBox::valueChanged),
87 
88  connect(xOriXDoubleSpinBox,
89  static_cast<void (QDoubleSpinBox::*)(double)>(
90  &QDoubleSpinBox::valueChanged),
92  connect(xOriYDoubleSpinBox,
93  static_cast<void (QDoubleSpinBox::*)(double)>(
94  &QDoubleSpinBox::valueChanged),
96  connect(xOriZDoubleSpinBox,
97  static_cast<void (QDoubleSpinBox::*)(double)>(
98  &QDoubleSpinBox::valueChanged),
100  connect(yOriXDoubleSpinBox,
101  static_cast<void (QDoubleSpinBox::*)(double)>(
102  &QDoubleSpinBox::valueChanged),
104  connect(yOriYDoubleSpinBox,
105  static_cast<void (QDoubleSpinBox::*)(double)>(
106  &QDoubleSpinBox::valueChanged),
108  connect(yOriZDoubleSpinBox,
109  static_cast<void (QDoubleSpinBox::*)(double)>(
110  &QDoubleSpinBox::valueChanged),
112  connect(zOriXDoubleSpinBox,
113  static_cast<void (QDoubleSpinBox::*)(double)>(
114  &QDoubleSpinBox::valueChanged),
116  connect(zOriYDoubleSpinBox,
117  static_cast<void (QDoubleSpinBox::*)(double)>(
118  &QDoubleSpinBox::valueChanged),
120  connect(zOriZDoubleSpinBox,
121  static_cast<void (QDoubleSpinBox::*)(double)>(
122  &QDoubleSpinBox::valueChanged),
124 
125  defaultPushButton->setVisible(false);
126  lastPushButton->setVisible(s_lastBBox.isValid());
128 }
129 
130 // Helper
131 void MakeSquare(ccBBox& box, int pivotType, int defaultDim = -1) {
132  assert(defaultDim < 3);
133  assert(pivotType >= 0 && pivotType < 3);
134 
135  CCVector3 W = box.getDiagVec();
136  if (W.x != W.y || W.x != W.z) {
137  if (defaultDim < 0) {
138  // we take the largest one!
139  defaultDim = 0;
140  if (W.u[1] > W.u[defaultDim]) defaultDim = 1;
141  if (W.u[2] > W.u[defaultDim]) defaultDim = 2;
142  }
143 
144  CCVector3 newW(W.u[defaultDim], W.u[defaultDim], W.u[defaultDim]);
145  switch (pivotType) {
146  case 0: // min corner
147  {
148  CCVector3 A = box.minCorner();
149  box = ccBBox(A, A + newW);
150  } break;
151  case 1: // center
152  {
153  CCVector3 C = box.getCenter();
154  box = ccBBox(C - newW / 2.0, C + newW / 2.0);
155  } break;
156  case 2: // max corner
157  {
158  CCVector3 B = box.maxCorner();
159  box = ccBBox(B - newW, B);
160  } break;
161  }
162  }
163 }
164 
166  return keepSquareCheckBox->isChecked();
167 }
168 
170  if (state) keepSquareCheckBox->setChecked(true);
171  keepSquareCheckBox->setDisabled(state);
172 }
173 
175  if (state) {
176  MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex());
177  reflectChanges();
178  }
179 }
180 
181 void ccBoundingBoxEditorDlg::set2DMode(bool state, unsigned char dim) {
182  bool hideX = (state && dim == 0);
183  bool hideY = (state && dim == 1);
184  bool hideZ = (state && dim == 2);
185 
186  xDoubleSpinBox->setHidden(hideX);
187  dxDoubleSpinBox->setHidden(hideX);
188  xLabel->setHidden(hideX);
189 
190  yDoubleSpinBox->setHidden(hideY);
191  dyDoubleSpinBox->setHidden(hideY);
192  yLabel->setHidden(hideY);
193 
194  zDoubleSpinBox->setHidden(hideZ);
195  dzDoubleSpinBox->setHidden(hideZ);
196  zLabel->setHidden(hideZ);
197 }
198 
200  bool isMinimal /*=true*/) {
201  // set new default one
202  m_initBBox = m_baseBBox = box;
203  m_baseBoxIsMinimal = isMinimal;
204 
205  defaultPushButton->setVisible(m_baseBBox.isValid());
206 
207  resetToDefault();
208 }
209 
211  bool exclude = false;
212  if (m_baseBBox.isValid()) {
213  exclude = !m_currentBBox.contains(m_baseBBox.minCorner()) ||
215  }
216 
217  warningLabel->setVisible(m_showInclusionWarning && exclude);
218  okPushButton->setEnabled(!m_baseBoxIsMinimal || !exclude);
219 }
220 
223 
224  if (keepSquare())
225  squareModeActivated(true); // will call reflectChanges
226  else
227  reflectChanges();
228 
230 }
231 
234 
235  if (keepSquare())
236  squareModeActivated(true); // will call reflectChanges
237  else
238  reflectChanges();
239 
241 }
242 
244  if (oriGroupBox->isVisible()) {
245  CCVector3d X;
246  CCVector3d Y;
247  CCVector3d Z;
248  getBoxAxes(X, Y, Z);
249  X.normalize();
250  Y.normalize();
251  Z.normalize();
252  if (X.norm2d() == 0 || Y.norm2d() == 0 || Z.norm2d() == 0) {
253  CVLog::Error(
254  "Invalid axes definition: at least two vectors are "
255  "colinear");
256  return;
257  }
258 
259  // if ( fabs(X.dot(Y)) > 1.0e-6
260  // || fabs(Y.dot(Z)) > 1.0e-6
261  // || fabs(Z.dot(X)) > 1.0e-6 )
262  //{
263  // CVLog::Error("Invalid axes definition: vectors must be
264  // orthogonal"); return;
265  // }
266  }
267 
269 
270  accept();
271 }
272 
274  // backup current box
276 
277  // call 'true' exec
278  return QDialog::exec();
279 }
280 
282  // restore init. box
284 
285  reject();
286 }
287 
289  updateCurrentBBox(value);
290  if (keepSquare()) {
291  MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 0);
292  reflectChanges();
293  // base box (if valid) should always be included!
295  }
296 }
297 
299  updateCurrentBBox(value);
300  if (keepSquare()) {
301  MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 1);
302  reflectChanges();
303  // base box (if valid) should always be included!
305  }
306 }
307 
309  updateCurrentBBox(value);
310  if (keepSquare()) {
311  MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 2);
312  reflectChanges();
313  // base box (if valid) should always be included!
315  }
316 }
317 
319  CCVector3 A(static_cast<PointCoordinateType>(xDoubleSpinBox->value()),
320  static_cast<PointCoordinateType>(yDoubleSpinBox->value()),
321  static_cast<PointCoordinateType>(zDoubleSpinBox->value()));
322  CCVector3 W(static_cast<PointCoordinateType>(dxDoubleSpinBox->value()),
323  static_cast<PointCoordinateType>(dyDoubleSpinBox->value()),
324  static_cast<PointCoordinateType>(dzDoubleSpinBox->value()));
325 
326  switch (pointTypeComboBox->currentIndex()) {
327  case 0: // A = min corner
328  m_currentBBox = ccBBox(A, A + W);
329  break;
330  case 1: // A = center
331  m_currentBBox = ccBBox(A - W / 2.0, A + W / 2.0);
332  break;
333  case 2: // A = max corner
334  m_currentBBox = ccBBox(A - W, A);
335  break;
336  default:
337  assert(false);
338  return;
339  }
340 
341  // base box (if valid) should always be included!
343 }
344 
346  // left column
347  {
348  xDoubleSpinBox->blockSignals(true);
349  yDoubleSpinBox->blockSignals(true);
350  zDoubleSpinBox->blockSignals(true);
351 
352  switch (pointTypeComboBox->currentIndex()) {
353  case 0: // A = min corner
354  {
355  const CCVector3& A = m_currentBBox.minCorner();
356  xDoubleSpinBox->setValue(A.x);
357  yDoubleSpinBox->setValue(A.y);
358  zDoubleSpinBox->setValue(A.z);
359  } break;
360  case 1: // A = center
361  {
363  xDoubleSpinBox->setValue(C.x);
364  yDoubleSpinBox->setValue(C.y);
365  zDoubleSpinBox->setValue(C.z);
366  } break;
367  case 2: // A = max corner
368  {
369  const CCVector3& B = m_currentBBox.maxCorner();
370  xDoubleSpinBox->setValue(B.x);
371  yDoubleSpinBox->setValue(B.y);
372  zDoubleSpinBox->setValue(B.z);
373  } break;
374  default:
375  assert(false);
376  return;
377  }
378 
379  xDoubleSpinBox->blockSignals(false);
380  yDoubleSpinBox->blockSignals(false);
381  zDoubleSpinBox->blockSignals(false);
382  }
383 
384  // right column
385  {
386  dxDoubleSpinBox->blockSignals(true);
387  dyDoubleSpinBox->blockSignals(true);
388  dzDoubleSpinBox->blockSignals(true);
389 
391  // if 'square mode' is on, all width values should be the same!
392  assert(!keepSquare() || fabs(W.x - W.y) * 1.0e-6 < 1.0 &&
393  fabs(W.x - W.z) * 1.0e-6 < 1.0);
394  dxDoubleSpinBox->setValue(W.x);
395  dyDoubleSpinBox->setValue(W.y);
396  dzDoubleSpinBox->setValue(W.z);
397 
398  dxDoubleSpinBox->blockSignals(false);
399  dyDoubleSpinBox->blockSignals(false);
400  dzDoubleSpinBox->blockSignals(false);
401  }
402 }
403 
405  oriGroupBox->setVisible(state);
406 
407  resize(QSize(600, state ? 400 : 250));
408 }
409 
411  const CCVector3& Y,
412  const CCVector3& Z) {
413  // if (xOriFrame->isEnabled())
414  {
415  xOriXDoubleSpinBox->setValue(X.x);
416  xOriYDoubleSpinBox->setValue(X.y);
417  xOriZDoubleSpinBox->setValue(X.z);
418  }
419 
420  // if (yOriFrame->isEnabled())
421  {
422  yOriXDoubleSpinBox->setValue(Y.x);
423  yOriYDoubleSpinBox->setValue(Y.y);
424  yOriZDoubleSpinBox->setValue(Y.z);
425  }
426 
427  // if (zOriFrame->isEnabled())
428  {
429  zOriXDoubleSpinBox->setValue(Z.x);
430  zOriYDoubleSpinBox->setValue(Z.y);
431  zOriZDoubleSpinBox->setValue(Z.z);
432  }
433 }
434 
436  CCVector3d& Y,
437  CCVector3d& Z) {
438  X = CCVector3d(xOriXDoubleSpinBox->value(), xOriYDoubleSpinBox->value(),
439  xOriZDoubleSpinBox->value());
440 
441  Y = CCVector3d(yOriXDoubleSpinBox->value(), yOriYDoubleSpinBox->value(),
442  yOriZDoubleSpinBox->value());
443 
444  Z = CCVector3d(zOriXDoubleSpinBox->value(), zOriYDoubleSpinBox->value(),
445  zOriZDoubleSpinBox->value());
446 }
447 
449  CCVector3d X;
450  CCVector3d Y;
451  CCVector3d Z;
452  getBoxAxes(X, Y, Z);
453 
454  QDoubleSpinBox* vecSpinBoxes[3] = {nullptr, nullptr, nullptr};
455  CCVector3d N(0, 0, 0);
456  if (oriXCheckBox->isChecked()) {
457  N = Y.cross(Z);
458  vecSpinBoxes[0] = xOriXDoubleSpinBox;
459  vecSpinBoxes[1] = xOriYDoubleSpinBox;
460  vecSpinBoxes[2] = xOriZDoubleSpinBox;
461  } else if (oriYCheckBox->isChecked()) {
462  N = Z.cross(X);
463  vecSpinBoxes[0] = yOriXDoubleSpinBox;
464  vecSpinBoxes[1] = yOriYDoubleSpinBox;
465  vecSpinBoxes[2] = yOriZDoubleSpinBox;
466  } else if (oriZCheckBox->isChecked()) {
467  N = X.cross(Y);
468  vecSpinBoxes[0] = zOriXDoubleSpinBox;
469  vecSpinBoxes[1] = zOriYDoubleSpinBox;
470  vecSpinBoxes[2] = zOriZDoubleSpinBox;
471  } else {
472  assert(false);
473  }
474 
475  for (int i = 0; i < 3; ++i) {
476  vecSpinBoxes[i]->blockSignals(true);
477  vecSpinBoxes[i]->setValue(N.u[i]);
478  vecSpinBoxes[i]->blockSignals(false);
479  }
480 }
481 
483  QClipboard* clipboard = QApplication::clipboard();
484  if (clipboard) {
485  QString clipText = clipboard->text();
486  if (!clipText.isEmpty()) {
487  bool success = false;
488  ccGLMatrix matrix = ccGLMatrix::FromString(clipText, success);
489  if (success) {
490  // set center
492  CCVector3 delta = matrix.getTranslationAsVec3D() - C;
493  m_currentBBox += delta;
494  reflectChanges();
495  // change axes
496  setBoxAxes(matrix.getColumnAsVec3D(0),
497  matrix.getColumnAsVec3D(1),
498  matrix.getColumnAsVec3D(2));
499  } else {
500  CVLog::Error("Failed to extract matrix from clipboard");
501  }
502  } else {
503  CVLog::Error("Clipboard is empty");
504  }
505  }
506 }
507 
509  QClipboard* clipboard = QApplication::clipboard();
510  if (clipboard) {
512 
513  CCVector3d X;
514  CCVector3d Y;
515  CCVector3d Z;
516  getBoxAxes(X, Y, Z);
517 
518  ccGLMatrix matrix;
519  matrix.setColumn(0, CCVector3::fromArray(X.u));
520  matrix.setColumn(1, CCVector3::fromArray(Y.u));
521  matrix.setColumn(2, CCVector3::fromArray(Z.u));
522  matrix.setTranslation(C);
523 
524  clipboard->setText(matrix.toString());
525  CVLog::Print("Matrix saved to clipboard:");
526  CVLog::Print(matrix.toString(12, ' ')); // full precision
527  }
528 }
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
void * X
Definition: SmallVector.cpp:45
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
void normalize()
Sets vector norm to unity.
Definition: CVGeom.h:428
double norm2d() const
Returns vector square norm (forces double precision output)
Definition: CVGeom.h:419
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
Bounding box structure.
Definition: ecvBBox.h:25
void showBoxAxes(bool state)
Whether to display or not the box axes.
void updateCurrentBBox(double dummy=0.0)
Updates current box based on the dialog state.
void forceKeepSquare(bool state)
Forces the 'keep square' mode.
ccBBox m_baseBBox
Base box (invalid if none)
void onAxisValueChanged(double)
Slot called anytime a component of the box axes is modified.
void getBoxAxes(CCVector3d &X, CCVector3d &Y, CCVector3d &Z)
Returns the box axes.
void checkBaseInclusion()
Checks if currentBox includes baseBox.
bool m_baseBoxIsMinimal
Whether base box is minimal or not.
void set2DMode(bool state, unsigned char dim)
Sets 2D mode (the line 'dim' will be hidden)
void reflectChanges(int dummy=0)
Reflects changes on bbox.
ccBBox m_initBBox
Box state at dialog start.
ccBBox m_currentBBox
Current box.
ccBoundingBoxEditorDlg(QWidget *parent=0)
Default constructor.
void setBaseBBox(const ccBBox &box, bool isMinimal=true)
Sets the (minimal) base box.
bool m_showInclusionWarning
Whether to show 'inclusion' warning or not.
bool keepSquare() const
Returns whether 'keep square' mode is enabled or not.
void setBoxAxes(const CCVector3 &X, const CCVector3 &Y, const CCVector3 &Z)
Sets the box axes.
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
void setColumn(unsigned index, const Vector3Tpl< T > &v)
Sets the content of a given column.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Vector3Tpl< T > getColumnAsVec3D(unsigned index) const
Returns a copy of a given column as a CCVector3.
static ccGLMatrixTpl< float > FromString(const QString &matText, bool &success)
Converts a 'text' matrix to a ccGLMatrix.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Vector3Tpl< T > getDiagVec() const
Returns diagonal vector.
Definition: BoundingBox.h:169
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
bool contains(const Vector3Tpl< T > &P) const
Returns whether a points is inside the box or not.
Definition: BoundingBox.h:231
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
Definition: BoundingBox.h:156
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
static ccBBox s_lastBBox
void MakeSquare(ccBBox &box, int pivotType, int defaultDim=-1)