ccPointPairRegistrationDlg.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 or later of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
  15. //# #
  16. //##########################################################################
  17. #include <ccPointPairRegistrationDlg.h>
  18. //Local
  19. #include "mainwindow.h"
  20. #include "ccAskThreeDoubleValuesDlg.h"
  21. //common
  22. #include <ccPickingHub.h>
  23. //qCC_gl
  24. #include <ccGLWindowInterface.h>
  25. //qCC_db
  26. #include <ccGenericPointCloud.h>
  27. #include <cc2DLabel.h>
  28. #include <ccPointCloud.h>
  29. #include <ccProgressDialog.h>
  30. #include <ccSphere.h>
  31. //qCC_io
  32. #include <ccGlobalShiftManager.h>
  33. //CC_FBO
  34. #include <ccGlFilter.h>
  35. //CCCoreLib
  36. #include <RegistrationTools.h>
  37. #include <GeometricalAnalysisTools.h>
  38. //Qt
  39. #include <QMdiSubWindow>
  40. #include <QMessageBox>
  41. #include <QToolButton>
  42. #include <QSettings>
  43. #ifdef CC_USES_EIGEN
  44. //Eigen
  45. #include <Eigen/Core>
  46. #include <Eigen/Geometry> //Umeyama
  47. #endif
  48. //default position of each columns in the to-be-aligned and ref. table widgets
  49. static const int XYZ_COL_INDEX = 0;
  50. static const int RMS_COL_INDEX = 6;
  51. static const int DEL_BUTTON_COL_INDEX = 7;
  52. //minimum number of pairs to let the user click on the align button
  53. static const unsigned MIN_PAIRS_COUNT = 3;
  54. ccPointPairRegistrationDlg::ccPointPairRegistrationDlg(ccPickingHub* pickingHub, ccMainAppInterface* app, QWidget* parent/*=nullptr*/)
  55. : ccOverlayDialog(parent)
  56. , m_alignedPoints("to-be-aligned points")
  57. , m_refPoints("reference points")
  58. , m_paused(false)
  59. , m_pickingHub(pickingHub)
  60. , m_app(app)
  61. {
  62. assert(m_pickingHub);
  63. setupUi(this);
  64. //restore from persistent settings
  65. {
  66. QSettings settings;
  67. settings.beginGroup("PointPairAlign");
  68. bool pickSpheres = settings.value("PickSpheres", useSphereToolButton->isChecked()).toBool();
  69. double sphereRadius = settings.value("SphereRadius", radiusDoubleSpinBox->value()).toDouble();
  70. int maxRMS = settings.value("MaxRMS", maxRmsSpinBox->value()).toInt();
  71. bool adjustScale = settings.value("AdjustScale", adjustScaleCheckBox->isChecked()).toBool();
  72. bool autoUpdateZoom = settings.value("AutoUpdateZom",autoZoomCheckBox->isChecked()).toBool();
  73. settings.endGroup();
  74. useSphereToolButton->setChecked(pickSpheres);
  75. radiusDoubleSpinBox->setValue(sphereRadius);
  76. maxRmsSpinBox->setValue(maxRMS);
  77. adjustScaleCheckBox->setChecked(adjustScale);
  78. autoZoomCheckBox->setChecked(autoUpdateZoom);
  79. }
  80. connect(showAlignedCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::showAlignedEntities);
  81. connect(showReferenceCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::showReferenceEntities);
  82. connect(typeAlignToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::addManualAlignedPoint);
  83. connect(typeRefToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::addManualRefPoint);
  84. connect(unstackAlignToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::unstackAligned);
  85. connect(unstackRefToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::unstackRef);
  86. connect(alignToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::align);
  87. connect(resetToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::reset);
  88. connect(validToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::apply);
  89. connect(cancelToolButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::cancel);
  90. connect(adjustScaleCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::updateAlignInfo);
  91. connect(TxCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::updateAlignInfo);
  92. connect(TyCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::updateAlignInfo);
  93. connect(TzCheckBox, &QCheckBox::toggled, this, &ccPointPairRegistrationDlg::updateAlignInfo);
  94. connect(rotComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccPointPairRegistrationDlg::updateAlignInfo);
  95. m_alignedPoints.setEnabled(true);
  96. m_alignedPoints.setVisible(false);
  97. m_refPoints.setEnabled(true);
  98. m_refPoints.setVisible(false);
  99. }
  100. ccPointPairRegistrationDlg::EntityContext::EntityContext(ccHObject* ent)
  101. : entity(ent)
  102. , originalDisplay(entity ? entity->getDisplay() : nullptr)
  103. , wasVisible(entity ? entity->isVisible() : false)
  104. , wasEnabled(entity ? entity->isEnabled() : false)
  105. , wasSelected(entity ? entity->isSelected() : false)
  106. {
  107. }
  108. void ccPointPairRegistrationDlg::EntityContext::restore()
  109. {
  110. if (entity)
  111. {
  112. entity->setDisplay(originalDisplay);
  113. entity->setVisible(wasVisible);
  114. entity->setEnabled(wasEnabled);
  115. entity->setSelected(wasSelected);
  116. if (originalDisplay)
  117. originalDisplay->redraw();
  118. }
  119. }
  120. void ccPointPairRegistrationDlg::EntityContexts::fill(const ccHObject::Container& entities)
  121. {
  122. clear();
  123. isShifted = false;
  124. for (ccHObject* entity : entities)
  125. {
  126. if (!entity)
  127. {
  128. assert(false);
  129. continue;
  130. }
  131. if (!isShifted)
  132. {
  133. ccShiftedObject* shiftedEntity = ccHObjectCaster::ToShifted(entity);
  134. if (shiftedEntity && shiftedEntity->isShifted())
  135. {
  136. isShifted = true;
  137. shift = shiftedEntity->getGlobalShift(); //we can only consider the first shift!
  138. scale = shiftedEntity->getGlobalScale();
  139. }
  140. }
  141. insert(entity, EntityContext(entity));
  142. }
  143. }
  144. void ccPointPairRegistrationDlg::clear()
  145. {
  146. alignToolButton->setEnabled(false);
  147. validToolButton->setEnabled(false);
  148. while (alignedPointsTableWidget->rowCount() != 0)
  149. alignedPointsTableWidget->removeRow(alignedPointsTableWidget->rowCount() - 1);
  150. while (refPointsTableWidget->rowCount() != 0)
  151. refPointsTableWidget->removeRow(refPointsTableWidget->rowCount() - 1);
  152. m_alignedPoints.removeAllChildren();
  153. m_alignedPoints.resize(0);
  154. m_alignedPoints.setGlobalShift(0, 0, 0);
  155. m_alignedPoints.setGlobalScale(1.0);
  156. m_alignedEntities.clear();
  157. m_refPoints.removeAllChildren();
  158. m_refPoints.resize(0);
  159. m_refPoints.setGlobalShift(0, 0, 0);
  160. m_refPoints.setGlobalScale(1.0);
  161. m_referenceEntities.clear();
  162. }
  163. bool ccPointPairRegistrationDlg::linkWith(ccGLWindowInterface* win)
  164. {
  165. ccGLWindowInterface* oldWin = m_associatedWin;
  166. if (oldWin)
  167. {
  168. if (oldWin != win)
  169. {
  170. //TODO FIXME: is it still necessary?
  171. oldWin->signalEmitter()->disconnect(this);
  172. }
  173. oldWin->removeFromOwnDB(&m_alignedPoints);
  174. m_alignedPoints.setDisplay(nullptr);
  175. oldWin->removeFromOwnDB(&m_refPoints);
  176. m_refPoints.setDisplay(nullptr);
  177. m_pickingHub->removeListener(this);
  178. }
  179. if (!ccOverlayDialog::linkWith(win))
  180. {
  181. return false;
  182. }
  183. m_alignedEntities.restoreAll();
  184. m_referenceEntities.restoreAll();
  185. if (oldWin && MainWindow::TheInstance())
  186. {
  187. QMdiSubWindow* subWindow = MainWindow::TheInstance()->getMDISubWindow(oldWin);
  188. if (subWindow)
  189. subWindow->close();
  190. }
  191. if (m_associatedWin)
  192. {
  193. if (!m_pickingHub->addListener(this, true, true, ccGLWindowInterface::POINT_OR_TRIANGLE_OR_LABEL_PICKING))
  194. {
  195. ccLog::Error("Picking mechanism is already in use! Close the other tool first, and then restart this one.");
  196. return false;
  197. }
  198. m_associatedWin->addToOwnDB(&m_alignedPoints);
  199. m_associatedWin->addToOwnDB(&m_refPoints);
  200. m_associatedWin->displayNewMessage(QString(), ccGLWindowInterface::LOWER_LEFT_MESSAGE);
  201. m_associatedWin->displayNewMessage("(you can add points 'manually' if necessary)", ccGLWindowInterface::LOWER_LEFT_MESSAGE, true, 3600);
  202. m_associatedWin->displayNewMessage(QString("Pick equivalent points on both clouds (at least %1 pairs - mind the order)").arg(MIN_PAIRS_COUNT), ccGLWindowInterface::LOWER_LEFT_MESSAGE, true, 3600);
  203. }
  204. return true;
  205. }
  206. bool ccPointPairRegistrationDlg::start()
  207. {
  208. assert(!m_alignedEntities.empty());
  209. return ccOverlayDialog::start();
  210. }
  211. void ccPointPairRegistrationDlg::stop(bool state)
  212. {
  213. reset();
  214. ccOverlayDialog::stop(state);
  215. }
  216. static void SetEnabled_recursive(ccHObject* ent)
  217. {
  218. assert(ent);
  219. ent->setEnabled(true);
  220. if (ent->getParent())
  221. SetEnabled_recursive(ent->getParent());
  222. }
  223. bool ccPointPairRegistrationDlg::init( ccGLWindowInterface* win,
  224. const ccHObject::Container& alignedEntities,
  225. const ccHObject::Container* referenceEntities/*=nullptr*/)
  226. {
  227. if (!win)
  228. {
  229. assert(false);
  230. return false;
  231. }
  232. clear();
  233. if (alignedEntities.empty())
  234. {
  235. ccLog::Error("[PointPairRegistration] Need at least one to-be-aligned entity!");
  236. return false;
  237. }
  238. m_alignedEntities.fill(alignedEntities);
  239. if (referenceEntities)
  240. {
  241. m_referenceEntities.fill(*referenceEntities);
  242. }
  243. //create dedicated 3D view
  244. if (!m_associatedWin)
  245. {
  246. //import GL filter so as to get the same rendering aspect!
  247. {
  248. //find an orgin display (we'll take the first valid one)
  249. ccGenericGLDisplay* sourceDisplay = nullptr;
  250. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  251. {
  252. sourceDisplay = it.key()->getDisplay();
  253. if (sourceDisplay)
  254. // we found a display
  255. break;
  256. }
  257. if (!sourceDisplay && !m_referenceEntities.empty())
  258. {
  259. for (auto it = m_referenceEntities.begin(); it != m_referenceEntities.end(); ++it)
  260. {
  261. sourceDisplay = it.key()->getDisplay();
  262. if (sourceDisplay)
  263. // we found a display
  264. break;
  265. }
  266. }
  267. if (sourceDisplay)
  268. {
  269. ccGlFilter* filter = static_cast<ccGLWindowInterface*>(sourceDisplay)->getGlFilter();
  270. if (filter)
  271. // transfer the GL filter to the dedicated tool window
  272. win->setGlFilter(filter->clone());
  273. }
  274. }
  275. linkWith(win);
  276. assert(m_associatedWin);
  277. }
  278. //add aligned entity to display
  279. ccViewportParameters originViewportParams;
  280. bool hasOriginViewportParams = false;
  281. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  282. {
  283. ccHObject* aligned = it.key();
  284. if (!hasOriginViewportParams && aligned->getDisplay())
  285. {
  286. hasOriginViewportParams = true;
  287. originViewportParams = aligned->getDisplay()->getViewportParameters();
  288. }
  289. //DGM: it's already in the global DB!
  290. //m_associatedWin->addToOwnDB(aligned);
  291. aligned->setDisplay(m_associatedWin);
  292. aligned->setVisible(true);
  293. SetEnabled_recursive(aligned);
  294. //SetVisible_recursive(aligned);
  295. //add the 1-point child labels as well (if any)
  296. for (unsigned i = 0; i < aligned->getChildrenNumber(); ++i)
  297. {
  298. cc2DLabel* label = ccHObjectCaster::To2DLabel(aligned->getChild(i));
  299. if (label && label->size() == 1)
  300. {
  301. m_alignedEntities.insert(label, EntityContext(label));
  302. m_associatedWin->addToOwnDB(label);
  303. label->setDisplay(m_associatedWin);
  304. label->setVisible(true);
  305. }
  306. }
  307. }
  308. //add reference entity (if any) to display
  309. for (auto it = m_referenceEntities.begin(); it != m_referenceEntities.end(); ++it)
  310. {
  311. ccHObject* reference = it.key();
  312. if (!hasOriginViewportParams && reference->getDisplay())
  313. {
  314. hasOriginViewportParams = true;
  315. originViewportParams = reference->getDisplay()->getViewportParameters();
  316. }
  317. //DGM: it's already in the global DB!
  318. //m_associatedWin->addToOwnDB(reference);
  319. reference->setDisplay(m_associatedWin);
  320. reference->setVisible(true);
  321. SetEnabled_recursive(reference);
  322. //SetVisible_recursive(reference);
  323. //add the 1-point child labels as well (if any)
  324. for (unsigned i = 0; i < reference->getChildrenNumber(); ++i)
  325. {
  326. cc2DLabel* label = ccHObjectCaster::To2DLabel(reference->getChild(i));
  327. if (label && label->size() == 1)
  328. {
  329. m_referenceEntities.insert(label, EntityContext(label));
  330. m_associatedWin->addToOwnDB(label);
  331. label->setDisplay(m_associatedWin);
  332. label->setVisible(true);
  333. }
  334. }
  335. }
  336. showReferenceCheckBox->setChecked(!m_referenceEntities.empty());
  337. showReferenceCheckBox->setEnabled(!m_referenceEntities.empty());
  338. showAlignedCheckBox->setChecked(true);
  339. m_associatedWin->doShowMaximized();
  340. resetTitle();
  341. if (hasOriginViewportParams)
  342. {
  343. m_associatedWin->setViewportParameters(originViewportParams);
  344. m_associatedWin->redraw();
  345. }
  346. else
  347. {
  348. m_associatedWin->zoomGlobal();
  349. m_associatedWin->redraw(); //already called by zoomGlobal
  350. }
  351. onPointCountChanged();
  352. return true;
  353. }
  354. static QString s_aligned_tooltip = QObject::tr("Whether the point is expressed in the entity original coordinate system (before being shifted by CC) or not");
  355. static double s_last_ax = 0;
  356. static double s_last_ay = 0;
  357. static double s_last_az = 0;
  358. static bool s_lastAlignePointIsGlobal = true;
  359. void ccPointPairRegistrationDlg::addManualAlignedPoint()
  360. {
  361. ccAskThreeDoubleValuesDlg ptsDlg("x", "y", "z", -1.0e12, 1.0e12, s_last_ax, s_last_ay, s_last_az, 8, "Add aligned point", this);
  362. //if the aligned entity is shifted, the user has the choice to input virtual point either
  363. //in the original coordinate system or the shifted one
  364. if (m_alignedEntities.isShifted)
  365. ptsDlg.showCheckbox("Not shifted", s_lastAlignePointIsGlobal, s_aligned_tooltip);
  366. if (!ptsDlg.exec())
  367. return;
  368. //save values for current session
  369. s_last_ax = ptsDlg.doubleSpinBox1->value();
  370. s_last_ay = ptsDlg.doubleSpinBox2->value();
  371. s_last_az = ptsDlg.doubleSpinBox3->value();
  372. bool shifted = true;
  373. if (m_alignedEntities.isShifted)
  374. {
  375. s_lastAlignePointIsGlobal = ptsDlg.getCheckboxState();
  376. shifted = !s_lastAlignePointIsGlobal;
  377. }
  378. CCVector3d P(s_last_ax, s_last_ay, s_last_az);
  379. addAlignedPoint(P, nullptr, shifted);
  380. }
  381. static double s_last_rx = 0;
  382. static double s_last_ry = 0;
  383. static double s_last_rz = 0;
  384. static bool s_lastRefPointisGlobal = true;
  385. void ccPointPairRegistrationDlg::addManualRefPoint()
  386. {
  387. ccAskThreeDoubleValuesDlg ptsDlg("x", "y", "z", -1.0e12, 1.0e12, s_last_rx, s_last_ry, s_last_rz, 8, "Add reference point", this);
  388. //if the reference entity is shifted, the user has the choice to input virtual
  389. //points either in the original coordinate system or the shifted one
  390. //(if there's no reference entity, we use a 'global' one by default)
  391. if (m_referenceEntities.isShifted)
  392. ptsDlg.showCheckbox("Not shifted", s_lastRefPointisGlobal, s_aligned_tooltip);
  393. if (!ptsDlg.exec())
  394. return;
  395. //save values for current session
  396. s_last_rx = ptsDlg.doubleSpinBox1->value();
  397. s_last_ry = ptsDlg.doubleSpinBox2->value();
  398. s_last_rz = ptsDlg.doubleSpinBox3->value();
  399. bool shifted = (!m_referenceEntities.empty());
  400. if (m_referenceEntities.isShifted)
  401. {
  402. s_lastRefPointisGlobal = ptsDlg.getCheckboxState();
  403. shifted = !s_lastRefPointisGlobal;
  404. }
  405. CCVector3d P(s_last_rx, s_last_ry, s_last_rz);
  406. addReferencePoint(P, nullptr, shifted);
  407. }
  408. void ccPointPairRegistrationDlg::pause(bool state)
  409. {
  410. m_paused = state;
  411. setDisabled(state);
  412. }
  413. bool ccPointPairRegistrationDlg::convertToSphereCenter(CCVector3d& P, ccHObject* entity, PointCoordinateType& sphereRadius)
  414. {
  415. sphereRadius = 0;
  416. if ( !entity
  417. || !useSphereToolButton->isChecked()
  418. || !entity->isKindOf(CC_TYPES::POINT_CLOUD) ) //only works with cloud right now
  419. {
  420. //nothing to do
  421. return true;
  422. }
  423. //we'll now try to detect the sphere
  424. double searchRadius = radiusDoubleSpinBox->value();
  425. double maxRMSPercentage = maxRmsSpinBox->value() / 100.0;
  426. ccGenericPointCloud* cloud = static_cast<ccGenericPointCloud*>(entity);
  427. assert(cloud);
  428. //crop points inside a box centered on the current point
  429. ccBBox box;
  430. box.add((P - CCVector3d(1, 1, 1)*searchRadius).toPC());
  431. box.add((P + CCVector3d(1, 1, 1)*searchRadius).toPC());
  432. CCCoreLib::ReferenceCloud* part = cloud->crop(box,true);
  433. bool success = false;
  434. if (part && part->size() > 16)
  435. {
  436. PointCoordinateType radius;
  437. CCVector3 C;
  438. double rms;
  439. ccProgressDialog pDlg(true, this);
  440. //first roughly search for the sphere
  441. if (CCCoreLib::GeometricalAnalysisTools::DetectSphereRobust(part, 0.5, C, radius, rms, &pDlg, 0.9) == CCCoreLib::GeometricalAnalysisTools::NoError)
  442. {
  443. if (radius / searchRadius < 0.5 || radius / searchRadius > 2.0)
  444. {
  445. ccLog::Warning(QString("[ccPointPairRegistrationDlg] Detected sphere radius (%1) is too far from search radius!").arg(radius));
  446. }
  447. else
  448. {
  449. //now look again (more precisely)
  450. {
  451. delete part;
  452. box.clear();
  453. box.add(C - CCVector3(1, 1, 1)*radius*static_cast<PointCoordinateType>(1.05)); //add 5%
  454. box.add(C + CCVector3(1, 1, 1)*radius*static_cast<PointCoordinateType>(1.05)); //add 5%
  455. part = cloud->crop(box, true);
  456. if (part && part->size() > 16)
  457. CCCoreLib::GeometricalAnalysisTools::DetectSphereRobust(part, 0.5, C, radius, rms, &pDlg, 0.99);
  458. }
  459. ccLog::Print(QString("[ccPointPairRegistrationDlg] Detected sphere radius = %1 (rms = %2)").arg(radius).arg(rms));
  460. if (radius / searchRadius < 0.5 || radius / searchRadius > 2.0)
  461. {
  462. ccLog::Warning("[ccPointPairRegistrationDlg] Sphere radius is too far from search radius!");
  463. }
  464. else if (rms / searchRadius >= maxRMSPercentage)
  465. {
  466. ccLog::Warning("[ccPointPairRegistrationDlg] RMS is too high!");
  467. }
  468. else
  469. {
  470. sphereRadius = radius;
  471. P = C;
  472. success = true;
  473. }
  474. }
  475. }
  476. else
  477. {
  478. ccLog::Warning("[ccPointPairRegistrationDlg] Failed to fit a sphere around the picked point!");
  479. }
  480. }
  481. else
  482. {
  483. //not enough memory? No points inside the
  484. ccLog::Warning("[ccPointPairRegistrationDlg] Failed to crop points around the picked point?!");
  485. }
  486. if (part)
  487. delete part;
  488. return success;
  489. }
  490. void ccPointPairRegistrationDlg::onItemPicked(const PickedItem& pi)
  491. {
  492. //no point picking when paused!
  493. if (m_paused)
  494. return;
  495. if (!pi.entity)
  496. return;
  497. if (m_alignedEntities.contains(pi.entity))
  498. {
  499. CCVector3d P = pi.P3D.toDouble();
  500. addAlignedPoint(P, pi.entity, true); //picked points are always shifted by default
  501. }
  502. else if (m_referenceEntities.contains(pi.entity))
  503. {
  504. CCVector3d P = pi.P3D.toDouble();
  505. addReferencePoint(P, pi.entity, true); //picked points are always shifted by default
  506. }
  507. else
  508. {
  509. if (pi.entity && pi.entity->isA(CC_TYPES::LABEL_2D))
  510. {
  511. ccLog::Error(tr("Point/label already picked"));
  512. }
  513. else
  514. {
  515. assert(false);
  516. }
  517. return;
  518. }
  519. if (m_associatedWin)
  520. {
  521. m_associatedWin->redraw();
  522. }
  523. }
  524. void ccPointPairRegistrationDlg::onPointCountChanged()
  525. {
  526. bool canAlign = (m_alignedPoints.size() == m_refPoints.size() && m_refPoints.size() >= MIN_PAIRS_COUNT);
  527. alignToolButton->setEnabled(canAlign);
  528. validToolButton->setEnabled(false);
  529. unstackAlignToolButton->setEnabled(m_alignedPoints.size() != 0);
  530. unstackRefToolButton->setEnabled(m_refPoints.size() != 0);
  531. updateAlignInfo();
  532. }
  533. static QToolButton* CreateDeleteButton()
  534. {
  535. QToolButton* delButton = new QToolButton();
  536. delButton->setIcon(QIcon(":/CC/images/smallCancel.png"));
  537. return delButton;
  538. }
  539. static cc2DLabel* CreateLabel(cc2DLabel* label, ccPointCloud* cloud, unsigned pointIndex, QString pointName, ccGenericGLDisplay* display = nullptr)
  540. {
  541. assert(label);
  542. label->addPickedPoint(cloud, pointIndex);
  543. label->setName(pointName);
  544. label->setVisible(true);
  545. label->setDisplayedIn2D(false);
  546. label->displayPointLegend(true);
  547. label->setDisplay(display);
  548. return label;
  549. }
  550. static cc2DLabel* CreateLabel(ccPointCloud* cloud, unsigned pointIndex, QString pointName, ccGenericGLDisplay* display = nullptr)
  551. {
  552. return CreateLabel(new cc2DLabel, cloud, pointIndex, pointName, display);
  553. }
  554. void ccPointPairRegistrationDlg::onDelButtonPushed()
  555. {
  556. QObject* senderButton = sender();
  557. //go through all the buttons and find which one has been pushed!
  558. bool alignedPoint = true;
  559. int pointIndex = -1;
  560. //test 'aligned' buttons first
  561. {
  562. for (int i = 0; i < alignedPointsTableWidget->rowCount(); ++i)
  563. {
  564. if (alignedPointsTableWidget->cellWidget(i, DEL_BUTTON_COL_INDEX) == senderButton)
  565. {
  566. pointIndex = i;
  567. break;
  568. }
  569. }
  570. }
  571. if (pointIndex < 0)
  572. {
  573. //test reference points if necessary
  574. alignedPoint = false;
  575. for (int i = 0; i < refPointsTableWidget->rowCount(); ++i)
  576. {
  577. if (refPointsTableWidget->cellWidget(i, DEL_BUTTON_COL_INDEX) == senderButton)
  578. {
  579. pointIndex = i;
  580. break;
  581. }
  582. }
  583. }
  584. if (pointIndex < 0)
  585. {
  586. assert(false);
  587. return;
  588. }
  589. if (alignedPoint)
  590. removeAlignedPoint(pointIndex);
  591. else
  592. removeRefPoint(pointIndex);
  593. }
  594. void ccPointPairRegistrationDlg::addPointToTable( QTableWidget* tableWidget,
  595. int rowIndex,
  596. const CCVector3d& P,
  597. QString pointName )
  598. {
  599. assert(tableWidget);
  600. if (!tableWidget)
  601. return;
  602. //add corresponding row in table
  603. tableWidget->setRowCount(std::max<int>(rowIndex + 1, tableWidget->rowCount()));
  604. tableWidget->setVerticalHeaderItem(rowIndex, new QTableWidgetItem(pointName));
  605. //add point coordinates
  606. for (int d = 0; d < 3; ++d)
  607. {
  608. QTableWidgetItem* item = new QTableWidgetItem();
  609. item->setData(Qt::EditRole, QString::number(P.u[d], 'f', 6));
  610. tableWidget->setItem(rowIndex, XYZ_COL_INDEX + d, item);
  611. }
  612. //add 'remove' button
  613. {
  614. if (rowIndex == 0)
  615. tableWidget->setColumnWidth(DEL_BUTTON_COL_INDEX, 20);
  616. //QTableWidgetItem* item = new QTableWidgetItem();
  617. //tableWidget->setItem(rowIndex, DEL_BUTTON_COL_INDEX, item);
  618. QToolButton* delButton = CreateDeleteButton();
  619. connect(delButton, &QToolButton::clicked, this, &ccPointPairRegistrationDlg::onDelButtonPushed);
  620. tableWidget->setCellWidget(rowIndex, DEL_BUTTON_COL_INDEX, delButton);
  621. }
  622. }
  623. bool ccPointPairRegistrationDlg::addAlignedPoint(CCVector3d& Pin, ccHObject* entity/*=nullptr*/, bool shifted/*=false*/)
  624. {
  625. //if the input point is not shifted, we shift it to the aligned coordinate system
  626. assert(entity == nullptr || m_alignedEntities.contains(entity));
  627. //first point?
  628. if (m_alignedPoints.size() == 0)
  629. {
  630. //simply copy the cloud global shift/scale
  631. ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(entity);
  632. if (cloud)
  633. {
  634. m_alignedPoints.copyGlobalShiftAndScale(*cloud);
  635. }
  636. }
  637. PointCoordinateType sphereRadius = 0;
  638. if (!convertToSphereCenter(Pin, entity, sphereRadius))
  639. return false;
  640. //transform the input point in the 'global world' by default
  641. if (shifted)
  642. Pin = m_alignedPoints.toGlobal3d<double>(Pin);
  643. //check that we don't duplicate points
  644. for (unsigned i = 0; i < m_alignedPoints.size(); ++i)
  645. {
  646. CCVector3d Pi = m_alignedPoints.toGlobal3d<PointCoordinateType>(*m_alignedPoints.getPoint(i));
  647. if (CCCoreLib::LessThanSquareEpsilon((Pi - Pin).norm2d()))
  648. {
  649. ccLog::Error("Point already picked or too close to an already selected one!");
  650. return false;
  651. }
  652. }
  653. unsigned newPointIndex = m_alignedPoints.size();
  654. if (newPointIndex == m_alignedPoints.capacity() && !m_alignedPoints.reserve(newPointIndex + 1))
  655. {
  656. ccLog::Error("Not enough memory?!");
  657. return false;
  658. }
  659. //shift point to the local coordinate system before pushing it
  660. CCVector3 P = m_alignedPoints.toLocal3pc<double>(Pin);
  661. m_alignedPoints.addPoint(P);
  662. QString pointName = QString("A%1").arg(newPointIndex);
  663. //eventually add a label (or a sphere)
  664. if (sphereRadius <= 0)
  665. {
  666. //if the picked point is associated to a label
  667. cc2DLabel* originLabel = nullptr;
  668. if (entity && entity->isA(CC_TYPES::LABEL_2D))
  669. {
  670. for (const EntityContext& ec : m_alignedEntities)
  671. {
  672. if (ec.entity == entity)
  673. {
  674. originLabel = ccHObjectCaster::To2DLabel(ec.entity);
  675. //we've found a label corresponding to the chosen point
  676. pointName += QString(" (%1)").arg(originLabel->getName());
  677. originLabel->setEnabled(false);
  678. //TODO add the label name to a new column?
  679. }
  680. }
  681. }
  682. cc2DLabel* label = CreateLabel(&m_alignedPoints, newPointIndex, pointName, m_associatedWin);
  683. if (originLabel)
  684. {
  685. //remember the associated label
  686. label->setMetaData("AssociatedLabelID", originLabel->getUniqueID());
  687. }
  688. m_alignedPoints.addChild(label);
  689. }
  690. else
  691. {
  692. ccGLMatrix trans;
  693. trans.setTranslation(P);
  694. ccSphere* sphere = new ccSphere(sphereRadius, &trans, pointName);
  695. sphere->showNameIn3D(true);
  696. sphere->setTempColor(ccColor::red,true);
  697. m_alignedPoints.addChild(sphere);
  698. }
  699. //add corresponding row in table
  700. addPointToTable(alignedPointsTableWidget, newPointIndex, Pin, pointName);
  701. if (m_associatedWin)
  702. {
  703. m_associatedWin->redraw();
  704. }
  705. onPointCountChanged();
  706. return true;
  707. }
  708. void ccPointPairRegistrationDlg::removeLabel( ccPointCloud& points,
  709. unsigned childIndex,
  710. const EntityContexts& entities )
  711. {
  712. if (childIndex >= points.getChildrenNumber())
  713. {
  714. assert(false);
  715. return;
  716. }
  717. cc2DLabel* label = ccHObjectCaster::To2DLabel(points.getChild(childIndex));
  718. if (label)
  719. {
  720. //if the label had an associated label
  721. if (label->hasMetaData("AssociatedLabelID"))
  722. {
  723. unsigned uuid = label->metaData()["AssociatedLabelID"].toUInt();
  724. for (const EntityContext& ec : entities)
  725. {
  726. if (ec.entity && ec.entity->getUniqueID() == uuid)
  727. {
  728. //restore the original entity status (normally it was enabled!)
  729. ec.entity->setEnabled(ec.wasEnabled);
  730. break;
  731. }
  732. }
  733. }
  734. }
  735. else
  736. {
  737. //should be a label!
  738. assert(false);
  739. }
  740. points.removeChild(childIndex);
  741. }
  742. void ccPointPairRegistrationDlg::unstackAligned()
  743. {
  744. unsigned pointCount = m_alignedPoints.size();
  745. if (pointCount == 0) //nothing to do
  746. return;
  747. assert(alignedPointsTableWidget->rowCount() > 0);
  748. alignedPointsTableWidget->removeRow(alignedPointsTableWidget->rowCount() - 1);
  749. //remove label
  750. assert(m_alignedPoints.getChildrenNumber() == pointCount);
  751. --pointCount;
  752. removeLabel(m_alignedPoints, pointCount, m_alignedEntities);
  753. //remove point
  754. m_alignedPoints.resize(pointCount);
  755. if (m_associatedWin)
  756. {
  757. m_associatedWin->redraw();
  758. }
  759. onPointCountChanged();
  760. }
  761. void ccPointPairRegistrationDlg::removeAlignedPoint(int index, bool autoRemoveDualPoint/*=false*/)
  762. {
  763. if (index >= static_cast<int>(m_alignedPoints.size()))
  764. {
  765. ccLog::Error("[ccPointPairRegistrationDlg::removeAlignedPoint] Invalid index!");
  766. assert(false);
  767. return;
  768. }
  769. int pointCount = static_cast<int>(m_alignedPoints.size());
  770. //remove the label (or sphere)
  771. removeLabel(m_alignedPoints, index, m_alignedEntities);
  772. //remove array row
  773. alignedPointsTableWidget->removeRow(index);
  774. //shift points & rename labels
  775. for (int i = index + 1; i < pointCount; ++i)
  776. {
  777. *const_cast<CCVector3*>(m_alignedPoints.getPoint(i - 1)) = *m_alignedPoints.getPoint(i);
  778. //new name
  779. QString pointName = QString("A%1").arg(i - 1);
  780. //update the label (if any)
  781. ccHObject* child = m_alignedPoints.getChild(i - 1);
  782. if (child)
  783. {
  784. if (child->isKindOf(CC_TYPES::LABEL_2D))
  785. {
  786. cc2DLabel* label = static_cast<cc2DLabel*>(child);
  787. label->clear();
  788. CreateLabel(label, &m_alignedPoints, static_cast<unsigned>(i - 1), pointName, m_associatedWin);
  789. }
  790. else //probably a sphere
  791. {
  792. child->setName(pointName);
  793. }
  794. }
  795. //update array
  796. alignedPointsTableWidget->setVerticalHeaderItem(i - 1, new QTableWidgetItem(pointName));
  797. }
  798. m_alignedPoints.invalidateBoundingBox();
  799. pointCount--;
  800. assert(pointCount >= 0);
  801. m_alignedPoints.resize(static_cast<unsigned>(pointCount));
  802. if (m_alignedPoints.size() == 0)
  803. {
  804. //reset global shift (if any)
  805. m_alignedPoints.setGlobalShift(0, 0, 0);
  806. m_alignedPoints.setGlobalScale(1.0);
  807. }
  808. if (m_associatedWin)
  809. {
  810. m_associatedWin->redraw();
  811. }
  812. onPointCountChanged();
  813. //auto-remove the other point?
  814. if ( autoRemoveDualPoint
  815. && index < static_cast<int>(m_refPoints.size())
  816. && QMessageBox::question(nullptr, tr("Remove dual point"), tr("Remove the equivalent reference point as well?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
  817. {
  818. removeRefPoint(index, false);
  819. }
  820. }
  821. bool ccPointPairRegistrationDlg::addReferencePoint(CCVector3d& Pin, ccHObject* entity/*=nullptr*/, bool shifted/*=true*/)
  822. {
  823. assert(entity == nullptr || m_referenceEntities.contains(entity));
  824. ccGenericPointCloud* cloud = entity ? ccHObjectCaster::ToGenericPointCloud(entity) : nullptr;
  825. //first point?
  826. if (m_refPoints.size() == 0)
  827. {
  828. if (entity) //picked point
  829. {
  830. //simply copy the cloud global shift/scale
  831. if (cloud)
  832. {
  833. m_refPoints.copyGlobalShiftAndScale(*cloud);
  834. }
  835. }
  836. else //virtual point
  837. {
  838. m_refPoints.setGlobalScale(1.0);
  839. m_refPoints.setGlobalShift(0, 0, 0);
  840. if (!shifted)
  841. {
  842. //test that the input point has not too big coordinates
  843. //we use the aligned shift by default (if any)
  844. bool shiftEnabled = m_alignedEntities.isShifted;
  845. CCVector3d Pshift = m_alignedEntities.shift;
  846. double scale = 1.0;
  847. if (ccGlobalShiftManager::Handle( Pin,
  848. 0.0,
  849. ccGlobalShiftManager::NO_DIALOG_AUTO_SHIFT,
  850. shiftEnabled,
  851. Pshift,
  852. nullptr,
  853. &scale)
  854. )
  855. {
  856. m_refPoints.setGlobalShift(Pshift);
  857. m_refPoints.setGlobalScale(scale);
  858. }
  859. }
  860. }
  861. }
  862. PointCoordinateType sphereRadius = 0;
  863. if (!convertToSphereCenter(Pin, entity, sphereRadius))
  864. return false;
  865. //transform the input point in the 'global world' by default
  866. if (shifted && cloud)
  867. {
  868. Pin = cloud->toGlobal3d<double>(Pin);
  869. }
  870. //check that we don't duplicate points
  871. for (unsigned i = 0; i < m_refPoints.size(); ++i)
  872. {
  873. //express the 'Pi' point in the current global coordinate system
  874. CCVector3d Pi = m_refPoints.toGlobal3d<PointCoordinateType>(*m_refPoints.getPoint(i));
  875. if (CCCoreLib::LessThanSquareEpsilon((Pi - Pin).norm2d()))
  876. {
  877. ccLog::Error("Point already picked or too close to an already selected one!");
  878. return false;
  879. }
  880. }
  881. //add point to the 'reference' set
  882. unsigned newPointIndex = m_refPoints.size();
  883. if (newPointIndex == m_refPoints.capacity() && !m_refPoints.reserve(newPointIndex + 1))
  884. {
  885. ccLog::Error("Not enough memory?!");
  886. return false;
  887. }
  888. //shift point to the local coordinate system before pushing it
  889. CCVector3 P = m_refPoints.toLocal3pc<double>(Pin);
  890. m_refPoints.addPoint(P);
  891. QString pointName = QString("R%1").arg(newPointIndex);
  892. //eventually add a label (or a sphere)
  893. if (sphereRadius <= 0)
  894. {
  895. //if the picked point is associated to a label
  896. cc2DLabel* originLabel = nullptr;
  897. if (entity && entity->isA(CC_TYPES::LABEL_2D))
  898. {
  899. for (const EntityContext& ec : m_referenceEntities)
  900. {
  901. if (ec.entity == entity)
  902. {
  903. originLabel = ccHObjectCaster::To2DLabel(ec.entity);
  904. //we've found a label corresponding to the chosen point
  905. pointName += QString(" (%1)").arg(originLabel->getName());
  906. originLabel->setEnabled(false);
  907. //TODO add the label name to a new column?
  908. }
  909. }
  910. }
  911. cc2DLabel* label = CreateLabel(&m_refPoints, newPointIndex, pointName, m_associatedWin);
  912. if (originLabel)
  913. {
  914. //remember the associated label
  915. label->setMetaData("AssociatedLabelID", originLabel->getUniqueID());
  916. }
  917. m_refPoints.addChild(label);
  918. }
  919. else
  920. {
  921. ccGLMatrix trans;
  922. trans.setTranslation(P);
  923. ccSphere* sphere = new ccSphere(sphereRadius, &trans, pointName);
  924. sphere->showNameIn3D(true);
  925. sphere->setTempColor(ccColor::yellow, true);
  926. m_refPoints.addChild(sphere);
  927. }
  928. //add corresponding row in table
  929. addPointToTable(refPointsTableWidget, newPointIndex, Pin, pointName);
  930. if (m_associatedWin)
  931. {
  932. m_associatedWin->redraw();
  933. }
  934. onPointCountChanged();
  935. return true;
  936. }
  937. void ccPointPairRegistrationDlg::unstackRef()
  938. {
  939. unsigned pointCount = m_refPoints.size();
  940. if (pointCount == 0)
  941. return;
  942. assert(refPointsTableWidget->rowCount() > 0);
  943. refPointsTableWidget->removeRow(refPointsTableWidget->rowCount() - 1);
  944. //remove label
  945. assert(m_refPoints.getChildrenNumber() == pointCount);
  946. pointCount--;
  947. removeLabel(m_refPoints, pointCount, m_referenceEntities);
  948. //remove point
  949. m_refPoints.resize(pointCount);
  950. if (pointCount == 0)
  951. {
  952. //reset global shift (if any)
  953. m_refPoints.setGlobalShift(0, 0, 0);
  954. m_refPoints.setGlobalScale(1.0);
  955. }
  956. if (m_associatedWin)
  957. {
  958. m_associatedWin->redraw();
  959. }
  960. onPointCountChanged();
  961. }
  962. void ccPointPairRegistrationDlg::removeRefPoint(int index, bool autoRemoveDualPoint/*=false*/)
  963. {
  964. if (index >= static_cast<int>(m_refPoints.size()))
  965. {
  966. ccLog::Error("[ccPointPairRegistrationDlg::removeRefPoint] Invalid index!");
  967. assert(false);
  968. return;
  969. }
  970. int pointCount = static_cast<int>(m_refPoints.size());
  971. //remove the label (or sphere)
  972. removeLabel(m_refPoints, index, m_referenceEntities);
  973. //remove array row
  974. refPointsTableWidget->removeRow(index);
  975. //shift points & rename labels
  976. for (int i = index + 1; i < pointCount; ++i)
  977. {
  978. *const_cast<CCVector3*>(m_refPoints.getPoint(i - 1)) = *m_refPoints.getPoint(i);
  979. //new name
  980. QString pointName = QString("R%1").arg(i - 1);
  981. //update the label (if any)
  982. ccHObject* child = m_refPoints.getChild(i - 1);
  983. if (child)
  984. {
  985. if (child->isKindOf(CC_TYPES::LABEL_2D))
  986. {
  987. cc2DLabel* label = static_cast<cc2DLabel*>(child);
  988. label->clear();
  989. CreateLabel(label, &m_refPoints, static_cast<unsigned>(i - 1), pointName, m_associatedWin);
  990. }
  991. else //probably a sphere
  992. {
  993. child->setName(pointName);
  994. }
  995. }
  996. //update array
  997. refPointsTableWidget->setVerticalHeaderItem(i - 1, new QTableWidgetItem(pointName));
  998. }
  999. m_refPoints.invalidateBoundingBox();
  1000. pointCount--;
  1001. assert(pointCount >= 0);
  1002. m_refPoints.resize(static_cast<unsigned>(pointCount));
  1003. if (m_refPoints.size() == 0)
  1004. {
  1005. //reset global shift (if any)
  1006. m_refPoints.setGlobalShift(0, 0, 0);
  1007. m_refPoints.setGlobalScale(1.0);
  1008. }
  1009. if (m_associatedWin)
  1010. {
  1011. m_associatedWin->redraw();
  1012. }
  1013. onPointCountChanged();
  1014. //auto-remove the other point?
  1015. if ( autoRemoveDualPoint
  1016. && index < static_cast<int>(m_alignedPoints.size())
  1017. && QMessageBox::question(nullptr, "Remove dual point", "Remove the equivalent to-be-aligned point as well?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
  1018. {
  1019. removeAlignedPoint(index, false);
  1020. }
  1021. }
  1022. void ccPointPairRegistrationDlg::showAlignedEntities(bool state)
  1023. {
  1024. if (m_alignedEntities.empty())
  1025. {
  1026. return;
  1027. }
  1028. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1029. {
  1030. it.key()->setVisible(state);
  1031. }
  1032. m_alignedPoints.setEnabled(state);
  1033. if (m_associatedWin)
  1034. {
  1035. if (autoZoomCheckBox->isChecked())
  1036. {
  1037. m_associatedWin->zoomGlobal();
  1038. }
  1039. m_associatedWin->redraw();
  1040. }
  1041. }
  1042. void ccPointPairRegistrationDlg::showReferenceEntities(bool state)
  1043. {
  1044. if (m_referenceEntities.empty())
  1045. {
  1046. return;
  1047. }
  1048. for (auto it = m_referenceEntities.begin(); it != m_referenceEntities.end(); ++it)
  1049. {
  1050. it.key()->setVisible(state);
  1051. }
  1052. m_refPoints.setEnabled(state);
  1053. if (m_associatedWin)
  1054. {
  1055. if (autoZoomCheckBox->isChecked())
  1056. {
  1057. m_associatedWin->zoomGlobal();
  1058. }
  1059. m_associatedWin->redraw();
  1060. }
  1061. }
  1062. static bool UmeyamaRegistration(CCCoreLib::GenericCloud* toBeAlignedPoints,
  1063. CCCoreLib::GenericCloud* referencePoints,
  1064. CCCoreLib::PointProjectionTools::Transformation& trans,
  1065. bool fixedScale)
  1066. {
  1067. #ifdef CC_USES_EIGEN
  1068. //output transformation (R is invalid on initialization, T is (0,0,0) and s==1)
  1069. trans.R.invalidate();
  1070. trans.T = CCVector3d(0, 0, 0);
  1071. trans.s = 1.0;
  1072. if (toBeAlignedPoints == nullptr
  1073. || referencePoints == nullptr
  1074. || toBeAlignedPoints->size() != referencePoints->size()
  1075. || toBeAlignedPoints->size() < 3)
  1076. {
  1077. return false;
  1078. }
  1079. unsigned pointCount = toBeAlignedPoints->size();
  1080. // Use Eigen Umeyama with 3D vectors
  1081. Eigen::MatrixXd srcMatrix(3, pointCount), destMatrix(3, pointCount);
  1082. toBeAlignedPoints->placeIteratorAtBeginning();
  1083. referencePoints->placeIteratorAtBeginning();
  1084. for (size_t i = 0; i < pointCount; ++i)
  1085. {
  1086. const CCVector3* P = toBeAlignedPoints->getNextPoint();
  1087. srcMatrix(0, i) = P->x;
  1088. srcMatrix(1, i) = P->y;
  1089. srcMatrix(2, i) = P->z;
  1090. const CCVector3* Q = referencePoints->getNextPoint();
  1091. destMatrix(0, i) = Q->x;
  1092. destMatrix(1, i) = Q->y;
  1093. destMatrix(2, i) = Q->z;
  1094. }
  1095. Eigen::MatrixXd resultUmeyama = Eigen::umeyama(srcMatrix, destMatrix, !fixedScale);
  1096. //scale
  1097. if (!fixedScale)
  1098. {
  1099. //calculate the scale
  1100. trans.s = CCVector3d(resultUmeyama(0, 0), resultUmeyama(1, 0), resultUmeyama(2, 0)).normd();
  1101. if (trans.s == 0)
  1102. {
  1103. assert(false);
  1104. return false;
  1105. }
  1106. }
  1107. //rotation
  1108. {
  1109. trans.R = CCCoreLib::SquareMatrixd(3);
  1110. //1st column
  1111. trans.R.m_values[0][0] = resultUmeyama(0, 0) / trans.s;
  1112. trans.R.m_values[0][1] = resultUmeyama(0, 1) / trans.s;
  1113. trans.R.m_values[0][2] = resultUmeyama(0, 2) / trans.s;
  1114. //2nd column
  1115. trans.R.m_values[1][0] = resultUmeyama(1, 0) / trans.s;
  1116. trans.R.m_values[1][1] = resultUmeyama(1, 1) / trans.s;
  1117. trans.R.m_values[1][2] = resultUmeyama(1, 2) / trans.s;
  1118. //3rd column
  1119. trans.R.m_values[2][0] = resultUmeyama(2, 0) / trans.s;
  1120. trans.R.m_values[2][1] = resultUmeyama(2, 1) / trans.s;
  1121. trans.R.m_values[2][2] = resultUmeyama(2, 2) / trans.s;
  1122. }
  1123. //translation
  1124. {
  1125. trans.T.x = resultUmeyama(0, 3);
  1126. trans.T.y = resultUmeyama(1, 3);
  1127. trans.T.z = resultUmeyama(2, 3);
  1128. }
  1129. return true;
  1130. #else
  1131. // Eigen is not supported
  1132. return false;
  1133. #endif
  1134. }
  1135. bool ccPointPairRegistrationDlg::callRegistration( CCCoreLib::PointProjectionTools::Transformation& trans,
  1136. double& rms,
  1137. bool autoUpdateTab,
  1138. bool& withUmeyama,
  1139. QStringList* report/*=nullptr*/ )
  1140. {
  1141. withUmeyama = false;
  1142. if (report)
  1143. {
  1144. report->clear();
  1145. }
  1146. if (m_alignedEntities.empty())
  1147. {
  1148. assert(false);
  1149. return false;
  1150. }
  1151. if (m_alignedPoints.size() != m_refPoints.size() || m_refPoints.size() < MIN_PAIRS_COUNT)
  1152. {
  1153. assert(false);
  1154. ccLog::Error(QString("Need at least %1 points for each entity (and the same number of points in both subsets)!").arg(MIN_PAIRS_COUNT));
  1155. return false;
  1156. }
  1157. //fixed scale?
  1158. bool adjustScale = adjustScaleCheckBox->isChecked();
  1159. //! Calls Horn or Umeyama registration procedure (depending on which one is available)
  1160. bool success = false;
  1161. #ifdef CC_USES_EIGEN
  1162. // we prefer using Umeyama's method if available
  1163. withUmeyama = true;
  1164. success = UmeyamaRegistration(&m_alignedPoints, &m_refPoints, trans, !adjustScale);
  1165. #else
  1166. success = CCCoreLib::HornRegistrationTools::FindAbsoluteOrientation(&m_alignedPoints, &m_refPoints, trans, !adjustScale);
  1167. #endif
  1168. if (!success)
  1169. {
  1170. ccLog::Error("Registration failed! (points are all aligned?)");
  1171. return false;
  1172. }
  1173. //apply constraints (if any)
  1174. {
  1175. int filters = 0;
  1176. switch (rotComboBox->currentIndex())
  1177. {
  1178. case 1:
  1179. filters |= CCCoreLib::RegistrationTools::SKIP_RYZ;
  1180. break;
  1181. case 2:
  1182. filters |= CCCoreLib::RegistrationTools::SKIP_RXZ;
  1183. break;
  1184. case 3:
  1185. filters |= CCCoreLib::RegistrationTools::SKIP_RXY;
  1186. break;
  1187. case 4:
  1188. filters |= CCCoreLib::RegistrationTools::SKIP_ROTATION;
  1189. break;
  1190. default:
  1191. //nothing to do
  1192. break;
  1193. }
  1194. if (!TxCheckBox->isChecked())
  1195. filters |= CCCoreLib::RegistrationTools::SKIP_TX;
  1196. if (!TyCheckBox->isChecked())
  1197. filters |= CCCoreLib::RegistrationTools::SKIP_TY;
  1198. if (!TzCheckBox->isChecked())
  1199. filters |= CCCoreLib::RegistrationTools::SKIP_TZ;
  1200. if (filters != 0)
  1201. {
  1202. CCCoreLib::RegistrationTools::FilterTransformation( trans,
  1203. filters,
  1204. m_alignedPoints.computeGravityCenter(),
  1205. m_refPoints.computeGravityCenter(),
  1206. trans);
  1207. }
  1208. }
  1209. //compute RMS
  1210. rms = CCCoreLib::HornRegistrationTools::ComputeRMS(&m_alignedPoints, &m_refPoints, trans);
  1211. if (autoUpdateTab || report)
  1212. {
  1213. //display resulting RMS in the right column
  1214. if (rms >= 0)
  1215. {
  1216. assert(m_alignedPoints.size() == m_refPoints.size());
  1217. if (report)
  1218. {
  1219. // add header (first column is the timestamp)
  1220. report->append("; Ref.name; Ref.x; Ref.y; Ref.z; Aligned.name; Aligned.x; Aligned.y; Aligned.z; Delta X; Delta Y; Delta Z; Distance");
  1221. }
  1222. for (unsigned i = 0; i < m_alignedPoints.size(); ++i)
  1223. {
  1224. const CCVector3* Ri = m_refPoints.getPoint(i);
  1225. CCVector3d Rid = Ri->toDouble();
  1226. const CCVector3* Li = m_alignedPoints.getPoint(i);
  1227. CCVector3d Lit = trans.apply(*Li);
  1228. double dist = (Rid - Lit).norm();
  1229. if (report)
  1230. {
  1231. // create a new line in the report
  1232. QString alignedName;
  1233. auto* alignedHeader = alignedPointsTableWidget->verticalHeaderItem(static_cast<int>(i));
  1234. if (alignedHeader)
  1235. {
  1236. alignedName = alignedHeader->text();
  1237. }
  1238. else
  1239. {
  1240. assert(false);
  1241. alignedName = QString("A%1").arg(i);
  1242. }
  1243. QString refName;
  1244. auto* refHeader = refPointsTableWidget->verticalHeaderItem(static_cast<int>(i));
  1245. if (refHeader)
  1246. {
  1247. refName = refHeader->text();
  1248. }
  1249. else
  1250. {
  1251. assert(false);
  1252. refName = QString("R%1").arg(i);
  1253. }
  1254. //first column timestamp
  1255. QString reportLine = QString(";%1; %2; %3; %4").arg(refName)
  1256. .arg(Ri->x)
  1257. .arg(Ri->y)
  1258. .arg(Ri->z);
  1259. reportLine += QString("; %1; %2; %3; %4").arg(alignedName)
  1260. .arg(Lit.x)
  1261. .arg(Lit.y)
  1262. .arg(Lit.z);
  1263. //errors along axis
  1264. for (unsigned coordIndex = 0; coordIndex < 3; ++coordIndex)
  1265. {
  1266. reportLine += QString("; %1").arg(Lit[coordIndex]- Rid[coordIndex]);
  1267. }
  1268. reportLine += QString("; %1").arg(dist);
  1269. report->append(reportLine);
  1270. }
  1271. if (report)
  1272. {
  1273. QString registrationMethod = tr("Registration method: ") + (withUmeyama ? "Umeyama" : "Horn");
  1274. report->append(registrationMethod);
  1275. }
  1276. if (autoUpdateTab)
  1277. {
  1278. QTableWidgetItem* itemA = new QTableWidgetItem();
  1279. itemA->setData(Qt::EditRole, dist);
  1280. alignedPointsTableWidget->setItem(i, RMS_COL_INDEX, itemA);
  1281. QTableWidgetItem* itemR = new QTableWidgetItem();
  1282. itemR->setData(Qt::EditRole, dist);
  1283. refPointsTableWidget->setItem(i, RMS_COL_INDEX, itemR);
  1284. //update errors along Axis
  1285. for (unsigned j = 0; j < 3; ++j) {
  1286. double diffAlongAxis = Lit[j] - Rid[j];
  1287. QTableWidgetItem* itemAAlongAxis = new QTableWidgetItem();
  1288. itemAAlongAxis->setData(Qt::EditRole, diffAlongAxis);
  1289. alignedPointsTableWidget->setItem(i, RMS_COL_INDEX - 3 + j, itemAAlongAxis);
  1290. QTableWidgetItem* itemRAlongAxis = new QTableWidgetItem();
  1291. itemRAlongAxis->setData(Qt::EditRole, diffAlongAxis);
  1292. refPointsTableWidget->setItem(i, RMS_COL_INDEX - 3 + j, itemRAlongAxis);
  1293. }
  1294. }
  1295. }
  1296. }
  1297. else
  1298. {
  1299. //clear RMS columns
  1300. clearRMSColumns();
  1301. }
  1302. }
  1303. return true;
  1304. }
  1305. void ccPointPairRegistrationDlg::clearRMSColumns()
  1306. {
  1307. for (int i = 0; alignedPointsTableWidget->rowCount(); ++i)
  1308. alignedPointsTableWidget->setItem(i, RMS_COL_INDEX, new QTableWidgetItem());
  1309. for (int i = 0; refPointsTableWidget->rowCount(); ++i)
  1310. refPointsTableWidget->setItem(i, RMS_COL_INDEX, new QTableWidgetItem());
  1311. }
  1312. void ccPointPairRegistrationDlg::resetTitle()
  1313. {
  1314. if (m_associatedWin)
  1315. {
  1316. m_associatedWin->displayNewMessage(QString(), ccGLWindowInterface::UPPER_CENTER_MESSAGE, false);
  1317. m_associatedWin->displayNewMessage("[Point-pair registration]", ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 3600);
  1318. }
  1319. }
  1320. void ccPointPairRegistrationDlg::updateAlignInfo()
  1321. {
  1322. //reset title
  1323. resetTitle();
  1324. CCCoreLib::PointProjectionTools::Transformation trans;
  1325. double rms = 0.0;
  1326. bool withUmeyama = false;
  1327. if ( m_alignedPoints.size() == m_refPoints.size()
  1328. && m_refPoints.size() >= MIN_PAIRS_COUNT
  1329. && callRegistration(trans, rms, true, withUmeyama))
  1330. {
  1331. QString rmsString = tr("Achievable RMS: %1").arg(rms);
  1332. if (m_associatedWin)
  1333. {
  1334. m_associatedWin->displayNewMessage(rmsString, ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 60 * 60);
  1335. }
  1336. rmsString.prepend(withUmeyama ? "[Umeyama] " : "[Horn] ");
  1337. ccLog::Print(rmsString);
  1338. resetToolButton->setEnabled(true);
  1339. validToolButton->setEnabled(true);
  1340. }
  1341. else
  1342. {
  1343. resetToolButton->setEnabled(false);
  1344. validToolButton->setEnabled(false);
  1345. }
  1346. if (m_associatedWin)
  1347. {
  1348. m_associatedWin->redraw();
  1349. }
  1350. }
  1351. void ccPointPairRegistrationDlg::align()
  1352. {
  1353. if (!m_associatedWin)
  1354. {
  1355. assert(false);
  1356. return;
  1357. }
  1358. CCCoreLib::PointProjectionTools::Transformation trans;
  1359. double rms = std::numeric_limits<double>::quiet_NaN();
  1360. //reset title
  1361. resetTitle();
  1362. m_associatedWin->refresh(true);
  1363. bool withUmeyama = false;
  1364. if (callRegistration(trans, rms, true, withUmeyama))
  1365. {
  1366. if (rms >= 0)
  1367. {
  1368. QString rmsString = tr("Current RMS: %1").arg(rms);
  1369. ccLog::Print(QString("[PointPairRegistration][%1] ").arg(withUmeyama ? "Umeyama" : "Horn") + rmsString);
  1370. m_associatedWin->displayNewMessage(rmsString, ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 60 * 60);
  1371. }
  1372. else
  1373. {
  1374. ccLog::Warning("[PointPairRegistration] Internal error (negative RMS?!)");
  1375. return;
  1376. }
  1377. //apply (scaled) transformation (if not fixed)
  1378. bool adjustScale = adjustScaleCheckBox->isChecked();
  1379. if (adjustScale)
  1380. {
  1381. if (trans.R.isValid())
  1382. trans.R.scale(trans.s);
  1383. QString scaleString = QString("Scale: %1").arg(trans.s);
  1384. ccLog::Print(QString("[PointPairRegistration] ") + scaleString);
  1385. }
  1386. else
  1387. {
  1388. ccLog::Print(tr("[PointPairRegistration] Scale: fixed (1.0)"));
  1389. }
  1390. ccGLMatrix transMat = FromCCLibMatrix<double, float>(trans.R, trans.T);
  1391. //...virtually
  1392. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1393. {
  1394. it.key()->setGLTransformation(transMat);
  1395. }
  1396. m_alignedPoints.setGLTransformation(transMat);
  1397. //DGM: we have to 'counter-scale' the markers (otherwise they might appear very big or very small!)
  1398. for (unsigned i = 0; i < m_alignedPoints.getChildrenNumber(); ++i)
  1399. {
  1400. ccHObject* child = m_alignedPoints.getChild(i);
  1401. if (child->isA(CC_TYPES::LABEL_2D))
  1402. {
  1403. static_cast<cc2DLabel*>(child)->setRelativeMarkerScale(static_cast<float>(1.0 / trans.s));
  1404. }
  1405. }
  1406. //force clouds visibility
  1407. {
  1408. //we don't want the window zoom to change or the window to be be redrawn
  1409. ccGLWindowInterface* associatedWin = nullptr;
  1410. std::swap(m_associatedWin, associatedWin);
  1411. if (!showAlignedCheckBox->isChecked())
  1412. showAlignedCheckBox->setChecked(true);
  1413. if (!showReferenceCheckBox->isChecked())
  1414. showReferenceCheckBox->setChecked(true);
  1415. //restore window ref
  1416. std::swap(m_associatedWin, associatedWin);
  1417. }
  1418. if (m_associatedWin)
  1419. {
  1420. if (autoZoomCheckBox->isChecked())
  1421. {
  1422. m_associatedWin->zoomGlobal();
  1423. }
  1424. m_associatedWin->redraw();
  1425. }
  1426. resetToolButton->setEnabled(true);
  1427. validToolButton->setEnabled(true);
  1428. }
  1429. }
  1430. void ccPointPairRegistrationDlg::reset()
  1431. {
  1432. if (m_alignedEntities.empty())
  1433. {
  1434. return;
  1435. }
  1436. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1437. {
  1438. it.key()->enableGLTransformation(false);
  1439. }
  1440. m_alignedPoints.enableGLTransformation(false);
  1441. //DGM: we have to reset the markers scale
  1442. for (unsigned i = 0; i < m_alignedPoints.getChildrenNumber(); ++i)
  1443. {
  1444. ccHObject* child = m_alignedPoints.getChild(i);
  1445. if (child->isA(CC_TYPES::LABEL_2D))
  1446. {
  1447. static_cast<cc2DLabel*>(child)->setRelativeMarkerScale(1.0f);
  1448. }
  1449. }
  1450. if (m_associatedWin && autoZoomCheckBox->isChecked())
  1451. {
  1452. m_associatedWin->zoomGlobal();
  1453. }
  1454. updateAlignInfo();
  1455. }
  1456. void ccPointPairRegistrationDlg::apply()
  1457. {
  1458. CCCoreLib::PointProjectionTools::Transformation trans;
  1459. double rms = -1.0;
  1460. QStringList report;
  1461. bool withUmeyama = false;
  1462. if (callRegistration(trans, rms, false, withUmeyama, &report))
  1463. {
  1464. QStringList summary;
  1465. if (rms >= 0)
  1466. {
  1467. QString rmsString = QString("Final RMS: %1").arg(rms);
  1468. report << rmsString;
  1469. summary << rmsString;
  1470. summary << "----------------";
  1471. //output the report to the Console/Log
  1472. ccLog::Print("[PointPairRegistration] Final alignment report:");
  1473. for (const QString& line : report)
  1474. {
  1475. ccLog::Print(line);
  1476. }
  1477. }
  1478. //apply (scaled) transformation (if not fixed)
  1479. bool adjustScale = adjustScaleCheckBox->isChecked();
  1480. if (adjustScale && trans.R.isValid())
  1481. {
  1482. trans.R.scale(trans.s);
  1483. }
  1484. ccGLMatrix transMat = FromCCLibMatrix<double, float>(trans.R, trans.T);
  1485. //...for real this time!
  1486. assert(!m_alignedEntities.empty());
  1487. //we temporarily detach entity, as it may undergo
  1488. //"severe" modifications (octree deletion, etc.) --> see ccHObject::applyGLTransformation
  1489. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1490. {
  1491. ccMainAppInterface::ccHObjectContext objContext;
  1492. if (m_app)
  1493. objContext = m_app->removeObjectTemporarilyFromDBTree(it.key());
  1494. it.key()->applyGLTransformation_recursive();
  1495. if (m_app)
  1496. m_app->putObjectBackIntoDBTree(it.key(), objContext);
  1497. }
  1498. m_alignedPoints.setGLTransformation(transMat);
  1499. summary << QString("Transformation matrix");
  1500. summary << transMat.toString(3, '\t'); //low precision, just for display
  1501. summary << "----------------";
  1502. ccLog::Print("[PointPairRegistration] Applied transformation matrix:");
  1503. ccLog::Print(transMat.toString(12, ' ')); //full precision
  1504. if (adjustScale)
  1505. {
  1506. QString scaleString = QString("Scale: %1 (already integrated in above matrix!)").arg(trans.s);
  1507. ccLog::Warning(QString("[PointPairRegistration] ") + scaleString);
  1508. summary << scaleString;
  1509. }
  1510. else
  1511. {
  1512. ccLog::Print(QString("[PointPairRegistration] Scale: fixed (1.0)"));
  1513. summary << "Scale: fixed (1.0)";
  1514. }
  1515. summary << "----------------";
  1516. //pop-up summary
  1517. summary << "Refer to Console (F8) for more details";
  1518. QMessageBox::information(this, "Align info", summary.join("\n"));
  1519. //don't forget global shift:
  1520. //reference shift takes precedence on the aligned entities'
  1521. bool referenceIsShifted = false;
  1522. CCVector3d referenceShift(0, 0, 0);
  1523. double referenceScale = 1.0;
  1524. if (!m_referenceEntities.isEmpty())
  1525. {
  1526. referenceIsShifted = m_referenceEntities.isShifted;
  1527. referenceShift = m_referenceEntities.shift;
  1528. referenceScale = m_referenceEntities.scale;
  1529. ccGLMatrixd transMatD = FromCCLibMatrix<double, double>(trans.R, trans.T);
  1530. transMatD.scale(1.0 / referenceScale);
  1531. transMatD.setTranslation(transMatD.getTranslationAsVec3D() - referenceShift);
  1532. ccLog::Print("[PointPairRegistration] Transformation to global coordinates:");
  1533. ccLog::Print(transMatD.toString(12, ' ')); //full precision
  1534. }
  1535. else if (m_refPoints.isShifted())
  1536. {
  1537. // shift was automatically applied (temporarily, and for display purposes)
  1538. CCVector3d Pshift = m_refPoints.getGlobalShift();
  1539. double scale = m_refPoints.getGlobalScale();
  1540. CCVector3d Pin = m_refPoints.toGlobal3d(*m_refPoints.getPoint(0));
  1541. if (ccGlobalShiftManager::Handle( Pin,
  1542. 0.0,
  1543. ccGlobalShiftManager::ALWAYS_DISPLAY_DIALOG,
  1544. true,
  1545. Pshift,
  1546. nullptr,
  1547. &scale)
  1548. )
  1549. {
  1550. referenceIsShifted = true;
  1551. referenceShift = Pshift;
  1552. referenceScale = scale;
  1553. }
  1554. }
  1555. bool alwaysDropShift = false;
  1556. bool alwaysDropShiftQuestionAsked = false;
  1557. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1558. {
  1559. ccGenericPointCloud* alignedCloud = ccHObjectCaster::ToGenericPointCloud(it.key());
  1560. if (alignedCloud)
  1561. {
  1562. if (referenceIsShifted)
  1563. {
  1564. alignedCloud->setGlobalShift(referenceShift);
  1565. alignedCloud->setGlobalScale(referenceScale);
  1566. ccLog::Warning(tr("[PointPairRegistration] Cloud %1: global shift has been updated to match the reference: (%1,%2,%3) [x%4]")
  1567. .arg(alignedCloud->getName())
  1568. .arg(referenceShift.x)
  1569. .arg(referenceShift.y)
  1570. .arg(referenceShift.z)
  1571. .arg(referenceScale));
  1572. }
  1573. else if (alignedCloud->isShifted()) // the aligned cloud is shifted, but not the reference cloud
  1574. {
  1575. //we'll ask the user confirmation before dropping the shift information on the aligned cloud
  1576. if (!alwaysDropShiftQuestionAsked)
  1577. {
  1578. alwaysDropShift = (QMessageBox::question(this, tr("Drop shift information?"), tr("To-be-aligned cloud is shifted but reference cloud is not: drop global shift information?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes);
  1579. alwaysDropShiftQuestionAsked = true;
  1580. }
  1581. if (alwaysDropShift)
  1582. {
  1583. alignedCloud->setGlobalShift(0, 0, 0);
  1584. alignedCloud->setGlobalScale(1.0);
  1585. ccLog::Warning(tr("[PointPairRegistration] Cloud %1: global shift has been reset to match the reference!").arg(alignedCloud->getName()));
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. else
  1592. {
  1593. ccLog::Warning(QString("[PointPairRegistration] Failed to register entities?!"));
  1594. }
  1595. //save persistent settings
  1596. {
  1597. QSettings settings;
  1598. settings.beginGroup("PointPairAlign");
  1599. settings.setValue("PickSpheres", useSphereToolButton->isChecked());
  1600. settings.setValue("SphereRadius", radiusDoubleSpinBox->value());
  1601. settings.setValue("MaxRMS", maxRmsSpinBox->value());
  1602. settings.setValue("AdjustScale", adjustScaleCheckBox->isChecked());
  1603. settings.setValue("AutoUpdateZom",autoZoomCheckBox->isChecked());
  1604. settings.endGroup();
  1605. }
  1606. stop(true);
  1607. }
  1608. void ccPointPairRegistrationDlg::cancel()
  1609. {
  1610. for (auto it = m_alignedEntities.begin(); it != m_alignedEntities.end(); ++it)
  1611. it.key()->enableGLTransformation(false);
  1612. stop(false);
  1613. }