| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115 |
- //##########################################################################
- //# #
- //# 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 "ccGraphicalTransformationTool.h"
- #include "mainwindow.h"
- #include <ccGLUtils.h>
- #include <ccGLWindowInterface.h>
- //qCC_db
- #include <ccLog.h>
- #include <ccMesh.h>
- #include <ccPolyline.h>
- #include <ccPlane.h>
- #include <ccCoordinateSystem.h>
- #include <ccDBRoot.h>
- ccGraphicalTransformationTool::ccGraphicalTransformationTool(QWidget* parent)
- : ccOverlayDialog(parent)
- , Ui::GraphicalTransformationDlg()
- , m_toTransform("transformed")
- {
- setupUi(this);
-
- rotComboBox->clear();
- rotComboBox->insertItem(0, "XYZ", rotComboBoxItems::XYZ);
- rotComboBox->insertItem(1, "X", rotComboBoxItems::X);
- rotComboBox->insertItem(2, "Y", rotComboBoxItems::Y);
- rotComboBox->insertItem(3, "Z", rotComboBoxItems::Z);
- rotComboBox->insertItem(4, "None", rotComboBoxItems::NONE);
- rotComboBox->setCurrentIndex(rotComboBoxItems::XYZ);
-
- connect(pauseButton, &QAbstractButton::toggled, this, &ccGraphicalTransformationTool::pause);
- connect(okButton, &QAbstractButton::clicked, this, &ccGraphicalTransformationTool::apply);
- connect(razButton, &QAbstractButton::clicked, this, &ccGraphicalTransformationTool::reset);
- connect(cancelButton, &QAbstractButton::clicked, this, &ccGraphicalTransformationTool::cancel);
- connect(TxCheckBox, &QCheckBox::clicked, this, &ccGraphicalTransformationTool::incrementalTranslationToggle);
- connect(TyCheckBox, &QCheckBox::clicked, this, &ccGraphicalTransformationTool::incrementalTranslationToggle);
- connect(TzCheckBox, &QCheckBox::clicked, this, &ccGraphicalTransformationTool::incrementalTranslationToggle);
- connect(advPushButton, &QPushButton::toggled, this, &ccGraphicalTransformationTool::advModeToggle);
- connect(refAxisRadio, &QRadioButton::toggled, this, &ccGraphicalTransformationTool::advRefAxisRadioToggled);
- connect(objCenterRadio, &QRadioButton::toggled, this, &ccGraphicalTransformationTool::advObjectAxisRadioToggled);
- connect(advTranslateComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccGraphicalTransformationTool::advTranslateRefUpdate);
- connect(advRotateComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccGraphicalTransformationTool::advRotateRefUpdate);
- connect(rotComboBox, qOverload<int>(&QComboBox::activated), this, &ccGraphicalTransformationTool::advRotateComboBoxUpdate);
- connect(incrementalForwardButton, &QAbstractButton::clicked, this, &ccGraphicalTransformationTool::incrementalTransform);
- connect(incrementalBackwardButton, &QAbstractButton::clicked, this, &ccGraphicalTransformationTool::incrementalTransform);
- //add shortcuts
- addOverriddenShortcut(Qt::Key_Space); //space bar for the "pause" button
- addOverriddenShortcut(Qt::Key_Escape); //escape key for the "cancel" button
- addOverriddenShortcut(Qt::Key_Return); //return key for the "ok" button
- connect(this, &ccOverlayDialog::shortcutTriggered, this, &ccGraphicalTransformationTool::onShortcutTriggered);
- objCenterRadio->setChecked(true);
- advModeToggle(false);
- }
- ccGraphicalTransformationTool::~ccGraphicalTransformationTool()
- {
- clear();
- }
- void ccGraphicalTransformationTool::onShortcutTriggered(int key)
- {
- switch(key)
- {
- case Qt::Key_Space:
- pauseButton->toggle();
- return;
- case Qt::Key_Return:
- okButton->click();
- return;
- case Qt::Key_Escape:
- cancelButton->click();
- return;
- default:
- //nothing to do
- break;
- }
- }
- void ccGraphicalTransformationTool::pause(bool state)
- {
- if (!m_associatedWin)
- {
- return;
- }
- if (state)
- {
- m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_TRANSFORM_CAMERA);
- m_associatedWin->displayNewMessage("Transformation [PAUSED]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- m_associatedWin->displayNewMessage("Unpause to transform again", ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- }
- else
- {
- m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_TRANSFORM_ENTITIES);
- updateDisplayMessage();
- }
- //update mini-GUI
- pauseButton->blockSignals(true);
- pauseButton->setChecked(state);
- pauseButton->blockSignals(false);
- m_associatedWin->redraw(false, false); //we have to redraw the 3D layer for labels!
- }
- void ccGraphicalTransformationTool::advModeToggle(bool state)
- {
- advRotateComboBox->setVisible(state);
- advTranslateComboBox->setVisible(state);
- translateLabel->setVisible(state);
- rotateLabel->setVisible(state);
- rotAxisLabel->setVisible(state);
- objCenterRadio->setVisible(state);
- refAxisRadio->setVisible(state);
- incrementalRotLabel->setVisible(state);
- incrementalRotSpin->setVisible(state);
- incrementalTransLabel->setVisible(state);
- incrementalTransSpin->setVisible(state);
- incrementalForwardButton->setVisible(state);
- incrementalBackwardButton->setVisible(state);
- IncrementalTransformLabel->setVisible(state);
- m_advMode = state;
- int wPrev = this->width();
- if (state)
- {
- this->setGeometry(this->x() + (-wPrev + 250), this->y(), 250, 235);
- if (advTranslateComboBox->currentIndex() != 0)
- {
- TxCheckBox->setEnabled(false);
- TyCheckBox->setEnabled(false);
- }
- advRotateRefUpdate(advRotateComboBox->currentIndex());
- advTranslateRefUpdate(advTranslateComboBox->currentIndex());
- }
- else
- {
- TxCheckBox->setEnabled(true);
- TyCheckBox->setEnabled(true);
- this->setGeometry(this->x() , this->y(), 0, 0);
- this->adjustSize(); //adjust size will minimize the display height with the dropdowns not visible
- this->setGeometry(this->x() + (wPrev - 250), this->y(), 250, this->height());
- advRotateComboBox->setCurrentIndex(0); //index 0 is always the origin
- advTranslateComboBox->setCurrentIndex(0); //index 0 is always the origin
- }
- //update mini-GUI
- advPushButton->blockSignals(true);
- advPushButton->setChecked(state);
- advPushButton->blockSignals(false);
- updateDisplayMessage();
- }
- void ccGraphicalTransformationTool::populateAdvModeItems()
- {
- advRotateComboBox->clear();
- advTranslateComboBox->clear();
- advRotateComboBox->insertItem(0, "Origin");
- advTranslateComboBox->insertItem(0, "Origin");
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow)
- {
- ccHObject* root = mainWindow->dbRootObject();
- ccHObject::Container polylines;
- ccHObject::Container coordinateSystems;
- if (root)
- {
- root->filterChildren(polylines, true, CC_TYPES::POLY_LINE);
- root->filterChildren(coordinateSystems, true, CC_TYPES::COORDINATESYSTEM);
- root->filterChildren(m_advancedModeObjectList, true, CC_TYPES::PLANE);
- }
- if (!polylines.empty())
- {
- for (size_t i = 0; i < polylines.size(); i++)
- {
- ccPolyline* poly = static_cast<ccPolyline*>(polylines[i]);
- if (poly->size() == 2) //only single segment polylines allowed
- {
- m_advancedModeObjectList.push_back(polylines[i]);
- }
- }
- }
- if (!coordinateSystems.empty())
- {
- for (size_t i = 0; i < coordinateSystems.size(); i++)
- {
- m_advancedModeObjectList.push_back(coordinateSystems[i]);
- }
- }
- if (!m_advancedModeObjectList.empty())
- {
- for (size_t i = 0; i < m_advancedModeObjectList.size(); ++i)
- {
- QString item = QString("%1 (ID=%2)").arg(m_advancedModeObjectList[i]->getName()).arg(m_advancedModeObjectList[i]->getUniqueID());
- advTranslateComboBox->insertItem(static_cast<int>(i) + 1, item, QVariant(m_advancedModeObjectList[i]->getUniqueID()));
- advRotateComboBox->insertItem(static_cast<int>(i) + 1, item, QVariant(m_advancedModeObjectList[i]->getUniqueID()));
- }
- }
- }
- }
- ccGLMatrixd ccGraphicalTransformationTool::arbitraryVectorTranslation(const CCVector3& vec)
- {
- double theta = 0;
- if (CCCoreLib::LessThanEpsilon(std::abs(vec.z)))
- {
- if (CCCoreLib::LessThanEpsilon(std::abs(vec.y)))
- {
- theta = 0;
- }
- else if (vec.y < 0)
- {
- theta = -M_PI_2; //atan of -infinity is -pi/2
- }
- else
- {
- theta = M_PI_2; //atan of +infinity is pi/2
- }
- }
- else
- {
- theta = std::atan(vec.y / vec.z);
- if (vec.y < 0 && vec.z < 0)
- {
- theta = theta - M_PI;
- }
- else if (vec.z < 0 && vec.y > 0)
- {
- theta = M_PI + theta;
- }
- }
- double phiDenominator = std::sqrt((vec.y * vec.y) + (vec.z * vec.z));
- double phi = 0;
- if (CCCoreLib::LessThanEpsilon(phiDenominator))
- {
- if (CCCoreLib::LessThanEpsilon(std::abs(vec.x)))
- {
- phi = 0;
- }
- else if (vec.x < 0)
- {
- phi = -M_PI_2; //atan of -infinity is -pi/2
- }
- else
- {
- phi = M_PI_2; //atan of +infinity is pi/2
- }
- }
- else
- {
- phi = std::atan(vec.x / phiDenominator);
- }
- ccGLMatrixd xRotation = ccGLMatrixd();
- xRotation.setColumn(1, CCVector3d(0, std::cos(theta), -std::sin(theta)));
- xRotation.setColumn(2, CCVector3d(0, std::sin(theta), std::cos(theta)));
- ccGLMatrixd yRotation = ccGLMatrixd();
- yRotation.setColumn(0, CCVector3d(std::cos(phi), 0, -std::sin(phi)));
- yRotation.setColumn(2, CCVector3d(std::sin(phi), 0, std::cos(phi)));
- ccGLMatrixd arbitraryVectorTranslationAdjust = xRotation * yRotation;
- //special case
- if (CCCoreLib::LessThanEpsilon(std::abs(vec.x)) && CCCoreLib::LessThanEpsilon(std::abs(vec.y)) && vec.z < 0)
- {
- arbitraryVectorTranslationAdjust.scaleRotation(-1);
- }
- return arbitraryVectorTranslationAdjust;
- }
- ccGLMatrixd ccGraphicalTransformationTool::arbitraryVectorRotation(double angle, const CCVector3d& arbitraryVector)
- {
- // advRotationTransform = (cos(theta)*I) + ((1-cos(theta)*u) (X) u) + (sin(theta)*u_skewsym)
- // (X) represents tensor product
- // Reference: Ch 4.7.3 in Geometric Tools for Computer Graphics - P. Schneider & D. Eberly
- double cosTheta = std::cos(angle);
- double sinTheta = std::sin(angle);
- ccGLMatrixd firstTerm = ccGLMatrixd();
- firstTerm.scaleRotation(cosTheta);
- ccGLMatrixd secondTerm = ccGLMatrixd();
- CCVector3d v = (1 - cosTheta) * arbitraryVector;
- CCVector3d w = arbitraryVector;
- secondTerm.setColumn(0, CCVector3d(v[0] * w[0], v[1] * w[0], v[2] * w[0]));
- secondTerm.setColumn(1, CCVector3d(v[0] * w[1], v[1] * w[1], v[2] * w[1]));
- secondTerm.setColumn(2, CCVector3d(v[0] * w[2], v[1] * w[2], v[2] * w[2]));
- ccGLMatrixd thirdTerm = ccGLMatrixd();
- thirdTerm.setColumn(0, CCVector3d(0, arbitraryVector[2], -arbitraryVector[1]));
- thirdTerm.setColumn(1, CCVector3d(-arbitraryVector[2], 0, arbitraryVector[0]));
- thirdTerm.setColumn(2, CCVector3d(arbitraryVector[1], -arbitraryVector[0], 0));
- thirdTerm.scaleRotation(sinTheta);
- ccGLMatrixd advRotationTransform = firstTerm;
- advRotationTransform += secondTerm;
- advRotationTransform.scaleRow(3, .5);
- advRotationTransform += thirdTerm;
- advRotationTransform.scaleRow(3, .5);
- return advRotationTransform;
- }
- bool ccGraphicalTransformationTool::setAdvTranslationTransform(ccHObject* translateRef)
- {
- if (!m_associatedWin)
- {
- assert(false);
- return false;
- }
- if (translateRef == nullptr)
- {
- return false;
- }
- if (translateRef->isA(CC_TYPES::POLY_LINE))
- {
- ccPolyline* line = static_cast<ccPolyline*>(translateRef);
- CCVector3 arbitraryVec = *line->getPoint(1) - *line->getPoint(0);
- m_advTranslationTransform = arbitraryVectorTranslation(arbitraryVec);
- TxCheckBox->setChecked(false);
- TyCheckBox->setChecked(false);
- TxCheckBox->setEnabled(false);
- TyCheckBox->setEnabled(false);
- return true;
- }
- else if (translateRef->isA(CC_TYPES::PLANE))
- {
- ccPlane* plane = static_cast<ccPlane*>(translateRef);
- m_advTranslationTransform = ccGLMatrixd(plane->getTransformation().data());
- TxCheckBox->setEnabled(true);
- TyCheckBox->setEnabled(true);
- return true;
- }
- else if (translateRef->isA(CC_TYPES::COORDINATESYSTEM))
- {
- ccCoordinateSystem* cs = static_cast<ccCoordinateSystem*>(translateRef);
- m_advTranslationTransform = ccGLMatrixd(cs->getTransformation().data());
- TxCheckBox->setEnabled(true);
- TyCheckBox->setEnabled(true);
- return true;
- }
- else
- {
- advTranslateComboBox->setCurrentIndex(0);
- TxCheckBox->setEnabled(true);
- TyCheckBox->setEnabled(true);
- return false;
- }
- }
- bool ccGraphicalTransformationTool::setAdvRotationAxis(ccHObject* rotateRef, rotComboBoxItems selectedAxis)
- {
- if (!m_associatedWin || !rotateRef)
- {
- assert(false);
- return false;
- }
- CCVector3d newCenter;
- CCVector3d arbitraryVec;
- if (rotateRef->isA(CC_TYPES::POLY_LINE))
- {
- ccPolyline* line = static_cast<ccPolyline*>(rotateRef);
- CCVector3d end = *line->getPoint(1);
- CCVector3d start = *line->getPoint(0);
- arbitraryVec = end - start;
- rotComboBox->clear();
- rotComboBox->insertItem(0, "Z", rotComboBoxItems::Z);
- rotComboBox->insertItem(1, "None", rotComboBoxItems::NONE);
- rotComboBox->setCurrentIndex(rotComboBoxItems::Z);
- incrementalRotationToggle(rotComboBoxItems::Z);
- m_advRotationRefObjCenter = (start + end) / 2;
- arbitraryVec.normalize();
- }
- else if (rotateRef->isA(CC_TYPES::PLANE))
- {
- ccPlane* plane = static_cast<ccPlane*>(rotateRef);
- arbitraryVec = plane->getNormal();
- rotComboBox->clear();
- rotComboBox->insertItem(0, "Z", rotComboBoxItems::Z);
- rotComboBox->insertItem(1, "None", rotComboBoxItems::NONE);
- rotComboBox->setCurrentIndex(rotComboBoxItems::Z);
- incrementalRotationToggle(rotComboBoxItems::Z);
- m_advRotationRefObjCenter = plane->getCenter();
- }
- else if (rotateRef->isA(CC_TYPES::COORDINATESYSTEM))
- {
- ccCoordinateSystem* cs = static_cast<ccCoordinateSystem*>(rotateRef);
- switch (selectedAxis)
- {
- case rotComboBoxItems::X:
- {
- arbitraryVec = cs->getYZPlaneNormal();
- break;
- }
- case rotComboBoxItems::Y:
- {
- arbitraryVec = cs->getZXPlaneNormal();
- break;
- }
- case rotComboBoxItems::Z:
- default:
- {
- selectedAxis = rotComboBoxItems::Z;
- arbitraryVec = cs->getXYPlaneNormal();
- break;
- }
- }
- rotComboBox->clear();
- rotComboBox->insertItem(0, "X", rotComboBoxItems::X);
- rotComboBox->insertItem(1, "Y", rotComboBoxItems::Y);
- rotComboBox->insertItem(2, "Z", rotComboBoxItems::Z);
- rotComboBox->insertItem(3, "None", rotComboBoxItems::NONE);
- rotComboBox->setCurrentIndex(rotComboBoxItems::Z);
- incrementalRotationToggle(rotComboBoxItems::Z);
- m_advRotationRefObjCenter = cs->getOrigin();
- }
- else //Not a supported primitive for rotateRef
- {
- CCVector3d newCenter = m_toTransform.getBB_recursive().getCenter();
- setRotationCenter(newCenter);
- advRotateComboBox->setCurrentIndex(0);
- return false;
- }
- if (rotComboBox->findData(selectedAxis) == -1)
- {
- rotComboBox->setCurrentIndex(rotComboBox->findData(rotComboBoxItems::Z)); // Default to Z axis if passed an invalid axis selection
- }
- else
- {
- rotComboBox->setCurrentIndex(rotComboBox->findData(selectedAxis));
- }
- if (refAxisRadio->isChecked())
- {
- if (m_advRotateRefIsChild)
- {
- newCenter = m_advRotationRefObjCenter;
- m_position.applyRotation(arbitraryVec);
- }
- else
- {
- newCenter = (m_rotation.inverse() * m_advRotationRefObjCenter - m_rotation.inverse() * m_position.getTranslationAsVec3D());
- }
- }
- else if (objCenterRadio->isChecked())
- {
- if (m_advRotateRefIsChild)
- {
- m_position.applyRotation(arbitraryVec);
- }
- newCenter = m_toTransform.getBB_recursive().getCenter();
- }
- m_advRotationAxis = m_rotation.inverse() * arbitraryVec;
- setRotationCenter(newCenter);
- return true;
- }
- bool ccGraphicalTransformationTool::entityInTransformList(ccHObject* entity)
- {
- for (unsigned j = 0; j < m_toTransform.getChildrenNumber(); j++)
- {
- if (entity == m_toTransform.getChild(j) || m_toTransform.getChild(j)->isAncestorOf(entity))
- {
- return true;
- }
- }
- return false;
- }
- void ccGraphicalTransformationTool::advTranslateRefUpdate(int index)
- {
- if (index == 0 || m_advancedModeObjectList.empty()) // index 0 is always the origin
- {
- TxCheckBox->setEnabled(true);
- TyCheckBox->setEnabled(true);
- m_advTranslationTransform.toIdentity();
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow && m_advTranslateRef != nullptr)
- {
- mainWindow->db()->unselectEntity(m_advTranslateRef);
- m_advTranslateRef = nullptr;
- }
- return;
- }
- int id = advTranslateComboBox->itemData(index).toInt();
- for (size_t i = 0; i < m_advancedModeObjectList.size(); i++)
- {
- if (id == m_advancedModeObjectList[i]->getUniqueID())
- {
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow)
- {
- if (m_advTranslateRef != nullptr && m_advTranslateRef != m_advRotateRef) //leave selected if rotate ref
- {
- mainWindow->db()->unselectEntity(m_advTranslateRef);
- }
- m_advTranslateRef = m_advancedModeObjectList[i];
- m_advTranslateRefIsChild = entityInTransformList(m_advTranslateRef);
- if (m_advTranslateRef != m_advRotateRef) // already selected
- {
- mainWindow->db()->selectEntity(m_advTranslateRef, true);
- }
- }
- if (!setAdvTranslationTransform(m_advTranslateRef))
- {
- ccLog::Error("Error calculating adv translation transform, cannot translate along selected item");
- advTranslateComboBox->setCurrentIndex(0);
- }
- return;
- }
- }
- ccLog::Error("Error finding the selected object in DB Tree, cannot translate along selected object");
- advTranslateComboBox->setCurrentIndex(0);
- }
- void ccGraphicalTransformationTool::advRotateComboBoxUpdate(int index)
- {
- rotComboBoxItems selectedAxis = static_cast<rotComboBoxItems>(rotComboBox->itemData(index).toInt());
- incrementalRotationToggle(selectedAxis);
- if (!m_advMode || !m_advRotateRef)
- {
- return;
- }
- if (selectedAxis == rotComboBoxItems::NONE)
- {
- return;
- }
- if (!setAdvRotationAxis(m_advRotateRef, selectedAxis))
- {
- ccLog::Error("Error setting adv rotation axis, cannot rotate around selected item");
- advRotateComboBox->setCurrentIndex(0);
- }
- }
- void ccGraphicalTransformationTool::advRotateRefUpdate(int index)
- {
- if (index == 0 || m_advancedModeObjectList.empty()) // index 0 is always the origin
- {
- if (m_advRotateRef != nullptr)
- {
- rotComboBox->clear();
- rotComboBox->insertItem(0, "XYZ", rotComboBoxItems::XYZ);
- rotComboBox->insertItem(1, "X", rotComboBoxItems::X);
- rotComboBox->insertItem(2, "Y", rotComboBoxItems::Y);
- rotComboBox->insertItem(3, "Z", rotComboBoxItems::Z);
- rotComboBox->insertItem(4, "None", rotComboBoxItems::NONE);
- rotComboBox->setCurrentIndex(rotComboBoxItems::Z);
- incrementalRotationToggle(rotComboBoxItems::Z);
- }
- CCVector3d center = m_toTransform.getBB_recursive().getCenter();
- setRotationCenter(center);
- m_advRotationRefObjCenter = CCVector3d(0, 0, 0);
- m_advRotationAxis = CCVector3d(0, 0, 1);
- objCenterRadio->setEnabled(true);
- objCenterRadio->setChecked(true);
- refAxisRadio->setEnabled(false);
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow && m_advRotateRef != nullptr)
- {
- mainWindow->db()->unselectEntity(m_advRotateRef);
- m_advRotateRef = nullptr;
- }
- return;
- }
- int id = advRotateComboBox->itemData(index).toInt();
- for (size_t i = 0; i < m_advancedModeObjectList.size(); i++)
- {
- if (id == m_advancedModeObjectList[i]->getUniqueID())
- {
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow)
- {
- if (m_advRotateRef != nullptr && m_advTranslateRef != m_advRotateRef)//leave selected if translate ref
- {
- mainWindow->db()->unselectEntity(m_advRotateRef);
- }
- m_advRotateRef = m_advancedModeObjectList[i];
- if (m_advTranslateRef != m_advRotateRef) // already selected
- {
- mainWindow->db()->selectEntity(m_advRotateRef, true);
- }
- refAxisRadio->setEnabled(true);
- m_advRotateRefIsChild = entityInTransformList(m_advRotateRef);
- if (m_advRotateRefIsChild)
- {
- refAxisRadio->setChecked(true);
- objCenterRadio->setEnabled(false);
- }
- else
- {
- objCenterRadio->setEnabled(true);
- }
- }
- if (!setAdvRotationAxis(m_advRotateRef, static_cast<rotComboBoxItems>(rotComboBox->itemData(index).toInt())))
- {
- ccLog::Error("Error setting adv rotation axis, cannot rotate around selected item");
- advRotateComboBox->setCurrentIndex(0);
- }
- return;
- }
- }
- ccLog::Error("Error finding the selected object in DB Tree, cannot translate along selected object");
- advRotateComboBox->setCurrentIndex(0);
- }
- void ccGraphicalTransformationTool::advRefAxisRadioToggled(bool state)
- {
- if (state)
- {
- objCenterRadio->setChecked(false);
- advRotateRefUpdate(advRotateComboBox->currentIndex()); //force an update
- }
- }
- void ccGraphicalTransformationTool::incrementalTranslationToggle()
- {
- const bool isIncrementalActive = TxCheckBox->isChecked() || TxCheckBox->isChecked() || TzCheckBox->isChecked();
- incrementalTransLabel->setEnabled(isIncrementalActive);
- incrementalTransSpin->setEnabled(isIncrementalActive);
- }
- void ccGraphicalTransformationTool::incrementalRotationToggle(const rotComboBoxItems & selectedRotationItem)
- {
- if(selectedRotationItem == rotComboBoxItems::NONE || selectedRotationItem == rotComboBoxItems::XYZ) {
- incrementalRotLabel->setDisabled(true);
- incrementalRotSpin->setDisabled(true);
- } else {
- incrementalRotLabel->setEnabled(true);
- incrementalRotSpin->setEnabled(true);
- }
- }
- void ccGraphicalTransformationTool::advObjectAxisRadioToggled(bool state)
- {
- if (state)
- {
- refAxisRadio->setChecked(false);
- advRotateRefUpdate(advRotateComboBox->currentIndex()); //force an update
- }
- }
- void ccGraphicalTransformationTool::clear()
- {
- m_toTransform.detachAllChildren();
- m_rotation.toIdentity();
- m_translation = CCVector3d(0,0,0);
- m_rotationCenter = CCVector3d(0,0,0);
- }
- void ccGraphicalTransformationTool::clearAdvModeEntities()
- {
- MainWindow* mainWindow = MainWindow::TheInstance();
- if (mainWindow && m_advTranslateRef != nullptr && m_associatedWin)
- {
- mainWindow->db()->unselectEntity(m_advTranslateRef);
- m_advTranslateRef = nullptr;
- }
- if (mainWindow && m_advRotateRef != nullptr && m_associatedWin)
- {
- mainWindow->db()->unselectEntity(m_advRotateRef);
- m_advRotateRef = nullptr;
- }
- }
- bool ccGraphicalTransformationTool::addEntity(ccHObject* entity)
- {
- assert(entity);
- if (!entity)
- return false;
- //we don't handle entities associated to another context
- if (entity->getDisplay() != m_associatedWin)
- {
- ccLog::Warning(QString("[Graphical Transformation Tool] Can't use entity '%1' cause it's not displayed in the active 3D view!").arg(entity->getName()));
- return false;
- }
- //we can't transform locked entities
- if (entity->isLocked())
- {
- ccLog::Warning(QString("[Graphical Transformation Tool] Can't transform entity '%1' cause it's locked!").arg(entity->getName()));
- return false;
- }
- //we can't transform child meshes
- if (entity->isA(CC_TYPES::MESH) && entity->getParent() && entity->getParent()->isKindOf(CC_TYPES::MESH))
- {
- ccLog::Warning(QString("[Graphical Transformation Tool] Entity '%1' can't be modified as it is part of a mesh group. You should 'clone' it first.").arg(entity->getName()));
- return false;
- }
- //eventually, we must check that there is no "parent + sibling" in the selection!
- //otherwise, the sibling will be rotated twice!
- unsigned n = m_toTransform.getChildrenNumber();
- for (unsigned i=0; i<n; )
- {
- ccHObject* previous = m_toTransform.getChild(i);
- if (previous->isAncestorOf(entity))
- {
- //we have found a parent, we won't add this entity
- return false;
- }
- //if the inverse is true, then we get rid of the current element!
- else if (entity->isAncestorOf(previous))
- {
- m_toTransform.detachChild(previous);
- --n;
- }
- else
- {
- //proceed
- ++i;
- }
- }
- m_toTransform.addChild(entity,ccHObject::DP_NONE);
- return true;
- }
- unsigned ccGraphicalTransformationTool::getNumberOfValidEntities() const
- {
- return m_toTransform.getChildrenNumber();
- }
- bool ccGraphicalTransformationTool::linkWith(ccGLWindowInterface* win)
- {
- if (!ccOverlayDialog::linkWith(win))
- {
- return false;
- }
-
- assert(!win || m_toTransform.getChildrenNumber() == 0);
- m_toTransform.setDisplay(win);
-
- return true;
- }
- void ccGraphicalTransformationTool::updateDisplayMessage()
- {
- if (!m_associatedWin || pauseButton->isChecked())
- {
- return;
- }
- if (advPushButton->isChecked())
- {
- m_associatedWin->displayNewMessage("[Advanced mode]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- m_associatedWin->displayNewMessage("[Select Plane or Line to translate along/rotate around]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- m_associatedWin->displayNewMessage("[If plane selected, rotation will be around normal vector]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- }
- else
- {
- m_associatedWin->displayNewMessage("[Rotation/Translation mode]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 3600, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- }
- m_associatedWin->redraw(true, false);
- }
- bool ccGraphicalTransformationTool::start()
- {
- assert(!m_processing);
- assert(m_associatedWin);
- if (!m_associatedWin)
- return false;
- unsigned childNum = m_toTransform.getChildrenNumber();
- if (childNum == 0)
- return false;
- m_rotation.toIdentity();
- m_translation = CCVector3d(0,0,0);
- m_rotationCenter = m_toTransform.getBB_recursive().getCenter(); //m_rotation center == selected entities center
- //activate "moving mode" in associated GL window
- m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_TRANSFORM_ENTITIES);
- m_associatedWin->setPickingMode(ccGLWindowInterface::NO_PICKING, Qt::OpenHandCursor);
- //the user must not close this window!
- m_associatedWin->setUnclosable(true);
- connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::rotation, this, &ccGraphicalTransformationTool::glRotate);
- connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::translation, this, &ccGraphicalTransformationTool::glTranslate);
- m_associatedWin->displayNewMessage(QString(), ccGLWindowInterface::UPPER_CENTER_MESSAGE); //clear the area
- pauseButton->setChecked(false);
- m_advancedModeObjectList.clear();
- populateAdvModeItems();
- advRotateComboBox->setCurrentIndex(0);
- advTranslateComboBox->setCurrentIndex(0);
- updateDisplayMessage();
- m_associatedWin->redraw(true, false);
- return ccOverlayDialog::start();
- }
- void ccGraphicalTransformationTool::stop(bool state)
- {
- if (m_associatedWin)
- {
- //deactivate "moving mode" in associated GL window
- m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_TRANSFORM_CAMERA);
- m_associatedWin->setPickingMode(ccGLWindowInterface::DEFAULT_PICKING);
- m_associatedWin->setUnclosable(false);
- m_associatedWin->signalEmitter()->disconnect(this);
- m_associatedWin->displayNewMessage("[Rotation/Translation mode OFF]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 2, ccGLWindowInterface::MANUAL_TRANSFORMATION_MESSAGE);
- m_associatedWin->redraw(false, false); //we have to redraw the 3D layer for labels!
- }
- ccOverlayDialog::stop(state);
- }
- void ccGraphicalTransformationTool::glTranslate(const CCVector3d& realT)
- {
- CCVector3d t;
- if (m_advMode)
- {
- CCVector3d mouseMove = realT;
- m_advTranslationTransform.inverse().applyRotation(mouseMove);
- t = CCVector3d( mouseMove.x * (TxCheckBox->isChecked() ? 1 : 0),
- mouseMove.y * (TyCheckBox->isChecked() ? 1 : 0),
- mouseMove.z * (TzCheckBox->isChecked() ? 1 : 0));
- m_advTranslationTransform.applyRotation(t);
- }
- else
- {
- t = CCVector3d( realT.x * (TxCheckBox->isChecked() ? 1 : 0),
- realT.y * (TyCheckBox->isChecked() ? 1 : 0),
- realT.z * (TzCheckBox->isChecked() ? 1 : 0));
- }
-
- if (t.norm2() != 0)
- {
- m_translation += t;
-
- if (m_advMode && m_advRotateRef != nullptr && refAxisRadio->isChecked() && !m_advRotateRefIsChild)
- {
- //If we translate we have to update the rotation center when using the ref axis as center of rotation.
- //Dont update the rotation center if the rotation ref is contained in m_toTranslate.
- CCVector3d centerUpdate = m_rotation.inverse() * m_advRotationRefObjCenter - m_rotation.inverse() * m_position.getTranslationAsVec3D();
- m_translation += (m_rotationCenter - centerUpdate) - m_rotation * (m_rotationCenter - centerUpdate);
- m_rotationCenter = centerUpdate;
- }
- updateAllGLTransformations();
- }
- }
- void ccGraphicalTransformationTool::glRotate(const ccGLMatrixd& rotMat)
- {
- if (m_advMode && m_advRotateRef != nullptr)
- {
- rotComboBoxItems rotAxis = static_cast<rotComboBoxItems>(rotComboBox->itemData(rotComboBox->currentIndex()).toInt());
- double angle = 0;
- switch (rotAxis)
- {
- case rotComboBoxItems::X:
- angle = std::asin(rotMat.xRotation()(1, 2));
- break;
- case rotComboBoxItems::Y:
- angle = std::asin(rotMat.yRotation()(2, 0));
- break;
- case rotComboBoxItems::Z:
- angle = std::asin(rotMat.zRotation()(1, 0));
- break;
- default:
- return;
- }
- ccLog::Print(QString("Angles(%1)").arg(angle));
- m_rotation = m_rotation * arbitraryVectorRotation(angle, m_advRotationAxis);
- }
- else
- {
- switch (rotComboBox->currentIndex())
- {
- case rotComboBoxItems::XYZ:
- m_rotation = rotMat * m_rotation;
- break;
- case rotComboBoxItems::X:
- m_rotation = rotMat.xRotation() * m_rotation;
- break;
- case rotComboBoxItems::Y:
- m_rotation = rotMat.yRotation() * m_rotation;
- break;
- case rotComboBoxItems::Z:
- m_rotation = rotMat.zRotation() * m_rotation;
- break;
- case rotComboBoxItems::NONE:
- break;
- }
- }
- updateAllGLTransformations();
- }
- void ccGraphicalTransformationTool::incrementalTransform() {
-
- //We check the sender to handle forward or backward transformation
- QObject* senderButton = sender();
- const bool isForwardTransform = senderButton == incrementalForwardButton;
-
- // The rotation part:
- // No incremental rotation if rotComboBoxItems == XYZ or rotComboBoxItems == NONE because it has no sense
- // it could be handled in the subsequent switch cases but it is somehiow more explicit to check
- // if the SpinBox is enabled
- if(incrementalRotSpin->isEnabled()) {
- const double alpha = isForwardTransform ? CCCoreLib::DegreesToRadians(incrementalRotSpin->value()) : -CCCoreLib::DegreesToRadians(incrementalRotSpin->value());
- if (m_advMode && m_advRotateRef != nullptr)
- {
- m_rotation = m_rotation * arbitraryVectorRotation(alpha, m_advRotationAxis);
- }
- else
- {
- ccGLMatrixd rotMat;
- // compute rotation matrix from spinbox
- switch (static_cast<rotComboBoxItems>(rotComboBox->itemData(rotComboBox->currentIndex()).toInt()))
- {
- case rotComboBoxItems::X:
- rotMat.initFromParameters(alpha, CCVector3d(1.0,0.0,0.0), CCVector3d(0.0,0.0,0.0));
- break;
- case rotComboBoxItems::Y:
- rotMat.initFromParameters(alpha, CCVector3d(0.0,1.0,0.0), CCVector3d(0.0,0.0,0.0));
- break;
- case rotComboBoxItems::Z:
- rotMat.initFromParameters(alpha, CCVector3d(0.0,0.0,1.0), CCVector3d(0.0,0.0,0.0));
- break;
- default:
- return;
- }
- m_rotation = rotMat * m_rotation;
- }
- updateAllGLTransformations();
- }
- // The translation part:
- // we pass a vector with equal magnitude in the three components; gltranslate will handle the correct transform for us.
- const double transMagnitude = isForwardTransform ? incrementalTransSpin->value() : -incrementalTransSpin->value() ;
- glTranslate(CCVector3d(transMagnitude,transMagnitude,transMagnitude));
- }
- void ccGraphicalTransformationTool::reset()
- {
- m_rotation.toIdentity();
- m_translation = CCVector3d(0, 0, 0);
- updateAllGLTransformations();
- advRotateRefUpdate(advRotateComboBox->currentIndex()); //force an update
- advTranslateRefUpdate(advTranslateComboBox->currentIndex()); //force an update
- }
- void ccGraphicalTransformationTool::setRotationCenter(CCVector3d& center)
- {
- m_translation += (m_rotationCenter - center) - m_rotation * (m_rotationCenter - center);
- m_rotationCenter = center;
- updateAllGLTransformations();
- }
- void ccGraphicalTransformationTool::updateAllGLTransformations()
- {
- //we recompute global GL transformation matrix
- m_position = m_rotation;
- m_position += m_rotationCenter + m_translation - m_rotation * m_rotationCenter;
-
- ccGLMatrix newTransf(m_position.data());
- for (unsigned i = 0; i < m_toTransform.getChildrenNumber(); ++i)
- {
- ccHObject* child = m_toTransform.getChild(i);
- child->setGLTransformation(newTransf);
- child->prepareDisplayForRefresh_recursive();
- }
- if (m_advTranslateRefIsChild && m_advTranslateRef != nullptr)
- {
- //update m_advTranslationTransform if the ref object is translated/rotated by virtue of being in m_toTransform
- if (m_advTranslateRef->isA(CC_TYPES::PLANE))
- {
- m_advTranslationTransform = m_position * ccGLMatrixd(m_advTranslateRef->getGLTransformationHistory().data());
- }
- else if (m_advTranslateRef->isA(CC_TYPES::POLY_LINE))
- {
- ccPolyline* line = static_cast<ccPolyline*>(m_advTranslateRef);
- CCVector3 arbitraryVec = line->getGLTransformation() * (*line->getPoint(1) - *line->getPoint(0));
- m_advTranslationTransform = m_position * arbitraryVectorTranslation(arbitraryVec);
- }
- }
-
- MainWindow::RefreshAllGLWindow(false);
- }
- void ccGraphicalTransformationTool::apply()
- {
- //we recompute global GL transformation matrix and display it in console
- ccGLMatrixd finalTrans = m_rotation;
- finalTrans += m_rotationCenter + m_translation - m_rotation * m_rotationCenter;
- ccGLMatrixd finalTransCorrected = finalTrans;
- #define NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
- #ifdef NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
- {
- //convert matrix back and forth so as to be sure to get a 'true' rotation matrix
- //DGM: we use Euler angles, as the axis/angle method (formerly used) is not robust
- //enough! Shifts could be perceived by the user.
- double phi_rad = 0.0;
- double theta_rad = 0.0;
- double psi_rad = 0.0;
- CCVector3d t3D;
- finalTrans.getParameters(phi_rad,theta_rad,psi_rad,t3D);
- finalTransCorrected.initFromParameters(phi_rad,theta_rad,psi_rad,t3D);
- #ifdef QT_DEBUG
- ccLog::Print("[GraphicalTransformationTool] Final transformation (before correction):");
- ccLog::Print(finalTrans.toString(12,' ')); //full precision
- ccLog::Print(QString("Angles(%1,%2,%3) T(%5,%6,%7)").arg(phi_rad).arg(theta_rad).arg(psi_rad).arg(t3D.x).arg(t3D.y).arg(t3D.z));
- #endif
- }
- #endif //NORMALIZE_TRANSFORMATION_MATRIX_WITH_EULER
- #ifdef QT_DEBUG
- //test: compute rotation "norm" (as it may not be exactly 1 due to numerical (in)accuracy!)
- {
- ccGLMatrixd finalRotation = finalTransCorrected;
- finalRotation.setTranslation(CCVector3(0,0,0));
- ccGLMatrixd finalRotationT = finalRotation.transposed();
- ccGLMatrixd idTrans = finalRotation * finalRotationT;
- double norm = idTrans.data()[0] * idTrans.data()[5] * idTrans.data()[10];
- ccLog::PrintDebug("[GraphicalTransformationTool] T*T-1:");
- ccLog::PrintDebug(idTrans.toString(12,' ')); //full precision
- ccLog::PrintDebug(QString("Rotation norm = %1").arg(norm,0,'f',12));
- }
- #endif
- //update GL transformation for all entities
- ccGLMatrix correctedFinalTrans(finalTransCorrected.data());
- for (unsigned i = 0; i < m_toTransform.getChildrenNumber(); ++i)
- {
- ccHObject* toTransform = m_toTransform.getChild(i);
- toTransform->setGLTransformation(correctedFinalTrans);
- //DGM: warning, applyGLTransformation may delete the associated octree!
- MainWindow::ccHObjectContext objContext = MainWindow::TheInstance()->removeObjectTemporarilyFromDBTree(toTransform);
- toTransform->applyGLTransformation_recursive();
- toTransform->prepareDisplayForRefresh_recursive();
- MainWindow::TheInstance()->putObjectBackIntoDBTree(toTransform,objContext);
- //special case: if the object is a mesh vertices set, we may have to update the mesh normals!
- if (toTransform->isA(CC_TYPES::POINT_CLOUD) && toTransform->getParent() && toTransform->getParent()->isKindOf(CC_TYPES::MESH))
- {
- ccMesh* mesh = static_cast<ccMesh*>(toTransform->getParent());
- if (mesh->hasTriNormals() && !m_toTransform.isAncestorOf(mesh))
- {
- mesh->transformTriNormals(correctedFinalTrans);
- }
- }
- }
- clearAdvModeEntities();
- stop(true);
- clear();
- //output resulting transformation matrix
- ccLog::Print("[GraphicalTransformationTool] Applied transformation:");
- ccLog::Print(correctedFinalTrans.toString(12,' ')); //full precision
- #ifdef QT_DEBUG
- {
- float phi_rad = 0.0f;
- float theta_rad = 0.0f;
- float psi_rad = 0.0f;
- CCVector3f t3D;
- correctedFinalTrans.getParameters(phi_rad,theta_rad,psi_rad,t3D);
- ccLog::Print(QString("Angles(%1,%2,%3) T(%5,%6,%7)").arg(phi_rad).arg(theta_rad).arg(psi_rad).arg(t3D.x).arg(t3D.y).arg(t3D.z));
- }
- #endif
- }
- void ccGraphicalTransformationTool::cancel()
- {
- for (unsigned i=0; i<m_toTransform.getChildrenNumber(); ++i)
- {
- ccHObject* child = m_toTransform.getChild(i);
- child->resetGLTransformation();
- child->prepareDisplayForRefresh_recursive();
- }
- clearAdvModeEntities();
- stop(false);
- clear();
- //MainWindow::RefreshAllGLWindow();
- }
|