ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ccMPlaneDlgController.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 // app
11 #include <ecvPickingHub.h>
12 
13 // Qt
14 #include <QtCompat.h>
15 
16 #include "qfiledialog.h"
17 
18 constexpr char const *MPLANE_PLANE_NAME = "Fitting-plane";
19 
21  : QObject(), m_app(app) {
22  m_device = std::make_unique<ccMeasurementDevice>(m_app);
23  m_dialog = new ccMPlaneDlg((QWidget *)m_app->getMainWindow());
24  m_dialog->linkWith(m_app->getActiveWindow());
25 
26  connect(m_dialog, &ccMPlaneDlg::signalCloseButtonPressed, this,
28  connect(m_dialog, &ccMPlaneDlg::signalTabChanged, this,
30  connect(m_dialog, &ccMPlaneDlg::signalMeasureNameChanged, this,
32  connect(m_dialog, &ccMPlaneDlg::signalMeasurementModeChanged, this,
34  connect(m_dialog, &ccMPlaneDlg::signalFittingPointDelete, this,
36  connect(m_dialog, &ccMPlaneDlg::signalShowNormalCheckBoxClicked, this,
38  connect(m_dialog, &ccMPlaneDlg::signalSaveButtonClicked, this,
40 }
41 
43  m_app->freezeUI(true);
44  m_selectedCloud = selectedCloud;
45 
46  registerDialog();
47 
48  loadDataFromSelectedCloud();
49  startPicking();
50 }
51 
53  m_app->freezeUI(false);
54 
55  m_dialog->clearFittingPoints();
56  m_dialog->clearMeasurementPoints();
57 
58  m_dialog->stop(true);
59  m_app->unregisterOverlayDialog(m_dialog);
61  // m_app->getActiveWindow()->redraw(true, false);
62  m_app->refreshSelected(true);
63 
64  stopPicking();
65 }
66 
67 void ccMPlaneDlgController::onNewTab(int tabIndex) {
68  m_mode = tabIndex == 0 ? CC_Mode::CC_POINT_SELECTION
69  : CC_Mode::CC_MEASUREMENT;
70 }
71 
72 void ccMPlaneDlgController::onMeasureNameChanged(QTableWidgetItem *item) {
73  if (!m_data->renameMeasurement(item->text(), item->row())) {
74  m_app->dispToConsole(
75  QString("[MPlane] Measurement point %1 already exists!")
76  .arg(item->text()),
78  m_dialog->renameMeasurement(
79  m_data->getMeasurementPoints()[item->row()].getName(),
80  item->row());
81  }
82 }
83 
85  m_signedMeasurement = m_dialog->isSignedMeasurement();
86  updateScalarfield();
87  updateMeasurements();
88 }
89 
91  m_data->deleteFittingPoint(index);
92 
93  updateFittingPoints();
94  updatAllMeasurementEntities();
95 
96  m_dialog->selectFittingPoint(m_data->getActualFittingPointIndex());
97  m_app->refreshAll();
98  m_app->updateUI();
99  // m_selectedCloud->refreshDisplay();
100 }
101 
103  m_showNormal = checked;
104  m_data->getPlane()->showNormalVector(checked);
106  m_data->getPlane()->setRedrawFlagRecursive(true);
107  m_data->getPlane()->redrawDisplay();
108 
109  // m_selectedCloud->prepareDisplayForRefresh();
110  // m_selectedCloud->refreshDisplay();
111 }
112 
114  QString fileName = QFileDialog::getSaveFileName(
115  m_dialog, tr("Save Measurements"), "",
116  tr("Comma-separated values (*.csv);;All Files (*)"));
117  QFile file(fileName);
118  if (file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
119  QTextStream stream(&file);
120  stream << "measurement,x-coord,y-coord,z-coord,distance"
121  << QtCompat::endl;
122  for (ccMPlanePoint point : m_data->getMeasurementPoints()) {
123  stream << point.getName() << "," << point.getCoordinates().x << ","
124  << point.getCoordinates().y << ","
125  << point.getCoordinates().z << "," << point.getDistance()
126  << QtCompat::endl;
127  }
128  }
129 }
130 
132  const ccPickingListener::PickedItem &item) {
133  ccHObject *entity = item.entity;
134  if (entity && entity->isKindOf(CV_TYPES::POINT_CLOUD)) {
135  ccPointCloud *sourceCloud = static_cast<ccPointCloud *>(entity);
136  if (sourceCloud != m_selectedCloud) {
137  m_app->dispToConsole(
138  "[MPlane] Can only pick points from selected cloud.",
140  } else {
141  if (m_mode == CC_Mode::CC_POINT_SELECTION) {
142  pickFittingPoint(item);
143  updatAllMeasurementEntities();
144  m_dialog->selectFittingPoint(
145  m_data->getActualFittingPointIndex());
146  } else {
147  pickMeasurementPoint(item);
148  }
149  }
150  }
151 }
152 
153 void ccMPlaneDlgController::loadDataFromSelectedCloud() {
154  m_data = std::make_unique<ccMeasurementRecorder>(m_selectedCloud, m_app);
155  m_data->loadDataFromSelectedCloud();
156 
157  updateFittingPoints();
158  updatAllMeasurementEntities();
159 
160  m_dialog->selectFittingPoint(m_data->getActualFittingPointIndex());
161 }
162 
163 void ccMPlaneDlgController::registerDialog() {
164  m_app->registerOverlayDialog(m_dialog, Qt::Corner::TopLeftCorner);
165  m_dialog->start();
167  m_app->refreshSelected(true);
168 }
169 
170 void ccMPlaneDlgController::startPicking() {
171  if (!m_app->pickingHub()) {
172  m_app->dispToConsole(
173  "[MPlane] Could not retrieve valid picking hub. Measurement "
174  "aborted.",
176  }
177 
178  if (!m_app->pickingHub()->addListener(this, true, true)) {
179  m_app->dispToConsole(
180  "[MPlane] Another tool is already using the picking mechanism. "
181  "Stop it first",
183  }
184 
185  m_app->getActiveWindow()->installEventFilter(this);
186 }
187 
188 void ccMPlaneDlgController::stopPicking() {
189  m_app->pickingHub()->removeListener(this);
190  m_app->getActiveWindow()->removeEventFilter(this);
191 }
192 
193 void ccMPlaneDlgController::pickFittingPoint(
194  const ccPickingListener::PickedItem &item) {
195  m_data->addFittingPoint(item);
196 
197  // Display fitting point in table
198  m_dialog->addFittingPoint(m_data->getActualFittingPointIndex() - 1,
199  item.P3D);
200 
201  // Update display: Necessary for Stereo-build
202  // m_selectedCloud->prepareDisplayForRefresh();
203  m_app->refreshSelected();
204 }
205 
206 void ccMPlaneDlgController::pickMeasurementPoint(
207  const ccPickingListener::PickedItem &item) {
208  float distance = m_device->measurePointToPlaneDistance(
209  m_data->getPlane(), item.P3D, m_signedMeasurement);
210  m_data->addMeasurementPoint(item, distance);
211  const auto &point = m_data->getMeasurementPoints().back();
212  m_dialog->addMeasurementPoint(point.getName(), point.getDistance());
213 
214  // Update display: Necessary for Stereo-build
215  // m_selectedCloud->prepareDisplayForRefresh();
216  // m_app->refreshAll();
217  m_app->refreshSelected();
218 }
219 
220 void ccMPlaneDlgController::updatePlane() {
221  ccPlane *plane = m_device->fitPlaneToPoints(m_data->getFittingPoints(),
223  plane->showNormalVector(m_showNormal);
224  m_data->setPlane(plane);
225 }
226 
227 void ccMPlaneDlgController::updateScalarfield() {
228  m_device->createScalarFieldForCloud(m_data->getPlane(), m_selectedCloud,
229  m_signedMeasurement);
230 }
231 
232 void ccMPlaneDlgController::updateMeasurements() {
233  m_dialog->clearMeasurementPoints();
234  const std::vector<ccMPlanePoint> &points = m_data->getMeasurementPoints();
235  for (auto i = 0; i < points.size(); ++i) {
236  float distance = m_device->measurePointToPlaneDistance(
237  m_data->getPlane(), points[i].getCoordinates(),
238  m_signedMeasurement);
239  m_data->updateMeasurement(distance, i);
240  m_dialog->addMeasurementPoint(points[i].getName(), distance);
241  }
242 }
243 
244 void ccMPlaneDlgController::updateFittingPoints() {
245  m_dialog->clearFittingPoints();
246  const std::vector<ccMPlanePoint> &points = m_data->getFittingPoints();
247  for (auto i = 0; i < points.size(); ++i) {
248  m_data->renameFittingPoint(QString("Point %1").arg(i), i);
249  m_dialog->addFittingPoint(i, points[i].getCoordinates());
250  }
251  // m_selectedCloud->prepareDisplayForRefresh();
252  // m_app->refreshAll();
253  m_app->refreshSelected();
254 }
255 
256 void ccMPlaneDlgController::updatAllMeasurementEntities() {
257  if (m_data->getFittingPointAmount() >= 3) {
258  m_dialog->enableMeasurementTab(true);
259  updatePlane();
260  updateScalarfield();
261  updateMeasurements();
262  } else {
263  m_dialog->enableMeasurementTab(false);
264  m_data->deletePlane();
265  m_device->deleteScalarFieldFromCloud(m_selectedCloud);
266  }
267 }
int points
constexpr char const * MPLANE_PLANE_NAME
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void onMeasureNameChanged(QTableWidgetItem *)
virtual void onItemPicked(const ccPickingListener::PickedItem &pi) override
ccMPlaneDlgController(ecvMainAppInterface *app)
void onFittingPointDelete(int index)
void onNormalCheckBoxClicked(bool checked)
void openDialog(ccPointCloud *selectedCloud)
void signalShowNormalCheckBoxClicked(bool checked)
void enableMeasurementTab(bool enable)
void signalMeasureNameChanged(QTableWidgetItem *item)
void selectFittingPoint(unsigned int rowIndex)
void addFittingPoint(int rowIndex, const CCVector3 &point)
void renameMeasurement(const QString &name, unsigned int rowIndex)
void signalFittingPointDelete(int index)
void clearFittingPoints()
bool isSignedMeasurement() const
void signalTabChanged(int tab)
void addMeasurementPoint(const QString &name, float distance)
void signalSaveButtonClicked()
void clearMeasurementPoints()
void signalCloseButtonPressed()
void signalMeasurementModeChanged()
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
virtual void stop(bool accepted)
Stops process/dialog.
virtual bool start()
Starts process.
virtual bool linkWith(QWidget *win)
Links the overlay dialog with a MDI window.
void removeListener(ccPickingListener *listener, bool autoStopPickingIfLast=true)
Removes a listener.
bool addListener(ccPickingListener *listener, bool exclusive=false, bool autoStartPicking=true, ecvDisplayTools::PICKING_MODE mode=ecvDisplayTools::POINT_OR_TRIANGLE_PICKING)
Adds a listener.
void showNormalVector(bool state)
Show normal vector.
Plane (primitive)
Definition: ecvPlane.h:18
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
static void SetRedrawRecursive(bool redraw=false)
Main application interface (for plugins)
virtual void updateUI()=0
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual QWidget * getActiveWindow()=0
virtual void freezeUI(bool state)=0
Freezes/unfreezes UI.
virtual void refreshSelected(bool only2D=false, bool forceRedraw=true)=0
virtual void updateOverlayDialogsPlacement()=0
Forces the update of all registered MDI 'overlay' dialogs.
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual ccPickingHub * pickingHub()
virtual void registerOverlayDialog(ccOverlayDialog *dlg, Qt::Corner pos)=0
Registers a MDI area 'overlay' dialog.
virtual void unregisterOverlayDialog(ccOverlayDialog *dlg)=0
Unregisters a MDI area 'overlay' dialog.
@ POINT_CLOUD
Definition: CVTypes.h:104
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
ccGenericPointCloud * sourceCloud
Definition: lsd.c:149
int y
Definition: lsd.c:149
int x
Definition: lsd.c:149