| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# This program is free software; you can redistribute it and/or modify #
- //# it under the terms of the GNU General Public License as published by #
- //# the Free Software Foundation; version 2 or later of the License. #
- //# #
- //# This program is distributed in the hope that it will be useful, #
- //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
- //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
- //# GNU General Public License for more details. #
- //# #
- //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
- //# #
- //##########################################################################
- #include "ccPointListPickingDlg.h"
- //Qt
- #include <QApplication>
- #include <QClipboard>
- #include <QFileDialog>
- #include <QMenu>
- #include <QMessageBox>
- #include <QSettings>
- //CCCoreLib
- #include <CCConst.h>
- //qCC_db
- #include <cc2DLabel.h>
- #include <ccLog.h>
- #include <ccPointCloud.h>
- #include <ccPolyline.h>
- //qCC_io
- #include <AsciiFilter.h>
- //qCC_gl
- #include <ccGLWindowInterface.h>
- //local
- #include "mainwindow.h"
- #include "db_tree/ccDBRoot.h"
- //system
- #include <cassert>
- //semi persistent settings
- static int s_pickedPointsStartIndex = 0;
- static bool s_showGlobalCoordsCheckBoxChecked = false;
- static const char s_pickedPointContainerName[] = "Picked points list";
- static const char s_defaultLabelBaseName[] = "Point #";
- ccPointListPickingDlg::ccPointListPickingDlg(ccPickingHub* pickingHub, QWidget* parent)
- : ccPointPickingGenericInterface(pickingHub, parent)
- , Ui::PointListPickingDlg()
- , m_associatedEntity(nullptr)
- , m_lastPreviousID(0)
- , m_orderedLabelsContainer(nullptr)
- {
- setupUi(this);
- exportToolButton->setPopupMode(QToolButton::MenuButtonPopup);
- QMenu* menu = new QMenu(exportToolButton);
- QAction* exportASCII_xyz = menu->addAction("x,y,z");
- QAction* exportASCII_ixyz = menu->addAction("local index,x,y,z");
- QAction* exportASCII_gxyz = menu->addAction("global index,x,y,z");
- QAction* exportASCII_lxyz = menu->addAction("label name,x,y,z");
- QAction* exportToNewCloud = menu->addAction("new cloud");
- QAction* exportToNewPolyline = menu->addAction("new polyline");
- exportToolButton->setMenu(menu);
- tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- startIndexSpinBox->setValue(s_pickedPointsStartIndex);
- showGlobalCoordsCheckBox->setChecked(s_showGlobalCoordsCheckBoxChecked);
- connect(cancelToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::cancelAndExit);
- connect(revertToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::removeLastEntry);
- connect(validToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::applyAndExit);
- connect(exportToolButton, &QAbstractButton::clicked, exportToolButton, &QToolButton::showMenu);
- connect(exportASCII_xyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_xyz);
- connect(exportASCII_ixyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_ixyz);
- connect(exportASCII_gxyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_gxyz);
- connect(exportASCII_lxyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_lxyz);
- connect(exportToNewCloud, &QAction::triggered, this, &ccPointListPickingDlg::exportToNewCloud);
- connect(exportToNewPolyline, &QAction::triggered, this, &ccPointListPickingDlg::exportToNewPolyline);
-
- connect(markerSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &ccPointListPickingDlg::markerSizeChanged);
- connect(startIndexSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &ccPointListPickingDlg::startIndexChanged);
-
- connect(showGlobalCoordsCheckBox, &QAbstractButton::clicked, this, &ccPointListPickingDlg::updateList);
- updateList();
- }
- unsigned ccPointListPickingDlg::getPickedPoints(std::vector<cc2DLabel*>& pickedPoints)
- {
- pickedPoints.clear();
- if (m_orderedLabelsContainer)
- {
- //get all labels
- ccHObject::Container labels;
- unsigned count = m_orderedLabelsContainer->filterChildren(labels, false, CC_TYPES::LABEL_2D);
- try
- {
- pickedPoints.reserve(count);
- }
- catch (const std::bad_alloc&)
- {
- ccLog::Error("Not enough memory!");
- return 0;
- }
- for (unsigned i = 0; i < count; ++i)
- {
- if (labels[i]->isA(CC_TYPES::LABEL_2D)) //Warning: cc2DViewportLabel is also a kind of 'CC_TYPES::LABEL_2D'!
- {
- cc2DLabel* label = static_cast<cc2DLabel*>(labels[i]);
- if (label->isVisible() && label->size() == 1)
- {
- pickedPoints.push_back(label);
- }
- }
- }
- }
- return static_cast<unsigned>(pickedPoints.size());
- }
- void ccPointListPickingDlg::linkWithEntity(ccHObject* entity)
- {
- m_associatedEntity = entity;
- m_lastPreviousID = 0;
- if (m_associatedEntity)
- {
- //find default container
- m_orderedLabelsContainer = nullptr;
- ccHObject::Container groups;
- m_associatedEntity->filterChildren(groups, true, CC_TYPES::HIERARCHY_OBJECT);
- for (ccHObject::Container::const_iterator it = groups.begin(); it != groups.end(); ++it)
- {
- if ((*it)->getName() == s_pickedPointContainerName)
- {
- m_orderedLabelsContainer = *it;
- break;
- }
- }
- std::vector<cc2DLabel*> previousPickedPoints;
- unsigned count = getPickedPoints(previousPickedPoints);
- //find highest unique ID among the VISIBLE labels
- for (unsigned i = 0; i < count; ++i)
- {
- m_lastPreviousID = std::max(m_lastPreviousID, previousPickedPoints[i]->getUniqueID());
- }
- }
- ccShiftedObject* shifted = ccHObjectCaster::ToShifted(entity);
- showGlobalCoordsCheckBox->setEnabled(shifted ? shifted->isShifted() : false);
- updateList();
- }
- void ccPointListPickingDlg::cancelAndExit()
- {
- ccDBRoot* dbRoot = MainWindow::TheInstance()->db();
- if (!dbRoot)
- {
- assert(false);
- return;
- }
- if (m_orderedLabelsContainer)
- {
- //Restore previous state
- if (!m_toBeAdded.empty())
- {
- dbRoot->removeElements(m_toBeAdded);
- }
- for (auto & object : m_toBeDeleted)
- {
- object->prepareDisplayForRefresh();
- object->setEnabled(true);
- }
- if (m_orderedLabelsContainer->getChildrenNumber() == 0)
- {
- dbRoot->removeElement(m_orderedLabelsContainer);
- //m_associatedEntity->removeChild(m_orderedLabelsContainer);
- m_orderedLabelsContainer = nullptr;
- }
- }
- m_toBeDeleted.resize(0);
- m_toBeAdded.resize(0);
- m_associatedEntity = nullptr;
- m_orderedLabelsContainer = nullptr;
- MainWindow::RefreshAllGLWindow();
- updateList();
- stop(false);
- }
- void ccPointListPickingDlg::exportToNewCloud()
- {
- if (!m_associatedEntity)
- return;
- //get all labels
- std::vector<cc2DLabel*> labels;
- unsigned count = getPickedPoints(labels);
- if (count != 0)
- {
- ccPointCloud* cloud = new ccPointCloud();
- if (cloud->reserve(count))
- {
- cloud->setName("Picking list");
- for (unsigned i = 0; i < count; ++i)
- {
- const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
- cloud->addPoint(PP.getPointPosition());
- }
- cloud->setDisplay(m_associatedEntity->getDisplay());
- //retrieve Shift & Scale values
- ccShiftedObject* shifted = ccHObjectCaster::ToShifted(m_associatedEntity);
- if (shifted)
- {
- cloud->copyGlobalShiftAndScale(*shifted);
- }
- else
- {
- assert(false);
- }
- MainWindow::TheInstance()->addToDB(cloud);
- }
- else
- {
- ccLog::Error("Can't export picked points as point cloud: not enough memory!");
- delete cloud;
- cloud = nullptr;
- }
- }
- else
- {
- ccLog::Error("Pick some points first!");
- }
- }
- void ccPointListPickingDlg::exportToNewPolyline()
- {
- if (!m_associatedEntity)
- return;
- //get all labels
- std::vector<cc2DLabel*> labels;
- unsigned count = getPickedPoints(labels);
- if (count > 1)
- {
- //we create an "independent" polyline
- ccPointCloud* vertices = new ccPointCloud("vertices");
- ccPolyline* polyline = new ccPolyline(vertices);
- if (!vertices->reserve(count) || !polyline->reserve(count))
- {
- ccLog::Error("Not enough memory!");
- delete vertices;
- delete polyline;
- return;
- }
- for (unsigned i = 0; i < count; ++i)
- {
- const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
- vertices->addPoint(PP.getPointPosition());
- }
- polyline->addPointIndex(0, count);
- polyline->setVisible(true);
- vertices->setEnabled(false);
- //retrieve Shift & Scale values
- ccGenericPointCloud* asCloud = ccHObjectCaster::ToGenericPointCloud(m_associatedEntity);
- if (asCloud)
- {
- polyline->setGlobalShift(asCloud->getGlobalShift());
- polyline->setGlobalScale(asCloud->getGlobalScale());
- }
- else
- {
- assert(false);
- }
- polyline->addChild(vertices);
- polyline->setDisplay_recursive(m_associatedEntity->getDisplay());
- ccShiftedObject* shifted = ccHObjectCaster::ToShifted(m_associatedEntity);
- if (shifted)
- {
- polyline->copyGlobalShiftAndScale(*shifted);
- }
- MainWindow::TheInstance()->addToDB(polyline);
- }
- else
- {
- ccLog::Error("Pick at least two points!");
- }
- }
- void ccPointListPickingDlg::applyAndExit()
- {
- if (m_associatedEntity && !m_toBeDeleted.empty())
- {
- //apply modifications
- MainWindow::TheInstance()->db()->removeElements(m_toBeDeleted); //no need to redraw as they should already be invisible
- m_associatedEntity = nullptr;
- }
- m_toBeDeleted.resize(0);
- m_toBeAdded.resize(0);
- m_orderedLabelsContainer = nullptr;
- updateList();
- stop(true);
- }
- void ccPointListPickingDlg::removeLastEntry()
- {
- if (!m_associatedEntity)
- return;
- //get all labels
- std::vector<cc2DLabel*> labels;
- unsigned count = getPickedPoints(labels);
- if (count == 0)
- return;
- ccHObject* lastVisibleLabel = labels.back();
- if (lastVisibleLabel->getUniqueID() <= m_lastPreviousID)
- {
- //old label: hide it and add it to the 'to be deleted' list (will be restored if process is cancelled)
- lastVisibleLabel->setEnabled(false);
- m_toBeDeleted.push_back(lastVisibleLabel);
- }
- else
- {
- if (!m_toBeAdded.empty())
- {
- assert(m_toBeAdded.back() == lastVisibleLabel);
- m_toBeAdded.pop_back();
- }
- if (m_orderedLabelsContainer)
- {
- if (lastVisibleLabel->getParent())
- {
- lastVisibleLabel->getParent()->removeDependencyWith(lastVisibleLabel);
- lastVisibleLabel->removeDependencyWith(lastVisibleLabel->getParent());
- }
- //m_orderedLabelsContainer->removeChild(lastVisibleLabel);
- MainWindow::TheInstance()->db()->removeElement(lastVisibleLabel);
- }
- else
- m_associatedEntity->detachChild(lastVisibleLabel);
- }
- updateList();
- if (m_associatedWin)
- m_associatedWin->redraw();
- }
- void ccPointListPickingDlg::startIndexChanged(int value)
- {
- if (value != s_pickedPointsStartIndex)
- {
- s_pickedPointsStartIndex = value;
- updateList();
- if (m_associatedWin)
- {
- m_associatedWin->redraw();
- }
- }
- }
- void ccPointListPickingDlg::markerSizeChanged(int size)
- {
- if (size < 1 || !m_associatedWin)
- return;
- //display parameters
- ccGui::ParamStruct guiParams = m_associatedWin->getDisplayParameters();
- if (guiParams.labelMarkerSize != static_cast<unsigned>(size))
- {
- guiParams.labelMarkerSize = static_cast<unsigned>(size);
- m_associatedWin->setDisplayParameters(guiParams,m_associatedWin->hasOverriddenDisplayParameters());
- m_associatedWin->redraw();
- }
- }
- void ccPointListPickingDlg::exportToASCII(ExportFormat format)
- {
- if (!m_associatedEntity)
- return;
- //get all labels
- std::vector<cc2DLabel*> labels;
- unsigned count = getPickedPoints(labels);
- if (count == 0)
- return;
- QSettings settings;
- settings.beginGroup("PointListPickingDlg");
- QString filename = settings.value("filename", "picking_list.txt").toString();
- settings.endGroup();
- filename = QFileDialog::getSaveFileName(this,
- "Export to ASCII",
- filename,
- AsciiFilter::GetFileFilter());
- if (filename.isEmpty())
- return;
- settings.beginGroup("PointListPickingDlg");
- settings.setValue("filename", filename);
- settings.endGroup();
- QFile fp(filename);
- if (!fp.open(QFile::WriteOnly))
- {
- ccLog::Error(QString("Failed to open file '%1' for saving!").arg(filename));
- return;
- }
- //if a global shift exists, ask the user if it should be applied
- CCVector3d shift;
- double scale = 1.0;
- ccGenericPointCloud* asCloud = ccHObjectCaster::ToGenericPointCloud(m_associatedEntity);
- if (asCloud)
- {
- shift = asCloud->getGlobalShift();
- scale = asCloud->getGlobalScale();
- }
- if (shift.norm2() != 0 || scale != 1.0)
- {
- if (QMessageBox::warning( this,
- "Apply global shift",
- "Do you want to apply global shift/scale to exported points?",
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes ) == QMessageBox::No)
- {
- //reset shift
- shift = CCVector3d(0,0,0);
- scale = 1.0;
- }
- }
- //starting index
- unsigned startIndex = static_cast<unsigned>(std::max(0, startIndexSpinBox->value()));
- QTextStream stream(&fp);
- stream.setRealNumberPrecision(12);
- for (unsigned i = 0; i < count; ++i)
- {
- assert(labels[i]->size() == 1);
- const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
- CCVector3 P = PP.getPointPosition();
- switch (format)
- {
- case PLP_ASCII_EXPORT_IXYZ:
- stream << i + startIndex << ',';
- break;
- case PLP_ASCII_EXPORT_GXYZ:
- stream << PP.index << ',';
- break;
- case PLP_ASCII_EXPORT_LXYZ:
- stream << labels[i]->getName() << ',';
- break;
- default:
- //nothing to do
- break;
- }
- stream << static_cast<double>(P.x) / scale - shift.x << ','
- << static_cast<double>(P.y) / scale - shift.y << ','
- << static_cast<double>(P.z) / scale - shift.z << endl;
- }
- ccLog::Print(QString("[I/O] File '%1' saved successfully").arg(filename));
- }
- void ccPointListPickingDlg::updateList()
- {
- //get all labels
- std::vector<cc2DLabel*> labels;
- const int count = static_cast<int>( getPickedPoints(labels) );
- const int oldRowCount = tableWidget->rowCount();
- revertToolButton->setEnabled(count);
- validToolButton->setEnabled(count);
- exportToolButton->setEnabled(count);
- countLineEdit->setText(QString::number(count));
- tableWidget->setRowCount(count);
- if ( count == 0 )
- {
- return;
- }
- // If we have any new rows, create QTableWidgetItems for them
- if ( (count - oldRowCount) > 0 )
- {
- for ( int i = oldRowCount; i < count; ++i )
- {
- tableWidget->setVerticalHeaderItem( i, new QTableWidgetItem );
- for ( int j = 0; j < 4; ++j )
- {
- tableWidget->setItem( i, j, new QTableWidgetItem );
- }
- }
- }
- //starting index
- const int startIndex = startIndexSpinBox->value();
- const int precision = m_associatedWin ? static_cast<int>(m_associatedWin->getDisplayParameters().displayedNumPrecision) : 6;
- const bool showAbsolute = showGlobalCoordsCheckBox->isEnabled() && showGlobalCoordsCheckBox->isChecked();
- for ( int i = 0; i < count; ++i )
- {
- cc2DLabel* label = labels[static_cast<unsigned int>( i )];
- const cc2DLabel::PickedPoint& PP = label->getPickedPoint(0);
- CCVector3 P = PP.getPointPosition();
- CCVector3d Pd = (showAbsolute ? PP.cloudOrVertices()->toGlobal3d(P) : P);
- //point index in list
- tableWidget->verticalHeaderItem( i )->setText( QStringLiteral( "%1" ).arg( i + startIndex ) );
- //update name as well
- if ( label->getUniqueID() > m_lastPreviousID
- || label->getName().startsWith(s_defaultLabelBaseName) ) //DGM: we don't change the name of old labels that have a non-default name
- {
- label->setName(s_defaultLabelBaseName + QString::number(i+startIndex));
- }
- //point absolute index (in cloud)
- tableWidget->item( i, 0 )->setText( QStringLiteral( "%1" ).arg( PP.index ) );
- for ( int j = 0; j < 3; ++j )
- {
- tableWidget->item( i, j + 1 )->setText( QStringLiteral( "%1" ).arg( Pd.u[j], 0, 'f', precision ) );
- }
- }
- tableWidget->scrollToBottom();
- }
- void ccPointListPickingDlg::processPickedPoint(const PickedItem& picked)
- {
- if (!picked.entity || picked.entity != m_associatedEntity || !MainWindow::TheInstance())
- return;
- cc2DLabel* newLabel = new cc2DLabel();
- if (picked.entity->isKindOf(CC_TYPES::POINT_CLOUD))
- {
- newLabel->addPickedPoint(static_cast<ccGenericPointCloud*>(picked.entity), picked.itemIndex, picked.entityCenter);
- }
- else if (picked.entity->isKindOf(CC_TYPES::MESH))
- {
- newLabel->addPickedPoint(static_cast<ccGenericMesh*>(picked.entity), picked.itemIndex, CCVector2d(picked.uvw.x, picked.uvw.y), picked.entityCenter);
- }
- else
- {
- delete newLabel;
- assert(false);
- return;
- }
- newLabel->setVisible(true);
- newLabel->setDisplayedIn2D(false);
- newLabel->displayPointLegend(true);
- newLabel->setCollapsed(true);
- ccGenericGLDisplay* display = m_associatedEntity->getDisplay();
- if (display)
- {
- newLabel->setDisplay(display);
- QSize size = display->getScreenSize();
- newLabel->setPosition( static_cast<float>(picked.clickPoint.x() + 20) / size.width(),
- static_cast<float>(picked.clickPoint.y() + 20) / size.height() );
- }
- //add default container if necessary
- if (!m_orderedLabelsContainer)
- {
- m_orderedLabelsContainer = new ccHObject(s_pickedPointContainerName);
- m_associatedEntity->addChild(m_orderedLabelsContainer);
- m_orderedLabelsContainer->setDisplay(display);
- MainWindow::TheInstance()->addToDB(m_orderedLabelsContainer, false, true, false, false);
- }
- assert(m_orderedLabelsContainer);
- m_orderedLabelsContainer->addChild(newLabel);
- MainWindow::TheInstance()->addToDB(newLabel, false, true, false, false);
- m_toBeAdded.push_back(newLabel);
- //automatically send the new point coordinates to the clipboard
- QClipboard* clipboard = QApplication::clipboard();
- if (clipboard)
- {
- CCVector3 P = newLabel->getPickedPoint(0).getPointPosition();
- int precision = m_associatedWin ? m_associatedWin->getDisplayParameters().displayedNumPrecision : 6;
- int indexInList = startIndexSpinBox->value() + static_cast<int>(m_orderedLabelsContainer->getChildrenNumber()) - 1;
- clipboard->setText(QString("CC_POINT_#%0(%1;%2;%3)").arg(indexInList).arg(P.x, 0, 'f', precision).arg(P.y, 0, 'f', precision).arg(P.z, 0, 'f', precision));
- }
- updateList();
- if (m_associatedWin)
- m_associatedWin->redraw();
- }
|