15 #include <QGuiApplication>
111 #include "ui_MainWindow.h"
112 #include "ui_distanceMapDlg.h"
113 #include "ui_globalShiftSettingsDlg.h"
164 #ifdef CC_3DXWARE_SUPPORT
165 #include "cc3DMouseManager.h"
169 #ifdef CC_GAMEPAD_SUPPORT
170 #include "ccGamepadManager.h"
174 #ifdef BUILD_RECONSTRUCTION
179 #ifdef USE_PCL_BACKEND
180 #include <PclUtils/PCLDisplayTools.h>
181 #include <Tools/AnnotationTools/PclAnnotationTool.h>
182 #include <Tools/CameraTools/EditCameraTool.h>
183 #include <Tools/Common/CurveFitting.h>
184 #include <Tools/FilterTools/PclFiltersTool.h>
185 #include <Tools/MeasurementTools/PclMeasurementTools.h>
186 #include <Tools/SelectionTools/cvFindDataDockWidget.h>
187 #include <Tools/SelectionTools/cvSelectionData.h>
188 #include <Tools/SelectionTools/cvSelectionHighlighter.h>
189 #include <Tools/SelectionTools/cvSelectionToolController.h>
190 #include <Tools/SelectionTools/cvViewSelectionManager.h>
191 #include <Tools/TransformTools/PclTransformTool.h>
195 #ifdef USE_PYTHON_MODULE
196 #include <recognition/PythonInterface.h>
212 #include <tbb/tbb_stddef.h>
237 QFileDialog::Options dialogOptions = QFileDialog::Options();
238 dialogOptions |= QFileDialog::DontResolveSymlinks;
240 dialogOptions |= QFileDialog::DontUseNativeDialog;
242 return dialogOptions;
249 "^(?!^(PRN|AUX|CLOCK\\$|NUL|CON|COM\\d|LPT\\d|\\..*)(\\..+)?$)[^"
250 "\\x00-\\x1f\\\\?*:\\"
254 "^(([a-zA-Z]:|\\\\)\\\\)?(((\\.)|(\\.\\.)|([^\\\\/:\\*\\?"
255 "\\|<>\\. ](([^\\\\/:\\*\\?"
256 "\\|<>\\. ])|([^\\\\/:\\*\\?"
257 "\\|<>]*[^\\\\/:\\*\\?"
258 "\\|<>\\. ]))?))\\\\)*[^\\\\/:\\*\\?"
259 "\\|<>\\. ](([^\\\\/:\\*\\?"
260 "\\|<>\\. ])|([^\\\\/:\\*\\?"
261 "\\|<>]*[^\\\\/:\\*\\?"
267 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
268 QRegularExpressionMatch match = regex.match(
filename);
269 return match.hasMatch();
277 m_ui(new
Ui::MainViewerClass),
281 m_viewModePopupButton(nullptr),
282 m_pickingHub(nullptr),
283 m_updateDlg(nullptr),
285 m_animationDlg(nullptr),
288 m_transTool(nullptr),
294 m_filterTool(nullptr),
296 m_filterLabelTool(nullptr),
297 m_measurementTool(nullptr),
299 #ifdef USE_PCL_BACKEND
300 m_selectionController(nullptr),
301 m_findDataDock(nullptr),
304 m_uiManager(nullptr),
305 m_mousePosLabel(nullptr),
306 m_systemInfoLabel(nullptr),
307 m_memoryUsageWidget(nullptr),
308 m_memoryUsageProgressBar(nullptr),
309 m_memoryUsageLabel(nullptr),
310 m_memoryUsageTimer(nullptr),
311 m_currentFullWidget(nullptr),
312 m_exclusiveFullscreen(false),
313 m_lastViewMode(VIEWMODE::ORTHOGONAL),
314 m_shortcutDlg(nullptr)
315 #ifdef BUILD_RECONSTRUCTION
322 setWindowTitle(QStringLiteral(
"ACloudViewer v") +
323 ecvApp->versionLongStr(
false));
331 ecvApp->translationPath());
334 m_ui->actionAbout->setMenuRole(QAction::AboutRole);
335 m_ui->actionAboutPlugins->setMenuRole(QAction::ApplicationSpecificRole);
337 m_ui->actionFullScreen->setText(tr(
"Enter Full Screen"));
338 m_ui->actionFullScreen->setShortcut(
339 QKeySequence(Qt::CTRL | Qt::META | Qt::Key_F));
349 bool doNotAutoRestoreGeometry =
351 !m_ui->actionRestoreWindowOnStartup->isChecked())
353 m_ui->actionRestoreWindowOnStartup->setChecked(
354 !doNotAutoRestoreGeometry);
375 #ifdef USE_PCL_BACKEND
377 m_findDataDock =
new cvFindDataDockWidget(
this);
379 addDockWidget(Qt::RightDockWidgetArea, m_findDataDock);
382 m_findDataDock->hide();
385 connect(m_findDataDock, &cvFindDataDockWidget::extractedObjectReady,
388 addToDB(obj,
false,
true,
false);
392 if (m_findDataDock) {
393 QTimer::singleShot(100,
this, [
this]() {
394 if (m_findDataDock) {
395 m_findDataDock->refreshDataProducers();
407 QAction* selectionPropsAction = m_findDataDock->toggleViewAction();
408 selectionPropsAction->setText(tr(
"Find Data (Selection)"));
409 m_ui->menuToolbars->addAction(selectionPropsAction);
413 m_selectionPropsAction = selectionPropsAction;
418 initSelectionController();
421 "[MainWindow] USE_PCL_BACKEND not defined - Find Data dock not "
427 m_viewModePopupButton =
new QToolButton();
428 QMenu* menu =
new QMenu(m_viewModePopupButton);
429 menu->addAction(m_ui->actionOrthogonalProjection);
430 menu->addAction(m_ui->actionPerspectiveProjection);
432 m_viewModePopupButton->setMenu(menu);
433 m_viewModePopupButton->setPopupMode(QToolButton::InstantPopup);
434 m_viewModePopupButton->setToolTip(
"Set current view mode");
435 m_viewModePopupButton->setStatusTip(m_viewModePopupButton->toolTip());
436 m_ui->ViewToolBar->insertWidget(m_ui->actionZoomToBox,
437 m_viewModePopupButton);
438 m_viewModePopupButton->setEnabled(
false);
442 QToolBar* customViewpointsToolbar =
444 customViewpointsToolbar->setObjectName(
"customViewpointsToolbar");
445 customViewpointsToolbar->layout()->setSpacing(0);
446 this->addToolBar(Qt::TopToolBarArea, customViewpointsToolbar);
450 m_ui->actionOrthogonalProjection->trigger();
458 bool autoPickRotationCenter =
460 if (autoPickRotationCenter) {
461 m_ui->actionAutoPickPivot->toggle();
465 bool autoShowCenterAxis =
467 m_ui->actionShowPivot->blockSignals(
true);
468 m_ui->actionShowPivot->setChecked(autoShowCenterAxis);
469 m_ui->actionShowPivot->blockSignals(
false);
470 toggleRotationCenterVisibility(autoShowCenterAxis);
473 bool showCameraOrientationWidget =
474 settings.value(
"CameraOrientationWidget/Visible",
false)
476 m_ui->actionToggleCameraOrientationWidget->blockSignals(
true);
477 m_ui->actionToggleCameraOrientationWidget->setChecked(
478 showCameraOrientationWidget);
479 m_ui->actionToggleCameraOrientationWidget->blockSignals(
false);
482 showCameraOrientationWidget);
488 populateActionList();
490 std::sort(m_actions.begin(), m_actions.end(),
491 [](
const QAction* a,
const QAction* b) {
492 return a->text() < b->text();
498 connect(m_ui->actionShortcutSettings, &QAction::triggered,
this,
499 &MainWindow::showShortcutDialog);
507 doActionToggleOrientationMarker(
false);
509 doActionToggleOrientationMarker(
true);
512 doActionToggleOrientationMarker(
true);
515 #ifdef USE_PYTHON_MODULE
526 m_ui->actionSemanticSegmentation->setEnabled(
false);
532 updateAllToolbarIconSizes();
536 .arg(QString::number(TBB_VERSION_MAJOR),
537 QString::number(TBB_VERSION_MINOR)));
542 tr(
"[ACloudViewer Software start], Welcome to use ACloudViewer"));
546 destroyInputDevices();
551 #ifdef BUILD_RECONSTRUCTION
558 assert(m_ccRoot && m_mdiArea);
560 m_ccRoot->disconnect();
561 m_mdiArea->disconnect();
567 m_updateDlg =
nullptr;
569 m_animationDlg =
nullptr;
570 m_mousePosLabel =
nullptr;
571 m_systemInfoLabel =
nullptr;
572 m_memoryUsageWidget =
nullptr;
573 m_memoryUsageProgressBar =
nullptr;
574 m_memoryUsageLabel =
nullptr;
575 m_memoryUsageTimer =
nullptr;
577 m_measurementTool =
nullptr;
579 m_transTool =
nullptr;
580 m_filterTool =
nullptr;
581 m_annoTool =
nullptr;
583 m_filterLabelTool =
nullptr;
595 while (!m_mdiDialogs.empty()) {
596 ccMDIDialogs mdiDialog = m_mdiDialogs.back();
597 m_mdiDialogs.pop_back();
599 mdiDialog.dialog->disconnect();
600 mdiDialog.dialog->stop(
false);
601 mdiDialog.dialog->setParent(
nullptr);
602 delete mdiDialog.dialog;
606 std::vector<QWidget*> windows;
608 for (QWidget* window : windows) {
609 m_mdiArea->removeSubWindow(window);
630 QMenu* menu = QMainWindow::createPopupMenu();
633 if (menu && m_selectionPropsAction) {
634 menu->addSeparator();
635 menu->addAction(m_selectionPropsAction);
642 void MainWindow::initial() {
645 m_mdiArea =
new QMdiArea(
this);
646 setCentralWidget(m_mdiArea);
647 connect(m_mdiArea, &QMdiArea::subWindowActivated,
this,
648 &MainWindow::updateMenus);
649 connect(m_mdiArea, &QMdiArea::subWindowActivated,
this,
650 &MainWindow::on3DViewActivated);
651 m_mdiArea->installEventFilter(
this);
654 bool stereoMode = QSurfaceFormat::defaultFormat().stereo();
673 #ifdef BUILD_RECONSTRUCTION
674 initReconstructions();
678 viewWidget->setMinimumSize(400, 300);
679 m_mdiArea->addSubWindow(viewWidget);
683 viewWidget->installEventFilter(
this);
688 connect(m_mdiArea, &QMdiArea::subWindowActivated, m_pickingHub,
694 connect(
this, &QObject::destroyed, m_pickingHub,
698 viewWidget->showMaximized();
699 viewWidget->update();
702 void MainWindow::updateAllToolbarIconSizes() {
705 if (!m_layoutManager)
return;
707 QScreen* screen = QGuiApplication::primaryScreen();
708 int screenWidth = screen ? screen->geometry().width() : 1920;
709 QList<QToolBar*> allToolbars = findChildren<QToolBar*>();
711 for (QToolBar* toolbar : allToolbars) {
712 if (toolbar && toolbar->parent() ==
this) {
718 void MainWindow::initConsole() {
731 levelName =
"Verbose";
734 levelName =
"Standard";
737 levelName =
"Important";
740 levelName =
"Warning";
743 levelName = QString::number(
747 CVLog::Print(QString(
"Log verbosity level: %1").arg(levelName));
751 void MainWindow::connectActions() {
755 connect(m_ui->actionOpen, &QAction::triggered,
this,
756 &MainWindow::doActionOpenFile);
757 connect(m_ui->actionSave, &QAction::triggered,
this,
758 &MainWindow::doActionSaveFile);
759 connect(m_ui->actionSaveProject, &QAction::triggered,
this,
760 &MainWindow::doActionSaveProject);
761 connect(m_ui->actionPrimitiveFactory, &QAction::triggered,
this,
762 &MainWindow::doShowPrimitiveFactory);
763 connect(m_ui->actionClearAll, &QAction::triggered,
this,
764 &MainWindow::clearAll);
765 connect(m_ui->actionExit, &QAction::triggered,
this, &QWidget::close);
768 connect(m_ui->actionSetUniqueColor, &QAction::triggered,
this,
769 &MainWindow::doActionSetUniqueColor);
770 connect(m_ui->actionSetColorGradient, &QAction::triggered,
this,
771 &MainWindow::doActionSetColorGradient);
772 connect(m_ui->actionChangeColorLevels, &QAction::triggered,
this,
773 &MainWindow::doActionChangeColorLevels);
774 connect(m_ui->actionColorize, &QAction::triggered,
this,
775 &MainWindow::doActionColorize);
776 connect(m_ui->actionRGBToGreyScale, &QAction::triggered,
this,
777 &MainWindow::doActionRGBToGreyScale);
778 connect(m_ui->actionInterpolateColors, &QAction::triggered,
this,
779 &MainWindow::doActionInterpolateColors);
780 connect(m_ui->actionEnhanceRGBWithIntensities, &QAction::triggered,
this,
781 &MainWindow::doActionEnhanceRGBWithIntensities);
782 connect(m_ui->actionColorFromScalarField, &QAction::triggered,
this,
783 &MainWindow::doActionColorFromScalars);
784 connect(m_ui->actionRGBGaussianFilter, &QAction::triggered,
this,
785 &MainWindow::doActionRGBGaussianFilter);
786 connect(m_ui->actionRGBBilateralFilter, &QAction::triggered,
this,
787 &MainWindow::doActionRGBBilateralFilter);
788 connect(m_ui->actionRGBMeanFilter, &QAction::triggered,
this,
789 &MainWindow::doActionRGBMeanFilter);
790 connect(m_ui->actionRGBMedianFilter, &QAction::triggered,
this,
791 &MainWindow::doActionRGBMedianFilter);
792 connect(m_ui->actionClearColor, &QAction::triggered,
this, [=]() {
793 clearSelectedEntitiesProperty(ccEntityAction::CLEAR_PROPERTY::COLORS);
797 connect(m_ui->actionSORFilter, &QAction::triggered,
this,
798 &MainWindow::doActionSORFilter);
799 connect(m_ui->actionNoiseFilter, &QAction::triggered,
this,
800 &MainWindow::doActionFilterNoise);
801 connect(m_ui->actionVoxelSampling, &QAction::triggered,
this,
802 &MainWindow::doActionVoxelSampling);
805 connect(m_ui->actionSegment, &QAction::triggered,
this,
806 &MainWindow::activateSegmentationMode);
807 connect(m_ui->actionRemoveDuplicatePoints, &QAction::triggered,
this,
808 &MainWindow::doRemoveDuplicatePoints);
809 connect(m_ui->actionSubsample, &QAction::triggered,
this,
810 &MainWindow::doActionSubsample);
811 connect(m_ui->actionEditGlobalShiftAndScale, &QAction::triggered,
this,
812 &MainWindow::doActionEditGlobalShiftAndScale);
813 connect(m_ui->actionClone, &QAction::triggered,
this,
814 &MainWindow::doActionClone);
815 connect(m_ui->actionMerge, &QAction::triggered,
this,
816 &MainWindow::doActionMerge);
817 connect(m_ui->actionTracePolyline, &QAction::triggered,
this,
818 &MainWindow::activateTracePolylineMode);
819 connect(m_ui->actionDelete, &QAction::triggered, m_ccRoot,
821 connect(m_ui->actionApplyTransformation, &QAction::triggered,
this,
822 &MainWindow::doActionApplyTransformation);
823 connect(m_ui->actionApplyScale, &QAction::triggered,
this,
824 &MainWindow::doActionApplyScale);
825 connect(m_ui->actionTranslateRotate, &QAction::triggered,
this,
826 &MainWindow::activateTranslateRotateMode);
829 connect(m_ui->actionComputeOctree, &QAction::triggered,
this,
830 &MainWindow::doActionComputeOctree);
831 connect(m_ui->actionResampleWithOctree, &QAction::triggered,
this,
832 &MainWindow::doActionResampleWithOctree);
835 connect(m_ui->actionCreateSinglePointCloud, &QAction::triggered,
this,
836 &MainWindow::createSinglePointCloud);
837 connect(m_ui->actionPasteCloudFromClipboard, &QAction::triggered,
this,
838 &MainWindow::createPointCloudFromClipboard);
841 const QClipboard* clipboard = QApplication::clipboard();
843 m_ui->actionPasteCloudFromClipboard->setEnabled(
844 clipboard->mimeData()->hasText());
845 connect(clipboard, &QClipboard::dataChanged, [&]() {
846 m_ui->actionPasteCloudFromClipboard->setEnabled(
847 clipboard->mimeData()->hasText());
852 connect(m_ui->actionComputeNormals, &QAction::triggered,
this,
853 &MainWindow::doActionComputeNormals);
854 connect(m_ui->actionInvertNormals, &QAction::triggered,
this,
855 &MainWindow::doActionInvertNormals);
856 connect(m_ui->actionConvertNormalToHSV, &QAction::triggered,
this,
857 &MainWindow::doActionConvertNormalsToHSV);
858 connect(m_ui->actionConvertNormalToDipDir, &QAction::triggered,
this,
859 &MainWindow::doActionConvertNormalsToDipDir);
860 connect(m_ui->actionExportNormalToSF, &QAction::triggered,
this,
861 &MainWindow::doActionExportNormalToSF);
862 connect(m_ui->actionOrientNormalsMST, &QAction::triggered,
this,
863 &MainWindow::doActionOrientNormalsMST);
864 connect(m_ui->actionOrientNormalsFM, &QAction::triggered,
this,
865 &MainWindow::doActionOrientNormalsFM);
866 connect(m_ui->actionClearNormals, &QAction::triggered,
this, [=]() {
867 clearSelectedEntitiesProperty(ccEntityAction::CLEAR_PROPERTY::NORMALS);
871 connect(m_ui->actionComputeMeshAA, &QAction::triggered,
this,
872 &MainWindow::doActionComputeMeshAA);
873 connect(m_ui->actionComputeMeshLS, &QAction::triggered,
this,
874 &MainWindow::doActionComputeMeshLS);
875 connect(m_ui->actionConvexHull, &QAction::triggered,
this,
876 &MainWindow::doActionConvexHull);
877 connect(m_ui->actionPoissonReconstruction, &QAction::triggered,
this,
878 &MainWindow::doActionPoissonReconstruction);
879 connect(m_ui->actionMeshTwoPolylines, &QAction::triggered,
this,
880 &MainWindow::doMeshTwoPolylines);
881 connect(m_ui->actionMeshScanGrids, &QAction::triggered,
this,
882 &MainWindow::doActionMeshScanGrids);
883 connect(m_ui->actionConvertTextureToColor, &QAction::triggered,
this,
884 &MainWindow::doActionConvertTextureToColor);
885 connect(m_ui->actionSamplePointsOnMesh, &QAction::triggered,
this,
886 &MainWindow::doActionSamplePointsOnMesh);
887 connect(m_ui->actionSmoothMeshLaplacian, &QAction::triggered,
this,
888 &MainWindow::doActionSmoothMeshLaplacian);
889 connect(m_ui->actionFlipMeshTriangles, &QAction::triggered,
this,
890 &MainWindow::doActionFlipMeshTriangles);
891 connect(m_ui->actionSubdivideMesh, &QAction::triggered,
this,
892 &MainWindow::doActionSubdivideMesh);
893 connect(m_ui->actionMeasureMeshSurface, &QAction::triggered,
this,
894 &MainWindow::doActionMeasureMeshSurface);
895 connect(m_ui->actionMeasureMeshVolume, &QAction::triggered,
this,
896 &MainWindow::doActionMeasureMeshVolume);
897 connect(m_ui->actionFlagMeshVertices, &QAction::triggered,
this,
898 &MainWindow::doActionFlagMeshVertices);
900 connect(m_ui->actionSmoothMeshSF, &QAction::triggered,
this,
901 &MainWindow::doActionSmoothMeshSF);
902 connect(m_ui->actionEnhanceMeshSF, &QAction::triggered,
this,
903 &MainWindow::doActionEnhanceMeshSF);
905 connect(m_ui->actionSamplePointsOnPolyline, &QAction::triggered,
this,
906 &MainWindow::doActionSamplePointsOnPolyline);
907 connect(m_ui->actionSmoothPolyline, &QAction::triggered,
this,
908 &MainWindow::doActionSmoohPolyline);
909 connect(m_ui->actionConvertPolylinesToMesh, &QAction::triggered,
this,
910 &MainWindow::doConvertPolylinesToMesh);
911 connect(m_ui->actionBSplineFittingOnCloud, &QAction::triggered,
this,
912 &MainWindow::doBSplineFittingFromCloud);
914 connect(m_ui->actionCreatePlane, &QAction::triggered,
this,
915 &MainWindow::doActionCreatePlane);
916 connect(m_ui->actionEditPlane, &QAction::triggered,
this,
917 &MainWindow::doActionEditPlane);
918 connect(m_ui->actionFlipPlane, &QAction::triggered,
this,
919 &MainWindow::doActionFlipPlane);
920 connect(m_ui->actionComparePlanes, &QAction::triggered,
this,
921 &MainWindow::doActionComparePlanes);
924 connect(m_ui->actionPromoteCircleToCylinder, &QAction::triggered,
this,
925 &MainWindow::doActionPromoteCircleToCylinder);
928 connect(m_ui->actionShowHistogram, &QAction::triggered,
this,
929 &MainWindow::showSelectedEntitiesHistogram);
930 connect(m_ui->actionComputeStatParams, &QAction::triggered,
this,
931 &MainWindow::doActionComputeStatParams);
932 connect(m_ui->actionSFGradient, &QAction::triggered,
this,
933 &MainWindow::doActionSFGradient);
934 connect(m_ui->actionOpenColorScalesManager, &QAction::triggered,
this,
935 &MainWindow::doActionOpenColorScalesManager);
936 connect(m_ui->actionGaussianFilter, &QAction::triggered,
this,
937 &MainWindow::doActionSFGaussianFilter);
938 connect(m_ui->actionBilateralFilter, &QAction::triggered,
this,
939 &MainWindow::doActionSFBilateralFilter);
940 connect(m_ui->actionFilterByLabel, &QAction::triggered,
this,
941 &MainWindow::doActionFilterByLabel);
942 connect(m_ui->actionFilterByValue, &QAction::triggered,
this,
943 &MainWindow::doActionFilterByValue);
944 connect(m_ui->actionScalarFieldFromColor, &QAction::triggered,
this,
945 &MainWindow::doActionScalarFieldFromColor);
946 connect(m_ui->actionConvertToRGB, &QAction::triggered,
this,
947 &MainWindow::doActionSFConvertToRGB);
948 connect(m_ui->actionConvertToRandomRGB, &QAction::triggered,
this,
949 &MainWindow::doActionSFConvertToRandomRGB);
950 connect(m_ui->actionRenameSF, &QAction::triggered,
this,
951 &MainWindow::doActionRenameSF);
952 connect(m_ui->actionAddConstantSF, &QAction::triggered,
this,
953 &MainWindow::doActionAddConstantSF);
954 connect(m_ui->actionImportSFFromFile, &QAction::triggered,
this,
955 &MainWindow::doActionImportSFFromFile);
956 connect(m_ui->actionAddIdField, &QAction::triggered,
this,
957 &MainWindow::doActionAddIdField);
958 connect(m_ui->actionExportCoordToSF, &QAction::triggered,
this,
959 &MainWindow::doActionExportCoordToSF);
960 connect(m_ui->actionSetSFAsCoord, &QAction::triggered,
this,
961 &MainWindow::doActionSetSFAsCoord);
962 connect(m_ui->actionInterpolateSFs, &QAction::triggered,
this,
963 &MainWindow::doActionInterpolateScalarFields);
964 connect(m_ui->actionScalarFieldArithmetic, &QAction::triggered,
this,
965 &MainWindow::doActionScalarFieldArithmetic);
966 connect(m_ui->actionDeleteScalarField, &QAction::triggered,
this, [=]() {
967 clearSelectedEntitiesProperty(
968 ccEntityAction::CLEAR_PROPERTY::CURRENT_SCALAR_FIELD);
970 connect(m_ui->actionDeleteAllSF, &QAction::triggered,
this, [=]() {
971 clearSelectedEntitiesProperty(
972 ccEntityAction::CLEAR_PROPERTY::ALL_SCALAR_FIELDS);
976 connect(m_ui->actionShowDepthBuffer, &QAction::triggered,
this,
977 &MainWindow::doActionShowDepthBuffer);
978 connect(m_ui->actionExportDepthBuffer, &QAction::triggered,
this,
979 &MainWindow::doActionExportDepthBuffer);
980 connect(m_ui->actionComputePointsVisibility, &QAction::triggered,
this,
981 &MainWindow::doActionComputePointsVisibility);
984 connect(m_ui->actionModifySensor, &QAction::triggered,
this,
985 &MainWindow::doActionModifySensor);
986 connect(m_ui->actionCreateGBLSensor, &QAction::triggered,
this,
987 &MainWindow::doActionCreateGBLSensor);
988 connect(m_ui->actionCreateCameraSensor, &QAction::triggered,
this,
989 &MainWindow::doActionCreateCameraSensor);
990 connect(m_ui->actionProjectUncertainty, &QAction::triggered,
this,
991 &MainWindow::doActionProjectUncertainty);
992 connect(m_ui->actionCheckPointsInsideFrustum, &QAction::triggered,
this,
993 &MainWindow::doActionCheckPointsInsideFrustum);
994 connect(m_ui->actionComputeDistancesFromSensor, &QAction::triggered,
this,
995 &MainWindow::doActionComputeDistancesFromSensor);
996 connect(m_ui->actionComputeScatteringAngles, &QAction::triggered,
this,
997 &MainWindow::doActionComputeScatteringAngles);
998 connect(m_ui->actionViewFromSensor, &QAction::triggered,
this,
999 &MainWindow::doActionSetViewFromSensor);
1002 connect(m_ui->actionShowWaveDialog, &QAction::triggered,
this,
1003 &MainWindow::doActionShowWaveDialog);
1004 connect(m_ui->actionCompressFWFData, &QAction::triggered,
this,
1005 &MainWindow::doActionCompressFWFData);
1008 connect(m_ui->actionClipFilter, &QAction::triggered,
this,
1009 &MainWindow::activateClippingMode);
1010 connect(m_ui->actionSliceFilter, &QAction::triggered,
this,
1011 &MainWindow::activateSliceMode);
1012 connect(m_ui->actionProbeFilter, &QAction::triggered,
this,
1013 &MainWindow::activateProbeMode);
1014 connect(m_ui->actionDecimateFilter, &QAction::triggered,
this,
1015 &MainWindow::activateDecimateMode);
1016 connect(m_ui->actionIsoSurfaceFilter, &QAction::triggered,
this,
1017 &MainWindow::activateIsoSurfaceMode);
1018 connect(m_ui->actionThresholdFilter, &QAction::triggered,
this,
1019 &MainWindow::activateThresholdMode);
1020 connect(m_ui->actionSmoothFilter, &QAction::triggered,
this,
1021 &MainWindow::activateSmoothMode);
1022 connect(m_ui->actionStreamlineFilter, &QAction::triggered,
this,
1023 &MainWindow::activateStreamlineMode);
1024 connect(m_ui->actionGlyphFilter, &QAction::triggered,
this,
1025 &MainWindow::activateGlyphMode);
1028 connect(m_ui->actionDistanceWidget, &QAction::triggered,
this,
1029 &MainWindow::activateDistanceMode);
1030 connect(m_ui->actionProtractorWidget, &QAction::triggered,
this,
1031 &MainWindow::activateProtractorMode);
1032 connect(m_ui->actionContourWidget, &QAction::triggered,
this,
1033 &MainWindow::activateContourMode);
1036 connect(m_ui->actionCloudCloudDist, &QAction::triggered,
this,
1037 &MainWindow::doActionCloudCloudDist);
1038 connect(m_ui->actionCloudMeshDist, &QAction::triggered,
this,
1039 &MainWindow::doActionCloudMeshDist);
1040 connect(m_ui->actionCloudPrimitiveDist, &QAction::triggered,
this,
1041 &MainWindow::doActionCloudPrimitiveDist);
1042 connect(m_ui->actionCPS, &QAction::triggered,
this,
1043 &MainWindow::doActionComputeCPS);
1046 connect(m_ui->actionBoxAnnotation, &QAction::triggered,
this,
1047 &MainWindow::doBoxAnnotation);
1048 connect(m_ui->actionSemanticAnnotation, &QAction::triggered,
this,
1049 &MainWindow::doSemanticAnnotation);
1052 connect(m_ui->actionSemanticSegmentation, &QAction::triggered,
this,
1053 &MainWindow::doSemanticSegmentation);
1056 connect(m_ui->actionDBScanCluster, &QAction::triggered,
this,
1057 &MainWindow::doActionDBScanCluster);
1058 connect(m_ui->actionPlaneSegmentation, &QAction::triggered,
this,
1059 &MainWindow::doActionPlaneSegmentation);
1062 connect(m_ui->actionMatchBBCenters, &QAction::triggered,
this,
1063 &MainWindow::doActionMatchBBCenters);
1064 connect(m_ui->actionMatchScales, &QAction::triggered,
this,
1065 &MainWindow::doActionMatchScales);
1066 connect(m_ui->actionRegister, &QAction::triggered,
this,
1067 &MainWindow::doActionRegister);
1068 connect(m_ui->actionPointPairsAlign, &QAction::triggered,
this,
1069 &MainWindow::activateRegisterPointPairTool);
1070 connect(m_ui->actionBBCenterToOrigin, &QAction::triggered,
this,
1071 &MainWindow::doActionMoveBBCenterToOrigin);
1072 connect(m_ui->actionBBMinCornerToOrigin, &QAction::triggered,
this,
1073 &MainWindow::doActionMoveBBMinCornerToOrigin);
1074 connect(m_ui->actionBBMaxCornerToOrigin, &QAction::triggered,
this,
1075 &MainWindow::doActionMoveBBMaxCornerToOrigin);
1078 connect(m_ui->actionFitPlane, &QAction::triggered,
this,
1079 &MainWindow::doActionFitPlane);
1080 connect(m_ui->actionFitSphere, &QAction::triggered,
this,
1081 &MainWindow::doActionFitSphere);
1082 connect(m_ui->actionFitCircle, &QAction::triggered,
this,
1083 &MainWindow::doActionFitCircle);
1084 connect(m_ui->actionFitFacet, &QAction::triggered,
this,
1085 &MainWindow::doActionFitFacet);
1086 connect(m_ui->actionFitQuadric, &QAction::triggered,
this,
1087 &MainWindow::doActionFitQuadric);
1090 connect(m_ui->actionExportCloudInfo, &QAction::triggered,
this,
1091 &MainWindow::doActionExportCloudInfo);
1092 connect(m_ui->actionExportPlaneInfo, &QAction::triggered,
this,
1093 &MainWindow::doActionExportPlaneInfo);
1095 connect(m_ui->actionLabelConnectedComponents, &QAction::triggered,
this,
1096 &MainWindow::doActionLabelConnectedComponents);
1097 connect(m_ui->actionComputeGeometricFeature, &QAction::triggered,
this,
1098 &MainWindow::doComputeGeometricFeature);
1099 connect(m_ui->actionUnroll, &QAction::triggered,
this,
1100 &MainWindow::doActionUnroll);
1101 connect(m_ui->actionPointListPicking, &QAction::triggered,
this,
1102 &MainWindow::activatePointListPickingMode);
1103 connect(m_ui->actionPointPicking, &QAction::triggered,
this,
1104 &MainWindow::activatePointPickingMode);
1107 connect(m_ui->actionComputeKdTree, &QAction::triggered,
this,
1108 &MainWindow::doActionComputeKdTree);
1109 connect(m_ui->actionDistanceMap, &QAction::triggered,
this,
1110 &MainWindow::doActionComputeDistanceMap);
1111 connect(m_ui->actionDistanceToBestFitQuadric3D, &QAction::triggered,
this,
1112 &MainWindow::doActionComputeDistToBestFitQuadric3D);
1113 connect(m_ui->actionComputeBestFitBB, &QAction::triggered,
this,
1114 &MainWindow::doComputeBestFitBB);
1115 connect(m_ui->actionAlign, &QAction::triggered,
this,
1116 &MainWindow::doAction4pcsRegister);
1117 connect(m_ui->actionSNETest, &QAction::triggered,
this,
1118 &MainWindow::doSphericalNeighbourhoodExtractionTest);
1119 connect(m_ui->actionCNETest, &QAction::triggered,
this,
1120 &MainWindow::doCylindricalNeighbourhoodExtractionTest);
1121 connect(m_ui->actionFindBiggestInnerRectangle, &QAction::triggered,
this,
1122 &MainWindow::doActionFindBiggestInnerRectangle);
1123 connect(m_ui->actionCreateCloudFromEntCenters, &QAction::triggered,
this,
1124 &MainWindow::doActionCreateCloudFromEntCenters);
1125 connect(m_ui->actionComputeBestICPRmsMatrix, &QAction::triggered,
this,
1126 &MainWindow::doActionComputeBestICPRmsMatrix);
1129 connect(m_ui->actionFullScreen, &QAction::toggled,
this,
1130 &MainWindow::toggleFullScreen);
1131 connect(m_ui->actionExclusiveFullScreen, &QAction::toggled,
this,
1133 connect(m_ui->action3DView, &QAction::toggled,
this,
1135 connect(m_ui->actionResetGUIElementsPos, &QAction::triggered,
this,
1136 &MainWindow::doActionResetGUIElementsPos);
1137 connect(m_ui->actionRestoreWindowOnStartup, &QAction::toggled,
this,
1138 &MainWindow::doActionRestoreWindowOnStartup);
1139 connect(m_ui->actionSaveCustomLayout, &QAction::triggered,
this,
1140 &MainWindow::doActionSaveCustomLayout);
1141 connect(m_ui->actionRestoreDefaultLayout, &QAction::triggered,
this,
1142 &MainWindow::doActionRestoreDefaultLayout);
1143 connect(m_ui->actionRestoreCustomLayout, &QAction::triggered,
this,
1144 &MainWindow::doActionRestoreCustomLayout);
1145 connect(m_ui->actionZoomAndCenter, &QAction::triggered,
this,
1147 connect(m_ui->actionGlobalZoom, &QAction::triggered,
this,
1155 connect(m_ui->actionLockRotationAxis, &QAction::triggered,
this,
1156 &MainWindow::toggleLockRotationAxis);
1157 connect(m_ui->actionSaveViewportAsObject, &QAction::triggered,
this,
1160 connect(m_ui->actionToggleActiveSFColorScale, &QAction::triggered,
this,
1161 &MainWindow::doActionToggleActiveSFColorScale);
1162 connect(m_ui->actionShowActiveSFPrevious, &QAction::triggered,
this,
1163 &MainWindow::doActionShowActiveSFPrevious);
1164 connect(m_ui->actionShowActiveSFNext, &QAction::triggered,
this,
1165 &MainWindow::doActionShowActiveSFNext);
1167 connect(m_ui->actionDisplayOptions, &QAction::triggered,
this,
1168 &MainWindow::showDisplayOptions);
1169 connect(m_ui->actionSetViewTop, &QAction::triggered,
this,
1170 [=]() { setView(CC_TOP_VIEW); });
1171 connect(m_ui->actionSetViewBottom, &QAction::triggered,
this,
1172 [=]() { setView(CC_BOTTOM_VIEW); });
1173 connect(m_ui->actionSetViewFront, &QAction::triggered,
this,
1174 [=]() { setView(CC_FRONT_VIEW); });
1175 connect(m_ui->actionSetViewBack, &QAction::triggered,
this,
1176 [=]() { setView(CC_BACK_VIEW); });
1177 connect(m_ui->actionSetViewLeft, &QAction::triggered,
this,
1178 [=]() { setView(CC_LEFT_VIEW); });
1179 connect(m_ui->actionSetViewRight, &QAction::triggered,
this,
1180 [=]() { setView(CC_RIGHT_VIEW); });
1181 connect(m_ui->actionSetViewIso1, &QAction::triggered,
this,
1182 [=]() { setView(CC_ISO_VIEW_1); });
1183 connect(m_ui->actionSetViewIso2, &QAction::triggered,
this,
1184 [=]() { setView(CC_ISO_VIEW_2); });
1186 connect(m_ui->actionAutoPickPivot, &QAction::toggled,
this,
1187 &MainWindow::toggleActiveWindowAutoPickRotCenter);
1188 connect(m_ui->actionShowPivot, &QAction::toggled,
this,
1189 &MainWindow::toggleRotationCenterVisibility);
1190 connect(m_ui->actionResetPivot, &QAction::triggered,
this,
1191 &MainWindow::doActionResetRotCenter);
1192 connect(m_ui->actionPerspectiveProjection, &QAction::triggered,
this,
1194 connect(m_ui->actionOrthogonalProjection, &QAction::triggered,
this,
1197 connect(m_ui->actionEditCamera, &QAction::triggered,
this,
1198 &MainWindow::doActionEditCamera);
1199 connect(m_ui->actionAnimation, &QAction::triggered,
this,
1200 &MainWindow::doActionAnimation);
1201 connect(m_ui->actionScreenShot, &QAction::triggered,
this,
1202 &MainWindow::doActionScreenShot);
1203 connect(m_ui->actionToggleOrientationMarker, &QAction::triggered,
this,
1204 &MainWindow::doActionToggleOrientationMarker);
1205 connect(m_ui->actionToggleCameraOrientationWidget, &QAction::toggled,
this,
1206 &MainWindow::doActionToggleCameraOrientationWidget);
1207 connect(m_ui->actionGlobalShiftSettings, &QAction::triggered,
this,
1208 &MainWindow::doActionGlobalShiftSeetings);
1211 connect(m_ui->helpAction, &QAction::triggered,
this, &MainWindow::help);
1212 connect(m_ui->actionAboutPlugins, &QAction::triggered, m_pluginUIManager,
1214 connect(m_ui->actionEnableQtWarnings, &QAction::toggled,
this,
1215 &MainWindow::doEnableQtWarnings);
1216 connect(m_ui->actionAbout, &QAction::triggered,
this, [
this]() {
1217 ecvAboutDialog* aboutDialog = new ecvAboutDialog(this);
1218 aboutDialog->exec();
1222 m_ui->consoleWidget->setProperty(
"contextMenuPolicy",
1223 Qt::CustomContextMenu);
1225 m_ui->consoleWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
1226 connect(m_ui->consoleWidget,
1227 &ecvCustomQListWidget::customContextMenuRequested,
this,
1228 &MainWindow::popMenuInConsole);
1234 connect(m_ui->actionEnableVisualDebugTraces, &QAction::triggered,
this,
1235 &MainWindow::toggleVisualDebugTraces);
1238 &MainWindow::handleNewLabel);
1246 connect(m_ui->actionKMeans, &QAction::triggered,
this,
1247 &MainWindow::doActionKMeans);
1248 connect(m_ui->actionFrontPropagation, &QAction::triggered,
this,
1249 &MainWindow::doActionFrontPropagation);
1252 initApplicationUpdate();
1255 m_ui->menuFile->insertMenu(m_ui->actionSave, m_recentFiles->
menu());
1258 void MainWindow::initApplicationUpdate() {
1262 connect(m_ui->actionCheckForUpdates, &QAction::triggered,
this,
1263 &MainWindow::doCheckForUpdate);
1267 void MainWindow::initThemes() {
1286 m_ui->MaterialDarkThemeAction->setData(
1288 m_ui->MaterialLightThemeAction->setData(
1299 connect(m_ui->DfaultThemeAction, &QAction::triggered,
this,
1300 &MainWindow::changeTheme);
1301 connect(m_ui->BlueThemeAction, &QAction::triggered,
this,
1302 &MainWindow::changeTheme);
1303 connect(m_ui->LightBlueThemeAction, &QAction::triggered,
this,
1304 &MainWindow::changeTheme);
1305 connect(m_ui->DarkBlueThemeAction, &QAction::triggered,
this,
1306 &MainWindow::changeTheme);
1307 connect(m_ui->BlackThemeAction, &QAction::triggered,
this,
1308 &MainWindow::changeTheme);
1309 connect(m_ui->LightBlackThemeAction, &QAction::triggered,
this,
1310 &MainWindow::changeTheme);
1311 connect(m_ui->FlatBlackThemeAction, &QAction::triggered,
this,
1312 &MainWindow::changeTheme);
1313 connect(m_ui->DarkBlackThemeAction, &QAction::triggered,
this,
1314 &MainWindow::changeTheme);
1315 connect(m_ui->GrayThemeAction, &QAction::triggered,
this,
1316 &MainWindow::changeTheme);
1317 connect(m_ui->LightGrayThemeAction, &QAction::triggered,
this,
1318 &MainWindow::changeTheme);
1319 connect(m_ui->DarkGrayThemeAction, &QAction::triggered,
this,
1320 &MainWindow::changeTheme);
1321 connect(m_ui->FlatWhiteThemeAction, &QAction::triggered,
this,
1322 &MainWindow::changeTheme);
1323 connect(m_ui->PsBlackThemeAction, &QAction::triggered,
this,
1324 &MainWindow::changeTheme);
1325 connect(m_ui->SilverThemeAction, &QAction::triggered,
this,
1326 &MainWindow::changeTheme);
1327 connect(m_ui->BFThemeAction, &QAction::triggered,
this,
1328 &MainWindow::changeTheme);
1329 connect(m_ui->TestThemeAction, &QAction::triggered,
this,
1330 &MainWindow::changeTheme);
1331 connect(m_ui->ParaviewThemeAction, &QAction::triggered,
this,
1332 &MainWindow::changeTheme);
1333 connect(m_ui->MaterialDarkThemeAction, &QAction::triggered,
this,
1334 &MainWindow::changeTheme);
1335 connect(m_ui->MaterialLightThemeAction, &QAction::triggered,
this,
1336 &MainWindow::changeTheme);
1337 connect(m_ui->NordThemeAction, &QAction::triggered,
this,
1338 &MainWindow::changeTheme);
1339 connect(m_ui->DraculaThemeAction, &QAction::triggered,
this,
1340 &MainWindow::changeTheme);
1341 connect(m_ui->FluentThemeAction, &QAction::triggered,
this,
1342 &MainWindow::changeTheme);
1343 connect(m_ui->MacOSThemeAction, &QAction::triggered,
this,
1344 &MainWindow::changeTheme);
1345 connect(m_ui->OneDarkThemeAction, &QAction::triggered,
this,
1346 &MainWindow::changeTheme);
1347 connect(m_ui->CatppuccinThemeAction, &QAction::triggered,
this,
1348 &MainWindow::changeTheme);
1349 connect(m_ui->TokyoNightThemeAction, &QAction::triggered,
this,
1350 &MainWindow::changeTheme);
1351 connect(m_ui->GruvboxThemeAction, &QAction::triggered,
this,
1352 &MainWindow::changeTheme);
1355 void MainWindow::initLanguages() {
1358 connect(m_ui->englishAction, &QAction::triggered,
this,
1359 &MainWindow::changeLanguage);
1360 connect(m_ui->chineseAction, &QAction::triggered,
this,
1361 &MainWindow::changeLanguage);
1364 void MainWindow::initStatusBar() {
1367 m_mousePosLabel =
new QLabel(
this);
1370 m_mousePosLabel->setFont(ft);
1371 m_mousePosLabel->setMinimumSize(m_mousePosLabel->sizeHint());
1372 m_mousePosLabel->setAlignment(Qt::AlignHCenter);
1373 m_ui->statusBar->insertWidget(0, m_mousePosLabel, 1);
1376 &MainWindow::onMousePosChanged);
1381 m_memoryUsageWidget =
new QWidget(
this);
1385 m_memoryUsageProgressBar =
new QProgressBar(m_memoryUsageWidget);
1386 m_memoryUsageProgressBar->setMinimumWidth(
1388 m_memoryUsageProgressBar->setMaximumWidth(
1390 m_memoryUsageProgressBar->setFixedHeight(
1392 m_memoryUsageProgressBar->setMinimum(0);
1393 m_memoryUsageProgressBar->setMaximum(100);
1394 m_memoryUsageProgressBar->setTextVisible(
false);
1395 m_memoryUsageProgressBar->setSizePolicy(QSizePolicy::Fixed,
1396 QSizePolicy::Fixed);
1397 m_memoryUsageProgressBar->move(0, 0);
1398 m_memoryUsageProgressBar->setStyleSheet(
1400 " border: 1px solid #999;"
1401 " border-radius: 2px;"
1402 " background-color: #e0e0e0;"
1404 "QProgressBar::chunk {"
1405 " background-color: #B8E6B8;"
1407 " border-radius: 1px;"
1411 m_memoryUsageLabel =
new QLabel(m_memoryUsageWidget);
1412 m_memoryUsageLabel->setMinimumWidth(240);
1413 m_memoryUsageLabel->setMaximumWidth(500);
1414 m_memoryUsageLabel->setFixedHeight(20);
1415 m_memoryUsageLabel->setAlignment(Qt::AlignCenter);
1416 m_memoryUsageLabel->setSizePolicy(QSizePolicy::Fixed,
1417 QSizePolicy::Fixed);
1418 m_memoryUsageLabel->move(0, 0);
1419 QFont labelFont = m_memoryUsageLabel->font();
1420 labelFont.setPointSize(labelFont.pointSize() - 1);
1421 m_memoryUsageLabel->setFont(labelFont);
1423 m_memoryUsageLabel->setStyleSheet(
"background: transparent;");
1426 m_memoryUsageWidget->setFixedSize(
1428 m_memoryUsageWidget->setSizePolicy(QSizePolicy::Fixed,
1429 QSizePolicy::Fixed);
1430 m_ui->statusBar->addPermanentWidget(m_memoryUsageWidget, 0);
1433 m_memoryUsageTimer =
new QTimer(
this);
1434 connect(m_memoryUsageTimer, &QTimer::timeout,
this,
1435 &MainWindow::updateMemoryUsage);
1436 m_memoryUsageTimer->start(5000);
1439 updateMemoryUsage();
1440 updateMemoryUsageWidgetSize();
1443 statusBar()->showMessage(tr(
"Ready"));
1446 void MainWindow::updateMemoryUsage() {
1447 if (!m_memoryUsageProgressBar || !m_memoryUsageLabel) {
1459 qint64 bytesTotal =
static_cast<qint64
>(memInfo.
totalRam);
1462 int percentage =
static_cast<int>((bytesUsed * 100) / bytesTotal);
1463 m_memoryUsageProgressBar->setValue(percentage);
1466 QString usedStr = formatBytes(bytesUsed);
1467 QString totalStr = formatBytes(bytesTotal);
1470 QString hostname = QHostInfo::localHostName();
1473 QString text = QString(
"%1: %2/%3 %4%")
1478 m_memoryUsageLabel->setText(text);
1482 void MainWindow::updateMemoryUsageWidgetSize() {
1483 if (!m_memoryUsageWidget || !m_memoryUsageProgressBar ||
1484 !m_memoryUsageLabel) {
1489 int windowWidth =
width();
1493 const int minWidth = 240;
1494 const int maxWidth = 500;
1497 if (windowWidth <= 1280) {
1499 widgetWidth = minWidth;
1500 }
else if (windowWidth >= 2560) {
1502 widgetWidth = maxWidth;
1505 double ratio =
static_cast<double>(windowWidth - 1280) / (2560 - 1280);
1507 static_cast<int>(minWidth + ratio * (maxWidth - minWidth));
1511 m_memoryUsageProgressBar->setFixedWidth(widgetWidth);
1512 m_memoryUsageLabel->setFixedWidth(widgetWidth);
1513 m_memoryUsageWidget->setFixedSize(widgetWidth,
1517 m_memoryUsageWidget->updateGeometry();
1518 m_memoryUsageProgressBar->update();
1519 m_memoryUsageLabel->update();
1522 QString MainWindow::formatBytes(qint64
bytes) {
1523 const qint64
KB = 1024;
1524 const qint64
MB =
KB * 1024;
1525 const qint64
GB =
MB * 1024;
1526 const qint64 TB =
GB * 1024;
1529 return QString(
"%1 TiB").arg(
bytes /
static_cast<double>(TB), 0,
'f',
1532 return QString(
"%1 GiB").arg(
bytes /
static_cast<double>(
GB), 0,
'f',
1535 return QString(
"%1 MiB").arg(
bytes /
static_cast<double>(
MB), 0,
'f',
1538 return QString(
"%1 KiB").arg(
bytes /
static_cast<double>(
KB), 0,
'f',
1541 return QString(
"%1 B").arg(
bytes);
1546 m_pluginUIManager->
init();
1549 QToolBar* glPclToolbar = m_pluginUIManager->
glPclToolbar();
1551 addToolBar(Qt::RightToolBarArea, glPclToolbar);
1552 addToolBar(Qt::RightToolBarArea, mainPluginToolbar);
1559 QList<QToolBar*> additionalToolbars =
1563 QList<QToolBar*> pythonPluginToolbars;
1564 QList<QToolBar*> otherPluginToolbars;
1566 for (QToolBar* toolbar : additionalToolbars) {
1568 pythonPluginToolbars.append(toolbar);
1570 otherPluginToolbars.append(toolbar);
1575 QString(
"[MainWindow] Found %1 additional plugin toolbars (%2 "
1576 "Python, %3 others)")
1577 .arg(additionalToolbars.size())
1578 .arg(pythonPluginToolbars.size())
1579 .arg(otherPluginToolbars.size()));
1582 for (QToolBar* pythonToolbar : pythonPluginToolbars) {
1584 QString(
"[MainWindow] Adding Python plugin toolbar '%1' "
1586 .arg(pythonToolbar->objectName()));
1587 addToolBar(Qt::TopToolBarArea, pythonToolbar);
1588 pythonToolbar->setVisible(
true);
1589 pythonToolbar->show();
1594 QToolBar* existingUnifiedToolbar =
1595 findChild<QToolBar*>(
"UnifiedPluginToolbar");
1596 if (existingUnifiedToolbar) {
1598 CVLog::Print(
"[MainWindow] Removing existing UnifiedPluginToolbar");
1599 removeToolBar(existingUnifiedToolbar);
1600 existingUnifiedToolbar->deleteLater();
1603 if (!otherPluginToolbars.isEmpty()) {
1604 QToolBar* unifiedPluginToolbar =
1605 new QToolBar(tr(
"MultipleActionsPlugins"),
this);
1606 unifiedPluginToolbar->setObjectName(
"UnifiedPluginToolbar");
1609 QSet<QAction*> addedActions;
1611 for (QToolBar* toolbar : otherPluginToolbars) {
1612 QList<QAction*> actions = toolbar->actions();
1614 QString(
"[MainWindow] Processing toolbar '%1' with %2 "
1616 .arg(toolbar->objectName())
1617 .arg(actions.size()));
1619 for (QAction* action : actions) {
1621 if (!addedActions.contains(action)) {
1622 unifiedPluginToolbar->addAction(action);
1623 addedActions.insert(action);
1629 if (toolbar != otherPluginToolbars.last()) {
1630 unifiedPluginToolbar->addSeparator();
1636 removeToolBar(toolbar);
1637 toolbar->setParent(
nullptr);
1638 toolbar->setVisible(
false);
1643 if (!unifiedPluginToolbar->actions().isEmpty()) {
1645 addToolBar(Qt::TopToolBarArea, unifiedPluginToolbar);
1646 unifiedPluginToolbar->setVisible(
true);
1647 unifiedPluginToolbar->show();
1650 QString(
"[MainWindow] Created UnifiedPluginToolbar "
1651 "with %1 actions from %2 toolbars")
1652 .arg(unifiedPluginToolbar->actions().size())
1653 .arg(otherPluginToolbars.size()));
1657 "[MainWindow] UnifiedPluginToolbar has no actions, "
1659 delete unifiedPluginToolbar;
1664 m_ui->menuBar->insertMenu(m_ui->menuDisplay->menuAction(),
1666 m_ui->menuBar->insertMenu(m_ui->menuDisplay->menuAction(),
1669 m_ui->menuToolbars->addAction(
1671 m_ui->menuToolbars->addAction(
1676 updateAllToolbarIconSizes();
1679 void MainWindow::initDBRoot() {
1683 new ccDBRoot(m_ui->dbTreeView, m_ui->propertiesTreeView,
this);
1685 &MainWindow::updateUIWithSelection);
1687 updateUIWithSelection();
1692 updateUIWithSelection();
1706 [=](std::unordered_set<int> entities) {
1711 #ifdef BUILD_RECONSTRUCTION
1712 void MainWindow::initReconstructions() {
1723 QAction* showToolbarAction =
new QAction(tr(
"Reconstruction"),
this);
1724 connect(showToolbarAction, &QAction::toggled,
this,
1725 &MainWindow::autoShowReconstructionToolBar);
1726 showToolbarAction->setCheckable(
true);
1727 showToolbarAction->setEnabled(
true);
1729 QScreen* screen = QGuiApplication::primaryScreen();
1730 int screenWidth = screen ? screen->geometry().width() : 1920;
1732 for (QToolBar* toolbar : m_rcw->getReconstructionToolbars()) {
1733 addToolBar(Qt::TopToolBarArea, toolbar);
1734 connect(showToolbarAction, &QAction::toggled, toolbar,
1735 &QToolBar::setVisible);
1737 m_ui->menuToolbars->addAction(showToolbarAction);
1738 showToolbarAction->setChecked(autoShowFlag);
1741 QMenu* rc_menu =
new QMenu(tr(
"Reconstruction"),
this);
1742 for (QMenu* menu : m_rcw->getReconstructionMenus()) {
1743 rc_menu->addMenu(menu);
1745 m_ui->menuBar->insertMenu(m_ui->menuDisplay->menuAction(), rc_menu);
1748 QDockWidget* logWidget = m_rcw->getLogWidget();
1749 this->addDockWidget(Qt::RightDockWidgetArea, logWidget);
1752 m_ui->statusBar->insertPermanentWidget(1, m_rcw->getImageStatusBar(), 0);
1753 m_ui->statusBar->insertPermanentWidget(1, m_rcw->getTimerStatusBar(), 0);
1756 void MainWindow::autoShowReconstructionToolBar(
bool state) {
1762 void MainWindow::toggleActiveWindowAutoPickRotCenter(
bool state) {
1774 void MainWindow::doActionResetRotCenter() {
1780 void MainWindow::toggleRotationCenterVisibility(
bool state) {
1797 void MainWindow::doActionToggleCameraOrientationWidget(
bool state) {
1805 settings.setValue(
"CameraOrientationWidget/Visible", state);
1808 CVLog::Print(QString(
"[MainWindow] Camera Orientation Widget: %1")
1809 .arg(state ?
"ON" :
"OFF"));
1813 void MainWindow::onMousePosChanged(
const QPoint& pos) {
1814 if (m_mousePosLabel) {
1817 QString labelText = QString(
"Location | (%1, %2)")
1818 .arg(QString::number(x))
1819 .arg(QString::number(y));
1820 m_mousePosLabel->setText(labelText);
1825 m_ui->actionAutoPickPivot->blockSignals(
true);
1826 m_ui->actionAutoPickPivot->setChecked(state);
1827 m_ui->actionAutoPickPivot->blockSignals(
false);
1828 toggleActiveWindowAutoPickRotCenter(state);
1836 if (m_viewModePopupButton)
1837 m_viewModePopupButton->setIcon(
1838 m_ui->actionOrthogonalProjection->icon());
1847 if (m_viewModePopupButton)
1848 m_viewModePopupButton->setIcon(
1849 m_ui->actionPerspectiveProjection->icon());
1854 return m_mdiArea ? m_mdiArea->subWindowList().size() : 0;
1858 QList<QMdiSubWindow*> subWindowList = m_mdiArea->subWindowList();
1859 for (
int i = 0; i < subWindowList.size(); ++i) {
1860 if (subWindowList[i]->widget() == win)
return subWindowList[i];
1868 QList<QMdiSubWindow*> windows = m_mdiArea->subWindowList();
1869 if (!windows.isEmpty()) {
1871 QAction* separator =
new QAction(
this);
1872 separator->setSeparator(
true);
1876 for (QMdiSubWindow* window : windows) {
1877 QWidget* child = window->widget();
1879 QString text = QString(
"&%1 %2").arg(++i).arg(child->windowTitle());
1889 if (!m_viewModePopupButton)
return;
1895 QAction* currentModeAction =
nullptr;
1896 if (!perspectiveEnabled) {
1897 currentModeAction = m_ui->actionOrthogonalProjection;
1899 currentModeAction = m_ui->actionPerspectiveProjection;
1902 assert(currentModeAction);
1903 m_viewModePopupButton->setIcon(currentModeAction->icon());
1904 m_viewModePopupButton->setEnabled(
true);
1906 m_viewModePopupButton->setIcon(QIcon());
1907 m_viewModePopupButton->setEnabled(
false);
1913 viewWidget->showMaximized();
1914 m_mdiArea->addSubWindow(viewWidget);
1925 const QList<QMdiSubWindow*> windows =
1928 if (windows.empty())
return;
1931 glWindows.reserve(windows.size());
1933 for (QMdiSubWindow* window : windows) {
1934 glWindows.push_back(window->widget());
1939 const QList<QMdiSubWindow*> windows =
1942 if (windows.empty())
return nullptr;
1944 for (QMdiSubWindow* window : windows) {
1945 QWidget* win = window->widget();
1946 if (win->windowTitle() == title)
return win;
1953 QList<QMdiSubWindow*> subWindowList = m_mdiArea->subWindowList();
1954 if (index >= 0 && index < subWindowList.size()) {
1955 QWidget* win = subWindowList[index]->widget();
1969 QMdiSubWindow* activeSubWindow = m_mdiArea->activeSubWindow();
1970 if (activeSubWindow) {
1971 return activeSubWindow->widget();
1973 QList<QMdiSubWindow*> subWindowList = m_mdiArea->subWindowList();
1974 if (!subWindowList.isEmpty()) {
1975 return subWindowList[0]->widget();
1983 QString fileName = qssFile;
1985 if (!fileName.isEmpty()) {
1986 QFile file(fileName);
1988 if (file.open(QFile::ReadOnly)) {
1989 QString str = file.readAll();
1990 static QString qssStyle;
1992 if (qssStyle == str) {
1997 QString paletteColor = str.mid(20, 7);
1998 ecvApp->setPalette(QPalette(QColor(paletteColor)));
1999 ecvApp->setStyleSheet(qssStyle);
2003 ecvApp->setPalette(QPalette(QColor(240, 240, 240, 255)));
2004 ecvApp->setStyleSheet(QString());
2009 this->m_uiManager = uiManager;
2012 void MainWindow::toggleVisualDebugTraces() {
2021 if (m_ui->consoleDock && m_ui->consoleDock->isHidden()) {
2022 m_ui->consoleDock->show();
2023 QApplication::processEvents();
2031 void MainWindow::doEnableQtWarnings(
bool state) {
2047 void MainWindow::on3DViewActivated(QMdiSubWindow* mdiWin) {
2052 QWidget* screen = mdiWin->widget();
2054 m_ui->actionExclusiveFullScreen->blockSignals(
true);
2055 m_ui->actionExclusiveFullScreen->setChecked(
2057 m_ui->actionExclusiveFullScreen->blockSignals(
false);
2059 m_ui->actionExclusiveFullScreen->setEnabled(screen !=
nullptr);
2082 void MainWindow::getFileFilltersAndHistory(QStringList& fileFilters,
2083 QString& currentOpenDlgFilter) {
2084 currentOpenDlgFilter =
2092 bool defaultFilterFound =
false;
2095 if (filter->importSupported()) {
2096 const QStringList fileFilterList = filter->getFileFilters(
true);
2098 for (
const QString& fileFilter : fileFilterList) {
2099 fileFilters.append(fileFilter);
2101 if (!defaultFilterFound &&
2102 (currentOpenDlgFilter == fileFilter)) {
2103 defaultFilterFound =
true;
2114 void MainWindow::doActionOpenFile() {
2116 QString currentPath =
2121 QString currentOpenDlgFilter;
2122 QStringList fileFilters;
2123 getFileFilltersAndHistory(fileFilters, currentOpenDlgFilter);
2126 QStringList selectedFiles = QFileDialog::getOpenFileNames(
2127 this, tr(
"Open file(s)"), currentPath,
2131 if (selectedFiles.isEmpty())
return;
2134 currentPath = QFileInfo(selectedFiles[0]).absolutePath();
2138 currentOpenDlgFilter);
2141 if (currentOpenDlgFilter ==
s_allFilesFilter) currentOpenDlgFilter.clear();
2144 addToDB(selectedFiles, currentOpenDlgFilter);
2148 bool displayDialog ) {
2149 addToDB(filenames, QString(), displayDialog);
2153 QString fileFilter ,
2154 bool displayDialog ) {
2157 bool loadCoordinatesTransEnabled =
false;
2176 for (
const QString&
filename : filenames) {
2194 addToDB(newGroup,
true,
true,
false);
2206 statusBar()->showMessage(tr(
"%1 file(s) loaded").arg(filenames.size()),
2212 bool autoExpandDBTree ,
2213 bool checkDimensions ,
2217 if (checkDimensions) {
2227 bool preserveCoordinateShift =
true;
2233 Pshift, &preserveCoordinateShift, &scale)) {
2234 bool needRescale = (scale != 1.0);
2235 bool needShift = (Pshift.
norm2() > 0);
2237 if (needRescale || needShift) {
2241 static_cast<float>(scale);
2245 tr(
"Entity '%1' has been translated: (%2,%3,%4) and "
2246 "rescaled of a factor %5 [original position will be "
2247 "restored when saving]")
2249 .arg(Pshift.
x, 0,
'f', 2)
2250 .arg(Pshift.
y, 0,
'f', 2)
2251 .arg(Pshift.
z, 0,
'f', 2)
2252 .arg(scale, 0,
'f', 6));
2257 if (preserveCoordinateShift) {
2260 children.push_back(obj);
2261 while (!children.empty()) {
2263 children.pop_back();
2273 children.push_back(child->
getChild(i));
2295 if (!childs.empty()) {
2303 tr(
"[MainWindow::addToDB] Internal error: no associated db?!"));
2310 }
else if (autoRedraw) {
2314 #ifdef USE_PCL_BACKEND
2319 if (m_findDataDock) {
2322 QTimer::singleShot(100,
this, [
this]() {
2323 if (m_findDataDock) {
2324 m_findDataDock->refreshDataProducers();
2326 "[MainWindow::addToDB] Data Producer combo refreshed "
2328 "adding new entity");
2336 void MainWindow::doActionEditCamera() {
2338 QMdiSubWindow* qWin = m_mdiArea->activeSubWindow();
2341 #ifdef USE_PCL_BACKEND
2344 EditCameraTool* tool =
2348 connect(m_mdiArea, &QMdiArea::subWindowActivated, m_cpeDlg,
2359 "[MainWindow] please use pcl as backend and then try again!");
2378 void MainWindow::toggleLockRotationAxis() {
2382 bool isLocked = !wasLocked;
2387 "x",
"y",
"z", -1.0e12, 1.0e12, s_lastAxis.x, s_lastAxis.y,
2388 s_lastAxis.z, 4, tr(
"Lock rotation axis"),
this);
2389 if (axisDlg.buttonBox->button(QDialogButtonBox::Ok))
2390 axisDlg.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
2391 if (!axisDlg.exec())
return;
2392 s_lastAxis.x = axisDlg.doubleSpinBox1->value();
2393 s_lastAxis.y = axisDlg.doubleSpinBox2->value();
2394 s_lastAxis.z = axisDlg.doubleSpinBox3->value();
2398 m_ui->actionLockRotationAxis->blockSignals(
true);
2399 m_ui->actionLockRotationAxis->setChecked(isLocked);
2400 m_ui->actionLockRotationAxis->blockSignals(
false);
2404 tr(
"[ROTATION LOCKED]"),
2417 void MainWindow::doActionAnimation() {
2419 QMdiSubWindow* qWin = m_mdiArea->activeSubWindow();
2422 if (!m_animationDlg) {
2425 connect(m_mdiArea, &QMdiArea::subWindowActivated, m_animationDlg,
2433 m_animationDlg->
start();
2437 void MainWindow::doActionScreenShot() {
2442 static_cast<unsigned>(win->height()),
this);
2444 if (rtfDlg.exec()) {
2445 QApplication::processEvents();
2447 rtfDlg.dontScalePoints(),
2448 rtfDlg.renderOverlayItems());
2452 void MainWindow::doActionToggleOrientationMarker(
bool state) {
2456 void MainWindow::doActionSaveFile() {
2463 ccHObject otherSerializable(tr(
"serializable"));
2465 entitiesToDispatch.insert(entitiesToDispatch.begin(),
2466 m_selectedEntities.begin(),
2467 m_selectedEntities.end());
2469 while (!entitiesToDispatch.empty()) {
2470 ccHObject* child = entitiesToDispatch.back();
2471 entitiesToDispatch.pop_back();
2475 entitiesToDispatch.push_back(child->
getChild(j));
2486 dest = &otherSerializable;
2501 bool hasCloud = (clouds.getChildrenNumber() != 0);
2502 bool hasMesh = (meshes.getChildrenNumber() != 0);
2503 bool hasPolylines = (polylines.getChildrenNumber() != 0);
2504 bool hasSerializable = (otherSerializable.getChildrenNumber() != 0);
2505 bool hasOther = (other.getChildrenNumber() != 0);
2507 int stdSaveTypes =
static_cast<int>(hasCloud) +
static_cast<int>(hasMesh) +
2508 static_cast<int>(hasPolylines) +
2509 static_cast<int>(hasSerializable);
2510 if (stdSaveTypes == 0) {
2517 QStringList fileFilters;
2520 bool atLeastOneExclusive =
false;
2523 bool canExportClouds =
true;
2525 bool isExclusive =
true;
2526 bool multiple =
false;
2530 (multiple || clouds.getChildrenNumber() == 1));
2531 atLeastOneExclusive |= isExclusive;
2535 bool canExportMeshes =
true;
2537 bool isExclusive =
true;
2538 bool multiple =
false;
2542 (multiple || meshes.getChildrenNumber() == 1));
2543 atLeastOneExclusive |= isExclusive;
2547 bool canExportPolylines =
true;
2549 bool isExclusive =
true;
2550 bool multiple =
false;
2551 canExportPolylines =
2554 (multiple || polylines.getChildrenNumber() == 1));
2555 atLeastOneExclusive |= isExclusive;
2559 bool canExportImages =
true;
2563 bool canExportSerializables =
true;
2564 if (hasSerializable) {
2568 otherSerializable.getChild(0)->getUniqueID();
2569 for (
unsigned j = 1;
2570 j < otherSerializable.getChildrenNumber(); ++j) {
2571 if (otherSerializable.getChild(j)->getUniqueID() !=
2581 for (
unsigned j = 0; j < otherSerializable.getChildrenNumber();
2584 bool isExclusive =
true;
2585 bool multiple =
false;
2586 canExportSerializables &=
2587 (filter->canSave(child->
getClassID(), multiple,
2590 otherSerializable.getChildrenNumber() == 1));
2591 atLeastOneExclusive |= isExclusive;
2595 bool useThisFilter = canExportClouds && canExportMeshes &&
2596 canExportImages && canExportPolylines &&
2597 canExportSerializables &&
2598 (!atLeastOneExclusive || stdSaveTypes == 1);
2600 if (useThisFilter) {
2601 QStringList ff = filter->getFileFilters(
false);
2602 for (
int j = 0; j < ff.size(); ++j) fileFilters.append(ff[j]);
2609 QString selectedFilter = fileFilters.first();
2622 else if (hasPolylines)
2630 QString currentPath =
2634 QString fullPathName = currentPath;
2640 QString defaultFileName(m_selectedEntities.front()->getName());
2644 if (!parts.empty()) {
2645 defaultFileName = parts[0];
2650 defaultFileName = QFileInfo(defaultFileName).baseName();
2654 tr(
"[I/O] First entity's name would make an invalid "
2655 "filename! Can't use it..."));
2656 defaultFileName = tr(
"project");
2659 fullPathName += QString(
"/") + defaultFileName;
2663 QString selectedFilename = QFileDialog::getSaveFileName(
2664 this, tr(
"Save file"), fullPathName,
2668 if (selectedFilename.isEmpty()) {
2676 tr(
"[I/O] The following selected entities won't be saved:"));
2677 for (
unsigned i = 0; i < other.getChildrenNumber(); ++i) {
2679 tr(
"\t- %1s").arg(other.getChild(i)->getName()));
2694 selectedFilename, parameters,
2703 selectedFilename, parameters,
2707 tr(
"[I/O] None of the selected entities can be saved "
2716 if (fileVersion != 0) {
2717 QString minVersion =
2720 CVLog::Print(tr(
"This file can be loaded by ACloudViewer "
2721 "version %1 and later")
2729 selectedFilename, parameters,
2752 currentPath = QFileInfo(selectedFilename).absolutePath();
2757 void MainWindow::doActionSaveProject() {
2771 QString currentPath =
2775 QString fullPathName = currentPath;
2777 static QString s_previousProjectName{
"project"};
2778 QString defaultFileName = s_previousProjectName;
2783 defaultFileName = topEntity->
getName();
2791 if (!parts.empty()) {
2792 defaultFileName = parts[0];
2797 defaultFileName = QFileInfo(defaultFileName).completeBaseName();
2801 tr(
"[I/O] Top entity's name would make an invalid "
2802 "filename! Can't use it..."));
2803 defaultFileName =
"project";
2806 fullPathName += QString(
"/") + defaultFileName;
2811 QString selectedFilename = QFileDialog::getSaveFileName(
2812 this, tr(
"Save file"), fullPathName, binFilter, &binFilter,
2815 if (selectedFilename.isEmpty()) {
2829 selectedFilename, parameters, binFilter);
2834 if (fileVersion != 0) {
2835 QString minVersion =
2837 CVLog::Print(tr(
"This file can be loaded by ACloudViewer version "
2844 QFileInfo fi(selectedFilename);
2845 s_previousProjectName = fi.fileName();
2846 currentPath = fi.absolutePath();
2848 settings.endGroup();
2851 void MainWindow::doActionApplyTransformation() {
2853 if (!dlg.exec())
return;
2856 applyTransformation(transMat);
2859 void MainWindow::applyTransformation(
const ccGLMatrixd& mat) {
2861 bool updateGlobalShiftAndScale =
false;
2862 double scaleChange = 1.0;
2870 bool firstCloud =
true;
2872 for (
ccHObject* entity : selectedEntities)
2879 bool lockedVertices;
2881 entity, &lockedVertices);
2883 if (lockedVertices) {
2912 if (needShift || needRescale) {
2920 globalTransMat.
scale(1.0 / globalScale);
2926 cloud->
getOwnBB() * globalTransMat;
2934 sasDlg.showApplyAllButton(
false);
2935 sasDlg.showTitle(
true);
2936 sasDlg.setKeepGlobalPos(
true);
2937 sasDlg.showKeepGlobalPosCheckbox(
2940 sasDlg.showPreserveShiftOnSave(
true);
2943 int index = sasDlg.addShiftInfo(
2945 tr(
"Original"), globalShift,
2951 double suggestedScale =
2953 index = sasDlg.addShiftInfo(
2955 tr(
"Suggested"), suggestedShift,
2957 sasDlg.setCurrentProfile(index);
2959 std::vector<ecvGlobalShiftManager::ShiftInfo>
2962 sasDlg.addShiftInfo(lastInfos);
2965 sasDlg.addFileInfo();
2967 if (sasDlg.exec()) {
2971 scaleChange = sasDlg.getScale() /
2973 shiftChange = (sasDlg.getShift() -
2976 updateGlobalShiftAndScale =
2977 (scaleChange != 1.0 ||
2978 shiftChange.norm2() != 0);
2981 if (updateGlobalShiftAndScale) {
2982 transMat.
scale(scaleChange);
2985 shiftChange * scaleChange);
2987 }
else if (sasDlg.cancelled()) {
2989 tr(
"[ApplyTransformation] Process "
2990 "cancelled by user"));
2999 if (updateGlobalShiftAndScale) {
3008 tr(
"[ApplyTransformation] Cloud '%1' global "
3009 "shift/scale information has been updated: "
3010 "shift = (%2,%3,%4) / scale = %5")
3036 CVLog::Print(tr(
"[ApplyTransformation] Applied transformation matrix:"));
3039 tr(
"Hint: copy it (CTRL+C) and apply it - or its inverse - on any "
3040 "entity with the 'Edit > Apply transformation' tool"));
3046 void MainWindow::doActionApplyScale() {
3048 if (!dlg.exec())
return;
3061 std::vector<EntityCloudAssociation> candidates;
3063 bool testBigCoordinates =
true;
3070 bool lockedVertices;
3073 entity, &lockedVertices);
3077 static_cast<ccPolyline*
>(entity)->getAssociatedCloud());
3079 lockedVertices =
true;
3083 tr(
"[Apply scale] Entity '%1' can't be scaled this way")
3087 if (lockedVertices) {
3099 if (testBigCoordinates) {
3104 double maxx =
static_cast<double>(
3106 double maxy =
static_cast<double>(
3108 double maxz =
static_cast<double>(
3111 const double maxCoord =
3113 bool oldCoordsWereTooBig =
3114 (maxx > maxCoord || maxy > maxCoord || maxz > maxCoord);
3116 if (!oldCoordsWereTooBig) {
3117 maxx =
static_cast<double>(
std::max(
3120 maxy =
static_cast<double>(
std::max(
3123 maxz =
static_cast<double>(
std::max(
3127 bool newCoordsAreTooBig =
3128 (maxx > maxCoord || maxy > maxCoord ||
3131 if (newCoordsAreTooBig) {
3132 if (QMessageBox::question(
3133 this, tr(
"Big coordinates"),
3134 tr(
"Resutling coordinates will be too big "
3135 "(original precision may be lost!). "
3138 QMessageBox::No) == QMessageBox::Yes) {
3140 testBigCoordinates =
false;
3152 candidates.emplace_back(parent, cloud);
3154 candidates.emplace_back(entity, cloud);
3159 if (candidates.empty()) {
3161 tr(
"[Apply scale] No eligible entities (point clouds or "
3162 "meshes) were selected!"));
3168 for (
auto& candidate : candidates) {
3180 ccHObjectContext objContext =
3191 if (rescaleGlobalShift) {
3195 shift.
z * scales.
z));
3207 for (
auto& candidate : candidates) {
3221 void MainWindow::activateTranslateRotateMode() {
3226 #ifdef USE_PCL_BACKEND
3227 PclTransformTool* pclTransTool =
3231 m_transTool->
clear();
3235 "[MainWindow] please use pcl as backend and then try again!");
3242 "[MainWindow::activateTranslateRotateMode] Initialization "
3247 bool rejectedEntities =
false;
3252 rejectedEntities =
true;
3258 "No entity eligible for manual transformation! (see console)"));
3260 }
else if (rejectedEntities) {
3265 if (m_transTool->
start()) {
3267 &MainWindow::deactivateTranslateRotateMode);
3276 void MainWindow::deactivateTranslateRotateMode(
bool state) {
3279 if (state && m_ccRoot) {
3286 transformedEntities[i] = transformedSet.
getChild(i);
3289 }
catch (
const std::bad_alloc&) {
3301 void MainWindow::clearAll() {
3302 if (!m_ccRoot)
return;
3304 if (QMessageBox::question(
3305 this, tr(
"Close all"),
3306 tr(
"Are you sure you want to remove all loaded entities?"),
3307 QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
3314 for (QMdiSubWindow* window : m_mdiArea->subWindowList()) {
3315 window->setEnabled(
true);
3320 for (QMdiSubWindow* window : m_mdiArea->subWindowList()) {
3321 window->setEnabled(
false);
3325 void MainWindow::updateUIWithSelection() {
3328 m_selectedEntities.clear();
3335 enableUIItems(selInfo);
3339 bool dbIsEmpty = (!m_ccRoot || !m_ccRoot->
getRootEntity() ||
3341 bool atLeastOneEntity = (selInfo.
selCount > 0);
3342 bool atLeastOneCloud = (selInfo.
cloudCount > 0);
3343 bool atLeastOneMesh = (selInfo.
meshCount > 0);
3344 bool atLeastOneOctree = (selInfo.
octreeCount > 0);
3346 bool atLeastOneColor = (selInfo.
colorCount > 0);
3347 bool atLeastOneSF = (selInfo.
sfCount > 0);
3348 bool atLeastOneGrid = (selInfo.
gridCound > 0);
3350 bool atLeastOneSensor = (selInfo.
sensorCount > 0);
3355 m_ui->actionTracePolyline->setEnabled(!dbIsEmpty);
3356 m_ui->actionZoomAndCenter->setEnabled(atLeastOneEntity);
3357 m_ui->actionSave->setEnabled(atLeastOneEntity);
3358 m_ui->actionSaveProject->setEnabled(!dbIsEmpty);
3359 m_ui->actionClone->setEnabled(atLeastOneEntity);
3360 m_ui->actionDelete->setEnabled(atLeastOneEntity);
3361 m_ui->actionImportSFFromFile->setEnabled(atLeastOneEntity);
3362 m_ui->actionExportCoordToSF->setEnabled(atLeastOneEntity);
3363 m_ui->actionSegment->setEnabled(atLeastOneEntity);
3364 m_ui->actionContourWidget->setEnabled(atLeastOneEntity);
3365 m_ui->actionDistanceWidget->setEnabled(atLeastOneEntity);
3366 m_ui->actionProtractorWidget->setEnabled(atLeastOneEntity);
3367 m_ui->actionTranslateRotate->setEnabled(atLeastOneEntity);
3368 m_ui->actionShowDepthBuffer->setEnabled(atLeastOneGBLSensor);
3369 m_ui->actionExportDepthBuffer->setEnabled(atLeastOneGBLSensor);
3370 m_ui->actionComputePointsVisibility->setEnabled(atLeastOneGBLSensor);
3371 m_ui->actionResampleWithOctree->setEnabled(atLeastOneCloud);
3372 m_ui->actionApplyScale->setEnabled(atLeastOneCloud || atLeastOneMesh ||
3373 atLeastOnePolyline);
3374 m_ui->actionApplyTransformation->setEnabled(atLeastOneEntity);
3375 m_ui->actionComputeOctree->setEnabled(atLeastOneCloud || atLeastOneMesh);
3376 m_ui->actionComputeNormals->setEnabled(atLeastOneCloud || atLeastOneMesh);
3377 m_ui->actionChangeColorLevels->setEnabled(atLeastOneCloud ||
3379 m_ui->actionEditGlobalShiftAndScale->setEnabled(
3380 atLeastOneCloud || atLeastOneMesh || atLeastOnePolyline);
3381 m_ui->actionSetUniqueColor->setEnabled(
3388 m_ui->actionColorize->setEnabled(
3397 m_ui->actionScalarFieldFromColor->setEnabled(atLeastOneEntity &&
3399 m_ui->actionComputeMeshAA->setEnabled(atLeastOneCloud);
3400 m_ui->actionComputeMeshLS->setEnabled(atLeastOneCloud);
3401 m_ui->actionConvexHull->setEnabled(atLeastOneCloud);
3402 m_ui->actionPoissonReconstruction->setEnabled(atLeastOneCloud);
3403 m_ui->actionMeshScanGrids->setEnabled(atLeastOneGrid);
3405 m_ui->actionComputeBestFitBB->setEnabled(atLeastOneEntity);
3406 m_ui->actionComputeGeometricFeature->setEnabled(atLeastOneCloud);
3407 m_ui->actionRemoveDuplicatePoints->setEnabled(atLeastOneCloud);
3408 m_ui->actionFitPlane->setEnabled(atLeastOneEntity);
3409 m_ui->actionFitPlaneProxy->setEnabled(atLeastOneEntity);
3410 m_ui->actionFitSphere->setEnabled(atLeastOneCloud);
3411 m_ui->actionFitCircle->setEnabled(atLeastOneCloud);
3413 m_ui->actionFitFacet->setEnabled(atLeastOneEntity);
3414 m_ui->actionFitQuadric->setEnabled(atLeastOneCloud);
3415 m_ui->actionSubsample->setEnabled(atLeastOneCloud);
3417 m_ui->actionSNETest->setEnabled(atLeastOneCloud);
3418 m_ui->actionExportCloudInfo->setEnabled(atLeastOneEntity);
3419 m_ui->actionExportPlaneInfo->setEnabled(atLeastOneEntity);
3421 m_ui->actionFilterByLabel->setEnabled(atLeastOneSF);
3422 m_ui->actionFilterByValue->setEnabled(atLeastOneSF);
3423 m_ui->actionConvertToRGB->setEnabled(atLeastOneSF);
3424 m_ui->actionConvertToRandomRGB->setEnabled(atLeastOneSF);
3425 m_ui->actionRenameSF->setEnabled(atLeastOneSF);
3426 m_ui->actionAddIdField->setEnabled(atLeastOneCloud);
3427 m_ui->actionComputeStatParams->setEnabled(atLeastOneSF);
3429 m_ui->actionShowHistogram->setEnabled(atLeastOneSF);
3430 m_ui->actionGaussianFilter->setEnabled(atLeastOneSF);
3431 m_ui->actionBilateralFilter->setEnabled(atLeastOneSF);
3432 m_ui->actionDeleteScalarField->setEnabled(atLeastOneSF);
3433 m_ui->actionDeleteAllSF->setEnabled(atLeastOneSF);
3435 m_ui->actionSFGradient->setEnabled(atLeastOneSF);
3436 m_ui->actionSetSFAsCoord->setEnabled(atLeastOneSF && atLeastOneCloud);
3437 m_ui->actionInterpolateSFs->setEnabled(atLeastOneCloud || atLeastOneMesh);
3439 m_ui->actionSamplePointsOnMesh->setEnabled(atLeastOneMesh);
3440 m_ui->actionMeasureMeshSurface->setEnabled(atLeastOneMesh);
3441 m_ui->actionMeasureMeshVolume->setEnabled(atLeastOneMesh);
3442 m_ui->actionFlagMeshVertices->setEnabled(atLeastOneMesh);
3443 m_ui->actionSmoothMeshLaplacian->setEnabled(atLeastOneMesh);
3444 m_ui->actionConvertTextureToColor->setEnabled(atLeastOneMesh);
3445 m_ui->actionSubdivideMesh->setEnabled(atLeastOneMesh);
3446 m_ui->actionDistanceToBestFitQuadric3D->setEnabled(atLeastOneCloud);
3447 m_ui->actionDistanceMap->setEnabled(atLeastOneMesh || atLeastOneCloud);
3449 m_ui->menuMeshScalarField->setEnabled(atLeastOneSF && atLeastOneMesh);
3453 m_ui->actionOrientNormalsMST->setEnabled(atLeastOneCloud &&
3455 m_ui->actionOrientNormalsFM->setEnabled(atLeastOneCloud &&
3457 m_ui->actionClearNormals->setEnabled(atLeastOneNormal);
3458 m_ui->actionInvertNormals->setEnabled(atLeastOneNormal);
3459 m_ui->actionConvertNormalToHSV->setEnabled(atLeastOneNormal);
3460 m_ui->actionConvertNormalToDipDir->setEnabled(atLeastOneNormal);
3461 m_ui->actionExportNormalToSF->setEnabled(atLeastOneNormal);
3462 m_ui->actionClearColor->setEnabled(atLeastOneColor);
3463 m_ui->actionRGBToGreyScale->setEnabled(atLeastOneColor);
3464 m_ui->actionEnhanceRGBWithIntensities->setEnabled(atLeastOneColor);
3465 m_ui->actionRGBGaussianFilter->setEnabled(atLeastOneColor);
3466 m_ui->actionRGBBilateralFilter->setEnabled(atLeastOneColor);
3467 m_ui->actionRGBMeanFilter->setEnabled(atLeastOneColor);
3468 m_ui->actionRGBMedianFilter->setEnabled(atLeastOneColor);
3469 m_ui->actionColorFromScalarField->setEnabled(atLeastOneSF);
3472 bool exactlyOneEntity = (selInfo.
selCount == 1);
3473 bool exactlyOneGroup = (selInfo.
groupCount == 1);
3474 bool exactlyOneCloud = (selInfo.
cloudCount == 1);
3475 bool exactlyOneMesh = (selInfo.
meshCount == 1);
3476 bool exactlyOneSF = (selInfo.
sfCount == 1);
3477 bool exactlyOneSensor = (selInfo.
sensorCount == 1);
3480 m_ui->actionSliceFilter->setEnabled(exactlyOneMesh);
3481 m_ui->actionDecimateFilter->setEnabled(exactlyOneMesh);
3482 m_ui->actionIsoSurfaceFilter->setEnabled(exactlyOneMesh);
3483 m_ui->actionSmoothFilter->setEnabled(exactlyOneMesh);
3485 m_ui->actionConvertPolylinesToMesh->setEnabled(atLeastOnePolyline ||
3487 m_ui->actionSamplePointsOnPolyline->setEnabled(atLeastOnePolyline);
3488 m_ui->actionSmoothPolyline->setEnabled(atLeastOnePolyline);
3489 m_ui->actionMeshTwoPolylines->setEnabled(selInfo.
selCount == 2 &&
3491 m_ui->actionModifySensor->setEnabled(exactlyOneSensor);
3492 m_ui->actionComputeDistancesFromSensor->setEnabled(atLeastOneCameraSensor ||
3493 atLeastOneGBLSensor);
3494 m_ui->actionComputeScatteringAngles->setEnabled(exactlyOneSensor);
3495 m_ui->actionViewFromSensor->setEnabled(exactlyOneSensor);
3496 m_ui->actionCreateGBLSensor->setEnabled(atLeastOneCloud);
3497 m_ui->actionCreateCameraSensor->setEnabled(selInfo.
selCount <=
3499 m_ui->actionProjectUncertainty->setEnabled(exactlyOneCameraSensor);
3500 m_ui->actionCheckPointsInsideFrustum->setEnabled(exactlyOneCameraSensor);
3501 m_ui->actionLabelConnectedComponents->setEnabled(atLeastOneCloud);
3502 m_ui->actionSORFilter->setEnabled(atLeastOneCloud);
3503 m_ui->actionNoiseFilter->setEnabled(atLeastOneCloud);
3504 m_ui->actionVoxelSampling->setEnabled(atLeastOneCloud);
3505 m_ui->actionUnroll->setEnabled(exactlyOneEntity);
3508 m_ui->actionAddConstantSF->setEnabled(exactlyOneCloud || exactlyOneMesh);
3511 m_ui->actionComputeKdTree->setEnabled(exactlyOneCloud || exactlyOneMesh);
3512 m_ui->actionShowWaveDialog->setEnabled(exactlyOneCloud);
3513 m_ui->actionCompressFWFData->setEnabled(atLeastOneCloud);
3515 m_ui->actionKMeans->setEnabled(
3517 m_ui->actionFrontPropagation->setEnabled(
3521 m_ui->actionEditPlane->setEnabled(selInfo.
planeCount == 1);
3522 m_ui->actionFlipPlane->setEnabled(selInfo.
planeCount != 0);
3523 m_ui->actionComparePlanes->setEnabled(selInfo.
planeCount == 2);
3525 m_ui->actionPromoteCircleToCylinder->setEnabled((selInfo.
selCount == 1) &&
3528 m_ui->actionFindBiggestInnerRectangle->setEnabled(exactlyOneCloud);
3532 m_ui->actionClipFilter->setEnabled(atLeastOneCloud || atLeastOneMesh ||
3534 m_ui->actionProbeFilter->setEnabled(atLeastOneCloud || atLeastOneMesh ||
3536 m_ui->actionGlyphFilter->setEnabled(atLeastOneCloud || atLeastOneMesh ||
3538 m_ui->actionStreamlineFilter->setEnabled(
3539 atLeastOneCloud || atLeastOneMesh || (selInfo.
groupCount != 0));
3540 m_ui->actionThresholdFilter->setEnabled(atLeastOneCloud || atLeastOneMesh ||
3543 #ifdef USE_PYTHON_MODULE
3544 m_ui->actionSemanticSegmentation->setEnabled(atLeastOneCloud);
3547 m_ui->actionDBScanCluster->setEnabled(atLeastOneCloud);
3548 m_ui->actionPlaneSegmentation->setEnabled(atLeastOneCloud);
3551 m_ui->actionBoxAnnotation->setEnabled(exactlyOneCloud);
3552 m_ui->actionSemanticAnnotation->setEnabled(exactlyOneCloud);
3554 m_ui->actionCompute2HalfDimVolume->setEnabled(
3557 m_ui->actionPointListPicking->setEnabled(exactlyOneEntity);
3560 bool exactlyTwoEntities = (selInfo.
selCount == 2);
3561 bool exactlyTwoClouds = (selInfo.
cloudCount == 2);
3564 m_ui->actionRegister->setEnabled(exactlyTwoEntities);
3565 m_ui->actionInterpolateColors->setEnabled(exactlyTwoEntities &&
3567 m_ui->actionPointPairsAlign->setEnabled(atLeastOneEntity);
3568 m_ui->actionBBCenterToOrigin->setEnabled(atLeastOneEntity);
3569 m_ui->actionBBMinCornerToOrigin->setEnabled(atLeastOneEntity);
3570 m_ui->actionBBMaxCornerToOrigin->setEnabled(atLeastOneEntity);
3571 m_ui->actionAlign->setEnabled(
3572 exactlyTwoEntities);
3573 m_ui->actionCloudCloudDist->setEnabled(exactlyTwoClouds);
3574 m_ui->actionCloudMeshDist->setEnabled(exactlyTwoEntities && atLeastOneMesh);
3575 m_ui->actionCloudPrimitiveDist->setEnabled(
3576 atLeastOneCloud && (atLeastOneMesh || atLeastOnePolyline));
3577 m_ui->actionCPS->setEnabled(exactlyTwoClouds);
3578 m_ui->actionScalarFieldArithmetic->setEnabled(exactlyOneEntity &&
3582 bool atLeastTwoEntities = (selInfo.
selCount > 1);
3584 m_ui->actionMerge->setEnabled(atLeastTwoEntities);
3585 m_ui->actionMatchBBCenters->setEnabled(atLeastTwoEntities);
3586 m_ui->actionMatchScales->setEnabled(atLeastTwoEntities);
3592 void MainWindow::moveEvent(QMoveEvent*
event) {
3593 QMainWindow::moveEvent(
event);
3598 void MainWindow::resizeEvent(QResizeEvent*
event) {
3599 QMainWindow::resizeEvent(
event);
3602 updateMemoryUsageWidgetSize();
3605 bool MainWindow::eventFilter(QObject* obj, QEvent*
event) {
3606 switch (
event->type()) {
3611 case QEvent::KeyPress: {
3614 QKeyEvent* keyEvent =
static_cast<QKeyEvent*
>(
event);
3615 if (keyEvent->key() == Qt::Key_Escape) {
3617 "[MainWindow::eventFilter] ESC key detected, calling "
3631 return QObject::eventFilter(obj,
event);
3634 void MainWindow::handleEscapeKey() {
3636 if (m_measurementTool && m_measurementTool->
started()) {
3637 m_measurementTool->
stop(
false);
3640 if (m_ui->actionDistanceWidget &&
3641 m_ui->actionDistanceWidget->isCheckable()) {
3642 m_ui->actionDistanceWidget->setChecked(
false);
3644 if (m_ui->actionProtractorWidget &&
3645 m_ui->actionProtractorWidget->isCheckable()) {
3646 m_ui->actionProtractorWidget->setChecked(
false);
3648 if (m_ui->actionContourWidget &&
3649 m_ui->actionContourWidget->isCheckable()) {
3650 m_ui->actionContourWidget->setChecked(
false);
3660 #ifdef USE_PCL_BACKEND
3661 if (m_selectionController) {
3662 m_selectionController->handleEscapeKey();
3671 if (m_exclusiveFullscreen) {
3675 else if (this->isFullScreen()) {
3680 void MainWindow::keyPressEvent(QKeyEvent*
event) {
3681 switch (
event->key()) {
3682 case Qt::Key_Escape:
3684 "[MainWindow::keyPressEvent] ESC key received, calling "
3689 QMainWindow::keyPressEvent(
event);
3694 ccHObject tempGroup(QString(
"TempGroup"));
3695 size_t selNum = m_selectedEntities.size();
3696 for (
size_t i = 0; i < selNum; ++i) {
3697 ccHObject* entity = m_selectedEntities[i];
3708 void MainWindow::addEditPlaneAction(QMenu& menu)
const {
3709 menu.addAction(m_ui->actionEditPlane);
3712 void MainWindow::zoomOn(
ccHObject*
object) {
3723 void MainWindow::updateMenus() {
3725 bool hasMdiChild = (active3DView !=
nullptr);
3727 bool hasLoadedEntities =
3730 bool hasSelectedEntities =
3734 m_ui->menuEdit->setEnabled(
true );
3735 m_ui->menuTools->setEnabled(
true );
3738 m_ui->ViewToolBar->setEnabled(hasMdiChild);
3741 m_ui->actionSegment->setEnabled(hasMdiChild && hasSelectedEntities);
3742 m_ui->actionContourWidget->setEnabled(hasMdiChild && hasSelectedEntities);
3743 m_ui->actionDistanceWidget->setEnabled(hasMdiChild && hasSelectedEntities);
3744 m_ui->actionProtractorWidget->setEnabled(hasMdiChild &&
3745 hasSelectedEntities);
3746 m_ui->actionTranslateRotate->setEnabled(hasMdiChild && hasSelectedEntities);
3747 m_ui->actionPointPicking->setEnabled(hasMdiChild && hasLoadedEntities);
3748 m_ui->actionPointListPicking->setEnabled(hasLoadedEntities);
3761 if (!obj || !m_ccRoot)
return;
3781 if (!m_ccRoot || !obj)
return context;
3789 context.parentFlags =
context.parent->getDependencyFlagsWith(obj);
3792 context.parent->removeDependencyWith(obj);
3806 void MainWindow::doActionResetGUIElementsPos() {
3809 if (this->m_uiManager) {
3810 this->m_uiManager->showMaximized();
3817 QMessageBox::information(
this, tr(
"Restart"),
3818 tr(
"To finish the process, you'll have to close "
3819 "and restart ACloudViewer"));
3825 void MainWindow::doActionSaveCustomLayout() {
3826 if (m_layoutManager) {
3828 QMessageBox::information(
3829 this, tr(
"Save Custom Layout"),
3830 tr(
"Current layout has been saved as custom layout. You can "
3831 "restore it later using the 'Restore Custom Layout' "
3834 CVLog::Error(
"[MainWindow] Layout manager is not initialized!");
3838 void MainWindow::doActionRestoreDefaultLayout() {
3839 if (m_layoutManager) {
3842 CVLog::Error(
"[MainWindow] Layout manager is not initialized!");
3846 void MainWindow::doActionRestoreCustomLayout() {
3847 if (m_layoutManager) {
3849 QMessageBox::warning(
this, tr(
"Restore Custom Layout"),
3850 tr(
"No saved custom layout found. Please save "
3851 "current layout first."));
3854 CVLog::Error(
"[MainWindow] Layout manager is not initialized!");
3858 void MainWindow::doActionRestoreWindowOnStartup(
bool state) {
3863 void MainWindow::toggleFullScreen(
bool state) {
3864 if (m_uiManager !=
nullptr) {
3867 state ? showFullScreen() : showNormal();
3872 m_ui->actionFullScreen->setText(tr(
"Exit Full Screen"));
3874 m_ui->actionFullScreen->setText(tr(
"Enter Full Screen"));
3878 m_ui->actionFullScreen->setChecked(state);
3884 if (!m_exclusiveFullscreen) {
3886 if (m_currentFullWidget) {
3887 m_formerGeometry = m_currentFullWidget->saveGeometry();
3888 m_currentFullWidget->setWindowFlags(Qt::Dialog);
3890 m_currentFullWidget->installEventFilter(
this);
3893 m_exclusiveFullscreen =
true;
3894 if (m_currentFullWidget) {
3895 m_currentFullWidget->showFullScreen();
3897 m_currentFullWidget->setFocus();
3902 onExclusiveFullScreenToggled(state);
3904 "Press F11 or ESC to disable full-screen mode",
3910 if (m_exclusiveFullscreen) {
3911 if (m_currentFullWidget) {
3912 m_currentFullWidget->setWindowFlags(Qt::SubWindow);
3915 m_exclusiveFullscreen =
false;
3916 onExclusiveFullScreenToggled(state);
3922 if (m_currentFullWidget) {
3923 m_currentFullWidget->showNormal();
3924 if (!m_formerGeometry.isNull()) {
3925 m_currentFullWidget->restoreGeometry(m_formerGeometry);
3926 m_formerGeometry.clear();
3934 QCoreApplication::processEvents();
3935 if (m_currentFullWidget) {
3936 m_currentFullWidget->setFocus();
3949 void MainWindow::onExclusiveFullScreenToggled(
bool state) {
3953 m_ui->actionExclusiveFullScreen->blockSignals(
true);
3954 m_ui->actionExclusiveFullScreen->setChecked(state);
3955 m_ui->actionExclusiveFullScreen->blockSignals(
false);
3960 m_ui->actionFullScreen->setChecked(state);
3963 void MainWindow::handleNewLabel(
ccHObject* entity) {
3971 void MainWindow::activatePointListPickingMode() {
3992 &MainWindow::deactivatePointListPickingMode);
3999 m_plpDlg->markerSizeSpinBox->setValue(
4007 if (!m_plpDlg->
start())
4008 deactivatePointListPickingMode(
false);
4013 void MainWindow::deactivatePointListPickingMode(
bool state) {
4024 void MainWindow::activatePointPickingMode() {
4033 &MainWindow::deactivatePointPickingMode);
4035 &MainWindow::handleNewLabel);
4044 if (!m_ppDlg->
start())
4045 deactivatePointPickingMode(
false);
4050 void MainWindow::deactivatePointPickingMode(
bool state) {
4056 void MainWindow::activateTracePolylineMode() {
4060 &MainWindow::deactivateTracePolylineMode);
4067 m_ui->ViewToolBar->setDisabled(
false);
4069 if (!m_tplTool->
start())
4070 deactivateTracePolylineMode(
false);
4075 void MainWindow::deactivateTracePolylineMode(
bool) {
4082 for (ccMDIDialogs& mdi : m_mdiDialogs) {
4083 if (mdi.dialog == dlg) {
4086 repositionOverlayDialog(mdi);
4092 m_mdiDialogs.push_back(ccMDIDialogs(dlg, pos));
4097 for (ccMDIDialogs& mdi : m_mdiDialogs) {
4098 if (mdi.dialog == dlg) {
4099 repositionOverlayDialog(mdi);
4105 repositionOverlayDialog(m_mdiDialogs.back());
4109 for (std::vector<ccMDIDialogs>::iterator it = m_mdiDialogs.begin();
4110 it != m_mdiDialogs.end(); ++it) {
4111 if (it->dialog == dialog) {
4112 m_mdiDialogs.erase(it);
4119 for (ccMDIDialogs& mdiDlg : m_mdiDialogs) {
4120 repositionOverlayDialog(mdiDlg);
4140 void MainWindow::repositionOverlayDialog(ccMDIDialogs& mdiDlg) {
4141 if (!mdiDlg.dialog || !mdiDlg.dialog->isVisible() || !m_mdiArea)
return;
4145 static const int margin = 5;
4147 switch (mdiDlg.position) {
4148 case Qt::TopLeftCorner:
4152 case Qt::TopRightCorner:
4154 m_mdiArea->width() - mdiDlg.dialog->width() - margin);
4157 case Qt::BottomLeftCorner:
4159 dy =
std::max(margin, m_mdiArea->height() -
4160 mdiDlg.dialog->height() - margin);
4162 case Qt::BottomRightCorner:
4164 m_mdiArea->width() - mdiDlg.dialog->width() - margin);
4165 dy =
std::max(margin, m_mdiArea->height() -
4166 mdiDlg.dialog->height() - margin);
4171 mdiDlg.dialog->move(m_mdiArea->mapToGlobal(QPoint(dx, dy)));
4172 mdiDlg.dialog->raise();
4180 std::size_t
count = toBeRemovedList.size();
4182 if (toBeRemovedList[j]->isAncestorOf(toRemove)) {
4185 }
else if (toRemove->
isAncestorOf(toBeRemovedList[j])) {
4186 toBeRemovedList[j] = toBeRemovedList.back();
4187 toBeRemovedList.pop_back();
4196 if (toRemove) toBeRemovedList.push_back(toRemove);
4199 void MainWindow::doActionMerge() {
4201 std::vector<ccPointCloud*> clouds;
4202 std::vector<ccMesh*> meshes;
4206 if (!entity)
continue;
4210 clouds.push_back(cloud);
4216 meshes.push_back(mesh);
4219 tr(
"Only meshes with standard vertices are handled "
4220 "for now! Can't merge entity '%1'...")
4225 "mesh, can't merge it!")
4229 }
catch (
const std::bad_alloc&) {
4234 if (clouds.empty() && meshes.empty()) {
4238 if (!clouds.empty() && !meshes.empty()) {
4239 CVLog::Error(tr(
"Can't mix point clouds and meshes!"));
4243 if (!clouds.empty()) {
4256 ccHObjectContext firstCloudContext;
4260 size_t cloudIndex = 0;
4262 for (
size_t i = 0; i < clouds.size(); ++i) {
4274 if (QMessageBox::question(
4275 this, tr(
"Original cloud index"),
4276 tr(
"Do you want to generate a scalar field with "
4277 "the original cloud index?")) ==
4287 tr(
"Couldn't allocate a new scalar field for "
4288 "storing the original cloud index! Try to "
4289 "free some memory ..."));
4298 unsigned countBefore = firstCloud->
size();
4299 unsigned countAdded = pc->
size();
4303 if (firstCloud->
size() == countBefore + countAdded) {
4312 parent != firstCloudContext.parent)
4321 static_cast<ScalarType
>(++cloudIndex);
4322 for (
unsigned i = 0; i < countAdded; ++i) {
4323 ocIndexSF->
setValue(countBefore + i, index);
4328 tr(
"Fusion failed! (not enough memory?)"));
4337 firstCloud->
showSF(
true);
4341 while (!toBeRemoved.empty()) {
4342 if (toBeRemoved.back() && m_ccRoot) {
4345 toBeRemoved.pop_back();
4355 else if (!meshes.empty()) {
4356 bool createSubMeshes =
true;
4365 baseMesh->
setName(
"Merged mesh");
4369 for (
ccMesh* mesh : meshes) {
4376 if (!baseMesh->
merge(mesh, createSubMeshes)) {
4392 bool forceRedraw ) {
4397 bool forceRedraw ) {
4437 void MainWindow::toggleActiveWindowCenteredPerspective() {
4444 if (
params.perspectiveView &&
params.objectCenteredView) {
4453 void MainWindow::toggleActiveWindowViewerBasedPerspective() {
4460 if (
params.perspectiveView && !
params.objectCenteredView) {
4470 void MainWindow::createSinglePointCloud() {
4473 static size_t s_lastPointIndex = 0;
4475 "x",
"y",
"z", -1.0e12, 1.0e12, s_lastPoint.x, s_lastPoint.y,
4476 s_lastPoint.z, 4, tr(
"Point coordinates"),
this);
4477 if (axisDlg.buttonBox->button(QDialogButtonBox::Ok))
4478 axisDlg.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
4479 if (!axisDlg.exec())
return;
4480 s_lastPoint.x = axisDlg.doubleSpinBox1->value();
4481 s_lastPoint.y = axisDlg.doubleSpinBox2->value();
4482 s_lastPoint.z = axisDlg.doubleSpinBox3->value();
4491 cloud->
setName(tr(
"Point #%1").arg(++s_lastPointIndex));
4496 addToDB(cloud,
true,
true,
true,
true);
4503 void MainWindow::createPointCloudFromClipboard() {
4504 const QClipboard* clipboard = QApplication::clipboard();
4506 const QMimeData* mimeData = clipboard->mimeData();
4512 if (!mimeData->hasText()) {
4527 QByteArray data = mimeData->data(
"text/plain");
4529 container, parameters);
4532 tr(
"from the clipboard"));
4555 static unsigned s_clipboardGroupID = 0;
4557 if (s_clipboardGroupID != 0) {
4559 if (
nullptr == clipboardGroup) {
4561 s_clipboardGroupID = 0;
4565 if (s_clipboardGroupID == 0) {
4566 clipboardGroup =
new ccHObject(tr(
"Clipboard"));
4567 s_clipboardGroupID = clipboardGroup->
getUniqueID();
4568 addToDB(clipboardGroup,
false,
false,
false,
false);
4571 assert(clipboardGroup);
4573 bool normalsDisplayedByDefault =
4581 if (!normalsDisplayedByDefault) {
4589 for (
size_t i = 0; i < clouds.size(); ++i) {
4592 bool lastCloud = (i + 1 == clouds.size());
4593 addToDB(cloud, lastCloud, lastCloud,
true, lastCloud);
4597 QMainWindow::statusBar()->showMessage(
4598 tr(
"%1 cloud(s) loaded from the clipboard").arg(clouds.size()),
4616 if (obj && m_ccRoot) {
4626 m_ui->menuBar->setDisabled(state);
4627 m_ui->DockableDBTree->setDisabled(state);
4628 m_ui->mainToolBar->setDisabled(state);
4629 m_ui->SFToolBar->setDisabled(state);
4630 m_ui->FilterToolBar->setDisabled(state);
4631 m_ui->AnnotationToolBar->setDisabled(state);
4636 toolbar->setDisabled(state);
4651 CVLog::Warning(tr(
"Selected entities have no valid bounding-box!"));
4679 #if defined(USE_PCL_BACKEND)
4680 void MainWindow::initSelectionController() {
4682 m_selectionController = cvSelectionToolController::instance();
4683 m_selectionController->initialize(
this);
4688 m_selectionController->setVisualizer(viewer);
4692 cvSelectionToolController::SelectionActions actions;
4693 actions.selectSurfaceCells = m_ui->actionSelectSurfaceCells;
4694 actions.selectSurfacePoints = m_ui->actionSelectSurfacePoints;
4695 actions.selectFrustumCells = m_ui->actionSelectFrustumCells;
4696 actions.selectFrustumPoints = m_ui->actionSelectFrustumPoints;
4697 actions.selectPolygonCells = m_ui->actionSelectPolygonCells;
4698 actions.selectPolygonPoints = m_ui->actionSelectPolygonPoints;
4699 actions.selectBlocks = m_ui->actionSelectBlocks;
4700 actions.selectFrustumBlocks = m_ui->actionSelectFrustumBlocks;
4701 actions.interactiveSelectCells = m_ui->actionInteractiveSelectCells;
4702 actions.interactiveSelectPoints = m_ui->actionInteractiveSelectPoints;
4703 actions.hoverCells = m_ui->actionHoverCells;
4704 actions.hoverPoints = m_ui->actionHoverPoints;
4705 actions.addSelection = m_ui->actionAddSelection;
4706 actions.subtractSelection = m_ui->actionSubtractSelection;
4707 actions.toggleSelection = m_ui->actionToggleSelection;
4708 actions.growSelection = m_ui->actionGrowSelection;
4709 actions.shrinkSelection = m_ui->actionShrinkSelection;
4710 actions.clearSelection = m_ui->actionClearSelection;
4711 actions.zoomToBox = m_ui->actionZoomToBox;
4713 m_selectionController->setupActions(actions);
4716 connect(m_selectionController,
4717 &cvSelectionToolController::selectionFinished,
this,
4718 &MainWindow::onSelectionFinished);
4724 connect(m_selectionController,
4725 &cvSelectionToolController::selectionToolStateChanged,
this,
4726 [
this](
bool active) {
4732 connect(m_selectionController,
4733 &cvSelectionToolController::selectionPropertiesUpdateRequested,
4734 this, [
this](
const cvSelectionData& data) {
4736 if (m_findDataDock) {
4737 m_findDataDock->updateSelection(data);
4749 "[MainWindow] Clearing selection data due to "
4751 auto* manager = getSelectionManager();
4753 manager->clearCurrentSelection();
4756 if (m_selectionController &&
4757 m_selectionController->highlighter()) {
4758 m_selectionController->highlighter()->clearHighlights();
4765 connect(m_selectionController,
4766 &cvSelectionToolController::zoomToBoxRequested,
this,
4767 [
this](
int xmin,
int ymin,
int xmax,
int ymax) {
4769 QString(
"[MainWindow] Zoom to box completed: [%1, "
4784 if (m_ccRoot && m_ccRoot->getPropertiesDelegate()) {
4785 m_selectionController->setPropertiesDelegate(
4786 m_ccRoot->getPropertiesDelegate());
4789 if (m_findDataDock) {
4791 cvSelectionHighlighter* highlighter =
4792 m_selectionController->highlighter();
4793 cvViewSelectionManager* manager = getSelectionManager();
4796 QString(
"[MainWindow::initSelectionController] Calling "
4798 "highlighter=%1, manager=%2, visualizer=%3")
4799 .arg(highlighter !=
nullptr)
4800 .arg(manager !=
nullptr)
4801 .arg(visualizer !=
nullptr));
4803 m_findDataDock->configure(highlighter, manager, visualizer);
4806 "[MainWindow::initSelectionController] m_findDataDock is "
4811 void MainWindow::disableAllSelectionTools(
void* except) {
4813 if (m_selectionController) {
4815 m_selectionController->disableAllTools(
nullptr);
4819 cvViewSelectionManager* MainWindow::getSelectionManager()
const {
4820 if (m_selectionController) {
4821 return m_selectionController->manager();
4826 void MainWindow::onSelectionFinished(
const cvSelectionData& selectionData) {
4834 cvViewSelectionManager* manager = getSelectionManager();
4841 bool hasSelection = !selectionData.isEmpty();
4844 m_ui->actionGrowSelection->setEnabled(hasSelection);
4845 m_ui->actionShrinkSelection->setEnabled(hasSelection);
4846 m_ui->actionClearSelection->setEnabled(hasSelection);
4850 if (m_findDataDock) {
4851 m_findDataDock->updateSelection(selectionData);
4859 if (!hasSelection) {
4860 cvSelectionHighlighter* highlighter =
4861 m_selectionController ? m_selectionController->highlighter()
4864 highlighter->clearHighlights();
4871 .arg(selectionData.count()));
4874 void MainWindow::onSelectionToolActivated(QAction* action) {
4875 bool isSelectionTool = (action && action->isChecked());
4878 QString(
"[MainWindow] Selection tool %1: %2")
4879 .arg(action ? action->text() :
"unknown")
4880 .arg(isSelectionTool ?
"activated" :
"deactivated"));
4884 if (isSelectionTool) {
4893 void MainWindow::onSelectionRestored(
const cvSelectionData& selection) {
4894 cvViewSelectionManager* manager = getSelectionManager();
4896 manager->setCurrentSelection(selection);
4898 .arg(selection.count())
4899 .arg(selection.fieldTypeString()));
4924 void MainWindow::setupInputDevices() {
4925 #ifdef CC_3DXWARE_SUPPORT
4926 m_3DMouseManager =
new cc3DMouseManager(
this,
this);
4927 m_ui->menuFile->insertMenu(m_UI->actionCloseAll, m_3DMouseManager->menu());
4930 #ifdef CC_GAMEPAD_SUPPORT
4931 m_gamepadManager =
new ccGamepadManager(
this,
this);
4932 m_ui->menuFile->insertMenu(m_ui->actionClearAll, m_gamepadManager->menu());
4935 #if defined(CC_3DXWARE_SUPPORT) || defined(CC_GAMEPAD_SUPPORT)
4936 m_ui->menuFile->insertSeparator(m_ui->actionClearAll);
4940 void MainWindow::destroyInputDevices() {
4941 #ifdef CC_GAMEPAD_SUPPORT
4942 delete m_gamepadManager;
4943 m_gamepadManager =
nullptr;
4946 #ifdef CC_3DXWARE_SUPPORT
4947 delete m_3DMouseManager;
4948 m_3DMouseManager =
nullptr;
4952 void MainWindow::showDisplayOptions() {
4957 displayOptionsDlg.exec();
4959 disconnect(&displayOptionsDlg);
4983 updateUIWithSelection();
4995 assert(m_pickingHub);
4998 tr(
"Can't start the picking mechanism (another tool is already "
5043 if (m_pprDlg) m_pprDlg->
pause(
false);
5052 if (!m_pickingHub) {
5072 if ((pickedPoint - *P).norm() < 1.0e-6) {
5074 tr(
"[Level] Point is too close from the others!"));
5084 label->
setName(tr(
"P#%1").arg(markerCount));
5090 if (markerCount == 3) {
5110 double* mat = trans.
data();
5135 applyTransformation(trans);
5156 if (m_transTool && m_transTool->
started()) {
5158 const unsigned& precision =
5165 tr(
"Point (%1 ; %2 ; %3) set as rotation center for "
5166 "interactive transformation")
5167 .arg(pickedPoint.
x, 0,
'f', precision)
5168 .arg(pickedPoint.
y, 0,
'f', precision)
5169 .arg(pickedPoint.
z, 0,
'f', precision),
5174 if (!
params.perspectiveView ||
params.objectCenteredView) {
5194 void MainWindow::showEvent(QShowEvent*
event) {
5195 QMainWindow::showEvent(
event);
5197 updateMemoryUsageWidgetSize();
5204 if (m_layoutManager) {
5209 updateAllToolbarIconSizes();
5211 CVLog::Error(
"[MainWindow] Layout manager is not initialized!");
5216 if (isFullScreen()) {
5217 m_ui->actionFullScreen->setChecked(
true);
5222 void MainWindow::closeEvent(QCloseEvent*
event) {
5224 bool askForConfirmation =
5237 if (!askForConfirmation) {
5246 QMessageBox msgBox(
this);
5247 msgBox.setWindowTitle(tr(
"Quit"));
5248 msgBox.setText(tr(
"Are you sure you want to quit?"));
5249 msgBox.setIcon(QMessageBox::Question);
5252 QPushButton* noButton = msgBox.addButton(tr(
"No"), QMessageBox::RejectRole);
5253 QPushButton* yesDontAskButton = msgBox.addButton(tr(
"Yes, don't ask again"),
5254 QMessageBox::ActionRole);
5255 QPushButton* yesButton =
5256 msgBox.addButton(tr(
"Yes"), QMessageBox::AcceptRole);
5259 msgBox.setDefaultButton(noButton);
5265 QAbstractButton* clickedButton = msgBox.clickedButton();
5266 if (clickedButton == yesButton || clickedButton == yesDontAskButton) {
5268 if (clickedButton == yesDontAskButton) {
5289 if (m_layoutManager) {
5292 CVLog::Error(
"[MainWindow] Layout manager is not initialized!");
5296 void MainWindow::doShowPrimitiveFactory() {
5299 m_pfDlg->setModal(
false);
5300 m_pfDlg->setWindowModality(Qt::NonModal);
5304 void MainWindow::doCheckForUpdate() {
5306 m_updateDlg->setModal(
false);
5307 m_updateDlg->setWindowModality(Qt::NonModal);
5308 m_updateDlg->show();
5312 void MainWindow::doActionComputeNormals() {
5319 void MainWindow::doActionInvertNormals() {
5325 void MainWindow::doActionConvertNormalsToDipDir() {
5336 void MainWindow::doActionExportNormalToSF() {
5345 void MainWindow::doActionConvertNormalsToHSV() {
5356 void MainWindow::doActionOrientNormalsMST() {
5363 void MainWindow::doActionOrientNormalsFM() {
5371 void MainWindow::doActionComputeKdTree() {
5375 ccHObject* ent = m_selectedEntities.back();
5376 bool lockedVertices;
5378 if (lockedVertices) {
5385 CVLog::Error(tr(
"Selected one and only one point cloud or mesh!"));
5391 this, tr(
"Compute Kd-tree"), tr(
"Max error per leaf cell:"),
5398 QElapsedTimer eTimer;
5406 qint64 elapsedTime_ms = eTimer.elapsed();
5409 static_cast<double>(elapsedTime_ms) / 1.0e3);
5431 void MainWindow::doActionComputeOctree() {
5438 void MainWindow::doActionResampleWithOctree() {
5440 int pointCount = QInputDialog::getInt(
this, tr(
"Resample with octree"),
5441 tr(
"Points (approx.)"), 1000000, 1,
5442 INT_MAX, 100000, &ok);
5446 pDlg.setAutoClose(
false);
5448 assert(pointCount > 0);
5449 unsigned aimedPoints =
static_cast<unsigned>(pointCount);
5451 bool errors =
false;
5469 tr(
"Could not compute octree for cloud '%1'")
5476 QElapsedTimer eTimer;
5482 CELL_GRAVITY_CENTER,
5487 eTimer.elapsed() / 1.0e3);
5504 tr(
"[ResampleWithOctree] Errors occurred during the process! "
5505 "Result may be incomplete!"));
5509 void MainWindow::doActionComputeMeshAA() {
5513 void MainWindow::doActionComputeMeshLS() {
5517 void MainWindow::doActionConvexHull() {
5528 clouds.push_back(ent);
5533 for (
size_t i = 0; i < meshes.size(); ++i) {
5544 void MainWindow::doActionPoissonReconstruction() {
5553 if (!prpDlg.addEntity(entity)) {
5558 if (prpDlg.start()) {
5560 for (
size_t i = 0; i < meshes.size(); ++i) {
5572 static double s_meshMaxEdgeLength = 0.0;
5575 double maxEdgeLength = QInputDialog::getDouble(
5576 this, tr(
"Triangulate"), tr(
"Max edge length (0 = no limit)"),
5577 s_meshMaxEdgeLength, 0, 1.0e9, 8, &ok);
5579 s_meshMaxEdgeLength = maxEdgeLength;
5584 bool hadNormals =
false;
5588 clouds.push_back(entity);
5599 bool updateNormals =
false;
5602 (QMessageBox::question(
5603 this, tr(
"Keep old normals?"),
5604 tr(
"Cloud(s) already have normals. Do you want to "
5605 "update them (yes) or keep the old ones (no)?"),
5607 QMessageBox::No) == QMessageBox::Yes);
5611 pDlg.setAutoClose(
false);
5612 pDlg.setWindowTitle(tr(
"Triangulation"));
5613 pDlg.setInfo(tr(
"Triangulation in progress..."));
5614 pDlg.setRange(0, 0);
5616 QApplication::processEvents();
5618 bool errors =
false;
5619 for (
size_t i = 0; i < clouds.size(); ++i) {
5626 cloud,
type, updateNormals,
5650 void MainWindow::doMeshTwoPolylines() {
5651 if (m_selectedEntities.size() != 2)
return;
5661 bool useViewingDir =
false;
5665 (QMessageBox::question(
this, tr(
"Projection method"),
5666 tr(
"Use best fit plane (yes) or the "
5667 "current viewing direction (no)"),
5669 QMessageBox::No) == QMessageBox::No);
5670 if (useViewingDir) {
5677 p1, p2, useViewingDir ? &viewingDir : 0);
5684 tr(
"[Mesh two polylines] Failed to compute normals!"));
5687 CVLog::Error(tr(
"Failed to create mesh (see Console)"));
5692 void MainWindow::doActionMeshScanGrids() {
5694 static double s_meshMinTriangleAngle_deg = 1.0;
5697 double minAngle_deg = QInputDialog::getDouble(
5698 this, tr(
"Triangulate"), tr(
"Min triangle angle (in degrees)"),
5699 s_meshMinTriangleAngle_deg, 0, 90.0, 3, &ok);
5701 s_meshMinTriangleAngle_deg = minAngle_deg;
5713 for (
size_t i = 0; i < cloud->
gridCount(); ++i) {
5725 addToDB(gridMesh,
false,
true,
false,
false);
5733 void MainWindow::doActionComputeDistancesFromSensor() {
5742 if (!cdDlg.exec())
return;
5747 if (!sensor)
continue;
5757 "Select a cloud on which to project the uncertainty:");
5767 bool squared = cdDlg.computeSquaredDistances();
5770 const char* defaultRangesSFname =
5783 for (
unsigned i = 0; i < cloud->
size(); ++i) {
5785 ScalarType s =
static_cast<ScalarType
>(
5786 squared ? (*P - sensorCenter).norm2()
5787 : (*P - sensorCenter).norm());
5800 void MainWindow::doActionComputeScatteringAngles() {
5823 "Select a cloud on which to project the uncertainty:");
5833 if (!cdDlg.exec())
return;
5835 bool toDegreeFlag = cdDlg.anglesInDegrees();
5838 const char* defaultScatAnglesSFname =
5852 for (
unsigned i = 0; i < cloud->
size(); ++i) {
5881 void MainWindow::doActionSetViewFromSensor() {
5897 void MainWindow::doActionCreateGBLSensor() {
5899 if (!spDlg.exec())
return;
5912 spDlg.updateGBLSensor(sensor);
5924 else if (diag > 10000.0)
5979 void MainWindow::doActionCreateCameraSensor() {
5986 ent = m_selectedEntities.front();
5996 else if (diag > 10000.0)
6007 if (!spDlg.exec()) {
6011 spDlg.updateCamSensor(sensor);
6013 QWidget* win =
nullptr;
6035 void MainWindow::doActionModifySensor() {
6050 spDlg.initWithGBLSensor(gbl);
6052 if (!spDlg.exec())
return;
6055 spDlg.updateGBLSensor(gbl);
6080 spDlg.initWithCamSensor(cam);
6082 if (!spDlg.exec())
return;
6085 spDlg.updateCamSensor(cam);
6098 void MainWindow::doActionProjectUncertainty() {
6103 "Select one and only one camera (projective) sensor!");
6119 "Sensor has no associated uncertainty model! (Brown, etc.)");
6131 "Select a cloud on which to project the uncertainty:");
6144 std::vector<Vector3Tpl<ScalarType>> accuracy;
6153 const char dimChar[3] = {
'x',
'y',
'z'};
6154 for (
unsigned d = 0; d < 3; ++d) {
6156 QString sfName = QString(
"[%1] Uncertainty (%2)")
6160 if (sfIdx < 0) sfIdx = pointCloud->
addScalarField(qPrintable(sfName));
6170 unsigned count =
static_cast<unsigned>(accuracy.size());
6172 for (
unsigned i = 0; i <
count; i++)
6185 QString(
"[%1] Uncertainty (3D)").arg(sensor->
getName());
6187 if (sfIdx < 0) sfIdx = pointCloud->
addScalarField(qPrintable(sfName));
6197 unsigned count =
static_cast<unsigned>(accuracy.size());
6199 for (
unsigned i = 0; i <
count; i++)
6200 sf->
setValue(i, accuracy[i].norm());
6204 pointCloud->
showSF(
true);
6211 void MainWindow::doActionCheckPointsInsideFrustum() {
6221 if (!sensor)
return;
6230 askUserToSelectACloud(defaultCloud,
"Select a cloud to filter:");
6247 std::vector<unsigned> inCameraFrustum;
6248 if (!
octree->intersectWithFrustum(sensor, inCameraFrustum)) {
6252 const char sfName[] =
"Frustum visibility";
6255 if (inCameraFrustum.empty()) {
6262 "Failed to allocate memory for output scalar field!");
6271 const ScalarType c_insideValue =
static_cast<ScalarType
>(1);
6273 for (
unsigned index : inCameraFrustum) {
6274 sf->
setValue(index, c_insideValue);
6279 pointCloud->
showSF(
true);
6288 void MainWindow::doActionShowDepthBuffer() {
6309 "parent is not a point cloud!")
6320 void MainWindow::doActionExportDepthBuffer() {
6326 QString currentPath =
6330 QString
filename = QFileDialog::getSaveFileName(
6331 this,
"Select output file", currentPath,
6341 settings.endGroup();
6344 bool multEntities =
false;
6346 toSave = m_selectedEntities.front();
6353 multEntities =
true;
6365 QString(
"[I/O] File '%1' saved successfully").arg(
filename));
6374 void MainWindow::doActionComputePointsVisibility() {
6383 if (!sensor)
return;
6392 askUserToSelectACloud(defaultCloud,
"Select a cloud to filter:");
6401 if (QMessageBox::warning(
this,
"Depth buffer.",
6402 "Sensor has no depth buffer: do you want "
6403 "to compute it now?",
6404 QMessageBox::Yes | QMessageBox::No,
6405 QMessageBox::Yes) == QMessageBox::No) {
6412 static_cast<ccPointCloud*
>(defaultCloud), errorCode)) {
6420 "Sensor has no depth buffer (and no associated cloud?)");
6426 const char sfName[] =
"Sensor visibility";
6430 CVLog::Error(
"Failed to allocate memory for output scalar field!");
6442 pdlg.setMethodTitle(tr(
"Compute visibility"));
6443 pdlg.setInfo(tr(
"Points: %L1").arg(pointCloud->
size()));
6445 QApplication::processEvents();
6447 for (
unsigned i = 0; i < pointCloud->
size(); i++) {
6450 ScalarType visValue =
static_cast<ScalarType
>(visibility);
6454 if (!nprogress.oneStep()) {
6465 pointCloud->
showSF(
true);
6482 void MainWindow::doActionCompressFWFData() {
6493 void MainWindow::doActionShowWaveDialog() {
6509 wDlg->setAttribute(Qt::WA_DeleteOnClose);
6510 wDlg->setModal(
false);
6514 void MainWindow::doActionConvertTextureToColor() {
6522 void MainWindow::doActionSamplePointsOnMesh() {
6523 static unsigned s_ptsSamplingCount = 1000000;
6524 static double s_ptsSamplingDensity = 10.0;
6525 static bool s_ptsSampleNormals =
true;
6526 static bool s_useDensity =
false;
6530 dlg.setPointsNumber(s_ptsSamplingCount);
6531 dlg.setDensityValue(s_ptsSamplingDensity);
6532 dlg.setGenerateNormals(s_ptsSampleNormals);
6533 dlg.setUseDensity(s_useDensity);
6534 if (!dlg.exec())
return;
6537 pDlg.setAutoClose(
false);
6539 bool withNormals = dlg.generateNormals();
6540 bool withRGB = dlg.interpolateRGB();
6541 bool withTexture = dlg.interpolateTexture();
6542 s_useDensity = dlg.useDensity();
6543 assert(dlg.getPointsNumber() >= 0);
6544 s_ptsSamplingCount =
static_cast<unsigned>(dlg.getPointsNumber());
6545 s_ptsSamplingDensity = dlg.getDensityValue();
6546 s_ptsSampleNormals = withNormals;
6548 bool errors =
false;
6558 s_useDensity ? s_ptsSamplingDensity : s_ptsSamplingCount,
6559 withNormals, withRGB, withTexture, &pDlg);
6571 tr(
"[doActionSamplePointsOnMesh] Errors occurred during the "
6572 "process! Result may be incomplete!"));
6575 void MainWindow::doActionSamplePointsOnPolyline() {
6576 static unsigned s_ptsSamplingCount = 1000;
6577 static double s_ptsSamplingDensity = 10.0;
6578 static bool s_useDensity =
false;
6581 dlg.setWindowTitle(tr(
"Points Sampling on polyline"));
6583 dlg.setPointsNumber(s_ptsSamplingCount);
6584 dlg.setDensityValue(s_ptsSamplingDensity);
6585 dlg.setUseDensity(s_useDensity);
6586 dlg.optionsFrame->setVisible(
false);
6587 if (!dlg.exec())
return;
6589 assert(dlg.getPointsNumber() >= 0);
6590 s_ptsSamplingCount =
static_cast<unsigned>(dlg.getPointsNumber());
6591 s_ptsSamplingDensity = dlg.getDensityValue();
6592 s_useDensity = dlg.useDensity();
6594 bool errors =
false;
6604 s_useDensity ? s_ptsSamplingDensity : s_ptsSamplingCount,
true);
6615 tr(
"[doActionSamplePointsOnPolyline] Errors occurred during "
6616 "the process! Result may be incomplete!"));
6620 void MainWindow::doActionSmoohPolyline() {
6621 static int s_iterationCount = 5;
6622 static double s_ratio = 0.25;
6626 dlg.setIerationCount(s_iterationCount);
6627 dlg.setRatio(s_ratio);
6628 if (!dlg.exec())
return;
6630 s_iterationCount = dlg.getIerationCount();
6631 s_ratio = dlg.getRatio();
6633 bool errors =
false;
6638 for (
ccHObject* entity : selectedEntities) {
6645 s_ratio,
static_cast<unsigned>(s_iterationCount));
6661 tr(
"[DoActionSmoohPolyline] Errors occurred during the "
6662 "process! Result may be incomplete!"));
6668 void MainWindow::doConvertPolylinesToMesh() {
6671 std::vector<ccPolyline*> polylines;
6675 ccHObject* obj = m_selectedEntities.back();
6678 polylines.push_back(
6684 polylines.push_back(
static_cast<ccPolyline*
>(entity));
6688 }
catch (
const std::bad_alloc&) {
6693 if (polylines.empty()) {
6695 tr(
"Select a group of polylines or multiple polylines (contour "
6701 tr(
"Contour plot to mesh"),
this);
6702 poeDlg.addElement(
"X");
6703 poeDlg.addElement(
"Y");
6704 poeDlg.addElement(
"Z");
6705 poeDlg.setDefaultIndex(2);
6706 if (!poeDlg.exec())
return;
6708 int dim = poeDlg.getSelectedIndex();
6709 assert(dim >= 0 && dim < 3);
6711 const unsigned char Z =
static_cast<unsigned char>(dim);
6712 const unsigned char X = Z == 2 ? 0 : Z + 1;
6713 const unsigned char Y =
X == 2 ? 0 :
X + 1;
6716 unsigned segmentCount = 0;
6717 unsigned vertexCount = 0;
6722 vertexCount += poly->
size();
6728 if (segmentCount < 2) {
6736 std::vector<CCVector2> points2D;
6737 std::vector<int> segments2D;
6739 points2D.reserve(vertexCount);
6740 segments2D.reserve(segmentCount * 2);
6741 }
catch (
const std::bad_alloc&) {
6750 if (poly ==
nullptr)
continue;
6752 unsigned vertCount = poly->
size();
6753 int vertIndex0 =
static_cast<int>(points2D.size());
6755 for (
unsigned v = 0; v < vertCount; ++v) {
6757 int vertIndex =
static_cast<int>(points2D.size());
6760 if (v + 1 < vertCount) {
6761 segments2D.push_back(vertIndex);
6762 segments2D.push_back(vertIndex + 1);
6763 }
else if (closed) {
6764 segments2D.push_back(vertIndex);
6765 segments2D.push_back(vertIndex0);
6769 assert(points2D.size() == vertexCount);
6770 assert(segments2D.size() == segmentCount * 2);
6774 std::string errorStr;
6775 if (!delaunayMesh->
buildMesh(points2D, segments2D, errorStr)) {
6777 .arg(QString::fromStdString(errorStr)));
6778 delete delaunayMesh;
6783 if (!vertices->
reserve(vertexCount)) {
6787 delete delaunayMesh;
6794 unsigned vertCount = poly->
size();
6795 for (
unsigned v = 0; v < vertCount; ++v) {
6806 unsigned vertCount = vertices->
size();
6807 for (
unsigned i = 0; i < delaunayMesh->
size(); ++i) {
6810 assert(tsi->
i1 < vertCount && tsi->
i2 < vertCount &&
6811 tsi->
i3 < vertCount);
6817 if (mesh->
size() != delaunayMesh->
size()) {
6824 delete delaunayMesh;
6825 delaunayMesh =
nullptr;
6836 tr(
"[Contour plot to mesh] Failed to compute normals!"));
6851 void MainWindow::doBSplineFittingFromCloud() {
6852 #ifdef USE_PCL_BACKEND
6854 std::vector<ccPointCloud*> clouds;
6859 clouds.push_back(cloud);
6864 if (clouds.empty()) {
6870 CurveFittingTool::CurveFitting::BsplineFitting(*clouds[0]);
6875 if (polyLine && m_ccRoot) {
6883 "[doBSplineFittingFromCloud] please use pcl as backend and then "
6889 void MainWindow::doActionSmoothMeshSF() {
6898 void MainWindow::doActionEnhanceMeshSF() {
6908 void MainWindow::doActionSubdivideMesh() {
6911 this, tr(
"Subdivide mesh"), tr(
"Max area per triangle:"),
6923 ccMesh* subdividedMesh =
nullptr;
6930 tr(
"[Subdivide] An error occurred while trying to "
6931 "subdivide mesh '%1' (not enough memory?)")
6935 if (subdividedMesh) {
6936 subdividedMesh->
setName(tr(
"%1.subdivided(S<%2)")
6945 "mesh '%1' (not enough memory?)")
6957 void MainWindow::doActionFlipMeshTriangles() {
6958 bool warningIssued =
false;
6967 }
else if (!warningIssued) {
6969 warningIssued =
true;
6977 void MainWindow::doActionSmoothMeshLaplacian() {
6978 static unsigned s_laplacianSmooth_nbIter = 20;
6979 static double s_laplacianSmooth_factor = 0.2;
6982 s_laplacianSmooth_nbIter =
6983 QInputDialog::getInt(
this, tr(
"Smooth mesh"), tr(
"Iterations:"),
6984 s_laplacianSmooth_nbIter, 1, 1000, 1, &ok);
6986 s_laplacianSmooth_factor = QInputDialog::getDouble(
6987 this, tr(
"Smooth mesh"), tr(
"Smoothing factor:"),
6988 s_laplacianSmooth_factor, 0, 100, 3, &ok);
6992 pDlg.setAutoClose(
false);
7004 s_laplacianSmooth_factor),
7009 tr(
"Failed to apply Laplacian smoothing to mesh '%1'")
7019 void MainWindow::doActionFlagMeshVertices() {
7020 bool errors =
false;
7021 bool success =
false;
7029 if (mesh && vertices) {
7038 "vertices of mesh '%1'!")
7049 mesh, flags, &stats)) {
7065 "total (normal: %3 / on hole borders: "
7066 "%4 / non-manifold: %5)")
7076 "vertices of mesh '%1'!")
7093 tr(
"[Mesh Quality] SF flags: %1 (NORMAL) / %2 (BORDER) / (%3) "
7098 VERTEX_NON_MANIFOLD));
7106 void MainWindow::doActionMeasureMeshVolume() {
7117 tr(
"[Mesh Volume] Mesh '%1': V=%2 (cube units)")
7124 computeMeshEdgesConnectivity(mesh, stats)) {
7127 tr(
"[Mesh Volume] The above volume might be "
7128 "invalid (mesh has holes)"));
7131 tr(
"[Mesh Volume] The above volume might be "
7132 "invalid (mesh has non-manifold edges)"));
7136 tr(
"[Mesh Volume] The above volume might be "
7137 "invalid (not enough memory to check if the "
7138 "mesh is closed)"));
7147 void MainWindow::doActionMeasureMeshSurface() {
7157 tr(
"[Mesh Surface] Mesh '%1': S=%2 (square units)")
7162 "surface: %1 (square units)")
7163 .arg(S /
double(mesh->
size())));
7172 void MainWindow::doActionCreatePlane() {
7177 void MainWindow::doActionEditPlane() {
7194 void MainWindow::doActionFlipPlane() {
7201 for (
ccHObject* entity : m_selectedEntities) {
7213 void MainWindow::doActionPromoteCircleToCylinder() {
7225 static double CylinderHeight = 0.0;
7226 if (CylinderHeight == 0.0) {
7227 CylinderHeight = 2 * circle->
getRadius();
7230 double value = QInputDialog::getDouble(
7231 this, tr(
"Cylinder height"), tr(
"Height"), CylinderHeight, 0.0,
7237 CylinderHeight = value;
7243 tr(
"Cylinder from ") + circle->
getName());
7250 addToDB(cylinder,
true,
true);
7255 void MainWindow::doActionComparePlanes() {
7256 if (m_selectedEntities.size() != 2) {
7271 info << QString(
"Plane 1: %1").arg(p1->
getName());
7274 info << QString(
"Plane 2: %1").arg(p2->
getName());
7285 info << QString(
"Angle P1/P2: %1 deg.")
7292 ScalarType distCenter1ToPlane2 =
7295 info << QString(
"Distance Center(P1)/P2: %1").arg(distCenter1ToPlane2);
7299 ScalarType distCenter2ToPlane1 =
7302 info << QString(
"Distance Center(P2)/P1: %1").arg(distCenter2ToPlane1);
7306 QMessageBox::information(
this,
"Plane comparison", info.join(
"\n"));
7311 void MainWindow::help() {
7312 QDesktopServices::openUrl(QUrl(
7313 QStringLiteral(
"https://asher-1.github.io/ACloudViewer/docs")));
7315 tr(
"[ACloudViewer help] "
7316 "https://asher-1.github.io/ACloudViewer/docs!"));
7320 void MainWindow::changeTheme() {
7321 QAction* action = qobject_cast<QAction*>(sender());
7322 QVariant v = action->data();
7324 QString qssfile = (QString)v.value<QString>();
7333 void MainWindow::changeLanguage() {
7334 QAction* action = qobject_cast<QAction*>(sender());
7335 QVariant v = action->data();
7336 int language = (int)v.value<
int>();
7341 tr(
"[changeLanguage] Change to English language"));
7346 tr(
"[changeLanguage] Doesn't support Chinese temporarily"));
7352 void MainWindow::doActionGlobalShiftSeetings() {
7353 QDialog dialog(
this);
7354 Ui_GlobalShiftSettingsDialog ui;
7355 ui.setupUi(&dialog);
7357 ui.maxAbsCoordSpinBox->setValue(
static_cast<int>(
7359 ui.maxAbsDiagSpinBox->setValue(
static_cast<int>(
7362 if (!dialog.exec()) {
7366 double maxAbsCoord =
7367 pow(10.0,
static_cast<double>(ui.maxAbsCoordSpinBox->value()));
7369 pow(10.0,
static_cast<double>(ui.maxAbsDiagSpinBox->value()));
7374 CVLog::Print(tr(
"[Global Shift] Max abs. coord = %1 / max abs. diag = %2")
7390 QString inviteMessage) {
7394 if (clouds.empty()) {
7399 int selectedIndex = 0;
7400 if (defaultCloudEntity) {
7401 for (
size_t i = 1; i < clouds.size(); ++i) {
7402 if (clouds[i] == defaultCloudEntity) {
7403 selectedIndex =
static_cast<int>(i);
7411 this, inviteMessage);
7412 if (selectedIndex < 0)
return 0;
7415 assert(selectedIndex >= 0 &&
7416 static_cast<size_t>(selectedIndex) < clouds.size());
7420 void MainWindow::toggleSelectedEntitiesProperty(
7430 void MainWindow::clearSelectedEntitiesProperty(
7441 void MainWindow::doActionFastRegistration(FastRegistrationMode mode) {
7443 if (m_selectedEntities.empty())
return;
7449 for (
ccHObject* entity : selectedEntities) {
7455 case MoveBBCenterToOrigin:
7458 case MoveBBMinCornerToOrigin:
7461 case MoveBBMaxCornerToOrigin:
7478 "Hint: copy it (CTRL+C) and apply it - or its inverse - on any "
7479 "entity with the 'Edit > Apply transformation' tool");
7498 void MainWindow::doActionColorize() { doActionSetColor(
true); }
7500 void MainWindow::doActionSetUniqueColor() { doActionSetColor(
false); }
7502 void MainWindow::doActionSetColor(
bool colorize) {
7509 void MainWindow::doActionRGBToGreyScale() {
7515 void MainWindow::doActionSetColorGradient() {
7522 void MainWindow::doActionChangeColorLevels() {
7527 void MainWindow::doActionInterpolateColors() {
7534 void MainWindow::doActionEnhanceRGBWithIntensities() {
7541 void MainWindow::doActionColorFromScalars() {
7549 cfsDlg->setAttribute(Qt::WA_DeleteOnClose,
true);
7555 void MainWindow::doActionSORFilter() {
7559 static int s_sorFilterKnn = 6;
7560 static double s_sorFilterNSigma = 1.0;
7561 sorDlg.knnSpinBox->setValue(s_sorFilterKnn);
7562 sorDlg.nSigmaDoubleSpinBox->setValue(s_sorFilterNSigma);
7563 if (!sorDlg.exec())
return;
7566 s_sorFilterKnn = sorDlg.knnSpinBox->value();
7567 s_sorFilterNSigma = sorDlg.nSigmaDoubleSpinBox->value();
7570 pDlg.setAutoClose(
false);
7572 bool firstCloud =
true;
7579 for (
ccHObject* entity : selectedEntities) {
7581 bool lockedVertices;
7584 if (cloud && lockedVertices) {
7593 cloud, s_sorFilterKnn, s_sorFilterNSigma, 0, &pDlg);
7595 if (selection && cloud) {
7596 if (selection->
size() == cloud->
size()) {
7598 "removed from cloud '%1'")
7612 "Previously selected entities (sources) have "
7619 QString(
"[doActionSORFilter] Not enough memory to "
7620 "create a clean version of cloud '%1'!")
7626 selection =
nullptr;
7629 if (cloud !=
nullptr) {
7631 QString(
"[doActionSORFilter] Failed to apply the noise "
7632 "filter to cloud '%1'! (not enough memory?)")
7636 "[doActionSORFilter] Trying to apply the noise filter "
7645 void MainWindow::doActionFilterNoise() {
7652 static bool s_noiseFilterUseKnn =
false;
7653 static int s_noiseFilterKnn = 6;
7654 static bool s_noiseFilterUseAbsError =
false;
7655 static double s_noiseFilterAbsError = 1.0;
7656 static double s_noiseFilterNSigma = 1.0;
7657 static bool s_noiseFilterRemoveIsolatedPoints =
false;
7658 noiseDlg.radiusDoubleSpinBox->setValue(kernelRadius);
7659 noiseDlg.knnSpinBox->setValue(s_noiseFilterKnn);
7660 noiseDlg.nSigmaDoubleSpinBox->setValue(s_noiseFilterNSigma);
7661 noiseDlg.absErrorDoubleSpinBox->setValue(s_noiseFilterAbsError);
7662 noiseDlg.removeIsolatedPointsCheckBox->setChecked(
7663 s_noiseFilterRemoveIsolatedPoints);
7664 if (s_noiseFilterUseAbsError)
7665 noiseDlg.absErrorRadioButton->setChecked(
true);
7667 noiseDlg.relativeRadioButton->setChecked(
true);
7668 if (s_noiseFilterUseKnn)
7669 noiseDlg.knnRadioButton->setChecked(
true);
7671 noiseDlg.radiusRadioButton->setChecked(
true);
7673 if (!noiseDlg.exec())
return;
7677 noiseDlg.radiusDoubleSpinBox->value());
7678 s_noiseFilterUseKnn = noiseDlg.knnRadioButton->isChecked();
7679 s_noiseFilterKnn = noiseDlg.knnSpinBox->value();
7680 s_noiseFilterUseAbsError = noiseDlg.absErrorRadioButton->isChecked();
7681 s_noiseFilterNSigma = noiseDlg.nSigmaDoubleSpinBox->value();
7682 s_noiseFilterAbsError = noiseDlg.absErrorDoubleSpinBox->value();
7683 s_noiseFilterRemoveIsolatedPoints =
7684 noiseDlg.removeIsolatedPointsCheckBox->isChecked();
7687 pDlg.setAutoClose(
false);
7689 bool firstCloud =
true;
7696 for (
ccHObject* entity : selectedEntities) {
7698 bool lockedVertices;
7701 if (cloud && lockedVertices) {
7710 cloud, kernelRadius, s_noiseFilterNSigma,
7711 s_noiseFilterRemoveIsolatedPoints, s_noiseFilterUseKnn,
7712 s_noiseFilterKnn, s_noiseFilterUseAbsError,
7713 s_noiseFilterAbsError, 0, &pDlg);
7715 if (selection && cloud) {
7716 if (selection->
size() == cloud->
size()) {
7718 "removed from cloud '%1'")
7732 "Previously selected entities (sources) have "
7739 QString(
"[doActionFilterNoise] Not enough memory "
7740 "to create a clean version of cloud '%1'!")
7746 selection =
nullptr;
7749 if (cloud !=
nullptr) {
7751 "apply the noise filter to cloud "
7752 "'%1'! (not enough memory?)")
7756 "[doActionFilterNoise] Trying to apply the noise "
7757 "filter to null cloud");
7765 void MainWindow::doActionVoxelSampling() {
7777 bool lockedVertices =
false;
7779 if (!pc || lockedVertices) {
7783 selectedClouds.push_back(ent);
7789 "[MainWindow::doActionVoxelSampling] voxel sampling failed!");
7793 assert(outClouds.size() == selectedClouds.size());
7795 bool firstCloud =
true;
7796 for (
size_t i = 0; i < outClouds.size(); ++i) {
7804 CVLog::Print(QString(
"%1 down sampled from %2 points to %3 points.")
7807 .arg(cleanCloud->
size()));
7812 "Previously selected entities (sources) have been "
7819 QString(
"[doActionSORFilter] Not enough memory to create a "
7820 "clean version of cloud '%1'!")
7828 void MainWindow::doActionClone() {
7844 tr(
"An error occurred while cloning primitive %1")
7858 tr(
"An error occurred while cloning polyline %1")
7865 tr(
"An error occurred while cloning circle %1")
7870 clone = (disc ? disc->
clone() : 0);
7877 clone = (facet ? facet->
clone() : 0);
7884 tr(
"Entity '%1' can't be cloned (type not supported yet!)")
7897 if (lastClone && m_ccRoot) {
7905 void MainWindow::popMenuInConsole(
const QPoint& pos) {
7906 QAction copyAction(tr(
"Copy"),
this);
7907 QAction clearItemsAction(tr(
"Clear selected items"),
this);
7908 QAction clearConsoleAction(tr(
"Clear console"),
this);
7911 QList<QListWidgetItem*> selectedItems =
7912 m_ui->consoleWidget->selectedItems();
7913 bool hasSelection = selectedItems.count() > 0;
7916 copyAction.setEnabled(hasSelection);
7917 clearItemsAction.setEnabled(hasSelection);
7919 connect(©Action, &QAction::triggered,
this,
7920 &MainWindow::copyConsoleItems);
7921 connect(&clearItemsAction, &QAction::triggered,
this,
7922 &MainWindow::clearConsoleItems);
7923 connect(&clearConsoleAction, &QAction::triggered,
this,
7924 &MainWindow::clearConsole);
7926 QMenu menu(m_ui->consoleWidget);
7927 menu.addAction(©Action);
7928 menu.addSeparator();
7929 menu.addAction(&clearItemsAction);
7930 menu.addAction(&clearConsoleAction);
7931 menu.exec(QCursor::pos());
7935 void MainWindow::clearConsole() { m_ui->consoleWidget->clear(); }
7938 void MainWindow::copyConsoleItems() {
7939 QList<QListWidgetItem*> items = m_ui->consoleWidget->selectedItems();
7941 if (items.count() > 0) {
7942 QStringList strings;
7944 QList<QListWidgetItem*> sortedItems = items;
7945 std::sort(sortedItems.begin(), sortedItems.end(),
7946 [](QListWidgetItem* a, QListWidgetItem* b) {
7947 return a->listWidget()->row(a) < b->listWidget()->row(b);
7950 foreach (QListWidgetItem* item, sortedItems) {
7951 strings << item->text();
7954 QApplication::clipboard()->setText(strings.join(
"\n"));
7959 void MainWindow::clearConsoleItems() {
7961 QList<QListWidgetItem*> items = m_ui->consoleWidget->selectedItems();
7963 if (items.count() > 0) {
7964 if (QMessageBox::Yes ==
7965 QMessageBox::question(
this, QStringLiteral(
"Remove Item"),
7966 QStringLiteral(
"Remove %1 log information(s)")
7967 .arg(QString::number(items.count())),
7968 QMessageBox::Yes | QMessageBox::No,
7969 QMessageBox::Yes)) {
7970 foreach (QListWidgetItem* var, items) {
7971 m_ui->consoleWidget->removeItemWidget(var);
7972 items.removeOne(var);
7980 void MainWindow::doComputeBestFitBB() {
7981 if (QMessageBox::warning(
7982 this, tr(
"This method is for test purpose only"),
7983 tr(
"Cloud(s) are going to be rotated while still displayed in "
7984 "their previous position! Proceed?"),
7985 QMessageBox::Yes | QMessageBox::No,
7986 QMessageBox::No) != QMessageBox::Yes) {
7996 for (
ccHObject* entity : selectedEntities)
8009 std::vector<double> eigValues;
8011 covMat, eigVectors, eigValues,
true)) {
8016 GLfloat* rotMat = trans.
data();
8017 for (
unsigned j = 0; j < 3; ++j) {
8024 rotMat[j * 4] =
static_cast<float>(v.x);
8025 rotMat[j * 4 + 1] =
static_cast<float>(v.y);
8026 rotMat[j * 4 + 2] =
static_cast<float>(v.z);
8029 const CCVector3* G = Yk.getGravityCenter();
8039 ccHObjectContext objContext =
8041 static_cast<ccPointCloud*
>(cloud)->applyRigidTransformation(
8057 void MainWindow::doActionComputeDistanceMap() {
8058 static unsigned steps = 128;
8059 static double margin = 0.0;
8060 static bool filterRange =
false;
8061 static double range[2] = {0.0, 1.0};
8065 QDialog dialog(
this);
8066 Ui_DistanceMapDialog ui;
8067 ui.setupUi(&dialog);
8069 ui.stepsSpinBox->setValue(
static_cast<int>(steps));
8070 ui.marginDoubleSpinBox->setValue(margin);
8071 ui.rangeCheckBox->setChecked(filterRange);
8072 ui.minDistDoubleSpinBox->setValue(range[0]);
8073 ui.maxDistDoubleSpinBox->setValue(range[1]);
8075 if (!dialog.exec()) {
8079 steps =
static_cast<unsigned>(ui.stepsSpinBox->value());
8080 margin = ui.marginDoubleSpinBox->value();
8081 filterRange = ui.rangeCheckBox->isChecked();
8082 range[0] = ui.minDistDoubleSpinBox->value();
8083 range[1] = ui.maxDistDoubleSpinBox->value();
8087 pDlg.setAutoClose(
false);
8131 entity->
getName() + tr(
".distance_grid(%1)").arg(steps));
8133 unsigned pointCount = steps * steps * steps;
8134 if (!gridCloud->
reserve(pointCount)) {
8148 for (
unsigned i = 0; i < steps; ++i) {
8149 for (
unsigned j = 0; j < steps; ++j) {
8150 for (
unsigned k = 0; k < steps; ++k) {
8151 ScalarType d = std::sqrt(
static_cast<ScalarType
>(
8155 if (!filterRange || (d >= range[0] && d <= range[1])) {
8169 if (gridCloud->
size() == 0) {
8171 "inside the specified range")
8174 gridCloud =
nullptr;
8185 void MainWindow::doActionComputeDistToBestFitQuadric3D() {
8187 int steps = QInputDialog::getInt(
8188 this, tr(
"Distance to best fit quadric (3D)"),
8189 tr(
"Steps (per dim.)"), 50, 10, 10000, 10, &ok);
8199 if (Yk.compute3DQuadric(Q)) {
8200 const double& a = Q[0];
8201 const double& b = Q[1];
8202 const double& c = Q[2];
8203 const double& e = Q[3];
8204 const double& f = Q[4];
8205 const double& g = Q[5];
8206 const double& l = Q[6];
8207 const double& m = Q[7];
8208 const double& n = Q[8];
8209 const double& d = Q[9];
8212 const CCVector3* G = Yk.getGravityCenter();
8215 "gravity of cloud '%1'!")
8227 if (!newCloud->
reserve(steps * steps * steps)) {
8231 const char defaultSFName[] =
"Dist. to 3D quadric";
8236 tr(
"Couldn't allocate a new scalar field for "
8237 "computing distances! Try to free some memory "
8248 for (
int x = 0; x < steps; ++x) {
8255 for (
int y = 0; y < steps; ++y) {
8262 for (
int z = 0; z < steps; ++z) {
8274 ScalarType
dist =
static_cast<ScalarType
>(
8275 a * Pc.
x * Pc.
x + b * Pc.
y * Pc.
y +
8276 c * Pc.
z * Pc.
z + e * Pc.
x * Pc.
y +
8277 f * Pc.
y * Pc.
z + g * Pc.
x * Pc.
z +
8278 l * Pc.
x + m * Pc.
y + n * Pc.
z + d);
8292 newCloud->
setName(tr(
"Distance map to 3D quadric"));
8297 tr(
"Failed to compute 3D quadric on cloud '%1'")
8305 void MainWindow::doAction4pcsRegister() {
8306 if (QMessageBox::warning(
8307 this, tr(
"Work in progress"),
8308 tr(
"This method is still under development: are you sure you "
8309 "want to use it? (a crash may likely happen)"),
8310 QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
8313 if (m_selectedEntities.size() != 2) {
8330 if (!aDlg.exec())
return;
8333 data = aDlg.getDataObject();
8339 unsigned nbMaxCandidates = aDlg.isNumberOfCandidatesLimited()
8340 ? aDlg.getMaxNumberOfCandidates()
8347 subModel, subData, transform,
8348 static_cast<ScalarType
>(aDlg.getDelta()),
8349 static_cast<ScalarType
>(aDlg.getDelta() / 2),
8351 aDlg.getNbTries(), 5000, &pDlg, nbMaxCandidates)) {
8355 FromCCLibMatrix<double, float>(transform.
R, transform.
T);
8360 "Hint: copy it (CTRL+C) and apply it - or its inverse - on "
8361 "any entity with the 'Edit > Apply transformation' tool"));
8371 transform.
apply(*newDataCloud);
8374 zoomOn(newDataCloud);
8381 if (subModel)
delete subModel;
8382 if (subData)
delete subData;
8393 void MainWindow::doActionMatchScales() {
8395 if (m_selectedEntities.size() < 2)
return;
8403 selectedEntities.push_back(entity);
8406 }
catch (
const std::bad_alloc&) {
8413 msDlg.rmsDifferenceLineEdit->setText(QString::number(
s_msRmsDiff,
'e', 1));
8416 if (!msDlg.exec())
return;
8421 s_msRmsDiff = msDlg.rmsDifferenceLineEdit->text().toDouble();
8435 void MainWindow::doActionMatchBBCenters() {
8437 if (m_selectedEntities.size() < 2)
return;
8445 ccHObject* refEnt = selectedEntities[0];
8448 for (
ccHObject* entity : selectedEntities)
8462 selectedEntities[0]->getName()));
8465 tr(
"Hint: copy it (CTRL+C) and apply it - or its inverse - on "
8466 "any entity with the 'Edit > Apply transformation' tool"));
8484 void MainWindow::doActionRegister() {
8485 if (m_selectedEntities.size() != 2 ||
8503 if (!rDlg.exec())
return;
8506 model = rDlg.getModelEntity();
8507 data = rDlg.getDataEntity();
8509 double minRMSDecrease = rDlg.getMinRMSDecrease();
8510 if (std::isnan(minRMSDecrease)) {
8511 CVLog::Error(tr(
"Invalid minimum RMS decrease value"));
8516 CVLog::Error(tr(
"Minimum RMS decrease value is too small.\n%1 will be "
8517 "used instead (numerical accuracy limit).")
8518 .arg(minRMSDecrease, 0,
'E', 1));
8519 rDlg.setMinRMSDecrease(minRMSDecrease);
8524 parameters.
convType = rDlg.getConvergenceMethod();
8536 bool useDataSFAsWeights = rDlg.useDataSFAsWeights();
8537 bool useModelSFAsWeights = rDlg.useModelSFAsWeights();
8540 rDlg.saveParameters();
8543 double finalError = 0.0;
8544 double finalScale = 1.0;
8545 unsigned finalPointCount = 0;
8548 data, model, transMat, finalScale, finalError, finalPointCount,
8549 parameters, useDataSFAsWeights, useModelSFAsWeights,
this)) {
8550 QString rmsString = tr(
"Final RMS*: %1 (computed on %2 points)")
8552 .arg(finalPointCount);
8553 QString rmsDisclaimerString =
8554 tr(
"(* RMS is potentially weighted, depending on the selected "
8557 CVLog::Print(QString(
"[Register] ") + rmsDisclaimerString);
8559 QStringList summary;
8560 summary << rmsString;
8561 summary << rmsDisclaimerString;
8562 summary <<
"----------------";
8566 summary <<
"Transformation matrix";
8569 summary <<
"----------------";
8571 CVLog::Print(tr(
"[Register] Applied transformation matrix:"));
8574 "Hint: copy it (CTRL+C) and apply it - or its inverse - on "
8575 "any entity with the 'Edit > Apply transformation' tool"));
8579 QString scaleString =
8580 tr(
"Scale: %1 (already integrated in above matrix!)")
8583 summary << scaleString;
8586 summary << tr(
"Scale: fixed (1.0)");
8590 summary <<
"----------------";
8591 QString overlapString =
8592 tr(
"Theoretical overlap: %1%")
8596 summary << overlapString;
8598 summary <<
"----------------";
8599 summary << tr(
"This report has been output to Console (F8)");
8614 QMessageBox::StandardButton
result = QMessageBox::question(
8615 this, tr(
"Registration"),
8616 tr(
"Data mesh vertices are locked (they may be shared "
8617 "with other meshes): Do you wish to clone this mesh "
8618 "to apply transformation?"),
8619 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
8622 if (
result == QMessageBox::Ok) {
8625 newMesh =
static_cast<ccMesh*
>(mesh)->cloneMesh();
8638 tr(
"Failed to clone 'data' mesh! (not enough "
8668 "been updated to match the reference: "
8678 if (QMessageBox::question(
8679 this, tr(
"Drop shift information?"),
8680 tr(
"Aligned entity is shifted but reference "
8681 "cloud is not: drop global shift "
8684 QMessageBox::No) == QMessageBox::Yes) {
8688 tr(
"[ICP] Aligned entity global shift has been "
8689 "reset to match the reference!"));
8701 QMessageBox::information(
this, tr(
"Register info"), summary.join(
"\n"));
8708 void MainWindow::activateRegisterPointPairTool() {
8711 tr(
"Select one or two entities (point cloud or mesh)!"));
8719 entities.reserve(m_selectedEntities.size());
8721 for (
ccHObject* entity : m_selectedEntities) {
8725 entities.push_back(entity);
8729 if (entities.empty()) {
8731 "Select at least one entity (point cloud or mesh)!");
8733 }
else if (entities.size() == 1) {
8734 alignedEntities = entities;
8736 std::vector<int> indexes;
8738 entities, indexes,
this,
8739 tr(
"Select aligned entities"))) {
8745 alignedEntities.reserve(indexes.size());
8746 for (
size_t i = 0; i < indexes.size(); ++i) {
8747 alignedEntities.push_back(entities[indexes[i]]);
8751 assert(indexes.size() <= entities.size());
8752 refEntities.reserve(entities.size() - indexes.size());
8753 for (
size_t i = 0; i < entities.size(); ++i) {
8754 if (std::find(indexes.begin(), indexes.end(), i) ==
8756 refEntities.push_back(entities[i]);
8760 }
catch (
const std::bad_alloc&) {
8765 if (alignedEntities.empty()) {
8778 &MainWindow::deactivateRegisterPointPairTool);
8784 "[PointPairRegistration] Failed to create dedicated 3D view!"));
8790 deactivateRegisterPointPairTool(
false);
8794 if (!m_pprDlg->
start()) {
8805 deactivateRegisterPointPairTool(
false);
8810 void MainWindow::deactivateRegisterPointPairTool(
bool state) {
8811 if (m_pprDlg) m_pprDlg->
clear();
8813 QList<QMdiSubWindow*> subWindowList = m_mdiArea->subWindowList();
8814 if (!subWindowList.isEmpty()) subWindowList.first()->showMaximized();
8821 void MainWindow::doSphericalNeighbourhoodExtractionTest() {
8822 size_t selNum = m_selectedEntities.size();
8823 if (selNum < 1)
return;
8828 if (sphereRadius < 0) {
8834 double val = QInputDialog::getDouble(
this, tr(
"SNE test"), tr(
"Radius:"),
8835 static_cast<double>(sphereRadius),
8836 DBL_MIN, 1.0e9, 8, &ok);
8840 QString sfName = tr(
"Spherical extraction test (%1)").arg(sphereRadius);
8843 pDlg.setAutoClose(
false);
8845 for (
size_t i = 0; i < selNum; ++i) {
8854 if (sfIdx < 0) sfIdx = cloud->
addScalarField(qPrintable(sfName));
8857 "(not enough memory?)")
8878 QElapsedTimer eTimer;
8881 size_t extractedPoints = 0;
8882 unsigned char level =
8885 std::random_device rd;
8886 std::mt19937 gen(rd());
8887 std::uniform_int_distribution<unsigned>
dist(0, cloud->
size() - 1);
8889 const unsigned samples = 1000;
8890 for (
unsigned j = 0; j < samples; ++j) {
8891 unsigned randIndex =
dist(gen);
8894 *cloud->
getPoint(randIndex), sphereRadius, neighbours,
8896 size_t neihgboursCount = neighbours.size();
8897 extractedPoints += neihgboursCount;
8898 for (
size_t k = 0; k < neihgboursCount; ++k)
8900 static_cast<ScalarType
>(sqrt(
8901 neighbours[k].squareDistd)));
8904 tr(
"[SNE_TEST] Mean extraction time = %1 ms (radius = %2, "
8905 "mean(neighbours) = %3)")
8906 .arg(eTimer.elapsed())
8908 .arg(extractedPoints /
static_cast<double>(samples)));
8920 void MainWindow::doCylindricalNeighbourhoodExtractionTest() {
8922 double radius = QInputDialog::getDouble(
this, tr(
"CNE Test"), tr(
"radius"),
8923 0.02, 1.0e-6, 1.0e6, 6, &ok);
8926 double height = QInputDialog::getDouble(
this, tr(
"CNE Test"), tr(
"height"),
8927 0.05, 1.0e-6, 1.0e6, 6, &ok);
8931 const unsigned ptsCount = 1000000;
8932 if (!cloud->
reserve(ptsCount)) {
8940 std::random_device rd;
8941 std::mt19937 gen(rd());
8942 std::uniform_real_distribution<double>
dist(0, 1);
8944 for (
unsigned i = 0; i < ptsCount; ++i) {
8952 static const char DEFAULT_CNE_TEST_TEMP_SF_NAME[] =
"CNE test";
8954 if (sfIdx < 0) sfIdx = cloud->
addScalarField(DEFAULT_CNE_TEST_TEMP_SF_NAME);
8968 QElapsedTimer subTimer;
8970 unsigned long long extractedPoints = 0;
8971 unsigned char level =
8975 const unsigned samples = 1000;
8976 std::random_device rd;
8977 std::mt19937 gen(rd());
8978 std::uniform_real_distribution<PointCoordinateType> distAngle(
8980 std::uniform_int_distribution<unsigned> distIndex(0, ptsCount - 1);
8982 for (
unsigned j = 0; j < samples; ++j) {
8991 unsigned randIndex = distIndex(gen);
9002 size_t neihgboursCount = cn.
neighbours.size();
9003 extractedPoints +=
static_cast<unsigned long long>(neihgboursCount);
9004 for (
size_t k = 0; k < neihgboursCount; ++k) {
9007 static_cast<ScalarType
>(
9012 tr(
"[CNE_TEST] Mean extraction time = %1 ms (radius = %2, "
9013 "height = %3, mean(neighbours) = %4)")
9014 .arg(subTimer.elapsed())
9017 .arg(
static_cast<double>(extractedPoints) / samples));
9034 void MainWindow::doActionCreateCloudFromEntCenters() {
9038 if (!centers->
reserve(
static_cast<unsigned>(selNum))) {
9050 if (cloud ==
nullptr) {
9064 if (centers->
size() == 0) {
9076 void MainWindow::doActionComputeBestICPRmsMatrix() {
9078 std::vector<ccPointCloud*> clouds;
9083 clouds.push_back(cloud);
9086 }
catch (
const std::bad_alloc&) {
9091 size_t cloudCount = clouds.size();
9092 if (cloudCount < 2) {
9098 std::vector<double> rmsMatrix;
9099 std::vector<ccGLMatrix> matrices;
9100 std::vector<std::pair<double, double>> matrixAngles;
9102 rmsMatrix.resize(cloudCount * cloudCount, 0);
9105 static const double angularStep_deg = 45.0;
9106 unsigned phiSteps =
static_cast<unsigned>(360.0 / angularStep_deg);
9108 std::abs(360.0 - phiSteps * angularStep_deg)));
9109 unsigned thetaSteps =
static_cast<unsigned>(180.0 / angularStep_deg);
9111 std::abs(180.0 - thetaSteps * angularStep_deg)));
9112 unsigned rotCount = phiSteps * (thetaSteps - 1) + 2;
9113 matrices.reserve(rotCount);
9114 matrixAngles.reserve(rotCount);
9116 for (
unsigned j = 0; j <= thetaSteps; ++j) {
9118 double theta_deg = j * angularStep_deg - 90.0;
9119 for (
unsigned i = 0; i < phiSteps; ++i) {
9120 double phi_deg = i * angularStep_deg;
9128 matrices.push_back(trans);
9129 matrixAngles.push_back(
9130 std::pair<double, double>(phi_deg, theta_deg));
9133 if (j == 0 || j == thetaSteps)
break;
9136 }
catch (
const std::bad_alloc&) {
9144 pDlg.setMethodTitle(tr(
"Testing all possible positions"));
9145 pDlg.setInfo(tr(
"%1 clouds and %2 positions")
9147 .arg(matrices.size()));
9150 static_cast<unsigned>(((cloudCount * (cloudCount - 1)) / 2) *
9153 QApplication::processEvents();
9156 #ifdef TEST_GENERATION
9158 testSphere->
reserve(matrices.size());
9161 for (
size_t i = 0; i < cloudCount - 1; ++i) {
9165 for (
size_t j = i + 1; j < cloudCount; ++j) {
9174 #ifndef TEST_GENERATION
9175 double minRMS = -1.0;
9176 int bestMatrixIndex = -1;
9179 for (
size_t k = 0; k < matrices.size(); ++k) {
9187 transFromZeroToA * matrices[k] * transBToZero;
9190 #ifndef TEST_GENERATION
9191 double finalRMS = 0.0;
9192 unsigned finalPointCount = 0;
9200 params.minRMSDecrease = 1.0e-6;
9204 A, 0, B,
params, registerTrans, finalRMS,
9210 if (bestB)
delete bestB;
9212 tr(
"An error occurred while performing ICP!"));
9216 if (minRMS < 0 || finalRMS < minRMS) {
9218 bestMatrixIndex =
static_cast<int>(k);
9231 matrices[k].apply(Y);
9241 #ifndef TEST_GENERATION
9242 if (bestMatrixIndex >= 0) {
9245 new ccHObject(tr(
"Best case #%1 / #%2 - RMS = %3")
9253 tr(
"[doActionComputeBestICPRmsMatrix] Comparison "
9254 "#%1 / #%2: min RMS = %3 (phi = %4 / theta = %5 "
9259 .arg(matrixAngles[bestMatrixIndex].first)
9260 .arg(matrixAngles[bestMatrixIndex].second));
9264 "Comparison #%1 / #%2: INVALID")
9269 rmsMatrix[i * cloudCount + j] = minRMS;
9280 #ifdef TEST_GENERATION
9290 QString outputFilename = QFileDialog::getSaveFileName(
9291 this, tr(
"Select output file"), currentPath,
"*.csv",
nullptr,
9294 if (outputFilename.isEmpty())
return;
9296 QFile fp(outputFilename);
9297 if (fp.open(QFile::Text | QFile::WriteOnly)) {
9298 QTextStream stream(&fp);
9310 for (
size_t j = 0; j < cloudCount; ++j) {
9311 stream << clouds[j]->getName();
9313 for (
size_t i = 0; i < cloudCount; ++i) {
9314 stream << rmsMatrix[j * cloudCount + i];
9320 CVLog::Print(tr(
"[doActionComputeBestICPRmsMatrix] Job done"));
9328 void MainWindow::doActionFindBiggestInnerRectangle() {
9338 int dim = QInputDialog::getInt(
this, tr(
"Dimension"),
9339 tr(
"Orthogonal dim (X=0 / Y=1 / Z=2)"),
9346 static_cast<unsigned char>(dim));
9362 QString xAxisLabel) {
9364 hDlg->setAttribute(Qt::WA_DeleteOnClose,
true);
9365 hDlg->setWindowTitle(
"Histogram");
9378 void MainWindow::showSelectedEntitiesHistogram() {
9388 hDlg->setAttribute(Qt::WA_DeleteOnClose,
true);
9389 hDlg->setWindowTitle(
9390 tr(
"Histogram [%1]").arg(cloud->
getName()));
9394 unsigned numberOfPoints = cloud->
size();
9395 unsigned numberOfClasses =
static_cast<unsigned>(
9396 sqrt(
static_cast<double>(numberOfPoints)));
9398 numberOfClasses &= (~3);
9399 numberOfClasses = std::max<unsigned>(4, numberOfClasses);
9400 numberOfClasses = std::min<unsigned>(256, numberOfClasses);
9402 histogram->
setTitle(tr(
"%1 (%2 values) ")
9404 .arg(numberOfPoints));
9406 histogram->
fromSF(sf, numberOfClasses,
true,
9407 showNaNValuesInGrey);
9417 void MainWindow::doActionComputeStatParams() {
9421 void MainWindow::doActionSFGradient() {
9430 void MainWindow::doActionOpenColorScalesManager() {
9434 if (cseDlg.exec()) {
9442 void MainWindow::doActionRGBGaussianFilter() {
9444 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::GAUSSIAN;
9453 void MainWindow::doActionRGBBilateralFilter() {
9455 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::BILATERAL;
9464 void MainWindow::doActionRGBMeanFilter() {
9466 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::MEAN;
9475 void MainWindow::doActionRGBMedianFilter() {
9477 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::MEDIAN;
9486 void MainWindow::doActionSFGaussianFilter() {
9488 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::GAUSSIAN;
9497 void MainWindow::doActionSFBilateralFilter() {
9499 filterParams.
filterType = ccPointCloud::RGB_FILTER_TYPES::BILATERAL;
9508 void MainWindow::doActionFilterByLabel() {
9514 ccHObject* entity = m_selectedEntities[0];
9521 if (!m_filterLabelTool) {
9534 "[MainWindow::doSemanticSegmentation] Initialization failed!");
9542 if (m_filterLabelTool->
start()) {
9552 void MainWindow::doActionFilterByValue() {
9553 typedef std::pair<ccHObject*, ccPointCloud*> EntityAndVerticesType;
9554 std::vector<EntityAndVerticesType> toFilter;
9563 toFilter.emplace_back(entity, pc);
9566 tr(
"Entity [%1] has no active scalar field !")
9572 if (toFilter.empty())
return;
9574 double minVald = 0.0;
9575 double maxVald = 1.0;
9580 for (
size_t i = 0; i < toFilter.size(); ++i) {
9582 toFilter[i].second->getCurrentDisplayedScalarField();
9598 if (!dlg.exec())
return;
9603 ScalarType minVal =
static_cast<ScalarType
>(dlg.minDoubleSpinBox->value());
9604 ScalarType maxVal =
static_cast<ScalarType
>(dlg.maxDoubleSpinBox->value());
9608 for (
auto& item : toFilter) {
9613 assert(outSfIdx >= 0);
9663 results.push_back(resultInside);
9665 if (resultOutside) {
9670 results.push_back(resultOutside);
9675 if (!results.empty()) {
9677 tr(
"Previously selected entities (sources) have been hidden!"));
9684 void MainWindow::doActionScalarFieldFromColor() {
9691 void MainWindow::doActionSFConvertToRGB() {
9698 void MainWindow::doActionSFConvertToRandomRGB() {
9705 void MainWindow::doActionToggleActiveSFColorScale() {
9706 doApplyActiveSFAction(0);
9709 void MainWindow::doActionShowActiveSFPrevious() { doApplyActiveSFAction(1); }
9711 void MainWindow::doActionShowActiveSFNext() { doApplyActiveSFAction(2); }
9713 void MainWindow::doApplyActiveSFAction(
int action) {
9722 bool lockedVertices;
9761 void MainWindow::doActionRenameSF() {
9768 void MainWindow::doActionAddConstantSF() {
9777 bool lockedVertices;
9787 QString defaultName = tr(
"Constant");
9791 defaultName = tr(
"Constant #%1").arg(++trys);
9796 QString sfName = QInputDialog::getText(
this, tr(
"New SF name"),
9797 tr(
"SF name (must be unique)"),
9798 QLineEdit::Normal, defaultName, &ok);
9800 if (sfName.isNull()) {
9809 ScalarType sfValue =
static_cast<ScalarType
>(
9810 QInputDialog::getDouble(
this, tr(
"Add constant value"), tr(
"value"),
9815 if (sfIdx < 0) sfIdx = cloud->
addScalarField(qPrintable(sfName));
9832 CVLog::Print(tr(
"New scalar field added to %1 (constant value: %2)")
9837 void MainWindow::doActionImportSFFromFile() {
9844 bool lockedVertices;
9855 QString currentPath =
9859 QString filters =
"*.labels";
9860 QString selectedFilter = filters;
9861 QString selectedFilename =
9862 QFileDialog::getOpenFileName(
this, tr(
"import sf value from file"),
9863 currentPath, filters, &selectedFilter);
9865 if (selectedFilename.isEmpty()) {
9871 currentPath = QFileInfo(selectedFilename).absolutePath();
9876 std::string scalarName =
9879 std::vector<size_t> scalars;
9884 if (scalars.size() != cloud->
size()) {
9885 CVLog::Warning(
"scalar files are probably corrupted and drop it!");
9889 std::vector<std::vector<ScalarType>> scalarsVector;
9890 std::vector<std::vector<size_t>> tempScalarsvector;
9891 tempScalarsvector.push_back(scalars);
9892 ccEntityAction::ConvertToScalarType<size_t>(tempScalarsvector,
9895 scalarName.c_str())) {
9897 "[MainWindow::doActionImportSFFromFile] import sf failed!");
9899 CVLog::Print(tr(
"[MainWindow::doActionImportSFFromFile] "
9900 "Import sf from file %1 successfully!")
9906 void MainWindow::doActionAddIdField() {
9913 void MainWindow::doActionExportCoordToSF() {
9922 void MainWindow::doActionSetSFAsCoord() {
9929 void MainWindow::doActionInterpolateScalarFields() {
9936 void MainWindow::doActionScalarFieldArithmetic() {
9943 void MainWindow::doRemoveDuplicatePoints() {
9949 double minDistanceBetweenPoints =
9956 minDistanceBetweenPoints = QInputDialog::getDouble(
9957 this, tr(
"Remove duplicate points"),
9958 tr(
"Min distance between points:"), minDistanceBetweenPoints, 0,
9965 minDistanceBetweenPoints);
9967 static const char DEFAULT_DUPLICATE_TEMP_SF_NAME[] =
"DuplicateFlags";
9970 pDlg.setAutoClose(
false);
9978 for (
ccHObject* entity : selectedEntities) {
9983 DEFAULT_DUPLICATE_TEMP_SF_NAME);
9990 tr(
"Couldn't create temporary scalar field! Not enough "
9999 cloud, minDistanceBetweenPoints, &pDlg,
10005 unsigned duplicateCount = 0;
10008 for (
unsigned j = 0; j < flagSF->
currentSize(); ++j) {
10015 if (duplicateCount == 0) {
10020 tr(
"Cloud '%1' has %2 duplicate point(s)")
10022 .arg(duplicateCount));
10026 if (filteredCloud) {
10028 DEFAULT_DUPLICATE_TEMP_SF_NAME);
10029 assert(sfIdx2 >= 0);
10032 tr(
"%1.clean").arg(cloud->
getName()));
10044 tr(
"An error occurred! (Not enough memory?)"));
10053 tr(
"Previously selected entities (sources) have been hidden!"));
10056 void MainWindow::doActionSubsample() {
10058 std::vector<ccPointCloud*> clouds;
10059 unsigned maxPointCount = 0;
10060 double maxCloudRadius = 0;
10067 clouds.push_back(cloud);
10070 std::max<unsigned>(maxPointCount, cloud->
size());
10071 maxCloudRadius = std::max<double>(
10081 sfMax < sf->getMax())
10088 if (clouds.empty()) {
10097 if (hasValidSF) sDlg.enableSFModulation(sfMin, sfMax);
10098 if (!sDlg.exec())
return;
10104 pDlg.setAutoClose(
false);
10106 pDlg.setMethodTitle(tr(
"Subsampling"));
10108 bool errors =
false;
10110 QElapsedTimer eTimer;
10113 for (
size_t i = 0; i < clouds.size(); ++i) {
10116 sDlg.getSampledCloud(cloud, &pDlg);
10117 if (!sampledCloud) {
10119 tr(
"[Subsampling] Failed to subsample cloud '%1'!")
10129 delete sampledCloud;
10132 if (newPointCloud) {
10134 QString(
".subsampled"));
10142 resultingClouds.push_back(newPointCloud);
10146 tr(
"[Subsampling] Not enough memory: colors, "
10147 "normals or scalar fields may be missing!"));
10157 .arg(eTimer.elapsed() / 1000.0, 7));
10169 void MainWindow::doActionEditGlobalShiftAndScale() {
10171 std::vector<std::pair<ccShiftedObject*, ccHObject*>> shiftedEntities;
10178 double scale = 1.0;
10180 bool uniqueShift =
true;
10181 bool uniqueScale =
true;
10184 CCVector3d globalBBmin(0, 0, 0), globalBBmax(0, 0, 0);
10187 bool lockedVertices;
10194 if (lockedVertices) {
10199 ->getAssociatedCloud();
10218 if (shiftedEntities.empty()) {
10241 shiftedEntities.emplace_back(shifted, entity);
10245 Dg = (globalBBmax - globalBBmin).norm();
10250 if (!uniqueShift) shift = Pl - Pg;
10251 if (!uniqueScale) scale = Dg / Dl;
10254 if (shiftedEntities.empty()) {
10259 sasDlg.showApplyAllButton(shiftedEntities.size() > 1);
10260 sasDlg.showApplyButton(shiftedEntities.size() == 1);
10261 sasDlg.showNoButton(
false);
10262 sasDlg.setShiftFieldsPrecision(6);
10264 int index = sasDlg.addShiftInfo(
10266 sasDlg.setCurrentProfile(index);
10268 std::vector<ecvGlobalShiftManager::ShiftInfo> lastInfos;
10270 sasDlg.addShiftInfo(lastInfos);
10273 sasDlg.addFileInfo();
10275 if (!sasDlg.exec())
return;
10277 shift = sasDlg.getShift();
10278 scale = sasDlg.getScale();
10279 bool preserveGlobalPos = sasDlg.keepGlobalPos();
10281 CVLog::Print(tr(
"[Global Shift/Scale] New shift: (%1, %2, %3)")
10285 CVLog::Print(tr(
"[Global Shift/Scale] New scale: %1").arg(scale));
10289 for (
auto& entity : shiftedEntities) {
10292 if (preserveGlobalPos) {
10309 transMat.
scale(
static_cast<float>(scaleCoef));
10318 tr(
"[Global Shift/Scale] To preserve its original "
10319 "position, the entity '%1' has been translated "
10320 "of (%2,%3,%4) and rescaled of a factor %5")
10338 void MainWindow::activateDistanceMode() {
10339 #ifdef USE_PCL_BACKEND
10340 doActionMeasurementMode(
10341 ecvGenericMeasurementTools::MeasurementType::DISTANCE_WIDGET);
10344 "[MainWindow] please use pcl as backend and then try again!");
10349 void MainWindow::activateProtractorMode() {
10350 #ifdef USE_PCL_BACKEND
10351 doActionMeasurementMode(
10352 ecvGenericMeasurementTools::MeasurementType::PROTRACTOR_WIDGET);
10355 "[MainWindow] please use pcl as backend and then try again!");
10360 void MainWindow::activateContourMode() {
10361 #ifdef USE_PCL_BACKEND
10362 doActionMeasurementMode(
10363 ecvGenericMeasurementTools::MeasurementType::CONTOUR_WIDGET);
10366 "[MainWindow] please use pcl as backend and then try again!");
10371 void MainWindow::doActionMeasurementMode(
int mode) {
10378 if (!m_measurementTool) {
10387 if (!outs.empty()) {
10389 for (ccHObject* entity : selectedEntities) {
10390 entity->setEnabled(false);
10403 #ifdef USE_PCL_BACKEND
10406 CVLog::Error(
"[MainWindow] No visualizer available!");
10414 m_measurementTool->setMeasurementTool(measurementTool);
10417 for (
ccHObject* entity : selectedEntities) {
10418 if (m_measurementTool->addAssociatedEntity(entity)) {
10421 m_ccRoot->unselectEntity(entity);
10425 if (m_measurementTool->getNumberOfAssociatedEntity() == 0) {
10426 CVLog::Warning(
"[MainWindow] No valid entities for measurement!");
10430 if (m_measurementTool->start()) {
10431 updateOverlayDialogsPlacement();
10440 "[MainWindow] please use pcl as backend and then try again!");
10445 void MainWindow::activateClippingMode() {
10446 #ifdef USE_PCL_BACKEND
10447 doActionFilterMode(ecvGenericFiltersTool::FilterType::CLIP_FILTER);
10450 "[MainWindow] please use pcl as backend and then try again!");
10455 void MainWindow::activateSliceMode() {
10456 #ifdef USE_PCL_BACKEND
10457 doActionFilterMode(ecvGenericFiltersTool::FilterType::SLICE_FILTER);
10460 "[MainWindow] please use pcl as backend and then try again!");
10465 void MainWindow::activateProbeMode() {
10466 #ifdef USE_PCL_BACKEND
10467 doActionFilterMode(ecvGenericFiltersTool::FilterType::PROBE_FILTER);
10470 "[MainWindow] please use pcl as backend and then try again!");
10475 void MainWindow::activateDecimateMode() {
10476 #ifdef USE_PCL_BACKEND
10477 doActionFilterMode(ecvGenericFiltersTool::FilterType::DECIMATE_FILTER);
10480 "[MainWindow] please use pcl as backend and then try again!");
10485 void MainWindow::activateIsoSurfaceMode() {
10486 #ifdef USE_PCL_BACKEND
10487 doActionFilterMode(ecvGenericFiltersTool::FilterType::ISOSURFACE_FILTER);
10490 "[MainWindow] please use pcl as backend and then try again!");
10495 void MainWindow::activateThresholdMode() {
10496 #ifdef USE_PCL_BACKEND
10497 doActionFilterMode(ecvGenericFiltersTool::FilterType::THRESHOLD_FILTER);
10500 "[MainWindow] please use pcl as backend and then try again!");
10505 void MainWindow::activateSmoothMode() {
10506 #ifdef USE_PCL_BACKEND
10507 doActionFilterMode(ecvGenericFiltersTool::FilterType::SMOOTH_FILTER);
10510 "[MainWindow] please use pcl as backend and then try again!");
10515 void MainWindow::activateGlyphMode() {
10516 #ifdef USE_PCL_BACKEND
10517 doActionFilterMode(ecvGenericFiltersTool::FilterType::GLYPH_FILTER);
10520 "[MainWindow] please use pcl as backend and then try again!");
10525 void MainWindow::activateStreamlineMode() {
10526 #ifdef USE_PCL_BACKEND
10527 doActionFilterMode(ecvGenericFiltersTool::FilterType::STREAMLINE_FILTER);
10530 "[MainWindow] please use pcl as backend and then try again!");
10535 void MainWindow::doActionFilterMode(
int mode) {
10538 #ifdef USE_PCL_BACKEND
10544 "[MainWindow] please use pcl as backend and then try again!");
10551 if (!m_filterTool) {
10559 if (!outs.empty()) {
10561 for (ccHObject* entity : selectedEntities) {
10562 entity->setEnabled(false);
10574 m_filterTool->setFilter(filter);
10577 for (
ccHObject* entity : selectedEntities) {
10578 if (m_filterTool->addAssociatedEntity(entity)) {
10581 m_ccRoot->unselectEntity(entity);
10585 if (m_filterTool->getNumberOfAssociatedEntity() == 0) {
10586 m_filterTool->close();
10591 m_ui->ViewToolBar->setDisabled(
false);
10593 if (m_filterTool->start()) {
10594 registerOverlayDialog(m_filterTool, Qt::TopRightCorner);
10596 updateOverlayDialogsPlacement();
10602 void MainWindow::doBoxAnnotation() {
10603 #ifdef USE_PCL_BACKEND
10604 doAnnotations(ecvGenericAnnotationTool::AnnotationMode::BOUNDINGBOX);
10607 "[MainWindow] please use pcl as backend and then try again!");
10612 void MainWindow::doSemanticAnnotation() {
10613 #ifdef USE_PCL_BACKEND
10614 doAnnotations(ecvGenericAnnotationTool::AnnotationMode::SEMANTICS);
10617 "[MainWindow] please use pcl as backend and then try again!");
10622 void MainWindow::doAnnotations(
int mode) {
10628 ccHObject* ent = m_selectedEntities[0];
10634 #ifdef USE_PCL_BACKEND
10635 PclAnnotationTool* annoTools =
new PclAnnotationTool(
10648 "[MainWindow] please use pcl as backend and then try again!");
10654 CVLog::Warning(
"[MainWindow::doAnnotations] Initialization failed!");
10665 m_annoTool->close();
10670 m_ui->ViewToolBar->setDisabled(
false);
10672 if (m_annoTool->
start()) {
10681 void MainWindow::doSemanticSegmentation() {
10682 #ifdef USE_PYTHON_MODULE
10686 m_dssTool =
new ecvDeepSemanticSegmentationTool(
this);
10688 &MainWindow::deactivateSemanticSegmentation);
10694 "[MainWindow::doSemanticSegmentation] Initialization failed!");
10699 if (m_dssTool->addEntity(ent)) {
10706 if (m_dssTool->getNumberOfValidEntities() == 0) {
10707 m_dssTool->close();
10712 if (m_dssTool->start()) {
10720 CVLog::Warning(
"python interface library has not been compiled!");
10725 void MainWindow::deactivateSemanticSegmentation(
bool state) {
10726 #ifdef USE_PYTHON_MODULE
10727 if (m_dssTool && state) {
10729 m_dssTool->getSegmentations(
result);
10735 segmentedEntities.push_back(obj->
getChild(i));
10741 tr(
"segmentation info has been exported to sf field!"));
10751 void MainWindow::doActionDBScanCluster() {
10762 clouds.push_back(ent);
10774 void MainWindow::doActionPlaneSegmentation() {
10785 clouds.push_back(ent);
10790 for (
size_t i = 0; i < entities.size(); ++i) {
10801 void MainWindow::activateSegmentationMode() {
10807 &MainWindow::deactivateSegmentationMode);
10824 m_ui->ViewToolBar->setDisabled(
false);
10826 if (!m_gsTool->
start()) {
10827 deactivateSegmentationMode(
false);
10831 if (!perspectiveEnabled)
10834 m_lastViewMode = VIEWMODE::ORTHOGONAL;
10836 m_lastViewMode = VIEWMODE::PERSPECTIVE;
10841 void MainWindow::deactivateSegmentationMode(
bool state) {
10842 bool deleteHiddenParts =
false;
10851 std::unordered_set<ccGenericPointCloud*> verticesToReset;
10853 QSet<ccHObject*>& segmentedEntities = m_gsTool->
entities();
10854 for (QSet<ccHObject*>::iterator p = segmentedEntities.begin();
10855 p != segmentedEntities.end();) {
10874 for (ccHObject::Container::iterator it = labels.begin();
10875 it != labels.end(); ++it) {
10884 bool removeLabel =
false;
10885 for (
unsigned i = 0; i < label->
size(); ++i) {
10887 removeLabel =
true;
10892 if (removeLabel && label->
getParent()) {
10894 tr(
"[Segmentation] Label %1 depends on "
10895 "cloud %2 and will be removed")
10899 ccHObjectContext objContext =
10914 ccHObjectContext objContext =
10918 ccHObject* segmentationResult =
nullptr;
10919 bool deleteOriginalEntity = deleteHiddenParts;
10925 !deleteHiddenParts);
10926 if (segmentedCloud && segmentedCloud->
size() == 0) {
10927 delete segmentationResult;
10928 segmentationResult =
nullptr;
10930 segmentationResult = segmentedCloud;
10933 deleteOriginalEntity |= (genCloud->
size() == 0);
10938 segmentationResult =
10941 !deleteHiddenParts);
10943 segmentationResult =
10946 !deleteHiddenParts);
10949 deleteOriginalEntity |=
10954 if (segmentationResult) {
10958 if (!deleteHiddenParts) {
10961 if (!deleteOriginalEntity) {
10963 QString(
".remaining"));
10982 if (deleteHiddenParts &&
10984 verticesToReset.insert(
10988 assert(deleteOriginalEntity);
10995 objContext.parent =
10996 static_cast<ccSubMesh*
>(segmentationResult)
10997 ->getAssociatedMesh();
11001 while (objContext.parent &&
11003 objContext.parent->isKindOf(
11005 objContext.parent = objContext.parent->
getParent();
11009 if (objContext.parent) {
11011 segmentationResult);
11017 if (!firstResult) {
11018 firstResult = segmentationResult;
11020 }
else if (!deleteOriginalEntity) {
11026 if (deleteOriginalEntity) {
11027 p = segmentedEntities.erase(p);
11044 if (firstResult && m_ccRoot) {
11050 if (m_lastViewMode == VIEWMODE::ORTHOGONAL) {
11052 }
else if (m_lastViewMode == VIEWMODE::PERSPECTIVE) {
11085 void MainWindow::createComponentsClouds(
11088 unsigned minPointsPerComponent,
11090 bool selectComponents,
11091 bool sortBysize ) {
11092 if (!cloud || components.empty())
return;
11094 std::vector<ComponentIndexAndSize> sortedIndexes;
11095 std::vector<ComponentIndexAndSize>* _sortedIndexes =
nullptr;
11098 sortedIndexes.reserve(components.size());
11099 }
catch (
const std::bad_alloc&) {
11101 tr(
"[CreateComponentsClouds] Not enough memory to sort "
11102 "components by size!"));
11103 sortBysize =
false;
11108 unsigned compCount =
static_cast<unsigned>(components.size());
11109 for (
unsigned i = 0; i < compCount; ++i) {
11110 sortedIndexes.emplace_back(i, components[i]->
size());
11113 ParallelSort(sortedIndexes.begin(), sortedIndexes.end(),
11116 _sortedIndexes = &sortedIndexes;
11131 for (
size_t i = 0; i < components.size(); ++i) {
11133 _sortedIndexes ? components[_sortedIndexes->at(i).index]
11137 if (compIndexes->
size() >= minPointsPerComponent) {
11144 if (randomColors) {
11148 compCloud->
showSF(
false);
11163 if (selectComponents && m_ccRoot)
11167 tr(
"[createComponentsClouds] Failed to create "
11168 "component #%1! (not enough memory)")
11173 delete compIndexes;
11174 compIndexes =
nullptr;
11177 components.
clear();
11181 "No component was created! Check the minimum size...");
11187 "were created from cloud '%2'")
11196 tr(
"[createComponentsClouds] Original cloud has been "
11197 "automatically hidden"));
11202 void MainWindow::doActionLabelConnectedComponents() {
11204 std::vector<ccGenericPointCloud*> clouds;
11212 size_t count = clouds.size();
11213 if (
count == 0)
return;
11216 if (
count == 1) dlg.octreeLevelSpinBox->setCloud(clouds.front());
11217 if (!dlg.exec())
return;
11220 unsigned minComponentSize =
11221 static_cast<unsigned>(
std::max(0, dlg.getMinPointsNb()));
11222 bool randColors = dlg.randomColors();
11225 pDlg.setAutoClose(
false);
11243 tr(
"Couldn't compute octree for cloud '%s'!")
11258 tr(
"Couldn't allocate a new scalar field for computing "
11259 "ECV labels! Try to free some memory ..."));
11269 false, &pDlg, theOctree.data());
11271 if (componentCount >= 0) {
11275 int realComponentCount = 0;
11277 for (
size_t i = 0; i < components.size(); ++i) {
11278 if (components[i]->
size() >= minComponentSize) {
11279 ++realComponentCount;
11284 if (realComponentCount > 500) {
11286 if (QMessageBox::warning(
11287 this, tr(
"Many components"),
11288 tr(
"Do you really expect up to %1 "
11289 "components?\n(this may take a lot of time "
11290 "to process and display)")
11291 .arg(realComponentCount),
11293 QMessageBox::No) == QMessageBox::No) {
11310 extractConnectedComponents(cloud, components)) {
11312 "Something went wrong while "
11313 "extracting CCs from cloud %1...")
11318 tr(
"[doActionLabelConnectedComponents] Something went "
11319 "wrong while extracting CCs from cloud %1...")
11328 if (!components.empty()) {
11329 createComponentsClouds(cloud, components, minComponentSize,
11338 void MainWindow::doActionKMeans()
11343 void MainWindow::doActionFrontPropagation()
11348 void MainWindow::doActionCloudCloudDist() {
11361 m_selectedEntities[1], tr(
"Reference"),
this);
11362 if (!dlg.exec())
return;
11370 if (m_compDlg)
delete m_compDlg;
11373 connect(m_compDlg, &QDialog::finished,
this,
11374 &MainWindow::deactivateComparisonMode);
11381 void MainWindow::doActionCloudMeshDist() {
11387 bool isMesh[2] = {
false,
false};
11388 unsigned meshNum = 0;
11389 unsigned cloudNum = 0;
11390 for (
unsigned i = 0; i < 2; ++i) {
11399 if (meshNum == 0) {
11402 }
else if (meshNum + cloudNum < 2) {
11410 if (meshNum == 1) {
11411 compEnt = m_selectedEntities[isMesh[0] ? 1 : 0];
11413 m_selectedEntities[isMesh[0] ? 0 : 1]);
11416 m_selectedEntities[1], tr(
"Reference"),
this);
11417 if (!dlg.exec())
return;
11419 compEnt = dlg.getFirstEntity();
11424 if (m_compDlg)
delete m_compDlg;
11427 connect(m_compDlg, &QDialog::finished,
this,
11428 &MainWindow::deactivateComparisonMode);
11434 void MainWindow::doActionCloudPrimitiveDist() {
11435 bool foundPrimitive =
false;
11439 const char* errString =
11440 "[Compute Primitive Distances] Cloud to %s failed, error code = "
11453 if (foundPrimitive) {
11455 "[Compute Primitive Distances] Select only a "
11456 "single Plane/Box/Sphere/Cylinder/Cone/Polyline "
11460 foundPrimitive =
true;
11461 refEntity = m_selectedEntities[i];
11465 clouds.push_back(m_selectedEntities[i]);
11469 if (!foundPrimitive) {
11471 "[Compute Primitive Distances] Select at least one "
11472 "Plane/Box/Sphere/Cylinder/Cone/Disc/Polyline Primitive!");
11475 if (clouds.size() <= 0) {
11477 "[Compute Primitive Distances] Select at least one cloud!");
11483 pDD.treatPlanesAsBoundedCheckBox->setUpdatesEnabled(
true);
11485 bool execute =
true;
11487 execute = pDD.exec();
11491 bool flippedNormals = signedDist && pDD.flipNormals();
11492 bool treatPlanesAsBounded = pDD.treatPlanesAsBounded();
11493 for (
auto& cloud : clouds) {
11503 QString(
"[Compute Primitive Distances] [Cloud: %1] "
11504 "Couldn't allocate a new scalar field for "
11505 "computing distances! Try to free some "
11516 switch (entityType) {
11519 computeCloud2SphereEquation(
11530 if (treatPlanesAsBounded) {
11535 computeCloud2RectangleEquation(
11547 computeCloud2PlaneEquation(
11560 computeCloud2CylinderEquation(
11563 ->getBottomCenter(),
11567 ->getBottomRadius(),
11574 computeCloud2ConeEquation(
11576 static_cast<ccCone*
>(refEntity)
11577 ->getLargeCenter(),
11578 static_cast<ccCone*
>(refEntity)
11579 ->getSmallCenter(),
11580 static_cast<ccCone*
>(refEntity)
11581 ->getLargeRadius(),
11582 static_cast<ccCone*
>(refEntity)
11583 ->getSmallRadius(),
11592 glTransform.
data(),
true);
11595 computeCloud2BoxEquation(
11597 static_cast<ccBox*
>(refEntity)
11599 rotationTransform, boxCenter,
11609 computeCloud2DiscEquation(
11612 static_cast<ccDisc*
>(refEntity)
11614 rotationTransform, signedDist)))
11619 signedDist =
false;
11620 flippedNormals =
false;
11631 "[Compute Primitive Distances] Unsupported "
11642 if (flippedNormals) {
11645 sfName += QString(
"[-]");
11662 ScalarType variance;
11666 "[Compute Primitive Distances] [Primitive: %s] [Cloud: "
11667 "%s] [%s] Mean distance = %f / std deviation = %f",
11668 qPrintable(refEntity->
getName()),
11669 qPrintable(compEnt->
getName()), qPrintable(sfName),
11670 mean, sqrt(variance));
11673 compEnt->
showSF(sfIdx >= 0);
11682 void MainWindow::deactivateComparisonMode(
int result) {
11692 if (m_compDlg &&
result == QDialog::Accepted && m_ccRoot) {
11704 void MainWindow::doActionExportPlaneInfo() {
11708 if (selectedEntities.size() == 1 &&
11711 selectedEntities.front()->filterChildren(planes,
true,
CV_TYPES::PLANE,
11714 for (
ccHObject* ent : selectedEntities) {
11717 planes.push_back(
static_cast<ccPlane*
>(ent));
11722 if (planes.empty()) {
11728 QString currentPath =
11733 QString outputFilename = QFileDialog::getSaveFileName(
11734 this, tr(
"Select output file"), currentPath,
"*.csv",
nullptr,
11737 if (outputFilename.isEmpty()) {
11742 QFile csvFile(outputFilename);
11743 if (!csvFile.open(QFile::WriteOnly | QFile::Text)) {
11745 "Failed to open file for writing! (check file permissions)"));
11751 QFileInfo(outputFilename).absolutePath());
11754 QTextStream csvStream(&csvFile);
11755 csvStream <<
"Name,";
11756 csvStream <<
"Width,";
11757 csvStream <<
"Height,";
11758 csvStream <<
"Cx,";
11759 csvStream <<
"Cy,";
11760 csvStream <<
"Cz,";
11761 csvStream <<
"Nx,";
11762 csvStream <<
"Ny,";
11763 csvStream <<
"Nz,";
11764 csvStream <<
"Dip,";
11765 csvStream <<
"Dip dir,";
11768 QChar separator(
',');
11779 csvStream << plane->
getName() << separator;
11780 csvStream << plane->
getXWidth() << separator;
11781 csvStream << plane->
getYWidth() << separator;
11782 csvStream << C.
x << separator;
11783 csvStream << C.
y << separator;
11784 csvStream << C.
z << separator;
11785 csvStream << N.
x << separator;
11786 csvStream << N.
y << separator;
11787 csvStream << N.
z << separator;
11788 csvStream << dip_deg << separator;
11789 csvStream << dipDir_deg << separator;
11794 .arg(outputFilename)
11795 .arg(planes.size()));
11799 void MainWindow::doActionExportCloudInfo() {
11804 if (selectedEntities.size() == 1 &&
11807 selectedEntities.front()->filterChildren(clouds,
true,
11810 for (
ccHObject* entity : selectedEntities) {
11813 clouds.push_back(cloud);
11818 if (clouds.empty()) {
11824 QString currentPath =
11829 QString outputFilename = QFileDialog::getSaveFileName(
11830 this, tr(
"Select output file"), currentPath,
"*.csv",
nullptr,
11832 if (outputFilename.isEmpty()) {
11837 QFile csvFile(outputFilename);
11838 if (!csvFile.open(QFile::WriteOnly | QFile::Text)) {
11840 "Failed to open file for writing! (check file permissions)"));
11846 QFileInfo(outputFilename).absolutePath());
11849 unsigned maxSFCount = 0;
11851 maxSFCount = std::max<unsigned>(
11853 static_cast<ccPointCloud*
>(entity)->getNumberOfScalarFields());
11857 QTextStream csvStream(&csvFile);
11858 csvStream <<
"Name,";
11859 csvStream <<
"Points,";
11860 csvStream <<
"meanX,";
11861 csvStream <<
"meanY,";
11862 csvStream <<
"meanZ,";
11864 for (
unsigned i = 0; i < maxSFCount; ++i) {
11865 QString sfIndex = QString(
"SF#%1").arg(i + 1);
11866 csvStream << sfIndex <<
" name,";
11867 csvStream << sfIndex <<
" valid values,";
11868 csvStream << sfIndex <<
" mean,";
11869 csvStream << sfIndex <<
" std.dev.,";
11870 csvStream << sfIndex <<
" sum,";
11881 csvStream << cloud->
getName() <<
"," ;
11882 csvStream << cloud->
size() <<
"," ;
11883 csvStream << G.
x <<
"," ;
11884 csvStream << G.
y <<
"," ;
11885 csvStream << G.
z <<
"," ;
11888 csvStream << sf->
getName() <<
"," ;
11890 unsigned validCount = 0;
11891 double sfSum = 0.0;
11892 double sfSum2 = 0.0;
11893 for (
unsigned k = 0; k < sf->
currentSize(); ++k) {
11894 const ScalarType& val = sf->
getValue(k);
11898 sfSum2 += val * val;
11901 csvStream << validCount <<
"," ;
11902 double mean = sfSum / validCount;
11903 csvStream << mean <<
"," ;
11904 csvStream << sqrt(
std::abs(sfSum2 / validCount - mean * mean))
11906 csvStream << sfSum <<
"," ;
11913 .arg(outputFilename)
11914 .arg(clouds.size()));
11918 void MainWindow::doActionComputeCPS() {
11919 if (m_selectedEntities.size() != 2) {
11931 m_selectedEntities[1], tr(
"Reference"),
this);
11932 if (!dlg.exec())
return;
11946 static const char DEFAULT_CPS_TEMP_SF_NAME[] =
"CPS temporary";
11948 if (sfIdx < 0) sfIdx = cmpPC->
addScalarField(DEFAULT_CPS_TEMP_SF_NAME);
11951 tr(
"Couldn't allocate a new scalar field for computing "
11952 "distances! Try to free some memory ..."));
11970 compCloud, srcCloud,
params, &pDlg);
11983 QString(
"[%1]->CPSet(%2)")
11992 void MainWindow::doActionFitSphere() {
11993 double outliersRatio = 0.5;
11994 double confidence = 0.99;
11997 pDlg.setAutoClose(
false);
12001 if (!cloud)
continue;
12007 cloud, outliersRatio, center, radius, rms, &pDlg,
12011 tr(
"[Fit sphere] Failed to fit a sphere on cloud '%1'")
12016 CVLog::Print(tr(
"[Fit sphere] Cloud '%1': center (%2,%3,%4) - radius = "
12029 tr(
"Sphere r=%1 [rms %2]").arg(radius).arg(rms));
12032 addToDB(sphere,
false,
false,
false);
12036 void MainWindow::doActionFitCircle() {
12038 pDlg.setAutoClose(
false);
12043 if (!cloud)
continue;
12048 double rms = std::numeric_limits<double>::quiet_NaN();
12050 cloud, center,
normal, radius, rms, &pDlg) !=
12053 tr(
"[Fit circle] Failed to fit a circle on cloud '%1'")
12058 CVLog::Print(tr(
"[Fit circle] Cloud '%1': center (%2,%3,%4) - radius = "
12075 circle->
setName(QObject::tr(
"Circle r=%1").arg(radius));
12086 addToDB(circle,
false,
false,
false);
12093 void MainWindow::doActionFitPlane() { doComputePlaneOrientation(
false); }
12095 void MainWindow::doComputePlaneOrientation(
bool fitFacet) {
12098 double maxEdgeLength = 0.0;
12101 static double s_polygonMaxEdgeLength = 0.0;
12102 maxEdgeLength = QInputDialog::getDouble(
12103 this, tr(
"Fit facet"), tr(
"Max edge length (0 = no limit)"),
12104 s_polygonMaxEdgeLength, 0, 1.0e9, 8, &ok);
12106 s_polygonMaxEdgeLength = maxEdgeLength;
12112 bool firstEntity =
true;
12114 for (
ccHObject* entity : selectedEntities) {
12128 shifted = gencloud;
12146 plane =
static_cast<ccHObject*
>(facet);
12163 plane =
static_cast<ccHObject*
>(pPlane);
12175 tr(
"[Orientation] Entity '%1'").arg(entity->
getName()));
12179 if (N.
z < 0.0) N *= -1.0;
12189 QString dipAndDipDirStr =
12202 tr(
"[Orientation] A matrix that would make this plane "
12203 "horizontal (normal towards Z+) is:"));
12205 makeZPosMatrix.
toString(12,
' '));
12207 tr(
"[Orientation] You can copy this matrix values "
12208 "(CTRL+C) and paste them in the 'Apply "
12209 "transformation tool' dialog"));
12211 plane->
setName(dipAndDipDirStr);
12227 tr(
"Failed to fit a plane/facet on entity '%1'")
12236 void MainWindow::doActionFitFacet() { doComputePlaneOrientation(
true); }
12238 void MainWindow::doActionFitQuadric() {
12239 bool errors =
false;
12257 tr(
"[doActionFitQuadric] Quadric local coordinate "
12262 "(in local coordinate system): ") +
12276 const unsigned char dX = dims.
x;
12277 const unsigned char dY = dims.
y;
12278 const unsigned char dZ = dims.
z;
12282 for (
unsigned i = 0; i < newCloud->
size(); ++i)
12286 Q.
u[dZ] = eq[0] + eq[1] * Q.
u[dX] + eq[2] * Q.
u[dY] + eq[3] * Q.
u[dX] * Q.
u[dX] + eq[4] * Q.
u[dX] * Q.
u[dY] + eq[5] * Q.
u[dY] * Q.
u[dY];
12290 newCloud->
setName(newCloud->
getName() +
".projection_on_quadric");
12297 tr(
"Failed to compute quadric on cloud '%1'")
12309 void MainWindow::doActionUnroll() {
12317 bool lockedVertices;
12319 m_selectedEntities[0], &lockedVertices);
12320 if (lockedVertices) {
12329 tr(
"Method can't be applied on locked vertices or virtual "
12336 unrollDlg.fromPersistentSettings();
12337 if (!unrollDlg.exec())
return;
12338 unrollDlg.toPersistentSettings();
12343 unsigned char dim =
12344 static_cast<unsigned char>(unrollDlg.getAxisDimension());
12345 bool exportDeviationSF = unrollDlg.exportDeviationSF();
12346 CCVector3 center = unrollDlg.getAxisPosition();
12351 double startAngle_deg = 0.0;
12352 double stopAngle_deg = 360.0;
12353 unrollDlg.getAngleRange(startAngle_deg, stopAngle_deg);
12354 if (startAngle_deg >= stopAngle_deg) {
12355 QMessageBox::critical(
this,
"Error",
"Invalid angular range");
12365 if (unrollDlg.isAxisPositionAuto()) {
12370 startAngle_deg, stopAngle_deg, &pDlg);
12379 params.coneAngle_deg = unrollDlg.getConeHalfAngle();
12382 startAngle_deg, stopAngle_deg, &pDlg);
12395 "[Unroll] Original mesh has been automatically hidden");
12400 addToDB(outputMesh,
true,
true,
false,
true);
12404 "[Unroll] Original cloud has been automatically hidden");
12408 addToDB(output,
true,
true,
false,
true);
12414 void MainWindow::doComputeGeometricFeature() {
12417 static bool s_upDirDefined =
false;
12422 gfDlg.setRadius(radius);
12425 gfDlg.setSelectedFeatures(s_selectedCharacteristics);
12426 if (s_upDirDefined) {
12427 gfDlg.setUpDirection(s_upDir);
12430 if (!gfDlg.exec())
return;
12432 radius = gfDlg.getRadius();
12433 if (!gfDlg.getSelectedFeatures(s_selectedCharacteristics)) {
12438 CCVector3* upDir = gfDlg.getUpDirection();
12441 s_upDirDefined = (upDir !=
nullptr);
12442 if (s_upDirDefined) {
12448 m_selectedEntities, upDir,
this);
12457 QList<QAction*>& actions,
12458 QSet<QAction*>& collected,
12459 const QSet<QMenu*>& excludedMenus) {
12460 if (!menu || excludedMenus.contains(menu)) {
12464 for (QAction* action : menu->actions()) {
12470 if (action->isSeparator()) {
12475 if (action->text().isEmpty()) {
12480 if (collected.contains(action)) {
12487 if (!action->objectName().isEmpty() &&
12488 action->objectName().startsWith(
"actionDisplay")) {
12493 QMenu* submenu = action->menu();
12500 actions.append(action);
12501 collected.insert(action);
12505 void MainWindow::populateActionList() {
12509 QSet<QMenu*> excludedMenus;
12512 if (m_recentFiles) {
12513 QMenu* recentFilesMenu = m_recentFiles->
menu();
12514 if (recentFilesMenu) {
12515 excludedMenus.insert(recentFilesMenu);
12520 #ifdef CC_3DXWARE_SUPPORT
12521 if (m_3DMouseManager) {
12522 QMenu* mouseMenu = m_3DMouseManager->menu();
12524 excludedMenus.insert(mouseMenu);
12530 #ifdef CC_GAMEPAD_SUPPORT
12531 if (m_gamepadManager) {
12532 QMenu* gamepadMenu = m_gamepadManager->menu();
12534 excludedMenus.insert(gamepadMenu);
12540 excludedMenus.insert(m_ui->menuToolbars);
12543 QSet<QAction*> collected;
12547 for (QAction* menuBarAction : m_ui->menuBar->actions()) {
12548 QMenu* menu = menuBarAction->menu();
12555 for (QToolBar* toolbar : findChildren<QToolBar*>()) {
12557 QString toolbarName = toolbar->objectName();
12563 for (QAction* action : toolbar->actions()) {
12564 if (!action || action->isSeparator() || action->text().isEmpty()) {
12569 if (collected.contains(action)) {
12574 if (!action->objectName().isEmpty() &&
12575 action->objectName().startsWith(
"actionDisplay")) {
12580 if (action->menu()) {
12584 m_actions.append(action);
12585 collected.insert(action);
12590 void MainWindow::showShortcutDialog() {
12591 if (m_shortcutDlg) {
12592 m_shortcutDlg->exec();
constexpr unsigned char POINT_VISIBLE
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
constexpr unsigned char POINT_HIDDEN
constexpr unsigned char POINT_OUT_OF_FOV
CC_VIEW_ORIENTATION
View orientation.
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
constexpr unsigned char POINT_OUT_OF_RANGE
Vector2Tpl< PointCoordinateType > CCVector2
Default 2D Vector.
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
float PointCoordinateType
Type of the coordinates of a (N-D) point.
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
CC_FILE_ERROR
Typical I/O filter errors.
@ CC_FERR_CANCELED_BY_USER
static QFileDialog::Options ECVFileDialogOptions()
static int s_innerRectDim
static std::vector< cc2DLabel * > s_levelLabels
@ PICKING_ROTATION_CENTER
static const QString s_fileFilterSeparator(";;")
static const float GLOBAL_OPACITY
static double s_constantSFValue
static const QString s_allFilesFilter("All (*.*)")
void AddToRemoveList(ccHObject *toRemove, ccHObject::Container &toBeRemovedList)
static double s_msRmsDiff
static ccLibAlgorithms::ScaleMatchingAlgorithm s_msAlgorithm
std::pair< ccHObject *, ccGenericPointCloud * > EntityCloudAssociation
static PickingOperation s_currentPickingOperation
static ccPointCloud * s_levelMarkersCloud
static void collectActionsFromMenu(QMenu *menu, QList< QAction * > &actions, QSet< QAction * > &collected, const QSet< QMenu * > &excludedMenus)
static double s_kdTreeMaxErrorPerCell
static MainWindow * s_instance
static double s_subdivideMaxArea
static unsigned s_viewportIndex
static int s_msFinalOverlap
static ccHObject * s_levelEntity
static bool IsValidFileName(QString filename)
const int CLOUDVIEWER_LANG_CHINESE
const int CLOUDVIEWER_LANG_ENGLISH
QRegularExpression QtCompatRegExp
cmdLineReadable * params[]
ASCII point cloud I/O filter.
CC_FILE_ERROR loadAsciiData(const QByteArray &data, QString sourceName, ccHObject &container, LoadParameters ¶meters)
Loads a cloud from a QByteArray.
static QString GetFileFilter()
static short GetLastSavedFileVersion()
Returns the last saved file version.
static QString GetFileFilter()
virtual void release()
Decrease counter and deletes object when 0.
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static void SetVerbosityLevel(int level)
Sets the verbosity level.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
virtual CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters ¶meters) override
Saves an entity (or a group of) to a file.
static QString GetFileFilter()
static CC_FILE_ERROR SaveToFile(ccHObject *entities, const QString &filename, const SaveParameters ¶meters, Shared filter)
static void ResetSesionCounter()
static const FilterContainer & GetFilters()
Returns the set of all registered filters.
QSharedPointer< FileIOFilter > Shared
Shared type.
static void DisplayErrorMessage(CC_FILE_ERROR err, const QString &action, const QString &filename)
Displays (to console) the message corresponding to a given error code.
static ccHObject * LoadFromFile(const QString &filename, LoadParameters ¶meters, Shared filter, CC_FILE_ERROR &result)
Loads one or more entities from a file with a known filter.
Jacobi eigen vectors/values decomposition.
static bool GetEigenVector(const SquareMatrix &eigenVectors, unsigned index, Scalar eigenVector[])
Returns the given eigenvector.
static bool SortEigenValuesAndVectors(SquareMatrix &eigenVectors, EigenValues &eigenValues)
QMenu * createPopupMenu() override
Override to add custom actions to right-click menu on toolbars.
static QWidget * GetRenderWindow(const QString &title)
Returns a given GL sub-window (determined by its title)
QWidget * getWindow(int index) const
void enablePickingOperation(QString message)
ccHObject * dbRootObject() override
Returns DB root (as a ccHObject)
ccHObjectContext removeObjectTemporarilyFromDBTree(ccHObject *obj) override
Removes object temporarily from DB tree.
void increasePointSize() override
QMdiSubWindow * getMDISubWindow(QWidget *win)
Returns MDI area subwindow corresponding to a given 3D view.
void decreasePointSize() override
int getRenderWindowCount() const
Returns the number of 3D views.
const ccHObject::Container & getSelectedEntities() const override
Returns currently selected entities ("read only")
void freezeUI(bool state) override
Freezes/unfreezes UI.
static MainWindow * TheInstance()
Returns the unique instance of this object.
void cancelPreviousPickingOperation(bool aborted)
void setUiManager(QUIWidget *uiManager)
void refreshObjects(ccHObject::Container objs, bool only2D=false, bool forceRedraw=true) override
void registerOverlayDialog(ccOverlayDialog *dlg, Qt::Corner pos) override
Registers a MDI area 'overlay' dialog.
void addToDB(const QStringList &filenames, QString fileFilter=QString(), bool displayDialog=true)
void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE) override
void updateOverlayDialogsPlacement() override
Forces the update of all registered MDI 'overlay' dialogs.
static void GetRenderWindows(std::vector< QWidget * > &windows)
Returns all GL sub-windows.
void doActionSaveViewportAsCamera()
void addToDBAuto(const QStringList &filenames, bool displayDialog=true)
static QWidget * GetActiveRenderWindow()
Static shortcut to MainWindow::getActiveWindow.
void setAutoPickPivot(bool state)
ccUniqueIDGenerator::Shared getUniqueIDGenerator() override
Returns the unique ID generator.
static bool s_autoSaveGuiElementPos
static void UpdateUI()
Static shortcut to MainWindow::updateUI.
void setGlobalZoom() override
void zoomOnEntities(ccHObject *obj) override
void refreshObject(ccHObject *obj, bool only2D=false, bool forceRedraw=true) override
void doActionOrthogonalProjection()
void removeFromDB(ccHObject *obj, bool autoDelete=true) override
Removes an entity from main db tree.
void onItemPicked(const PickedItem &pi) override
Inherited from ccPickingListener.
ccColorScalesManager * getColorScalesManager() override
Returns color scale manager (unique instance)
void spawnHistogramDialog(const std::vector< unsigned > &histoValues, double minVal, double maxVal, QString title, QString xAxisLabel) override
void addWidgetToQMdiArea(QWidget *widget) override
void refreshAll(bool only2D=false, bool forceRedraw=true) override
Redraws all GL windows that have the 'refresh' flag on.
void toggleExclusiveFullScreen(bool state) override
ccHObject * loadFile(QString filename, bool silent) override
Attempts to load a file.
void toggle3DView(bool state) override
void refreshSelected(bool only2D=false, bool forceRedraw=true) override
void forceConsoleDisplay() override
Forces display of console widget.
void doActionPerspectiveProjection()
void updateFullScreenMenu(bool state)
bool m_FirstShow
Flag: first time the window is made visible.
void unregisterOverlayDialog(ccOverlayDialog *dlg) override
Unregisters a MDI area 'overlay' dialog.
ccBBox getSelectedEntityBbox()
void updateViewModePopUpMenu()
static void ChangeStyle(const QString &qssFile)
QWidget * getActiveWindow() override
void resetSelectedBBox() override
void initPlugins()
Sets up the UI (menus and toolbars) based on loaded plugins.
void putObjectBackIntoDBTree(ccHObject *obj, const ccHObjectContext &context) override
Adds back object to DB tree.
void zoomOnSelectedEntities() override
void enableAll() override
Enables all windows.
void setView(CC_VIEW_ORIENTATION view) override
void updatePropertiesView()
Updates the 'Properties' view.
void setPerspectiveView()
static void DestroyInstance()
Deletes current main window instance.
void disableAll() override
Disables all windows.
void saveGUIElementsPos()
Saves position and state of all GUI elements.
void setSelectedInDB(ccHObject *obj, bool selected) override
Selects or unselects an entity (in db tree)
void normalize()
Sets vector norm to unity.
Type dot(const Vector3Tpl &v) const
Dot product.
Type norm2() const
Returns vector square norm.
Type norm() const
Returns vector norm.
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Type angle_rad(const Vector3Tpl &v) const
Returns the angle to another vector (in radians - in [0, pi].
2D label (typically attached to points)
bool addPickedPoint(ccGenericPointCloud *cloud, unsigned pointIndex, bool entityCenter=false)
Adds a point to this label.
const PickedPoint & getPickedPoint(unsigned index) const
Returns a given point.
void setDisplayedIn2D(bool state)
Whether to display the label in 2D.
unsigned size() const
Returns current size.
virtual QString getName() const override
Returns object name.
void setParameters(const ecvViewportParameters ¶ms)
Sets perspective view state.
Rough registration dialog.
Generic dialog to query 3 (double) values.
Camera sensor parameters dialog.
Camera (projective) sensor.
const LensDistortionParameters::Shared & getDistortionParameters() const
Returns uncertainty parameters.
bool computeUncertainty(const CCVector2 &pixel, const float depth, Vector3Tpl< ScalarType > &sigma) const
ccCircle * clone() const
Clones this circle.
double getRadius() const
Returns the radius of the circle.
Dialog to change the color levels.
Dialog to edit/create color scales.
QSharedPointer< ccColorScale > Shared
Shared pointer type.
Color scales manager/container.
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
void toPersistentSettings() const
Save custom color scales to persistent settings.
static ccColorScalesManager * GetUniqueInstance()
Returns unique instance.
Dialog for cloud/cloud or cloud/mesh comparison setting.
ccHObject * getComparedEntity() const
Returns compared entity.
ccPropertiesTreeDelegate * getPropertiesDelegate()
Get properties tree delegate.
void updatePropertiesView()
Updates properties view.
void dbIsNotEmptyAnymore()
int countSelectedEntities(CV_CLASS_ENUM filter=CV_TYPES::OBJECT)
void unselectEntity(ccHObject *obj)
Unselects a given entity.
void selectEntity(ccHObject *obj, bool forceAdditiveSelection=false)
Selects a given entity.
void selectEntities(std::unordered_set< int > entIDs)
Selects multiple entities at once (shortcut to the other version)
ccHObject * getRootEntity()
Returns associated root object.
size_t getSelectedEntities(ccHObject::Container &selectedEntities, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, dbTreeSelectionInfo *info=nullptr)
void unselectAllEntities()
Unselects all entities.
void unloadAll()
Unloads all entities.
void removeElement(ccHObject *object)
Removes an element from the DB tree.
void deleteSelectedEntities()
void addElement(ccHObject *object, bool autoExpand=true)
Adds an element to the DB tree.
std::vector< PointCoordinateType > zBuff
Z-Buffer grid.
PointCoordinateType getRadius() const
Returns radius.
ccGenericPrimitive * clone() const override
Clones primitive.
Dialog to setup display settings.
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual void setTempColor(const ecvColor::Rgb &col, bool autoActivate=true)
Sets current temporary (unique)
virtual void setVisible(bool state)
Sets entity visibility.
virtual const ccGLMatrix & getGLTransformation() const
Returns associated GL transformation.
virtual void showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
virtual void setOpacity(float opacity)
Set opacity activation state.
virtual void setGLTransformation(const ccGLMatrix &trans)
Associates entity with a GL transformation (rotation + translation)
ccFacet * clone() const
Clones this facet.
ccMesh * getPolygon()
Returns polygon mesh (if any)
ccPolyline * getContour()
Returns contour polyline (if any)
CCVector3 getNormal() const override
Returns the entity normal.
double getRMS() const
Returns associated RMS.
static ccFacet * Create(cloudViewer::GenericIndexedCloudPersist *cloud, PointCoordinateType maxEdgeLength=0, bool transferOwnership=false, const PointCoordinateType *planeEquation=nullptr)
Creates a facet from a set of points.
const CCVector3 & getCenter() const
Returns the facet center.
Ground-based (lidar) sensor parameters dialog.
Ground-based Laser sensor.
bool computeDepthBuffer(cloudViewer::GenericCloud *cloud, int &errorCode, ccPointCloud *projectedCloud=nullptr)
bool computeAutoParameters(cloudViewer::GenericCloud *theCloud)
Computes angular parameters automatically (all but the angular steps!)
static QString GetErrorString(int errorCode)
Returns the error string corresponding to an error code.
const ccDepthBuffer & getDepthBuffer() const
Returns the associated depth buffer.
unsigned char checkVisibility(const CCVector3 &P) const override
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
void applyRotation(Vector3Tpl< float > &vec) const
Applies rotation only to a 3D vector (in place) - float version.
static ccGLMatrixTpl< float > FromToRotation(const Vector3Tpl< float > &from, const Vector3Tpl< float > &to)
Creates a transformation matrix that rotates a vector to another.
T * data()
Returns a pointer to internal data.
void shiftRotationCenter(const Vector3Tpl< T > &vec)
Shifts rotation center.
void invert()
Inverts transformation.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
Vector3Tpl< T > getColumnAsVec3D(unsigned index) const
Returns a copy of a given column as a CCVector3.
void scale(T coef)
Scales the whole matrix.
void initFromParameters(T alpha_rad, const Vector3Tpl< T > &axis3D, const Vector3Tpl< T > &t3D)
Inits transformation from a rotation axis, an angle and a translation.
virtual void toIdentity()
Sets matrix to identity.
Float version of ccGLMatrixTpl.
Double version of ccGLMatrixTpl.
void showNormals(bool state) override
Sets normals visibility.
ccPointCloud * samplePoints(bool densityBased, double samplingParameter, bool withNormals, bool withRGB, bool withTexture, cloudViewer::GenericProgressCallback *pDlg=nullptr)
Samples points on a mesh.
virtual ccGenericPointCloud * getAssociatedCloud() const =0
Returns the vertices cloud.
void enableStippling(bool state)
Enables polygon stippling.
virtual void refreshBB()=0
Forces bounding-box update.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual void scale(PointCoordinateType fx, PointCoordinateType fy, PointCoordinateType fz, CCVector3 center=CCVector3(0, 0, 0))=0
Multiplies all coordinates by constant factors (one per dimension)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual ccGenericPointCloud * createNewCloudFromVisibilitySelection(bool removeSelectedPoints=false, VisibilityTableType *visTable=nullptr, std::vector< int > *newIndexesOfRemainingPoints=nullptr, bool silent=false, cloudViewer::ReferenceCloud *selection=nullptr)=0
void setPointSize(unsigned size=0)
Sets point size.
virtual void invertVisibilityArray()
Inverts the visibility array.
virtual void applyRigidTransformation(const ccGLMatrix &trans)=0
Applies a rigid transformation (rotation + translation)
virtual ccGenericPointCloud * clone(ccGenericPointCloud *destCloud=nullptr, bool ignoreChildren=false)=0
Clones this entity.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
virtual bool resetVisibilityArray()
Resets the associated visibility array.
virtual void unallocateVisibilityArray()
Erases the points visibility information.
Generic primitive interface.
virtual ccGLMatrix & getTransformation()
Returns the transformation that is currently applied to the vertices.
Dialog for computing the density of a point clouds.
static ccMesh * ToMesh(ccHObject *obj)
Converts current object to ccMesh (if possible)
static ccDisc * ToDisc(ccHObject *obj)
Converts current object to ccDisc (if possible)
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
static ccShiftedObject * ToShifted(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccShiftedObject.
static ccCameraSensor * ToCameraSensor(ccHObject *obj)
static ccPolyline * ToPolyline(ccHObject *obj)
Converts current object to ccPolyline (if possible)
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
static ccSensor * ToSensor(ccHObject *obj)
Converts current object to ccSensor (if possible)
static ccSubMesh * ToSubMesh(ccHObject *obj)
Converts current object to ccSubMesh (if possible)
static ccFacet * ToFacet(ccHObject *obj)
Converts current object to ccFacet (if possible)
static ccGenericMesh * ToGenericMesh(ccHObject *obj)
Converts current object to ccGenericMesh (if possible)
static ccGBLSensor * ToGBLSensor(ccHObject *obj)
static ccPlane * ToPlane(ccHObject *obj)
Converts current object to ccPlane (if possible)
static ccCircle * ToCircle(ccHObject *obj)
Converts current object to ccCircle (if possible)
Hierarchical CLOUDVIEWER Object.
virtual void notifyGeometryUpdate()
virtual ccBBox getOwnBB(bool withGLFeatures=false)
Returns the entity's own bounding-box.
virtual const ccGLMatrix & getGLTransformationHistory() const
Returns the transformation 'history' matrix.
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
void removeFromRenderScreen(bool recursive=true)
bool isSerializable() const override
Returns whether object is serializable of not.
int getDependencyFlagsWith(const ccHObject *otherObject)
Returns the dependency flags with a given object.
void removeAllChildren()
Removes all children.
virtual ccBBox getDisplayBB_recursive(bool relative)
Returns the bounding-box of this entity and it's children WHEN DISPLAYED.
void removeDependencyWith(ccHObject *otherObject)
Removes any dependency flags with a given object.
void addDependency(ccHObject *otherObject, int flags, bool additive=true)
Adds a new dependence (additive or not)
bool isAncestorOf(const ccHObject *anObject) const
Returns true if the current object is an ancestor of the specified one.
QString getViewId() const
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
virtual void setGLTransformationHistory(const ccGLMatrix &mat)
Sets the transformation 'history' matrix (handle with care!)
void setRedrawFlagRecursive(bool redraw=false)
virtual void setSelectionBehavior(SelectionBehavior mode)
Sets selection behavior (when displayed)
virtual void setSelected_recursive(bool p)
virtual ccBBox getBB_recursive(bool withGLFeatures=false, bool onlyEnabledChildren=true)
Returns the bounding-box of this entity and it's children.
ccHObject * getParent() const
Returns parent object.
void removeChild(ccHObject *child)
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
CV_CLASS_ENUM getClassID() const override
Returns class ID.
Encapsulating dialog for ccHistogramWindow.
ccHistogramWindow * window()
Returns encapsulated ccHistogramWindow.
void setTitle(const QString &str)
Sets title.
void refresh()
Updates the display.
void fromSF(ccScalarField *sf, unsigned initialNumberOfClasses=0, bool numberOfClassesCanBeChanged=true, bool showNaNValuesInGrey=true)
Computes histogram from a scalar field.
void fromBinArray(const std::vector< unsigned > &histoValues, double minVal, double maxVal)
void setAxisLabels(const QString &xLabel, const QString &yLabel)
Sets axis labels.
Finds a the biggets enclosed rectangle in a point cloud (2D)
ccBox * process(ccGenericPointCloud *cloud, unsigned char zDim=2)
Finds the biggest enclosed rectangle.
static int SelectEntity(const ccHObject::Container &entities, int defaultSelectedIndex=0, QWidget *parent=0, QString label=QString())
Static shortcut: unique selection mode.
bool convertCellIndexToRandomColor()
Flag points with a random color per leaf.
bool convertCellIndexToSF()
Flag points with cell index (as a scalar field)
Dialog to define connected components labelinng parameters.
Scales matching tool dialog.
ccLibAlgorithms::ScaleMatchingAlgorithm getSelectedAlgorithm() const
Returns the selected matching algorithm.
int getSelectedIndex() const
Returns selected index.
void setSelectedAlgorithm(ccLibAlgorithms::ScaleMatchingAlgorithm algorithm)
Sets the selected matching algorithm.
bool merge(const ccMesh *mesh, bool createSubMesh)
Merges another mesh into this one.
ccMesh * cloneMesh(ccGenericPointCloud *vertices=nullptr, ccMaterialSet *clonedMaterials=nullptr, NormsIndexesTableType *clonedNormsTable=nullptr, TextureCoordsContainer *cloneTexCoords=nullptr)
Clones this entity.
ccMesh * subdivide(PointCoordinateType maxArea) const
bool laplacianSmooth(unsigned nbIteration=100, PointCoordinateType factor=static_cast< PointCoordinateType >(0.01), ecvProgressDialog *progressCb=nullptr)
Laplacian smoothing.
void flipTriangles()
Flips the triangle.
static ccMesh * TriangulateTwoPolylines(ccPolyline *p1, ccPolyline *p2, CCVector3 *projectionDir=nullptr)
Creates a Delaunay 2.5D mesh from two polylines.
static ccMesh * Triangulate(ccGenericPointCloud *cloud, cloudViewer::TRIANGULATION_TYPES type, bool updateNormals=false, PointCoordinateType maxEdgeLength=0, unsigned char dim=2)
Creates a Delaunay 2.5D mesh from a point cloud.
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
ccMesh * createNewMeshFromSelection(bool removeSelectedTriangles, std::vector< int > *newIndexesOfRemainingTriangles=nullptr, bool withChildEntities=false)
Creates a new mesh with the selected vertices only.
bool computePerVertexNormals()
Computes per-vertex normals.
virtual unsigned size() const override
Returns the number of triangles.
static QString ConvertDipAndDipDirToString(PointCoordinateType dip_deg, PointCoordinateType dipDir_deg)
Converts geological 'dip direction & dip' parameters to a string.
static void ConvertNormalToDipAndDipDir(const CCVector3 &N, PointCoordinateType &dip_deg, PointCoordinateType &dipDir_deg)
Converts a normal vector to geological 'dip direction & dip' parameters.
virtual bool isLocked() const
Returns whether the object is locked or not.
virtual QString getName() const
Returns object name.
virtual unsigned getUniqueID() const
Returns object unique ID.
void setMetaData(const QString &key, const QVariant &data)
Sets a meta-data element.
bool isA(CV_CLASS_ENUM type) const
virtual void setName(const QString &name)
Sets object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
virtual bool isEnabled() const
Returns whether the object is enabled or not.
bool isKindOf(CV_CLASS_ENUM type) const
static ccUniqueIDGenerator::Shared GetUniqueIDGenerator()
Returns the unique ID generator.
QSharedPointer< ccOctree > Shared
Shared pointer.
Dialog to assign roles to two entities (e.g. compared/reference)
Generic overlay dialog interface.
void shown()
Signal emitted when a 'show' event is detected.
bool started() const
Returns whether the tool is currently started or not.
void processFinished(bool accepted)
Signal emitted when process is finished.
Minimal dialog to pick one element in a list (combox box)
Point/triangle picking hub.
void removeListener(ccPickingListener *listener, bool autoStopPickingIfLast=true)
Removes a listener.
void onActiveWindowDeleted(QObject *)
bool addListener(ccPickingListener *listener, bool exclusive=false, bool autoStartPicking=true, ecvDisplayTools::PICKING_MODE mode=ecvDisplayTools::POINT_OR_TRIANGLE_PICKING)
Adds a listener.
void onActiveWindowChanged(QMdiSubWindow *)
Dialog to create (or edit the parameters) of a plane.
void initWithPlane(ccPlane *plane)
Links this dialog with an existing plane.
static ccPlane * Fit(cloudViewer::GenericIndexedCloudPersist *cloud, double *rms=0)
Fits a plane primitive on a cloud.
CCVector3 getCenter() const
Returns the center.
CCVector3 getNormal() const override
Returns the entity normal.
void flip()
Flips the plane.
void getEquation(CCVector3 &N, PointCoordinateType &constVal) const
Returns the equation of the plane.
PointCoordinateType getXWidth() const
Returns 'X' width.
PointCoordinateType getYWidth() const
Returns 'Y' width.
void showAboutDialog() const
QList< QToolBar * > & additionalPluginToolbars()
void handleSelectionChanged()
QToolBar * mainPluginToolbar()
QMenu * pluginMenu() const
QToolBar * glPclToolbar()
QMenu * pclAlgorithmMenu() const
QAction * actionShowPCLAlgorithmToolbar()
static bool isPythonPluginToolbar(QToolBar *toolbar)
QAction * actionShowMainPluginToolbar()
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
bool sfColorScaleShown() const
Returns whether color scale should be displayed or not.
virtual void applyRigidTransformation(const ccGLMatrix &trans) override
Applies a rigid transformation (rotation + translation)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasNormals() const override
Returns whether normals are enabled or not.
ccPointCloud * unroll(UnrollMode mode, UnrollBaseParams *params, bool exportDeviationSF=false, double startAngle_deg=0.0, double stopAngle_deg=360.0, cloudViewer::GenericProgressCallback *progressCb=nullptr) const
Unrolls the cloud and its normals on a cylinder or a cone.
void showSFColorsScale(bool state)
Sets whether color scale should be displayed or not.
void invalidateBoundingBox() override
Invalidates bounding box.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool hasFWF() const
Returns whether the cloud has associated Full WaveForm data.
ccMesh * triangulateGrid(const Grid &grid, double minTriangleAngle_deg=0.0) const
Meshes a scan grid.
void hidePointsByScalarValue(ScalarType minVal, ScalarType maxVal)
Hides points whose scalar values falls into an interval.
bool compressFWFData()
Compresses the associated FWF data container.
int getCurrentDisplayedScalarFieldIndex() const
Returns the currently displayed scalar field index (or -1 if none)
ccPointCloud * cloneThis(ccPointCloud *destCloud=nullptr, bool ignoreChildren=false)
Clones this entity.
static ccPointCloud * From(const cloudViewer::GenericIndexedCloud *cloud, const ccGenericPointCloud *sourceCloud=nullptr)
Creates a new point cloud object from a GenericIndexedCloud.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void deleteScalarField(int index) override
Deletes a specific scalar field.
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
Grid::Shared & grid(size_t gridIndex)
Returns an associated grid.
void shrinkToFit()
Removes unused capacity.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
ccPointCloud * filterPointsByScalarValue(ScalarType minVal, ScalarType maxVal, bool outside=false)
Filters out points whose scalar values falls into an interval.
bool setRGBColor(ColorCompType r, ColorCompType g, ColorCompType b)
Set a unique color for the whole cloud (shortcut)
ccPointCloud * partialClone(const cloudViewer::ReferenceCloud *selection, int *warnings=nullptr, bool withChildEntities=true) const
Creates a new point cloud object from a ReferenceCloud (selection)
size_t gridCount() const
Returns the number of associated grids.
Dialog/interactor to graphically pick a list of points.
void linkWithCloud(ccPointCloud *cloud)
Associates dialog with cloud.
bool start() override
Starts process.
bool init(QWidget *win, const ccHObject::Container &alignedEntities, const ccHObject::Container *referenceEntities=nullptr)
Inits dialog.
void clear()
Clears dialog.
void pause(bool state)
Pauses the dialog.
bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
bool start() override
Starts process.
Dialog for simple point picking (information, distance, etc.)
void newLabel(ccHObject *)
Signal emitted when a new label is created.
virtual bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
virtual bool start() override
Starts process.
virtual void setGlobalShift(const CCVector3d &shift) override
Sets shift applied to original coordinates (information storage only)
ccPointCloud * samplePoints(bool densityBased, double samplingParameter, bool withRGB)
Samples points on the polyline.
ccPolyline * smoothChaikin(PointCoordinateType ratio, unsigned iterationCount) const
Smoothes the polyline (Chaikin algorithm)
unsigned segmentCount() const
Returns the number of segments.
virtual void setGlobalScale(double scale) override
void setColor(const ecvColor::Rgb &col)
Sets the polyline color.
void setVisualizer(ecvGenericVisualizer3D *viewer)
Set visualizer for other property editors.
void requestClearSelection() const
Request to clear all selection data (prevents crashes from stale references)
Dialog: points sampling on a mesh.
const Tuple3ub & getEquationDims() const
QString getEquationString() const
Returns the quadric equation coefficients as a string.
const PointCoordinateType * getEquationCoefs() const
Returns the quadric equation coefficients.
static ccQuadric * Fit(cloudViewer::GenericIndexedCloudPersist *cloud, double *rms)
Fits a quadric primitive on a cloud.
Point cloud or mesh registration dialog.
static double GetAbsoluteMinRMSDecrease()
Dialog for screen to file rendering.
A scalar field associated to display-related parameters.
void setColorScale(ccColorScale::Shared scale)
Sets associated color scale.
void showNaNValuesInGrey(bool state)
const Range & displayRange() const
Access to the range of displayed values.
void computeMinAndMax() override
Determines the min and max values.
bool areNaNValuesShownInGrey() const
Returns whether NaN values are displayed in gray or hidden.
void saveState()
Saves state.
bool keepInPlace() const
Whether the entity should be 'kept in place' or not.
bool rescaleGlobalShift() const
Whether the Global shift should be rescaled as well.
CCVector3d getScales() const
Returns scales.
Dialog for sensor range computation.
Dialog for scattering angles computation.
Generic sensor interface.
bool addPosition(ccGLMatrix &trans, double index)
Adds a new position (shortcut)
bool getActiveAbsoluteCenter(CCVector3 &vec) const
Gets currently active absolute position.
virtual bool applyViewport()
Apply sensor 'viewport' to a 3D view.
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
Shifted entity interface.
virtual void setGlobalScale(double scale)
CCVector3d toGlobal3d(const Vector3Tpl< T > &Plocal) const
Returns the point back-projected into the original coordinates system.
bool isShifted() const
Returns whether the cloud is shifted or not.
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
void copyGlobalShiftAndScale(const ccShiftedObject &s)
Copies the Global Shift and Scale from another entity.
Dialog to smooth a polyline (Chaikin algorithm)
PointCoordinateType getRadius() const
Returns radius.
ccSubMesh * createNewSubMeshFromSelection(bool removeSelectedTriangles, const std::vector< int > &selectedTriangleIndexes, IndexMap *newRemainingTriangleIndexes=nullptr)
Creates a new sub mesh with the visible vertices only.
Subsampling cloud dialog.
void populateMenu(QMenu *menu, const QString &pathToTranslationFiles)
static ccTranslationManager & get()
QSharedPointer< ccUniqueIDGenerator > Shared
Shared type.
Dialog: unroll clould on a cylinder or a cone.
double getDiagNormd() const
Returns diagonal length (double precision)
Vector3Tpl< T > getCenter() const
Returns center.
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
T getDiagNorm() const
Returns diagonal length.
T getMaxBoxDim() const
Returns maximal box dimension.
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
bool isValid() const
Returns whether bounding box is valid or not.
void add(const Vector3Tpl< T > &P)
'Enlarges' the bounding box with a point
A class to compute and handle a Delaunay 2D mesh on a subset of points.
VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex) override
Returns the indexes of the vertices of a given triangle.
virtual unsigned size() const override
Returns the number of triangles.
virtual bool buildMesh(const std::vector< CCVector2 > &points2D, std::size_t pointCountToUse, std::string &outputErrorStr)
Build the Delaunay mesh on top a set of 2D points.
virtual void linkMeshWith(GenericIndexedCloud *aCloud, bool passOwnership=false)
Associate this mesh to a point cloud.
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
int getPointsInSphericalNeighbourhood(const CCVector3 &sphereCenter, PointCoordinateType radius, NeighboursSet &neighbours, unsigned char level) const
Returns the points falling inside a sphere.
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
std::size_t getPointsInCylindricalNeighbourhood(CylindricalNeighbourhood ¶ms) const
Returns the points falling inside a cylinder.
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
A generic 3D point cloud with index-based point access.
virtual unsigned size() const =0
Returns the number of triangles.
const GridElement & getValue(int i, int j, int k) const
Returns the value of a given cell (const version)
const CCVector3 * getGravityCenter()
Returns gravity center.
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
void forEach(GenericCloud::genericPointAction action) override
void setCurrentOutScalarField(int index)
Sets the OUTPUT scalar field.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
void setCurrentScalarField(int index)
Sets both the INPUT & OUTPUT scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
bool renameScalarField(int index, const char *newName)
Renames a specific scalar field.
const CCVector3 * getPoint(unsigned index) const override
bool enableScalarField() override
ScalarField * getCurrentInScalarField() const
Returns the scalar field currently associated to the cloud input.
bool isClosed() const
Returns whether the polyline is closed or not.
A very simple point cloud (no point duplication)
unsigned size() const override
Returns the number of points.
virtual void clear(bool releaseMemory=false)
Clears the cloud.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
A simple scalar field (to be associated to a point cloud)
void fill(ScalarType fillValue=0)
Fills the array with a particular value.
virtual void computeMinAndMax()
Determines the min and max values.
ScalarType getMin() const
Returns the minimum value.
void addElement(ScalarType value)
ScalarType & getValue(std::size_t index)
void computeMeanAndVariance(ScalarType &mean, ScalarType *variance=nullptr) const
void setValue(std::size_t index, ScalarType value)
const char * getName() const
Returns scalar field name.
bool reserveSafe(std::size_t count)
Reserves memory (no exception thrown)
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
unsigned currentSize() const
ScalarType getMax() const
Returns the maximum value.
bool isValid() const
Returns matrix validity.
bool build(double maxError, DistanceComputationTools::ERROR_MEASURES errorMeasure=DistanceComputationTools::RMS, unsigned minPointCountPerCell=3, unsigned maxPointCountPerCell=0, GenericProgressCallback *progressCb=nullptr)
Builds KD-tree.
Dialog to interactively edit the camera pose parameters.
bool start() override
Starts process.
bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
static QString GetMinVersionForFileVersion(short fileVersion)
Dialog to interactively edit the camera pose parameters.
bool setCameraTool(ecvGenericCameraTool *tool)
bool start() override
Starts process.
bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
static Rgb Random(bool lightOnly=true)
Generates a random color.
static void ReleaseInstance(bool flush=true)
Releases unique instance.
static void Init(QListWidget *textDisplay=nullptr, QWidget *parentWidget=nullptr, MainWindow *parentWindow=nullptr, bool redirectToStdOut=false)
Inits console (and optionaly associates it with a text output widget)
static void EnableQtMessages(bool state)
Whether to show Qt messages (qDebug / qWarning / etc.) in Console.
static bool QtMessagesEnabled()
static bool SelectEntities(const ccHObject::Container &entities, std::vector< int > &indexes, QWidget *parent=0, QString label=QString())
Static shortcut: multi-selection mode.
bool setInputEntity(ccHObject *entity)
Adds an entity to the 'selected' entities set.
virtual bool start() override
Starts process.
virtual bool linkWith(QWidget *win) override
Links the overlay dialog with a MDI window.
Generic visualizer 3D interface.
static bool Handle(const CCVector3d &P, double diagonal, Mode mode, bool useInputCoordinatesShiftIfPossible, CCVector3d &coordinatesShift, bool *preserveCoordinateShift, double *coordinatesScale, bool *applyAll=0)
static double MaxCoordinateAbsValue()
Returns the max coordinate (absolute) value.
static bool GetLast(ShiftInfo &info)
static void SetMaxCoordinateAbsValue(double value)
Sets the max coordinate (absolute) value.
static void SetMaxBoundgBoxDiagonal(double value)
Sets the max bounding-box diagonal.
static bool NeedShift(const CCVector3d &P)
Returns whether a particular point (coordinates) is too big or not.
static CCVector3d BestShift(const CCVector3d &P)
Suggests a shift for a given point expressed in global coordinate space.
static bool NeedRescale(double d)
Returns whether a particular dimension (e.g. diagonal) is too big or not.
static double MaxBoundgBoxDiagonal()
Returns max bounding-box diagonal.
static double BestScale(double d)
Layout manager for MainWindow.
void registerBottomDockWidget(QDockWidget *dockWidget)
Register a dock widget to be placed at the bottom.
void registerLeftSideToolBar(QToolBar *toolbar)
Register a toolbar to be placed on the left side.
void restoreGUILayout(bool forceDefault=false)
Restore GUI layout from settings.
void setToolbarIconSize(QToolBar *toolbar, int screenWidth)
bool restoreCustomLayout()
Restore previously saved custom layout.
void registerRightSideToolBar(QToolBar *toolbar)
Register a toolbar to be placed on the right side.
void saveGUILayout()
Save current GUI layout to settings.
void registerRightSideDockWidget(QDockWidget *dockWidget)
Register a dock widget to be placed on the right side.
void restoreDefaultLayout()
Restore the default layout.
void saveCustomLayout()
Save current layout as custom layout.
bool haveSelection() const
Checks if we have any selections.
ConsoleMessageLevel
Console message level (see dispToConsole)
bool haveOneSelection() const
Checks if we have exactly one selection.
Main application options.
static void Set(const ecvOptions &options)
Sets parameters.
bool normalsDisplayedByDefault
Whether to display the normals by default or not.
bool askForConfirmationBeforeQuitting
Ask for confirmation before quitting.
void toPersistentSettings() const
Saves to persistent DB.
static const ecvOptions & Instance()
Returns the stored values of each parameter.
static const QString SelectedOutputFilterCloud()
static const QString AutoShowCenter()
static const QString CurrentTheme()
static const QString MainWinState()
static const QString CurrentPath()
static const QString GlobalShift()
static const QString MainWinGeom()
static const QString MaxAbsCoord()
static const QString AutoPickRotationCenter()
static const QString MaxAbsDiag()
static const QString SelectedOutputFilterPoly()
static const QString DuplicatePointsGroup()
static const QString SelectedOutputFilterMesh()
static const QString ThemeSettings()
static const QString AutoShowReconstructionToolBar()
static const QString SaveFile()
static const QString DuplicatePointsMinDist()
static const QString DoNotRestoreWindowGeometry()
static const QString LoadFile()
static const QString SelectedInputFilter()
Dialog for cloud sphere or cloud plane comparison setting.
Graphical progress indicator (thread-safe)
QMenu * menu()
Returns a "most recently used file" menu.
void addFilePath(const QString &filePath)
Adds a file path to the recently used menu.
Dialog to choose which dimension(s) (X, Y or Z) should be exported as SF(s)
static QVariant getValue(const QString §ion, const QString &key, const QVariant &defaultValue=QVariant())
static void setValue(const QString §ion, const QString &key, const QVariant &value)
Dialog for selection of cloud center.
void restoreShortcutsFromQSettings() const
Standard parameters for GL displays/viewports.
__host__ __device__ int2 abs(int2 v)
#define ecvApp
Mimic Qt's qApp for easy access to the application instance.
#define CC_DEFAULT_MESH_VERT_FLAGS_SF_NAME
#define CC_DEFAULT_DEG_SCATTERING_ANGLES_SF_NAME
#define CC_ORIGINAL_CLOUD_INDEX_SF_NAME
#define CC_DEFAULT_RAD_SCATTERING_ANGLES_SF_NAME
#define CC_DEFAULT_RANGES_SF_NAME
#define CC_TEMP_DISTANCES_DEFAULT_SF_NAME
#define CC_DEFAULT_SQUARED_RANGES_SF_NAME
#define CC_CONNECTED_COMPONENTS_DEFAULT_LABEL_NAME
#define CC_CLOUD2PRIMITIVE_DISTANCES_DEFAULT_SF_NAME
#define CC_CLOUD2PRIMITIVE_SIGNED_DISTANCES_DEFAULT_SF_NAME
void ConvertToGroup(const ccHObject::Container &origin, ccHObject &dest, int dependencyFlags=ccHObject::DP_NONE)
Puts all entities inside a container in a group.
static double dist(double x1, double y1, double x2, double y2)
constexpr QRegularExpression::PatternOption CaseInsensitive
constexpr Qt::SplitBehavior SkipEmptyParts
QTextStream & endl(QTextStream &stream)
static const QString THEME_DEFAULT
static const QString THEME_MATERIALDARK
static const QString THEME_MATERIALLIGHT
static const QString THEME_LIGHTBLUE
static const QString THEME_PSBLACK
static const QString THEME_DarkGRAY
static const QString THEME_ONEDARK
static const QString THEME_NORD
static const QString THEME_FLATBLACK
static const QString THEME_LIGHTGRAY
static const QString THEME_MACOS
static const QString THEME_LIGHTBLACK
static const QString THEME_BF
static const QString THEME_GRUVBOX
static const QString THEME_DarkBLACK
static const QString THEME_DARKBLUE
static const QString THEME_FLUENT
static const QString THEME_TOKYONIGHT
static const QString THEME_GRAY
static const QString THEME_TEST
static const QString THEME_CATPPUCCIN
static const QString THEME_PARAVIEW
static const QString THEME_BLUE
static const QString THEME_DRACULA
static const QString THEME_SILVER
static const QString THEME_FLATWHITE
static const QString THEME_BLACK
bool VoxelSampling(const ccHObject::Container &selectedEntities, ccHObject::Container &outEntities, QWidget *parent)
bool exportNormalToSF(const ccHObject::Container &selectedEntities, QWidget *parent, bool *exportDimensions)
bool invertNormals(const ccHObject::Container &selectedEntities)
bool sfSetAsCoord(const ccHObject::Container &selectedEntities, QWidget *parent)
bool sfRename(const ccHObject::Container &selectedEntities, QWidget *parent)
bool convertTextureToColor(const ccHObject::Container &selectedEntities, QWidget *parent)
bool computeOctree(const ccHObject::Container &selectedEntities, QWidget *parent)
bool orientNormalsFM(const ccHObject::Container &selectedEntities, QWidget *parent)
bool sfFromColor(const ccHObject::Container &selectedEntities, QWidget *parent)
bool changeColorLevels(const ccHObject::Container &selectedEntities, QWidget *parent)
bool computeStatParams(const ccHObject::Container &selectedEntities, QWidget *parent)
bool orientNormalsMST(const ccHObject::Container &selectedEntities, QWidget *parent)
bool interpolateColors(const ccHObject::Container &selectedEntities, QWidget *parent)
Interpolate colors from on entity and transfer them to another one.
bool rgbToGreyScale(const ccHObject::Container &selectedEntities)
bool ConvexHull(const ccHObject::Container &selectedEntities, ccHObject::Container &outEntities, QWidget *parent)
bool clearProperty(ccHObject::Container selectedEntities, CLEAR_PROPERTY property, QWidget *parent)
bool sfGaussianFilter(const ccHObject::Container &selectedEntities, ccPointCloud::RgbFilterOptions filterParams, QWidget *parent)
bool sfAddIdField(const ccHObject::Container &selectedEntities)
bool toggleProperty(const ccHObject::Container &selectedEntities, TOGGLE_PROPERTY property)
bool enhanceRGBWithIntensities(const ccHObject::Container &selectedEntities, QWidget *parent)
bool setColorGradient(const ccHObject::Container &selectedEntities, QWidget *parent)
bool exportCoordToSF(const ccHObject::Container &selectedEntities, QWidget *parent)
bool setColor(ccHObject::Container selectedEntities, bool colorize, QWidget *parent)
bool interpolateSFs(const ccHObject::Container &selectedEntities, ecvMainAppInterface *app)
Interpolate scalar fields from on entity and transfer them to another one.
bool RansacSegmentation(const ccHObject::Container &selectedEntities, ccHObject::Container &outEntities, QWidget *parent)
bool convertNormalsTo(const ccHObject::Container &selectedEntities, NORMAL_CONVERSION_DEST dest)
Converts a cloud's normals.
bool importToSF(const ccHObject::Container &selectedEntities, const std::vector< std::vector< ScalarType >> &scalarsVector, const std::string &name)
bool sfArithmetic(const ccHObject::Container &selectedEntities, QWidget *parent)
bool DBScanCluster(const ccHObject::Container &selectedEntities, QWidget *parent)
bool processMeshSF(const ccHObject::Container &selectedEntities, ccMesh::MESH_SCALAR_FIELD_PROCESS process, QWidget *parent)
bool sfConvertToRandomRGB(const ccHObject::Container &selectedEntities, QWidget *parent)
bool computeNormals(const ccHObject::Container &selectedEntities, QWidget *parent)
bool sfConvertToRGB(const ccHObject::Container &selectedEntities, QWidget *parent)
bool rgbGaussianFilter(const ccHObject::Container &selectedEntities, ccPointCloud::RgbFilterOptions filterParams, QWidget *parent)
bool ApplyScaleMatchingAlgorithm(ScaleMatchingAlgorithm algo, ccHObject::Container &entities, double icpRmsDiff, int icpFinalOverlap, unsigned refEntityIndex, QWidget *parent)
bool ApplyCCLibAlgorithm(CC_LIB_ALGORITHM algo, ccHObject::Container &entities, QWidget *parent, void **additionalParameters)
bool ComputeGeomCharacteristics(const GeomCharacteristicSet &characteristics, PointCoordinateType radius, ccHObject::Container &entities, const CCVector3 *roughnessUpDir, QWidget *parent)
ScaleMatchingAlgorithm
Scale matching algorithms.
std::vector< GeomCharacteristic > GeomCharacteristicSet
Set of GeomCharacteristic instances.
PointCoordinateType GetDefaultCloudKernelSize(ccGenericPointCloud *cloud, unsigned knn)
Returns a default first guess for algorithms kernel size (one cloud)
MemoryInfo getMemoryInfo()
void Resize(const core::Tensor &src_im, core::Tensor &dst_im, Image::InterpType interp_type)
float RadiansToDegrees(int radians)
Convert radians to degrees.
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
float DegreesToRadians(int degrees)
Convert degrees to radians.
TRIANGULATION_TYPES
Triangulation types.
@ DELAUNAY_2D_BEST_LS_PLANE
@ DELAUNAY_2D_AXIS_ALIGNED
std::vector< ReferenceCloud * > ReferenceCloudContainer
A standard container to store several subsets of points.
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
constexpr Rgb darkGrey(MAX/2, MAX/2, MAX/2)
constexpr Rgb green(0, MAX, 0)
QString defaultDocPath()
Shortcut for getting the documents location path.
void DisplayLockedVerticesWarning(const QString &meshName, bool displayAsError)
Display a warning or error for locked verts.
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
static bool DescendingCompOperator(const ComponentIndexAndSize &a, const ComponentIndexAndSize &b)
ComponentIndexAndSize(unsigned i, unsigned s)
Generic loading parameters.
CCVector3d * coordinatesShift
If applicable, applied shift on load (optional)
ecvGlobalShiftManager::Mode shiftHandlingMode
How to handle big coordinates.
QWidget * parentWidget
Parent widget (if any)
bool alwaysDisplayLoadDialog
bool * coordinatesShiftEnabled
Whether shift on load has been applied after loading (optional)
Generic saving parameters.
QWidget * parentWidget
Parent widget (if any)
bool alwaysDisplaySaveDialog
ccGenericPointCloud * cloud
Cloud.
QSharedPointer< LensDistortionParameters > Shared
Shared pointer type.
QSharedPointer< Grid > Shared
Shared type.
RGB_FILTER_TYPES filterType
unsigned char level
subdivision level at which to apply the extraction process
PointCoordinateType maxHalfLength
Cylinder (half) length.
NeighboursSet neighbours
Neighbour points falling inside the cylinder.
CCVector3 center
Cylinder center.
PointCoordinateType radius
Cylinder radius.
CCVector3 dir
Cylinder axis (direction)
Triangle described by the indexes of its 3 vertices.
Precise statistics about current selection.
unsigned displayedNumPrecision
Displayed numbers precision.
Backup "context" for an object.