| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# This program is free software; you can redistribute it and/or modify #
- //# it under the terms of the GNU General Public License as published by #
- //# the Free Software Foundation; version 2 or later of the License. #
- //# #
- //# This program is distributed in the hope that it will be useful, #
- //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
- //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
- //# GNU General Public License for more details. #
- //# #
- //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
- //# #
- //##########################################################################
- #include "ccBoundingBoxEditorDlg.h"
- //systems
- #include <limits>
- //Qt
- #include <QClipboard>
- //Box state at last dialog execution
- static ccBBox s_lastBBox;
- // Left 'point type' combo box indexes
- static const int MinCornerIndex = 0;
- static const int CenterIndex = 1;
- static const int MaxCornerIndex = 2;
- //Helper
- static void MakeSquare(ccBBox& box, int pivotType, int defaultDim = -1)
- {
- assert(defaultDim < 3);
- assert(pivotType >= 0 && pivotType < 3);
- CCVector3 W = box.getDiagVec();
- if (W.x != W.y || W.x != W.z)
- {
- if (defaultDim < 0)
- {
- //we take the largest one!
- defaultDim = 0;
- if (W.u[1] > W.u[defaultDim])
- defaultDim = 1;
- if (W.u[2] > W.u[defaultDim])
- defaultDim = 2;
- }
- CCVector3 newW(W.u[defaultDim], W.u[defaultDim], W.u[defaultDim]);
- switch (pivotType)
- {
- case 0: //min corner
- {
- CCVector3 A = box.minCorner();
- box = ccBBox(A, A + newW, box.isValid());
- }
- break;
- case 1: //center
- {
- CCVector3 C = box.getCenter();
- box = ccBBox(C - newW / 2.0, C + newW / 2.0, box.isValid());
- }
- break;
- case 2: //max corner
- {
- CCVector3 B = box.maxCorner();
- box = ccBBox(B - newW, B, box.isValid());
- }
- break;
- }
- }
- }
- ccBoundingBoxEditorDlg::ccBoundingBoxEditorDlg(bool showBoxAxes, bool showRasterGridImage, QWidget* parent/*=nullptr*/)
- : QDialog(parent, Qt::Tool)
- , Ui::BoundingBoxEditorDialog()
- , m_baseBoxIsMinimal(false)
- , m_showInclusionWarning(true)
- {
- setupUi(this);
- oriGroupBox->setVisible(showBoxAxes);
- rasterGridImageLabel->setVisible(showRasterGridImage);
- resize(QSize(width(), computeBestDialogHeight(showBoxAxes, showRasterGridImage)));
- xDoubleSpinBox->setMinimum(-1.0e9);
- yDoubleSpinBox->setMinimum(-1.0e9);
- zDoubleSpinBox->setMinimum(-1.0e9);
- xDoubleSpinBox->setMaximum( 1.0e9);
- yDoubleSpinBox->setMaximum( 1.0e9);
- zDoubleSpinBox->setMaximum( 1.0e9);
- dxDoubleSpinBox->setMinimum( 0.0);
- dyDoubleSpinBox->setMinimum( 0.0);
- dzDoubleSpinBox->setMinimum( 0.0);
- dxDoubleSpinBox->setMaximum(1.0e9);
- dyDoubleSpinBox->setMaximum(1.0e9);
- dzDoubleSpinBox->setMaximum(1.0e9);
- connect(keepSquareCheckBox, &QCheckBox::toggled, this, &ccBoundingBoxEditorDlg::squareModeActivated);
- connect(okPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::saveBoxAndAccept);
- connect(cancelPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::cancel);
- connect(defaultPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::resetToDefault);
- connect(lastPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::resetToLast);
- connect(fromClipboardPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::fromClipboardClicked);
- connect(toClipboardPushButton, &QPushButton::clicked, this, &ccBoundingBoxEditorDlg::toClipboardClicked);
- connect(pointTypeComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccBoundingBoxEditorDlg::reflectChanges);
- connect(xDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateCurrentBBox);
- connect(yDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateCurrentBBox);
- connect(zDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateCurrentBBox);
- connect(dxDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateXWidth);
- connect(dyDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateYWidth);
- connect(dzDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::updateZWidth);
- connect(xOriXDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(xOriYDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(xOriZDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(yOriXDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(yOriYDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(yOriZDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(zOriXDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(zOriYDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- connect(zOriZDoubleSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccBoundingBoxEditorDlg::onAxisValueChanged);
- defaultPushButton->setVisible(false);
- lastPushButton->setVisible(s_lastBBox.isValid());
- checkBaseInclusion();
- if (showRasterGridImage)
- {
- pointTypeComboBox->setCurrentIndex(MinCornerIndex);
- }
- }
- void ccBoundingBoxEditorDlg::setBox(const ccBBox& box)
- {
- m_currentBBox = box;
- reflectChanges();
- checkBaseInclusion();
- }
- bool ccBoundingBoxEditorDlg::keepSquare() const
- {
- return keepSquareCheckBox->isChecked();
- }
- void ccBoundingBoxEditorDlg::forceKeepSquare(bool state)
- {
- if (state)
- keepSquareCheckBox->setChecked(true);
- keepSquareCheckBox->setDisabled(state);
- }
- void ccBoundingBoxEditorDlg::squareModeActivated(bool state)
- {
- if (state)
- {
- MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex());
- reflectChanges();
- }
- }
- void ccBoundingBoxEditorDlg::set2DMode(bool state, unsigned char dim)
- {
- bool hideX = (state && dim == 0);
- bool hideY = (state && dim == 1);
- bool hideZ = (state && dim == 2);
- xDoubleSpinBox->setHidden(hideX);
- dxDoubleSpinBox->setHidden(hideX);
- xLabel->setHidden(hideX);
- yDoubleSpinBox->setHidden(hideY);
- dyDoubleSpinBox->setHidden(hideY);
- yLabel->setHidden(hideY);
- zDoubleSpinBox->setHidden(hideZ);
- dzDoubleSpinBox->setHidden(hideZ);
- zLabel->setHidden(hideZ);
- }
- void ccBoundingBoxEditorDlg::setBaseBBox(const ccBBox& box, bool isMinimal/*=true*/)
- {
- //set new default one
- m_initBBox = m_baseBBox = box;
- m_baseBoxIsMinimal = isMinimal;
- defaultPushButton->setVisible(m_baseBBox.isValid());
- resetToDefault();
- }
- void ccBoundingBoxEditorDlg::checkBaseInclusion()
- {
- bool exclude = false;
- if (m_baseBBox.isValid())
- {
- exclude = !m_currentBBox.contains(m_baseBBox.minCorner()) || !m_currentBBox.contains(m_baseBBox.maxCorner());
- }
- warningLabel->setVisible(m_showInclusionWarning && exclude);
- okPushButton->setEnabled(!m_baseBoxIsMinimal || !exclude);
- }
- void ccBoundingBoxEditorDlg::resetToDefault()
- {
- m_currentBBox = m_baseBBox;
-
- if (keepSquare())
- squareModeActivated(true); //will call reflectChanges
- else
- reflectChanges();
- checkBaseInclusion();
- }
- void ccBoundingBoxEditorDlg::resetToLast()
- {
- m_currentBBox = s_lastBBox;
-
- if (keepSquare())
- squareModeActivated(true); //will call reflectChanges
- else
- reflectChanges();
- checkBaseInclusion();
- }
- void ccBoundingBoxEditorDlg::saveBoxAndAccept()
- {
- if (oriGroupBox->isVisible())
- {
- CCVector3d X;
- CCVector3d Y;
- CCVector3d Z;
- getBoxAxes(X, Y, Z);
- X.normalize();
- Y.normalize();
- Z.normalize();
- if ( X.norm2d() == 0
- || Y.norm2d() == 0
- || Z.norm2d() == 0 )
- {
- ccLog::Error("Invalid axes definition: at least two vectors are colinear");
- return;
- }
- //if ( std::abs(X.dot(Y)) > 1.0e-6
- // || std::abs(Y.dot(Z)) > 1.0e-6
- // || std::abs(Z.dot(X)) > 1.0e-6 )
- //{
- // ccLog::Error("Invalid axes definition: vectors must be orthogonal");
- // return;
- //}
- }
- s_lastBBox = m_currentBBox;
-
- accept();
- }
- int ccBoundingBoxEditorDlg::exec()
- {
- //backup current box
- m_initBBox = m_currentBBox;
- //call 'true' exec
- return QDialog::exec();
- }
- void ccBoundingBoxEditorDlg::cancel()
- {
- //restore init. box
- setBox(m_initBBox);
- reject();
- }
- void ccBoundingBoxEditorDlg::updateXWidth(double value)
- {
- updateCurrentBBox(value);
- if (keepSquare())
- {
- MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 0);
- reflectChanges();
- //base box (if valid) should always be included!
- if (m_baseBBox.isValid())
- checkBaseInclusion();
- }
- }
- void ccBoundingBoxEditorDlg::updateYWidth(double value)
- {
- updateCurrentBBox(value);
- if (keepSquare())
- {
- MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 1);
- reflectChanges();
- //base box (if valid) should always be included!
- if (m_baseBBox.isValid())
- checkBaseInclusion();
- }
- }
- void ccBoundingBoxEditorDlg::updateZWidth(double value)
- {
- updateCurrentBBox(value);
- if (keepSquare())
- {
- MakeSquare(m_currentBBox, pointTypeComboBox->currentIndex(), 2);
- reflectChanges();
- //base box (if valid) should always be included!
- if (m_baseBBox.isValid())
- checkBaseInclusion();
- }
- }
- void ccBoundingBoxEditorDlg::updateCurrentBBox(double dummy)
- {
- CCVector3 A(static_cast<PointCoordinateType>(xDoubleSpinBox->value()),
- static_cast<PointCoordinateType>(yDoubleSpinBox->value()),
- static_cast<PointCoordinateType>(zDoubleSpinBox->value()));
- CCVector3 W(static_cast<PointCoordinateType>(dxDoubleSpinBox->value()),
- static_cast<PointCoordinateType>(dyDoubleSpinBox->value()),
- static_cast<PointCoordinateType>(dzDoubleSpinBox->value()));
- switch (pointTypeComboBox->currentIndex())
- {
- case 0: //A = min corner
- m_currentBBox = ccBBox(A, A + W, true);
- break;
- case 1: //A = center
- m_currentBBox = ccBBox(A - W / 2.0, A + W / 2.0, true);
- break;
- case 2: //A = max corner
- m_currentBBox = ccBBox(A - W, A, true);
- break;
- default:
- assert(false);
- return;
- }
- //base box (if valid) should always be included!
- if (m_baseBBox.isValid())
- {
- checkBaseInclusion();
- }
- }
- void ccBoundingBoxEditorDlg::reflectChanges(int dummy)
- {
- //left column
- {
- xDoubleSpinBox->blockSignals(true);
- yDoubleSpinBox->blockSignals(true);
- zDoubleSpinBox->blockSignals(true);
- switch (pointTypeComboBox->currentIndex())
- {
- case MinCornerIndex: //A = min corner
- {
- const CCVector3& A = m_currentBBox.minCorner();
- xDoubleSpinBox->setValue(A.x);
- yDoubleSpinBox->setValue(A.y);
- zDoubleSpinBox->setValue(A.z);
- }
- break;
- case CenterIndex: //A = center
- {
- CCVector3 C = m_currentBBox.getCenter();
- xDoubleSpinBox->setValue(C.x);
- yDoubleSpinBox->setValue(C.y);
- zDoubleSpinBox->setValue(C.z);
- }
- break;
- case MaxCornerIndex: //A = max corner
- {
- const CCVector3& B = m_currentBBox.maxCorner();
- xDoubleSpinBox->setValue(B.x);
- yDoubleSpinBox->setValue(B.y);
- zDoubleSpinBox->setValue(B.z);
- }
- break;
- default:
- assert(false);
- return;
- }
- xDoubleSpinBox->blockSignals(false);
- yDoubleSpinBox->blockSignals(false);
- zDoubleSpinBox->blockSignals(false);
- }
- //right column
- {
- dxDoubleSpinBox->blockSignals(true);
- dyDoubleSpinBox->blockSignals(true);
- dzDoubleSpinBox->blockSignals(true);
- CCVector3 W = m_currentBBox.getDiagVec();
- //if 'square mode' is on, all width values should be the same!
- assert(!keepSquare() || std::abs(W.x - W.y)*1.0e-6 < 1.0 && std::abs(W.x - W.z)*1.0e-6 < 1.0);
- dxDoubleSpinBox->setValue(W.x);
- dyDoubleSpinBox->setValue(W.y);
- dzDoubleSpinBox->setValue(W.z);
- dxDoubleSpinBox->blockSignals(false);
- dyDoubleSpinBox->blockSignals(false);
- dzDoubleSpinBox->blockSignals(false);
- }
- }
- int ccBoundingBoxEditorDlg::computeBestDialogHeight(bool showBoxAxes, bool showRasterGridImage) const
- {
- //we always keep space to display the grid
- int height = gridFrame->height();
- int blockCount = 1;
- //we always keep space to display the warning label
- {
- height += warningLabel->height();
- ++blockCount;
- }
- if (showBoxAxes)
- {
- height += oriGroupBox->height();
- ++blockCount;
- }
- if (showRasterGridImage)
- {
- height += rasterGridImageLabel->height();
- ++blockCount;
- }
- //we always keep space to display the buttons
- {
- height += buttonFrame->height();
- ++blockCount;
- }
- if (layout())
- {
- height += layout()->contentsMargins().top() + layout()->spacing() * (blockCount - 1) + layout()->contentsMargins().bottom();
- }
- return height;
- }
- void ccBoundingBoxEditorDlg::setBoxAxes(const CCVector3& X, const CCVector3& Y, const CCVector3& Z)
- {
- //if (xOriFrame->isEnabled())
- {
- xOriXDoubleSpinBox->setValue(X.x);
- xOriYDoubleSpinBox->setValue(X.y);
- xOriZDoubleSpinBox->setValue(X.z);
- }
- //if (yOriFrame->isEnabled())
- {
- yOriXDoubleSpinBox->setValue(Y.x);
- yOriYDoubleSpinBox->setValue(Y.y);
- yOriZDoubleSpinBox->setValue(Y.z);
- }
- //if (zOriFrame->isEnabled())
- {
- zOriXDoubleSpinBox->setValue(Z.x);
- zOriYDoubleSpinBox->setValue(Z.y);
- zOriZDoubleSpinBox->setValue(Z.z);
- }
- }
- void ccBoundingBoxEditorDlg::getBoxAxes(CCVector3d& X, CCVector3d& Y, CCVector3d& Z)
- {
- X = CCVector3d( xOriXDoubleSpinBox->value(),
- xOriYDoubleSpinBox->value(),
- xOriZDoubleSpinBox->value() );
- Y = CCVector3d( yOriXDoubleSpinBox->value(),
- yOriYDoubleSpinBox->value(),
- yOriZDoubleSpinBox->value() );
- Z = CCVector3d( zOriXDoubleSpinBox->value(),
- zOriYDoubleSpinBox->value(),
- zOriZDoubleSpinBox->value() );
- }
- void ccBoundingBoxEditorDlg::onAxisValueChanged(double)
- {
- CCVector3d X;
- CCVector3d Y;
- CCVector3d Z;
- getBoxAxes(X, Y, Z);
- QDoubleSpinBox* vecSpinBoxes[3] = { nullptr, nullptr, nullptr };
- CCVector3d N(0, 0, 0);
- if (oriXCheckBox->isChecked())
- {
- N = Y.cross(Z);
- vecSpinBoxes[0] = xOriXDoubleSpinBox;
- vecSpinBoxes[1] = xOriYDoubleSpinBox;
- vecSpinBoxes[2] = xOriZDoubleSpinBox;
- }
- else if (oriYCheckBox->isChecked())
- {
- N = Z.cross(X);
- vecSpinBoxes[0] = yOriXDoubleSpinBox;
- vecSpinBoxes[1] = yOriYDoubleSpinBox;
- vecSpinBoxes[2] = yOriZDoubleSpinBox;
- }
- else if (oriZCheckBox->isChecked())
- {
- N = X.cross(Y);
- vecSpinBoxes[0] = zOriXDoubleSpinBox;
- vecSpinBoxes[1] = zOriYDoubleSpinBox;
- vecSpinBoxes[2] = zOriZDoubleSpinBox;
- }
- else
- {
- assert(false);
- }
- for (int i = 0; i < 3; ++i)
- {
- vecSpinBoxes[i]->blockSignals(true);
- vecSpinBoxes[i]->setValue(N.u[i]);
- vecSpinBoxes[i]->blockSignals(false);
- }
- }
- void ccBoundingBoxEditorDlg::fromClipboardClicked()
- {
- QClipboard* clipboard = QApplication::clipboard();
- if (clipboard)
- {
- QString clipText = clipboard->text();
- if (!clipText.isEmpty())
- {
- bool success = false;
- ccGLMatrix matrix = ccGLMatrix::FromString(clipText, success);
- if (success)
- {
- //set center
- CCVector3 C = m_currentBBox.getCenter();
- CCVector3 delta = matrix.getTranslationAsVec3D() - C;
- m_currentBBox += delta;
- reflectChanges();
- //change axes
- setBoxAxes( matrix.getColumnAsVec3D(0),
- matrix.getColumnAsVec3D(1),
- matrix.getColumnAsVec3D(2) );
- }
- else
- {
- ccLog::Error("Failed to extract matrix from clipboard");
- }
- }
- else
- {
- ccLog::Error("Clipboard is empty");
- }
- }
- }
- void ccBoundingBoxEditorDlg::toClipboardClicked()
- {
- QClipboard* clipboard = QApplication::clipboard();
- if (clipboard)
- {
- CCVector3 C = m_currentBBox.getCenter();
- CCVector3d X;
- CCVector3d Y;
- CCVector3d Z;
- getBoxAxes(X, Y, Z);
- ccGLMatrix matrix;
- matrix.setColumn(0, X.toPC());
- matrix.setColumn(1, Y.toPC());
- matrix.setColumn(2, Z.toPC());
- matrix.setTranslation(C);
- clipboard->setText(matrix.toString());
- ccLog::Print("Matrix saved to clipboard:");
- ccLog::Print(matrix.toString(12, ' ')); //full precision
- }
- }
|