ccPointListPickingDlg.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 or later of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
  15. //# #
  16. //##########################################################################
  17. #include "ccPointListPickingDlg.h"
  18. //Qt
  19. #include <QApplication>
  20. #include <QClipboard>
  21. #include <QFileDialog>
  22. #include <QMenu>
  23. #include <QMessageBox>
  24. #include <QSettings>
  25. //CCCoreLib
  26. #include <CCConst.h>
  27. //qCC_db
  28. #include <cc2DLabel.h>
  29. #include <ccLog.h>
  30. #include <ccPointCloud.h>
  31. #include <ccPolyline.h>
  32. //qCC_io
  33. #include <AsciiFilter.h>
  34. //qCC_gl
  35. #include <ccGLWindowInterface.h>
  36. //local
  37. #include "mainwindow.h"
  38. #include "db_tree/ccDBRoot.h"
  39. //system
  40. #include <cassert>
  41. //semi persistent settings
  42. static int s_pickedPointsStartIndex = 0;
  43. static bool s_showGlobalCoordsCheckBoxChecked = false;
  44. static const char s_pickedPointContainerName[] = "Picked points list";
  45. static const char s_defaultLabelBaseName[] = "Point #";
  46. ccPointListPickingDlg::ccPointListPickingDlg(ccPickingHub* pickingHub, QWidget* parent)
  47. : ccPointPickingGenericInterface(pickingHub, parent)
  48. , Ui::PointListPickingDlg()
  49. , m_associatedEntity(nullptr)
  50. , m_lastPreviousID(0)
  51. , m_orderedLabelsContainer(nullptr)
  52. {
  53. setupUi(this);
  54. exportToolButton->setPopupMode(QToolButton::MenuButtonPopup);
  55. QMenu* menu = new QMenu(exportToolButton);
  56. QAction* exportASCII_xyz = menu->addAction("x,y,z");
  57. QAction* exportASCII_ixyz = menu->addAction("local index,x,y,z");
  58. QAction* exportASCII_gxyz = menu->addAction("global index,x,y,z");
  59. QAction* exportASCII_lxyz = menu->addAction("label name,x,y,z");
  60. QAction* exportToNewCloud = menu->addAction("new cloud");
  61. QAction* exportToNewPolyline = menu->addAction("new polyline");
  62. exportToolButton->setMenu(menu);
  63. tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
  64. startIndexSpinBox->setValue(s_pickedPointsStartIndex);
  65. showGlobalCoordsCheckBox->setChecked(s_showGlobalCoordsCheckBoxChecked);
  66. connect(cancelToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::cancelAndExit);
  67. connect(revertToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::removeLastEntry);
  68. connect(validToolButton, &QAbstractButton::clicked, this, &ccPointListPickingDlg::applyAndExit);
  69. connect(exportToolButton, &QAbstractButton::clicked, exportToolButton, &QToolButton::showMenu);
  70. connect(exportASCII_xyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_xyz);
  71. connect(exportASCII_ixyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_ixyz);
  72. connect(exportASCII_gxyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_gxyz);
  73. connect(exportASCII_lxyz, &QAction::triggered, this, &ccPointListPickingDlg::exportToASCII_lxyz);
  74. connect(exportToNewCloud, &QAction::triggered, this, &ccPointListPickingDlg::exportToNewCloud);
  75. connect(exportToNewPolyline, &QAction::triggered, this, &ccPointListPickingDlg::exportToNewPolyline);
  76. connect(markerSizeSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &ccPointListPickingDlg::markerSizeChanged);
  77. connect(startIndexSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &ccPointListPickingDlg::startIndexChanged);
  78. connect(showGlobalCoordsCheckBox, &QAbstractButton::clicked, this, &ccPointListPickingDlg::updateList);
  79. updateList();
  80. }
  81. unsigned ccPointListPickingDlg::getPickedPoints(std::vector<cc2DLabel*>& pickedPoints)
  82. {
  83. pickedPoints.clear();
  84. if (m_orderedLabelsContainer)
  85. {
  86. //get all labels
  87. ccHObject::Container labels;
  88. unsigned count = m_orderedLabelsContainer->filterChildren(labels, false, CC_TYPES::LABEL_2D);
  89. try
  90. {
  91. pickedPoints.reserve(count);
  92. }
  93. catch (const std::bad_alloc&)
  94. {
  95. ccLog::Error("Not enough memory!");
  96. return 0;
  97. }
  98. for (unsigned i = 0; i < count; ++i)
  99. {
  100. if (labels[i]->isA(CC_TYPES::LABEL_2D)) //Warning: cc2DViewportLabel is also a kind of 'CC_TYPES::LABEL_2D'!
  101. {
  102. cc2DLabel* label = static_cast<cc2DLabel*>(labels[i]);
  103. if (label->isVisible() && label->size() == 1)
  104. {
  105. pickedPoints.push_back(label);
  106. }
  107. }
  108. }
  109. }
  110. return static_cast<unsigned>(pickedPoints.size());
  111. }
  112. void ccPointListPickingDlg::linkWithEntity(ccHObject* entity)
  113. {
  114. m_associatedEntity = entity;
  115. m_lastPreviousID = 0;
  116. if (m_associatedEntity)
  117. {
  118. //find default container
  119. m_orderedLabelsContainer = nullptr;
  120. ccHObject::Container groups;
  121. m_associatedEntity->filterChildren(groups, true, CC_TYPES::HIERARCHY_OBJECT);
  122. for (ccHObject::Container::const_iterator it = groups.begin(); it != groups.end(); ++it)
  123. {
  124. if ((*it)->getName() == s_pickedPointContainerName)
  125. {
  126. m_orderedLabelsContainer = *it;
  127. break;
  128. }
  129. }
  130. std::vector<cc2DLabel*> previousPickedPoints;
  131. unsigned count = getPickedPoints(previousPickedPoints);
  132. //find highest unique ID among the VISIBLE labels
  133. for (unsigned i = 0; i < count; ++i)
  134. {
  135. m_lastPreviousID = std::max(m_lastPreviousID, previousPickedPoints[i]->getUniqueID());
  136. }
  137. }
  138. ccShiftedObject* shifted = ccHObjectCaster::ToShifted(entity);
  139. showGlobalCoordsCheckBox->setEnabled(shifted ? shifted->isShifted() : false);
  140. updateList();
  141. }
  142. void ccPointListPickingDlg::cancelAndExit()
  143. {
  144. ccDBRoot* dbRoot = MainWindow::TheInstance()->db();
  145. if (!dbRoot)
  146. {
  147. assert(false);
  148. return;
  149. }
  150. if (m_orderedLabelsContainer)
  151. {
  152. //Restore previous state
  153. if (!m_toBeAdded.empty())
  154. {
  155. dbRoot->removeElements(m_toBeAdded);
  156. }
  157. for (auto & object : m_toBeDeleted)
  158. {
  159. object->prepareDisplayForRefresh();
  160. object->setEnabled(true);
  161. }
  162. if (m_orderedLabelsContainer->getChildrenNumber() == 0)
  163. {
  164. dbRoot->removeElement(m_orderedLabelsContainer);
  165. //m_associatedEntity->removeChild(m_orderedLabelsContainer);
  166. m_orderedLabelsContainer = nullptr;
  167. }
  168. }
  169. m_toBeDeleted.resize(0);
  170. m_toBeAdded.resize(0);
  171. m_associatedEntity = nullptr;
  172. m_orderedLabelsContainer = nullptr;
  173. MainWindow::RefreshAllGLWindow();
  174. updateList();
  175. stop(false);
  176. }
  177. void ccPointListPickingDlg::exportToNewCloud()
  178. {
  179. if (!m_associatedEntity)
  180. return;
  181. //get all labels
  182. std::vector<cc2DLabel*> labels;
  183. unsigned count = getPickedPoints(labels);
  184. if (count != 0)
  185. {
  186. ccPointCloud* cloud = new ccPointCloud();
  187. if (cloud->reserve(count))
  188. {
  189. cloud->setName("Picking list");
  190. for (unsigned i = 0; i < count; ++i)
  191. {
  192. const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
  193. cloud->addPoint(PP.getPointPosition());
  194. }
  195. cloud->setDisplay(m_associatedEntity->getDisplay());
  196. //retrieve Shift & Scale values
  197. ccShiftedObject* shifted = ccHObjectCaster::ToShifted(m_associatedEntity);
  198. if (shifted)
  199. {
  200. cloud->copyGlobalShiftAndScale(*shifted);
  201. }
  202. else
  203. {
  204. assert(false);
  205. }
  206. MainWindow::TheInstance()->addToDB(cloud);
  207. }
  208. else
  209. {
  210. ccLog::Error("Can't export picked points as point cloud: not enough memory!");
  211. delete cloud;
  212. cloud = nullptr;
  213. }
  214. }
  215. else
  216. {
  217. ccLog::Error("Pick some points first!");
  218. }
  219. }
  220. void ccPointListPickingDlg::exportToNewPolyline()
  221. {
  222. if (!m_associatedEntity)
  223. return;
  224. //get all labels
  225. std::vector<cc2DLabel*> labels;
  226. unsigned count = getPickedPoints(labels);
  227. if (count > 1)
  228. {
  229. //we create an "independent" polyline
  230. ccPointCloud* vertices = new ccPointCloud("vertices");
  231. ccPolyline* polyline = new ccPolyline(vertices);
  232. if (!vertices->reserve(count) || !polyline->reserve(count))
  233. {
  234. ccLog::Error("Not enough memory!");
  235. delete vertices;
  236. delete polyline;
  237. return;
  238. }
  239. for (unsigned i = 0; i < count; ++i)
  240. {
  241. const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
  242. vertices->addPoint(PP.getPointPosition());
  243. }
  244. polyline->addPointIndex(0, count);
  245. polyline->setVisible(true);
  246. vertices->setEnabled(false);
  247. //retrieve Shift & Scale values
  248. ccGenericPointCloud* asCloud = ccHObjectCaster::ToGenericPointCloud(m_associatedEntity);
  249. if (asCloud)
  250. {
  251. polyline->setGlobalShift(asCloud->getGlobalShift());
  252. polyline->setGlobalScale(asCloud->getGlobalScale());
  253. }
  254. else
  255. {
  256. assert(false);
  257. }
  258. polyline->addChild(vertices);
  259. polyline->setDisplay_recursive(m_associatedEntity->getDisplay());
  260. ccShiftedObject* shifted = ccHObjectCaster::ToShifted(m_associatedEntity);
  261. if (shifted)
  262. {
  263. polyline->copyGlobalShiftAndScale(*shifted);
  264. }
  265. MainWindow::TheInstance()->addToDB(polyline);
  266. }
  267. else
  268. {
  269. ccLog::Error("Pick at least two points!");
  270. }
  271. }
  272. void ccPointListPickingDlg::applyAndExit()
  273. {
  274. if (m_associatedEntity && !m_toBeDeleted.empty())
  275. {
  276. //apply modifications
  277. MainWindow::TheInstance()->db()->removeElements(m_toBeDeleted); //no need to redraw as they should already be invisible
  278. m_associatedEntity = nullptr;
  279. }
  280. m_toBeDeleted.resize(0);
  281. m_toBeAdded.resize(0);
  282. m_orderedLabelsContainer = nullptr;
  283. updateList();
  284. stop(true);
  285. }
  286. void ccPointListPickingDlg::removeLastEntry()
  287. {
  288. if (!m_associatedEntity)
  289. return;
  290. //get all labels
  291. std::vector<cc2DLabel*> labels;
  292. unsigned count = getPickedPoints(labels);
  293. if (count == 0)
  294. return;
  295. ccHObject* lastVisibleLabel = labels.back();
  296. if (lastVisibleLabel->getUniqueID() <= m_lastPreviousID)
  297. {
  298. //old label: hide it and add it to the 'to be deleted' list (will be restored if process is cancelled)
  299. lastVisibleLabel->setEnabled(false);
  300. m_toBeDeleted.push_back(lastVisibleLabel);
  301. }
  302. else
  303. {
  304. if (!m_toBeAdded.empty())
  305. {
  306. assert(m_toBeAdded.back() == lastVisibleLabel);
  307. m_toBeAdded.pop_back();
  308. }
  309. if (m_orderedLabelsContainer)
  310. {
  311. if (lastVisibleLabel->getParent())
  312. {
  313. lastVisibleLabel->getParent()->removeDependencyWith(lastVisibleLabel);
  314. lastVisibleLabel->removeDependencyWith(lastVisibleLabel->getParent());
  315. }
  316. //m_orderedLabelsContainer->removeChild(lastVisibleLabel);
  317. MainWindow::TheInstance()->db()->removeElement(lastVisibleLabel);
  318. }
  319. else
  320. m_associatedEntity->detachChild(lastVisibleLabel);
  321. }
  322. updateList();
  323. if (m_associatedWin)
  324. m_associatedWin->redraw();
  325. }
  326. void ccPointListPickingDlg::startIndexChanged(int value)
  327. {
  328. if (value != s_pickedPointsStartIndex)
  329. {
  330. s_pickedPointsStartIndex = value;
  331. updateList();
  332. if (m_associatedWin)
  333. {
  334. m_associatedWin->redraw();
  335. }
  336. }
  337. }
  338. void ccPointListPickingDlg::markerSizeChanged(int size)
  339. {
  340. if (size < 1 || !m_associatedWin)
  341. return;
  342. //display parameters
  343. ccGui::ParamStruct guiParams = m_associatedWin->getDisplayParameters();
  344. if (guiParams.labelMarkerSize != static_cast<unsigned>(size))
  345. {
  346. guiParams.labelMarkerSize = static_cast<unsigned>(size);
  347. m_associatedWin->setDisplayParameters(guiParams,m_associatedWin->hasOverriddenDisplayParameters());
  348. m_associatedWin->redraw();
  349. }
  350. }
  351. void ccPointListPickingDlg::exportToASCII(ExportFormat format)
  352. {
  353. if (!m_associatedEntity)
  354. return;
  355. //get all labels
  356. std::vector<cc2DLabel*> labels;
  357. unsigned count = getPickedPoints(labels);
  358. if (count == 0)
  359. return;
  360. QSettings settings;
  361. settings.beginGroup("PointListPickingDlg");
  362. QString filename = settings.value("filename", "picking_list.txt").toString();
  363. settings.endGroup();
  364. filename = QFileDialog::getSaveFileName(this,
  365. "Export to ASCII",
  366. filename,
  367. AsciiFilter::GetFileFilter());
  368. if (filename.isEmpty())
  369. return;
  370. settings.beginGroup("PointListPickingDlg");
  371. settings.setValue("filename", filename);
  372. settings.endGroup();
  373. QFile fp(filename);
  374. if (!fp.open(QFile::WriteOnly))
  375. {
  376. ccLog::Error(QString("Failed to open file '%1' for saving!").arg(filename));
  377. return;
  378. }
  379. //if a global shift exists, ask the user if it should be applied
  380. CCVector3d shift;
  381. double scale = 1.0;
  382. ccGenericPointCloud* asCloud = ccHObjectCaster::ToGenericPointCloud(m_associatedEntity);
  383. if (asCloud)
  384. {
  385. shift = asCloud->getGlobalShift();
  386. scale = asCloud->getGlobalScale();
  387. }
  388. if (shift.norm2() != 0 || scale != 1.0)
  389. {
  390. if (QMessageBox::warning( this,
  391. "Apply global shift",
  392. "Do you want to apply global shift/scale to exported points?",
  393. QMessageBox::Yes | QMessageBox::No,
  394. QMessageBox::Yes ) == QMessageBox::No)
  395. {
  396. //reset shift
  397. shift = CCVector3d(0,0,0);
  398. scale = 1.0;
  399. }
  400. }
  401. //starting index
  402. unsigned startIndex = static_cast<unsigned>(std::max(0, startIndexSpinBox->value()));
  403. QTextStream stream(&fp);
  404. stream.setRealNumberPrecision(12);
  405. for (unsigned i = 0; i < count; ++i)
  406. {
  407. assert(labels[i]->size() == 1);
  408. const cc2DLabel::PickedPoint& PP = labels[i]->getPickedPoint(0);
  409. CCVector3 P = PP.getPointPosition();
  410. switch (format)
  411. {
  412. case PLP_ASCII_EXPORT_IXYZ:
  413. stream << i + startIndex << ',';
  414. break;
  415. case PLP_ASCII_EXPORT_GXYZ:
  416. stream << PP.index << ',';
  417. break;
  418. case PLP_ASCII_EXPORT_LXYZ:
  419. stream << labels[i]->getName() << ',';
  420. break;
  421. default:
  422. //nothing to do
  423. break;
  424. }
  425. stream << static_cast<double>(P.x) / scale - shift.x << ','
  426. << static_cast<double>(P.y) / scale - shift.y << ','
  427. << static_cast<double>(P.z) / scale - shift.z << endl;
  428. }
  429. ccLog::Print(QString("[I/O] File '%1' saved successfully").arg(filename));
  430. }
  431. void ccPointListPickingDlg::updateList()
  432. {
  433. //get all labels
  434. std::vector<cc2DLabel*> labels;
  435. const int count = static_cast<int>( getPickedPoints(labels) );
  436. const int oldRowCount = tableWidget->rowCount();
  437. revertToolButton->setEnabled(count);
  438. validToolButton->setEnabled(count);
  439. exportToolButton->setEnabled(count);
  440. countLineEdit->setText(QString::number(count));
  441. tableWidget->setRowCount(count);
  442. if ( count == 0 )
  443. {
  444. return;
  445. }
  446. // If we have any new rows, create QTableWidgetItems for them
  447. if ( (count - oldRowCount) > 0 )
  448. {
  449. for ( int i = oldRowCount; i < count; ++i )
  450. {
  451. tableWidget->setVerticalHeaderItem( i, new QTableWidgetItem );
  452. for ( int j = 0; j < 4; ++j )
  453. {
  454. tableWidget->setItem( i, j, new QTableWidgetItem );
  455. }
  456. }
  457. }
  458. //starting index
  459. const int startIndex = startIndexSpinBox->value();
  460. const int precision = m_associatedWin ? static_cast<int>(m_associatedWin->getDisplayParameters().displayedNumPrecision) : 6;
  461. const bool showAbsolute = showGlobalCoordsCheckBox->isEnabled() && showGlobalCoordsCheckBox->isChecked();
  462. for ( int i = 0; i < count; ++i )
  463. {
  464. cc2DLabel* label = labels[static_cast<unsigned int>( i )];
  465. const cc2DLabel::PickedPoint& PP = label->getPickedPoint(0);
  466. CCVector3 P = PP.getPointPosition();
  467. CCVector3d Pd = (showAbsolute ? PP.cloudOrVertices()->toGlobal3d(P) : P);
  468. //point index in list
  469. tableWidget->verticalHeaderItem( i )->setText( QStringLiteral( "%1" ).arg( i + startIndex ) );
  470. //update name as well
  471. if ( label->getUniqueID() > m_lastPreviousID
  472. || label->getName().startsWith(s_defaultLabelBaseName) ) //DGM: we don't change the name of old labels that have a non-default name
  473. {
  474. label->setName(s_defaultLabelBaseName + QString::number(i+startIndex));
  475. }
  476. //point absolute index (in cloud)
  477. tableWidget->item( i, 0 )->setText( QStringLiteral( "%1" ).arg( PP.index ) );
  478. for ( int j = 0; j < 3; ++j )
  479. {
  480. tableWidget->item( i, j + 1 )->setText( QStringLiteral( "%1" ).arg( Pd.u[j], 0, 'f', precision ) );
  481. }
  482. }
  483. tableWidget->scrollToBottom();
  484. }
  485. void ccPointListPickingDlg::processPickedPoint(const PickedItem& picked)
  486. {
  487. if (!picked.entity || picked.entity != m_associatedEntity || !MainWindow::TheInstance())
  488. return;
  489. cc2DLabel* newLabel = new cc2DLabel();
  490. if (picked.entity->isKindOf(CC_TYPES::POINT_CLOUD))
  491. {
  492. newLabel->addPickedPoint(static_cast<ccGenericPointCloud*>(picked.entity), picked.itemIndex, picked.entityCenter);
  493. }
  494. else if (picked.entity->isKindOf(CC_TYPES::MESH))
  495. {
  496. newLabel->addPickedPoint(static_cast<ccGenericMesh*>(picked.entity), picked.itemIndex, CCVector2d(picked.uvw.x, picked.uvw.y), picked.entityCenter);
  497. }
  498. else
  499. {
  500. delete newLabel;
  501. assert(false);
  502. return;
  503. }
  504. newLabel->setVisible(true);
  505. newLabel->setDisplayedIn2D(false);
  506. newLabel->displayPointLegend(true);
  507. newLabel->setCollapsed(true);
  508. ccGenericGLDisplay* display = m_associatedEntity->getDisplay();
  509. if (display)
  510. {
  511. newLabel->setDisplay(display);
  512. QSize size = display->getScreenSize();
  513. newLabel->setPosition( static_cast<float>(picked.clickPoint.x() + 20) / size.width(),
  514. static_cast<float>(picked.clickPoint.y() + 20) / size.height() );
  515. }
  516. //add default container if necessary
  517. if (!m_orderedLabelsContainer)
  518. {
  519. m_orderedLabelsContainer = new ccHObject(s_pickedPointContainerName);
  520. m_associatedEntity->addChild(m_orderedLabelsContainer);
  521. m_orderedLabelsContainer->setDisplay(display);
  522. MainWindow::TheInstance()->addToDB(m_orderedLabelsContainer, false, true, false, false);
  523. }
  524. assert(m_orderedLabelsContainer);
  525. m_orderedLabelsContainer->addChild(newLabel);
  526. MainWindow::TheInstance()->addToDB(newLabel, false, true, false, false);
  527. m_toBeAdded.push_back(newLabel);
  528. //automatically send the new point coordinates to the clipboard
  529. QClipboard* clipboard = QApplication::clipboard();
  530. if (clipboard)
  531. {
  532. CCVector3 P = newLabel->getPickedPoint(0).getPointPosition();
  533. int precision = m_associatedWin ? m_associatedWin->getDisplayParameters().displayedNumPrecision : 6;
  534. int indexInList = startIndexSpinBox->value() + static_cast<int>(m_orderedLabelsContainer->getChildrenNumber()) - 1;
  535. 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));
  536. }
  537. updateList();
  538. if (m_associatedWin)
  539. m_associatedWin->redraw();
  540. }