ccSectionExtractionTool.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233
  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 "ccSectionExtractionTool.h"
  18. //Local
  19. #include "ccEnvelopeExtractor.h"
  20. #include "ccItemSelectionDlg.h"
  21. #include "ccOrthoSectionGenerationDlg.h"
  22. #include "ccSectionExtractionSubDlg.h"
  23. #include "mainwindow.h"
  24. //qCC_db
  25. #include <ccGenericPointCloud.h>
  26. #include <ccHObjectCaster.h>
  27. #include <ccLog.h>
  28. #include <ccPointCloud.h>
  29. #include <ccPolyline.h>
  30. #include <ccProgressDialog.h>
  31. //qCC_gl
  32. #include <ccGLWindowInterface.h>
  33. //CCCoreLib
  34. #include <ReferenceCloud.h>
  35. //Qt
  36. #include <QCoreApplication>
  37. #include <QInputDialog>
  38. #include <QMdiSubWindow>
  39. #include <QMessageBox>
  40. //GUI
  41. #include <ui_sectionExtractionDlg.h>
  42. //System
  43. #include <cassert>
  44. #include <cmath>
  45. //default parameters
  46. static const ccColor::Rgb& s_defaultPolylineColor = ccColor::magenta;
  47. static const ccColor::Rgb& s_defaultEnvelopeColor = ccColor::green;
  48. static const ccColor::Rgb& s_defaultEditedPolylineColor = ccColor::green;
  49. static const ccColor::Rgb& s_defaultSelectedPolylineColor = ccColor::red;
  50. constexpr int s_defaultPolylineWidth = 1;
  51. constexpr int s_defaultSelectedPolylineWidth = 3;
  52. //default export groups
  53. static unsigned s_polyExportGroupID = 0;
  54. static unsigned s_profileExportGroupID = 0;
  55. static unsigned s_cloudExportGroupID = 0;
  56. //default arrow size
  57. static const PointCoordinateType s_defaultArrowSize = 20;
  58. ccSectionExtractionTool::ccSectionExtractionTool(QWidget* parent)
  59. : ccOverlayDialog(parent)
  60. , m_UI( new Ui::SectionExtractionDlg )
  61. , m_selectedPoly(nullptr)
  62. , m_state(0)
  63. , m_editedPoly(nullptr)
  64. , m_editedPolyVertices(nullptr)
  65. {
  66. m_UI->setupUi(this);
  67. connect(m_UI->undoToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::undo);
  68. connect(m_UI->validToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::apply);
  69. connect(m_UI->cancelToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::cancel);
  70. connect(m_UI->polylineToolButton, &QAbstractButton::toggled, this, &ccSectionExtractionTool::enableSectionEditingMode);
  71. connect(m_UI->importFromDBToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::doImportPolylinesFromDB);
  72. connect(m_UI->vertAxisComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccSectionExtractionTool::setVertDimension);
  73. connect(m_UI->generateOrthoSectionsToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::generateOrthoSections);
  74. connect(m_UI->extractPointsToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::extractPoints);
  75. connect(m_UI->unfoldToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::unfoldPoints);
  76. connect(m_UI->exportSectionsToolButton, &QAbstractButton::clicked, this, &ccSectionExtractionTool::exportSections);
  77. //add shortcuts
  78. addOverriddenShortcut(Qt::Key_Space); //space bar for the "pause" button
  79. addOverriddenShortcut(Qt::Key_Escape); //cancel current polyline edition
  80. addOverriddenShortcut(Qt::Key_Delete); //delete key to delete the selected polyline
  81. connect(this, &ccOverlayDialog::shortcutTriggered, this, &ccSectionExtractionTool::onShortcutTriggered);
  82. }
  83. ccSectionExtractionTool::~ccSectionExtractionTool()
  84. {
  85. if (m_editedPoly)
  86. {
  87. if (m_associatedWin)
  88. {
  89. m_associatedWin->removeFromOwnDB(m_editedPoly);
  90. }
  91. delete m_editedPoly;
  92. m_editedPoly = nullptr;
  93. }
  94. delete m_UI;
  95. }
  96. void ccSectionExtractionTool::setVertDimension(int dim)
  97. {
  98. assert(dim >= 0 && dim < 3);
  99. if (!m_associatedWin)
  100. return;
  101. switch (dim)
  102. {
  103. case 0:
  104. m_associatedWin->setView(CC_RIGHT_VIEW);
  105. break;
  106. case 1:
  107. m_associatedWin->setView(CC_FRONT_VIEW);
  108. break;
  109. case 2:
  110. default:
  111. m_associatedWin->setView(CC_TOP_VIEW);
  112. break;
  113. }
  114. m_associatedWin->updateConstellationCenterAndZoom();
  115. }
  116. void ccSectionExtractionTool::onShortcutTriggered(int key)
  117. {
  118. switch (key)
  119. {
  120. case Qt::Key_Space:
  121. m_UI->polylineToolButton->toggle();
  122. return;
  123. case Qt::Key_Escape:
  124. cancelCurrentPolyline();
  125. return;
  126. case Qt::Key_Delete:
  127. deleteSelectedPolyline();
  128. return;
  129. default:
  130. //nothing to do
  131. break;
  132. }
  133. }
  134. bool ccSectionExtractionTool::linkWith(ccGLWindowInterface* win)
  135. {
  136. ccGLWindowInterface* oldWin = m_associatedWin;
  137. if (!ccOverlayDialog::linkWith(win))
  138. {
  139. return false;
  140. }
  141. selectPolyline(nullptr);
  142. if (oldWin)
  143. {
  144. //restore sections original display
  145. for (auto & section : m_sections)
  146. {
  147. if (section.entity)
  148. {
  149. if (!section.isInDB)
  150. oldWin->removeFromOwnDB(section.entity);
  151. section.entity->setDisplay_recursive(section.originalDisplay);
  152. }
  153. }
  154. //Restore clouds original display
  155. for (auto & cloud : m_clouds)
  156. {
  157. if (cloud.entity)
  158. {
  159. if (!cloud.isInDB)
  160. oldWin->removeFromOwnDB(cloud.entity);
  161. cloud.entity->setDisplay(cloud.originalDisplay);
  162. }
  163. }
  164. if (m_editedPoly)
  165. {
  166. m_editedPoly->setDisplay_recursive(nullptr);
  167. }
  168. //auto-close formerly associated window
  169. if (MainWindow::TheInstance())
  170. {
  171. QMdiSubWindow* subWindow = MainWindow::TheInstance()->getMDISubWindow(oldWin);
  172. if (subWindow)
  173. {
  174. subWindow->close();
  175. }
  176. }
  177. }
  178. if (m_associatedWin)
  179. {
  180. connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::leftButtonClicked, this, &ccSectionExtractionTool::addPointToPolyline);
  181. connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::rightButtonClicked, this, &ccSectionExtractionTool::closePolyLine);
  182. connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::mouseMoved, this, &ccSectionExtractionTool::updatePolyLine);
  183. connect(m_associatedWin->signalEmitter(), &ccGLWindowSignalEmitter::entitySelectionChanged, this, &ccSectionExtractionTool::entitySelected);
  184. //import sections in current display
  185. for (auto & section : m_sections)
  186. {
  187. if (section.entity)
  188. {
  189. section.originalDisplay = section.entity->getDisplay();
  190. section.entity->setDisplay_recursive(m_associatedWin);
  191. if (!section.isInDB)
  192. m_associatedWin->addToOwnDB(section.entity);
  193. }
  194. }
  195. //import clouds in current display
  196. for (auto & cloud : m_clouds)
  197. {
  198. if (cloud.entity)
  199. {
  200. cloud.originalDisplay = cloud.entity->getDisplay();
  201. cloud.entity->setDisplay(m_associatedWin);
  202. if (!cloud.isInDB)
  203. {
  204. m_associatedWin->addToOwnDB(cloud.entity);
  205. }
  206. }
  207. }
  208. if (m_editedPoly)
  209. {
  210. m_editedPoly->setDisplay_recursive(m_associatedWin);
  211. }
  212. //update view direction
  213. setVertDimension(m_UI->vertAxisComboBox->currentIndex());
  214. //section extraction only works in orthoraphic mode!
  215. m_associatedWin->setPerspectiveState(false, true);
  216. }
  217. return true;
  218. }
  219. void ccSectionExtractionTool::selectPolyline(Section* poly, bool autoRefreshDisplay/*=true*/)
  220. {
  221. bool redraw = false;
  222. //deselect previously selected polyline
  223. if (m_selectedPoly && m_selectedPoly->entity)
  224. {
  225. m_selectedPoly->entity->showColors(true);
  226. m_selectedPoly->entity->setColor(s_defaultPolylineColor);
  227. m_selectedPoly->entity->setWidth(s_defaultPolylineWidth);
  228. redraw = true;
  229. }
  230. m_selectedPoly = poly;
  231. //select new polyline (if any)
  232. if (m_selectedPoly)
  233. {
  234. m_selectedPoly->entity->showColors(true);
  235. m_selectedPoly->entity->setColor(s_defaultSelectedPolylineColor);
  236. m_selectedPoly->entity->setWidth(s_defaultSelectedPolylineWidth);
  237. m_selectedPoly->entity->setSelected(false); //as the window selects it by default (with bounding-box, etc.) and we don't want that
  238. redraw = true;
  239. }
  240. if (redraw && autoRefreshDisplay && m_associatedWin)
  241. {
  242. m_associatedWin->redraw();
  243. }
  244. m_UI->generateOrthoSectionsToolButton->setEnabled(m_selectedPoly != nullptr);
  245. m_UI->extractPointsToolButton->setEnabled(!m_sections.empty());
  246. m_UI->unfoldToolButton->setEnabled(!m_sections.empty());
  247. }
  248. void ccSectionExtractionTool::releasePolyline(Section* section)
  249. {
  250. if (section && section->entity)
  251. {
  252. if (!section->isInDB)
  253. {
  254. //remove from display
  255. if (m_associatedWin)
  256. {
  257. m_associatedWin->removeFromOwnDB(section->entity);
  258. }
  259. //delete entity
  260. delete section->entity;
  261. section->entity = nullptr;
  262. }
  263. else
  264. {
  265. //restore original display and style
  266. section->entity->showColors(section->backupColorShown);
  267. section->entity->setColor(section->backupColor);
  268. section->entity->setWidth(section->backupWidth);
  269. section->entity->setDisplay_recursive(section->originalDisplay);
  270. }
  271. }
  272. }
  273. void ccSectionExtractionTool::deleteSelectedPolyline()
  274. {
  275. if (!m_selectedPoly)
  276. return;
  277. Section* selectedPoly = m_selectedPoly;
  278. //deslect polyline before anything
  279. selectPolyline(nullptr, false);
  280. releasePolyline(selectedPoly);
  281. //remove the section from the list
  282. m_sections.removeOne(*selectedPoly);
  283. m_undoCount.resize(0);
  284. m_UI->undoToolButton->setEnabled(false);
  285. if (m_associatedWin)
  286. {
  287. m_associatedWin->redraw();
  288. }
  289. }
  290. void ccSectionExtractionTool::entitySelected(ccHObject* entity)
  291. {
  292. //look if this selected entity corresponds to an active polyline
  293. for (auto & section : m_sections)
  294. {
  295. if (section.entity == entity)
  296. {
  297. selectPolyline(&section);
  298. return;
  299. }
  300. }
  301. selectPolyline(nullptr);
  302. }
  303. bool ccSectionExtractionTool::start()
  304. {
  305. assert(!m_editedPolyVertices && !m_editedPoly);
  306. if (!m_associatedWin)
  307. {
  308. ccLog::Warning("[Graphical Segmentation Tool] No associated window!");
  309. return false;
  310. }
  311. //the user must not close this window!
  312. m_associatedWin->setUnclosable(true);
  313. m_associatedWin->updateConstellationCenterAndZoom();
  314. updateCloudsBox();
  315. enableSectionEditingMode(true);
  316. return ccOverlayDialog::start();
  317. }
  318. void ccSectionExtractionTool::removeAllEntities()
  319. {
  320. reset(false);
  321. //and we remove the remaining clouds (if any)
  322. for (auto & cloud : m_clouds)
  323. {
  324. if (cloud.entity)
  325. {
  326. assert(cloud.isInDB);
  327. //restore original display
  328. cloud.entity->setDisplay(cloud.originalDisplay);
  329. }
  330. }
  331. m_clouds.clear();
  332. m_cloudsBox.clear();
  333. }
  334. void ccSectionExtractionTool::undo()
  335. {
  336. if (m_undoCount.empty())
  337. return;
  338. size_t count = 0;
  339. do
  340. {
  341. count = m_undoCount.back();
  342. m_undoCount.pop_back();
  343. } while (static_cast<int>(count) >= m_sections.size() && !m_undoCount.empty());
  344. //ask for a confirmation
  345. if (QMessageBox::question(MainWindow::TheInstance(), "Undo", QString("Remove %1 polylines?").arg(m_sections.size() - count), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
  346. {
  347. //restore undo stack!
  348. m_undoCount.push_back(count);
  349. return;
  350. }
  351. selectPolyline(nullptr);
  352. //we remove all polylines after a given point
  353. {
  354. while (m_sections.size() > static_cast<int>(count))
  355. {
  356. Section& section = m_sections.back();
  357. releasePolyline(&section);
  358. m_sections.pop_back();
  359. }
  360. }
  361. //update GUI
  362. m_UI->exportSectionsToolButton->setEnabled(count != 0);
  363. m_UI->extractPointsToolButton->setEnabled(count != 0);
  364. m_UI->undoToolButton->setEnabled(!m_undoCount.empty());
  365. if (m_associatedWin)
  366. {
  367. m_associatedWin->redraw();
  368. }
  369. }
  370. bool ccSectionExtractionTool::reset(bool askForConfirmation/*=true*/)
  371. {
  372. if (m_sections.empty() && m_clouds.empty())
  373. {
  374. //nothing to do
  375. return true;
  376. }
  377. if (askForConfirmation)
  378. {
  379. //if we found at least one temporary polyline, we display a confirmation message
  380. for (auto & section : m_sections)
  381. {
  382. if (section.entity && !section.isInDB)
  383. {
  384. if (QMessageBox::question(MainWindow::TheInstance(), "Reset", "You'll lose all manually defined polylines: are you sure?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
  385. return false;
  386. else
  387. break;
  388. }
  389. }
  390. }
  391. selectPolyline(nullptr);
  392. //we remove all polylines
  393. for (auto & section : m_sections)
  394. {
  395. releasePolyline(&section);
  396. }
  397. m_sections.clear();
  398. m_undoCount.resize(0);
  399. m_UI->undoToolButton->setEnabled(false);
  400. m_UI->exportSectionsToolButton->setEnabled(false);
  401. m_UI->extractPointsToolButton->setEnabled(false);
  402. //and we remove only temporary clouds
  403. for (int i = 0; i < m_clouds.size();)
  404. {
  405. Cloud& cloud = m_clouds[i];
  406. if (cloud.entity && !cloud.isInDB)
  407. {
  408. if (m_associatedWin)
  409. {
  410. m_associatedWin->removeFromOwnDB(cloud.entity);
  411. }
  412. delete cloud.entity;
  413. cloud.entity = nullptr;
  414. m_clouds.removeAt(i);
  415. }
  416. else
  417. {
  418. ++i;
  419. }
  420. }
  421. updateCloudsBox();
  422. if (m_associatedWin)
  423. {
  424. m_associatedWin->redraw();
  425. }
  426. return true;
  427. }
  428. void ccSectionExtractionTool::stop(bool accepted)
  429. {
  430. if (m_editedPoly)
  431. {
  432. if (m_associatedWin)
  433. {
  434. m_associatedWin->removeFromOwnDB(m_editedPoly);
  435. }
  436. delete m_editedPoly;
  437. m_editedPoly = nullptr;
  438. }
  439. m_editedPolyVertices = nullptr;
  440. enableSectionEditingMode(false);
  441. reset(true);
  442. if (m_associatedWin)
  443. {
  444. m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_TRANSFORM_CAMERA);
  445. m_associatedWin->setPickingMode(ccGLWindowInterface::DEFAULT_PICKING);
  446. m_associatedWin->setUnclosable(false);
  447. }
  448. ccOverlayDialog::stop(accepted);
  449. }
  450. void ccSectionExtractionTool::updateCloudsBox()
  451. {
  452. m_cloudsBox.clear();
  453. for (auto & cloud : m_clouds)
  454. {
  455. if (cloud.entity)
  456. m_cloudsBox += cloud.entity->getOwnBB();
  457. }
  458. }
  459. bool ccSectionExtractionTool::addPolyline(ccPolyline* inputPoly, bool alreadyInDB/*=true*/)
  460. {
  461. if (!inputPoly)
  462. {
  463. assert(false);
  464. return false;
  465. }
  466. if (!m_associatedWin)
  467. {
  468. ccLog::Warning("[Graphical Segmentation Tool] No associated window!");
  469. return false;
  470. }
  471. for (auto & section : m_sections)
  472. {
  473. if (section.entity == inputPoly)
  474. {
  475. //cloud already in DB
  476. return false;
  477. }
  478. }
  479. //convert the polyline to 3D mode if necessary
  480. if (inputPoly->is2DMode())
  481. {
  482. //viewing parameters (for conversion from 2D to 3D)
  483. ccGLCameraParameters camera;
  484. m_associatedWin->getGLCameraParameters(camera);
  485. const double half_w = camera.viewport[2] / 2.0;
  486. const double half_h = camera.viewport[3] / 2.0;
  487. //working dimension
  488. int vertDim = m_UI->vertAxisComboBox->currentIndex();
  489. assert(vertDim >= 0 && vertDim < 3);
  490. //get default altitude from the cloud(s) bouding-box
  491. PointCoordinateType defaultZ = 0;
  492. if (m_cloudsBox.isValid())
  493. {
  494. defaultZ = m_cloudsBox.maxCorner()[vertDim];
  495. }
  496. //duplicate polyline
  497. ccPolyline* duplicatePoly = new ccPolyline(nullptr);
  498. ccPointCloud* duplicateVertices = nullptr;
  499. if (duplicatePoly->initWith(duplicateVertices, *inputPoly))
  500. {
  501. assert(duplicateVertices);
  502. for (unsigned i = 0; i < duplicateVertices->size(); ++i)
  503. {
  504. CCVector3& P = const_cast<CCVector3&>(*duplicateVertices->getPoint(i));
  505. CCVector3d Pd(half_w + P.x, half_h + P.y, 0/*P.z*/);
  506. CCVector3d Q3D;
  507. camera.unproject(Pd, Q3D);
  508. P = Q3D.toPC();
  509. P.u[vertDim] = defaultZ;
  510. }
  511. duplicateVertices->invalidateBoundingBox();
  512. duplicateVertices->setEnabled(false);
  513. duplicatePoly->set2DMode(false);
  514. duplicatePoly->setDisplay_recursive(inputPoly->getDisplay());
  515. duplicatePoly->setName(inputPoly->getName());
  516. duplicatePoly->copyGlobalShiftAndScale(*inputPoly);
  517. if (!alreadyInDB)
  518. delete inputPoly;
  519. else
  520. alreadyInDB = false;
  521. inputPoly = duplicatePoly;
  522. }
  523. else
  524. {
  525. delete duplicatePoly;
  526. duplicatePoly = nullptr;
  527. ccLog::Error("Not enough memory to import polyline!");
  528. return false;
  529. }
  530. }
  531. //add polyline to the 'sections' set
  532. //(all its parameters will be backuped!)
  533. m_sections.push_back(Section(inputPoly, alreadyInDB));
  534. m_UI->exportSectionsToolButton->setEnabled(true);
  535. m_UI->extractPointsToolButton->setEnabled(true);
  536. //apply default look
  537. inputPoly->setEnabled(true);
  538. inputPoly->setVisible(true);
  539. inputPoly->showColors(true);
  540. inputPoly->setColor(s_defaultPolylineColor);
  541. inputPoly->setWidth(s_defaultPolylineWidth);
  542. //add to display
  543. inputPoly->setDisplay_recursive(m_associatedWin);
  544. if (!alreadyInDB)
  545. {
  546. m_associatedWin->addToOwnDB(inputPoly);
  547. }
  548. return true;
  549. }
  550. static bool s_mixedShiftAndScaleInfo = false;
  551. bool ccSectionExtractionTool::addCloud(ccGenericPointCloud* inputCloud, bool alreadyInDB/*=true*/)
  552. {
  553. assert(inputCloud);
  554. if (m_clouds.empty())
  555. s_mixedShiftAndScaleInfo = false;
  556. for (CloudPool::iterator it = m_clouds.begin(); it != m_clouds.end(); ++it)
  557. {
  558. Cloud& cloud = *it;
  559. if (cloud.entity == inputCloud)
  560. {
  561. //cloud already in DB
  562. return false;
  563. }
  564. //test (on the first cloud) that the global shift & scale info is the same
  565. if (!s_mixedShiftAndScaleInfo && it == m_clouds.begin())
  566. {
  567. if ( cloud.entity->getGlobalScale() != inputCloud->getGlobalScale()
  568. || CCCoreLib::LessThanEpsilon((cloud.entity->getGlobalShift() - inputCloud->getGlobalShift()).norm()))
  569. {
  570. ccLog::Warning("[ccSectionExtractionTool] Clouds have different shift & scale information! Only the first one will be used");
  571. s_mixedShiftAndScaleInfo = true;
  572. }
  573. }
  574. }
  575. m_clouds.push_back(Cloud(inputCloud, alreadyInDB));
  576. if (m_associatedWin)
  577. {
  578. inputCloud->setDisplay(m_associatedWin);
  579. if (!alreadyInDB)
  580. {
  581. m_associatedWin->addToOwnDB(inputCloud);
  582. }
  583. }
  584. return true;
  585. }
  586. void ccSectionExtractionTool::updatePolyLine(int x, int y, Qt::MouseButtons buttons)
  587. {
  588. Q_UNUSED( buttons );
  589. if (!m_associatedWin)
  590. {
  591. assert(false);
  592. return;
  593. }
  594. //process not started yet?
  595. if ((m_state & RUNNING) == 0)
  596. return;
  597. if (!m_editedPolyVertices)
  598. return;
  599. unsigned vertCount = m_editedPolyVertices->size();
  600. if (vertCount < 2)
  601. return;
  602. QPointF pos2D = m_associatedWin->toCenteredGLCoordinates(x, y);
  603. CCVector3 P(static_cast<PointCoordinateType>(pos2D.x()),
  604. static_cast<PointCoordinateType>(pos2D.y()),
  605. 0);
  606. //we replace last point by the current one
  607. CCVector3* lastP = const_cast<CCVector3*>(m_editedPolyVertices->getPointPersistentPtr(vertCount - 1));
  608. *lastP = P;
  609. m_associatedWin->redraw(true, false);
  610. }
  611. void ccSectionExtractionTool::addPointToPolyline(int x, int y)
  612. {
  613. if ((m_state & STARTED) == 0)
  614. {
  615. return;
  616. }
  617. if (!m_associatedWin)
  618. {
  619. assert(false);
  620. return;
  621. }
  622. if (!m_editedPoly)
  623. {
  624. assert(!m_editedPolyVertices);
  625. m_editedPolyVertices = new ccPointCloud("vertices");
  626. m_editedPoly = new ccPolyline(m_editedPolyVertices);
  627. m_editedPoly->setForeground(true);
  628. m_editedPoly->setColor(s_defaultEditedPolylineColor);
  629. m_editedPoly->showColors(true);
  630. m_editedPoly->set2DMode(true);
  631. m_editedPoly->addChild(m_editedPolyVertices); //make sure the polyline is a parent of the polyline before copying the Global Shift & Scale info!
  632. //copy (first) cloud shift & scale info!
  633. if (!m_clouds.empty() && m_clouds.front().entity)
  634. {
  635. ccGenericPointCloud* cloud = m_clouds.front().entity;
  636. m_editedPoly->copyGlobalShiftAndScale(*cloud);
  637. }
  638. m_associatedWin->addToOwnDB(m_editedPoly);
  639. }
  640. unsigned vertCount = m_editedPolyVertices->size();
  641. //clicked point (2D)
  642. QPointF pos2D = m_associatedWin->toCenteredGLCoordinates(x, y);
  643. CCVector3 P(static_cast<PointCoordinateType>(pos2D.x()),
  644. static_cast<PointCoordinateType>(pos2D.y()),
  645. 0);
  646. //start new polyline?
  647. if (((m_state & RUNNING) == 0) || vertCount == 0)
  648. {
  649. //reset state
  650. m_state = (STARTED | RUNNING);
  651. //reset polyline
  652. m_editedPolyVertices->clear();
  653. if (!m_editedPolyVertices->reserve(2))
  654. {
  655. ccLog::Error("Out of memory!");
  656. return;
  657. }
  658. //we add the same point twice (the last point will be used for display only)
  659. m_editedPolyVertices->addPoint(P);
  660. m_editedPolyVertices->addPoint(P);
  661. m_editedPoly->clear();
  662. if (!m_editedPoly->addPointIndex(0, 2))
  663. {
  664. ccLog::Error("Out of memory!");
  665. return;
  666. }
  667. }
  668. else //next points
  669. {
  670. if (!m_editedPolyVertices->reserve(vertCount + 1))
  671. {
  672. ccLog::Error("Out of memory!");
  673. return;
  674. }
  675. //we replace last point by the current one
  676. assert(vertCount >= 2);
  677. CCVector3* lastP = const_cast<CCVector3*>(m_editedPolyVertices->getPointPersistentPtr(vertCount - 1));
  678. CCVector3* lastQ = const_cast<CCVector3*>(m_editedPolyVertices->getPointPersistentPtr(vertCount - 2));
  679. PointCoordinateType tipLength = (*lastQ - *lastP).norm();
  680. *lastP = P;
  681. //and add a new (equivalent) one
  682. m_editedPolyVertices->addPoint(P);
  683. if (!m_editedPoly->addPointIndex(vertCount))
  684. {
  685. ccLog::Error("Out of memory!");
  686. return;
  687. }
  688. PointCoordinateType defaultArrowSize = std::min(s_defaultArrowSize, tipLength / 2);
  689. m_editedPoly->showArrow(true, vertCount - 1, defaultArrowSize);
  690. }
  691. m_associatedWin->redraw(true, false);
  692. }
  693. void ccSectionExtractionTool::closePolyLine(int, int)
  694. {
  695. //only in RUNNING mode
  696. if ((m_state & RUNNING) == 0 || !m_editedPoly)
  697. return;
  698. assert(m_editedPoly);
  699. unsigned vertCount = m_editedPoly->size();
  700. if (vertCount < 3)
  701. {
  702. m_editedPoly->clear();
  703. m_editedPolyVertices->clear();
  704. }
  705. else
  706. {
  707. //remove last point!
  708. m_editedPoly->resize(vertCount - 1); //can't fail --> smaller
  709. //remove polyline from the 'temporary' world
  710. if (m_associatedWin)
  711. {
  712. m_associatedWin->removeFromOwnDB(m_editedPoly);
  713. }
  714. //set default display style
  715. m_editedPoly->showColors(true);
  716. m_editedPoly->setColor(s_defaultPolylineColor);
  717. m_editedPoly->setWidth(s_defaultPolylineWidth);
  718. if (!m_clouds.isEmpty())
  719. {
  720. m_editedPoly->setDisplay_recursive(m_clouds.front().originalDisplay); //set the same 'default' display as the cloud
  721. }
  722. m_editedPoly->setName(QString("Polyline #%1").arg(m_sections.size() + 1));
  723. //save polyline
  724. if (!addPolyline(m_editedPoly, false))
  725. {
  726. //if something went wrong, we have to remove the polyline manually
  727. delete m_editedPoly;
  728. }
  729. m_editedPoly = nullptr;
  730. m_editedPolyVertices = nullptr;
  731. }
  732. //stop
  733. m_state &= (~RUNNING);
  734. if (m_associatedWin)
  735. {
  736. m_associatedWin->redraw(true, false);
  737. }
  738. }
  739. void ccSectionExtractionTool::cancelCurrentPolyline()
  740. {
  741. if ((m_state & STARTED) == 0
  742. || !m_editedPoly)
  743. {
  744. return;
  745. }
  746. assert(m_editedPolyVertices);
  747. m_editedPoly->clear();
  748. m_editedPolyVertices->clear();
  749. //stop
  750. m_state &= (~RUNNING);
  751. if (m_associatedWin)
  752. {
  753. m_associatedWin->redraw();
  754. }
  755. }
  756. void ccSectionExtractionTool::enableSectionEditingMode(bool state)
  757. {
  758. if (!m_associatedWin)
  759. return;
  760. if (!state/*=activate pause mode*/)
  761. {
  762. //select the last polyline by default (if any)
  763. if (!m_sections.empty() && !m_sections.back().isInDB)
  764. selectPolyline(&m_sections.back());
  765. m_state = PAUSED;
  766. if (m_editedPoly && m_editedPolyVertices)
  767. {
  768. m_editedPoly->clear();
  769. m_editedPolyVertices->clear();
  770. }
  771. m_associatedWin->setInteractionMode(ccGLWindowInterface::MODE_PAN_ONLY);
  772. m_associatedWin->displayNewMessage(QString(), ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 0, ccGLWindowInterface::MANUAL_SEGMENTATION_MESSAGE);
  773. m_associatedWin->setPickingMode(ccGLWindowInterface::ENTITY_PICKING); //to be able to select polylines!
  774. }
  775. else
  776. {
  777. //deselect all currently selected polylines
  778. selectPolyline(nullptr);
  779. //set new 'undo' step
  780. addUndoStep();
  781. m_state = STARTED;
  782. m_associatedWin->setPickingMode(ccGLWindowInterface::NO_PICKING);
  783. m_associatedWin->setInteractionMode(ccGLWindowInterface::INTERACT_SEND_ALL_SIGNALS);
  784. m_associatedWin->displayNewMessage("Section edition mode", ccGLWindowInterface::UPPER_CENTER_MESSAGE, false, 3600, ccGLWindowInterface::MANUAL_SEGMENTATION_MESSAGE);
  785. m_associatedWin->displayNewMessage("Left click: add section points / Right click: stop", ccGLWindowInterface::UPPER_CENTER_MESSAGE, true, 3600, ccGLWindowInterface::MANUAL_SEGMENTATION_MESSAGE);
  786. }
  787. //update mini-GUI
  788. m_UI->polylineToolButton->blockSignals(true);
  789. m_UI->polylineToolButton->setChecked(state);
  790. m_UI->frame->setEnabled(!state);
  791. m_UI->polylineToolButton->blockSignals(false);
  792. m_associatedWin->redraw();
  793. }
  794. void ccSectionExtractionTool::addUndoStep()
  795. {
  796. if (m_undoCount.empty() || (static_cast<int>(m_undoCount.back()) < m_sections.size()))
  797. {
  798. m_undoCount.push_back(m_sections.size());
  799. m_UI->undoToolButton->setEnabled(true);
  800. }
  801. }
  802. void ccSectionExtractionTool::doImportPolylinesFromDB()
  803. {
  804. MainWindow* mainWindow = MainWindow::TheInstance();
  805. if (!mainWindow)
  806. return;
  807. ccHObject* root = mainWindow->dbRootObject();
  808. ccHObject::Container polylines;
  809. if (root)
  810. {
  811. root->filterChildren(polylines, true, CC_TYPES::POLY_LINE);
  812. }
  813. if (!polylines.empty())
  814. {
  815. std::vector<int> indexes;
  816. if (!ccItemSelectionDlg::SelectEntities(polylines, indexes, this))
  817. {
  818. return;
  819. }
  820. //set new 'undo' step
  821. addUndoStep();
  822. enableSectionEditingMode(false);
  823. for (int index : indexes)
  824. {
  825. assert(index >= 0 && index < static_cast<int>(polylines.size()));
  826. assert(polylines[index]->isA(CC_TYPES::POLY_LINE));
  827. ccPolyline* poly = static_cast<ccPolyline*>(polylines[index]);
  828. addPolyline(poly, true);
  829. }
  830. //auto-select the last one
  831. if (!m_sections.empty())
  832. {
  833. selectPolyline(&(m_sections.back()));
  834. }
  835. if (m_associatedWin)
  836. {
  837. m_associatedWin->redraw();
  838. }
  839. }
  840. else
  841. {
  842. ccLog::Error("No polyline in DB!");
  843. }
  844. }
  845. void ccSectionExtractionTool::cancel()
  846. {
  847. reset(false);
  848. stop(false);
  849. }
  850. void ccSectionExtractionTool::apply()
  851. {
  852. if (!reset(true))
  853. return;
  854. stop(true);
  855. }
  856. static double s_orthoSectionWidth = -1.0;
  857. static double s_orthoSectionStep = -1.0;
  858. static bool s_autoSaveAndRemoveGeneratrix = true;
  859. void ccSectionExtractionTool::generateOrthoSections()
  860. {
  861. if (!m_selectedPoly)
  862. {
  863. ccLog::Warning("[ccSectionExtractionTool] No polyline selected");
  864. return;
  865. }
  866. if (!m_associatedWin)
  867. {
  868. ccLog::Warning("[Graphical Segmentation Tool] No associated window!");
  869. return;
  870. }
  871. //compute poyline length
  872. ccPolyline* poly = m_selectedPoly->entity;
  873. unsigned vertCount = (poly ? poly->size() : 0);
  874. if (vertCount < 2)
  875. {
  876. ccLog::Warning("[ccSectionExtractionTool] Invalid polyline");
  877. return;
  878. }
  879. PointCoordinateType length = poly->computeLength();
  880. //show arrow
  881. {
  882. assert(vertCount >= 2);
  883. const CCVector3* lastQ = poly->getPoint(vertCount - 2);
  884. const CCVector3* lastP = poly->getPoint(vertCount - 1);
  885. PointCoordinateType tipLength = (*lastQ - *lastP).norm();
  886. PointCoordinateType defaultArrowSize = m_associatedWin->computeActualPixelSize() * s_defaultArrowSize;
  887. defaultArrowSize = std::min(defaultArrowSize, tipLength / 2);
  888. poly->showArrow(true, poly->size() - 1, defaultArrowSize);
  889. m_associatedWin->redraw();
  890. }
  891. //display dialog
  892. ccOrthoSectionGenerationDlg osgDlg(MainWindow::TheInstance());
  893. osgDlg.setPathLength(length);
  894. if (s_orthoSectionWidth > 0.0)
  895. osgDlg.setSectionsWidth(s_orthoSectionWidth);
  896. if (s_orthoSectionStep > 0.0)
  897. osgDlg.setGenerationStep(s_orthoSectionStep);
  898. osgDlg.setAutoSaveAndRemove(s_autoSaveAndRemoveGeneratrix);
  899. if (osgDlg.exec())
  900. {
  901. //now generate the orthogonal sections
  902. s_orthoSectionStep = osgDlg.getGenerationStep();
  903. s_orthoSectionWidth = osgDlg.getSectionsWidth();
  904. s_autoSaveAndRemoveGeneratrix = osgDlg.autoSaveAndRemove();
  905. if (s_autoSaveAndRemoveGeneratrix)
  906. {
  907. //save
  908. if (!m_selectedPoly->isInDB)
  909. {
  910. ccHObject* destEntity = getExportGroup(s_polyExportGroupID, "Exported sections");
  911. if (!destEntity)
  912. {
  913. assert(false);
  914. return;
  915. }
  916. destEntity->addChild(m_selectedPoly->entity);
  917. m_selectedPoly->isInDB = true;
  918. m_selectedPoly->entity->setDisplay_recursive(destEntity->getDisplay());
  919. MainWindow::TheInstance()->addToDB(m_selectedPoly->entity, false, false);
  920. }
  921. //and remove
  922. deleteSelectedPolyline();
  923. }
  924. //set new 'undo' step
  925. addUndoStep();
  926. //normal to the plane
  927. CCVector3 N(0, 0, 0);
  928. int vertDim = m_UI->vertAxisComboBox->currentIndex();
  929. assert(vertDim >= 0 && vertDim < 3);
  930. {
  931. N.u[vertDim] = 1.0;
  932. }
  933. //curvilinear position
  934. double s = 0;
  935. //current length
  936. double l = 0;
  937. unsigned maxCount = vertCount;
  938. if (!poly->isClosed())
  939. maxCount--;
  940. unsigned polyIndex = 0;
  941. for (unsigned i = 0; i < maxCount; ++i)
  942. {
  943. const CCVector3* A = poly->getPoint(i);
  944. const CCVector3* B = poly->getPoint((i + 1) % vertCount);
  945. CCVector3 AB = (*B - *A);
  946. AB.u[vertDim] = 0;
  947. CCVector3 nAB = AB.cross(N);
  948. nAB.normalize();
  949. double lAB = (*B - *A).norm();
  950. while (s < l + lAB)
  951. {
  952. double s_local = s - l;
  953. assert(s_local < lAB);
  954. //create orhogonal polyline
  955. ccPointCloud* vertices = new ccPointCloud("vertices");
  956. ccPolyline* orthoPoly = new ccPolyline(vertices);
  957. orthoPoly->addChild(vertices);
  958. if (vertices->reserve(2) && orthoPoly->reserve(2))
  959. {
  960. //intersection point
  961. CCVector3 I = *A + AB * (s_local / lAB);
  962. CCVector3 I1 = I + nAB * static_cast<PointCoordinateType>(s_orthoSectionWidth / 2);
  963. CCVector3 I2 = I - nAB * static_cast<PointCoordinateType>(s_orthoSectionWidth / 2);
  964. vertices->addPoint(I1);
  965. orthoPoly->addPointIndex(0);
  966. vertices->addPoint(I2);
  967. orthoPoly->addPointIndex(1);
  968. orthoPoly->setClosed(false);
  969. orthoPoly->set2DMode(false);
  970. orthoPoly->copyGlobalShiftAndScale(*poly);
  971. //set default display style
  972. vertices->setEnabled(false);
  973. orthoPoly->showColors(true);
  974. orthoPoly->setColor(s_defaultPolylineColor);
  975. orthoPoly->setWidth(s_defaultPolylineWidth);
  976. if (!m_clouds.isEmpty())
  977. orthoPoly->setDisplay_recursive(m_clouds.front().originalDisplay); //set the same 'default' display as the cloud
  978. orthoPoly->setName(QString("%1.%2").arg(poly->getName()).arg(++polyIndex));
  979. //add meta data (for Mascaret export)
  980. {
  981. orthoPoly->setMetaData(ccPolyline::MetaKeyUpDir(), QVariant(vertDim));
  982. orthoPoly->setMetaData(ccPolyline::MetaKeyAbscissa(), QVariant(s));
  983. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixCenter() + ".x", QVariant(static_cast<double>(I.x)));
  984. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixCenter() + ".y", QVariant(static_cast<double>(I.y)));
  985. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixCenter() + ".z", QVariant(static_cast<double>(I.z)));
  986. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixDirection() + ".x", QVariant(static_cast<double>(nAB.x)));
  987. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixDirection() + ".y", QVariant(static_cast<double>(nAB.y)));
  988. orthoPoly->setMetaData(ccPolyline::MetaKeyPrefixDirection() + ".z", QVariant(static_cast<double>(nAB.z)));
  989. }
  990. if (!addPolyline(orthoPoly, false))
  991. {
  992. delete orthoPoly;
  993. orthoPoly = nullptr;
  994. }
  995. }
  996. else
  997. {
  998. delete orthoPoly;
  999. orthoPoly = nullptr;
  1000. ccLog::Error("Not enough memory!");
  1001. //early stop
  1002. i = maxCount;
  1003. break;
  1004. }
  1005. s += s_orthoSectionStep;
  1006. }
  1007. l += lAB;
  1008. }
  1009. }
  1010. poly->showArrow(false, 0, 0);
  1011. m_associatedWin->redraw();
  1012. }
  1013. ccHObject* ccSectionExtractionTool::getExportGroup(unsigned& defaultGroupID, const QString& defaultName)
  1014. {
  1015. MainWindow* mainWin = MainWindow::TheInstance();
  1016. if (!mainWin)
  1017. {
  1018. assert(false);
  1019. return nullptr;
  1020. }
  1021. ccHObject* root = mainWin->dbRootObject();
  1022. if (!root)
  1023. {
  1024. ccLog::Warning("Internal error (no MainWindow or DB?!)");
  1025. assert(false);
  1026. return nullptr;
  1027. }
  1028. ccHObject* destEntity = (defaultGroupID != 0 ? root->find(defaultGroupID) : nullptr);
  1029. if (!destEntity)
  1030. {
  1031. destEntity = new ccHObject(defaultName);
  1032. //assign default display
  1033. for (auto & cloud : m_clouds)
  1034. {
  1035. if (cloud.entity)
  1036. {
  1037. destEntity->setDisplay_recursive(cloud.originalDisplay);
  1038. break;
  1039. }
  1040. }
  1041. mainWin->addToDB(destEntity);
  1042. defaultGroupID = destEntity->getUniqueID();
  1043. }
  1044. return destEntity;
  1045. }
  1046. void ccSectionExtractionTool::exportSections()
  1047. {
  1048. if (m_sections.empty())
  1049. {
  1050. return;
  1051. }
  1052. if (!m_associatedWin)
  1053. {
  1054. ccLog::Warning("[Graphical Segmentation Tool] No associated window!");
  1055. return;
  1056. }
  1057. //we only export 'temporary' objects
  1058. unsigned exportCount = 0;
  1059. for (auto & section : m_sections)
  1060. {
  1061. if (section.entity && !section.isInDB)
  1062. ++exportCount;
  1063. }
  1064. if (!exportCount)
  1065. {
  1066. //nothing to do
  1067. ccLog::Warning("[ccSectionExtractionTool] All active sections are already in DB");
  1068. return;
  1069. }
  1070. ccHObject* destEntity = getExportGroup(s_polyExportGroupID, "Exported sections");
  1071. if (!destEntity)
  1072. {
  1073. assert(false);
  1074. return;
  1075. }
  1076. MainWindow* mainWin = MainWindow::TheInstance();
  1077. //export entites
  1078. for (auto & section : m_sections)
  1079. {
  1080. if (section.entity && !section.isInDB)
  1081. {
  1082. m_associatedWin->removeFromOwnDB(section.entity);
  1083. destEntity->addChild(section.entity);
  1084. section.isInDB = true;
  1085. section.originalDisplay = destEntity->getDisplay();
  1086. //section.entity->setDisplay_recursive(destEntity->getDisplay());
  1087. mainWin->addToDB(section.entity, false, false);
  1088. }
  1089. }
  1090. ccLog::Print(QString("[ccSectionExtractionTool] %1 sections exported").arg(exportCount));
  1091. //m_associatedWin->redraw();
  1092. }
  1093. bool ccSectionExtractionTool::extractSectionEnvelope(const ccPolyline* originalSection,
  1094. const ccPointCloud* originalSectionCloud,
  1095. ccPointCloud* unrolledSectionCloud,
  1096. unsigned sectionIndex,
  1097. ccEnvelopeExtractor::EnvelopeType envelopeType,
  1098. PointCoordinateType maxEdgeLength,
  1099. bool multiPass,
  1100. bool splitEnvelope,
  1101. bool& envelopeGenerated,
  1102. bool visualDebugMode/*=false*/)
  1103. {
  1104. envelopeGenerated = false;
  1105. if (!originalSectionCloud || !unrolledSectionCloud)
  1106. {
  1107. ccLog::Warning("[ccSectionExtractionTool][extract envelope] Internal error: invalid input parameter(s)");
  1108. return false;
  1109. }
  1110. if (originalSectionCloud->size() < 2)
  1111. {
  1112. //nothing to do
  1113. ccLog::Warning(QString("[ccSectionExtractionTool][extract envelope] Section #%1 contains less than 2 points and will be ignored").arg(sectionIndex));
  1114. return true;
  1115. }
  1116. //by default, the points in 'unrolledSectionCloud' are 2D (X = curvilinear coordinate, Y = height, Z = 0)
  1117. CCVector3 N(0, 0, 1);
  1118. CCVector3 Y(0, 1, 0);
  1119. std::vector<unsigned> vertIndexes;
  1120. ccPolyline* envelope = ccEnvelopeExtractor::ExtractFlatEnvelope(unrolledSectionCloud,
  1121. multiPass,
  1122. maxEdgeLength,
  1123. N.u,
  1124. Y.u,
  1125. envelopeType,
  1126. &vertIndexes,
  1127. visualDebugMode);
  1128. if (envelope)
  1129. {
  1130. //update vertices (to replace 'unrolled' points by 'original' ones
  1131. {
  1132. CCCoreLib::GenericIndexedCloud* vertices = envelope->getAssociatedCloud();
  1133. if (vertIndexes.size() == static_cast<size_t>(vertices->size()))
  1134. {
  1135. for (unsigned i = 0; i < vertices->size(); ++i)
  1136. {
  1137. const CCVector3* P = vertices->getPoint(i);
  1138. assert(vertIndexes[i] < originalSectionCloud->size());
  1139. *const_cast<CCVector3*>(P) = *originalSectionCloud->getPoint(vertIndexes[i]);
  1140. }
  1141. ccPointCloud* verticesAsPC = dynamic_cast<ccPointCloud*>(vertices);
  1142. if (verticesAsPC)
  1143. verticesAsPC->refreshBB();
  1144. }
  1145. else
  1146. {
  1147. ccLog::Warning("[ccSectionExtractionTool][extract envelope] Internal error (couldn't fetch original points indexes?!)");
  1148. delete envelope;
  1149. return false;
  1150. }
  1151. }
  1152. std::vector<ccPolyline*> parts;
  1153. if (splitEnvelope)
  1154. {
  1155. #ifdef QT_DEBUG
  1156. //compute some stats on the envelope
  1157. {
  1158. double minLength = 0;
  1159. double maxLength = 0;
  1160. double sumLength = 0;
  1161. unsigned count = envelope->size();
  1162. if (!envelope->isClosed())
  1163. --count;
  1164. for (unsigned i = 0; i < count; ++i)
  1165. {
  1166. const CCVector3* A = envelope->getPoint(i);
  1167. const CCVector3* B = envelope->getPoint((i+1) % envelope->size());
  1168. CCVector3 e = *B - *A;
  1169. double l = e.norm();
  1170. if (i != 0)
  1171. {
  1172. minLength = std::min(minLength,l);
  1173. maxLength = std::max(maxLength,l);
  1174. sumLength += l;
  1175. }
  1176. else
  1177. {
  1178. minLength = maxLength = sumLength = l;
  1179. }
  1180. }
  1181. ccLog::PrintDebug(QString("Envelope: min = %1 / avg = %2 / max = %3").arg(minLength).arg(sumLength/count).arg(maxLength));
  1182. }
  1183. #endif
  1184. /*bool success = */envelope->split(maxEdgeLength, parts);
  1185. delete envelope;
  1186. envelope = nullptr;
  1187. }
  1188. else
  1189. {
  1190. parts.push_back(envelope);
  1191. }
  1192. //create output group if necessary
  1193. ccHObject* destEntity = getExportGroup(s_profileExportGroupID, "Extracted profiles");
  1194. if (!destEntity)
  1195. {
  1196. assert(false);
  1197. return false;
  1198. }
  1199. for (size_t p = 0; p < parts.size(); ++p)
  1200. {
  1201. ccPolyline* envelopePart = parts[p];
  1202. QString name = QString("Section envelope #%1").arg(sectionIndex);
  1203. if (parts.size() > 1)
  1204. {
  1205. name += QString("(part %1/%2)").arg(p + 1).arg(parts.size());
  1206. }
  1207. envelopePart->setName(name);
  1208. envelopePart->copyGlobalShiftAndScale(*originalSectionCloud);
  1209. envelopePart->setColor(s_defaultEnvelopeColor);
  1210. envelopePart->showColors(true);
  1211. //copy meta-data (import for Mascaret export!)
  1212. {
  1213. const QVariantMap& metaData = originalSection->metaData();
  1214. for (QVariantMap::const_iterator it = metaData.begin(); it != metaData.end(); ++it)
  1215. {
  1216. envelopePart->setMetaData(it.key(), it.value());
  1217. }
  1218. }
  1219. //add to main DB
  1220. destEntity->addChild(envelopePart);
  1221. envelopePart->setDisplay_recursive(destEntity->getDisplay());
  1222. MainWindow::TheInstance()->addToDB(envelopePart, false, false);
  1223. }
  1224. envelopeGenerated = true;
  1225. }
  1226. return true;
  1227. }
  1228. bool ccSectionExtractionTool::extractSectionCloud(const std::vector<CCCoreLib::ReferenceCloud*>& refClouds,
  1229. unsigned sectionIndex,
  1230. bool& cloudGenerated)
  1231. {
  1232. cloudGenerated = false;
  1233. ccPointCloud* sectionCloud = nullptr;
  1234. for (int i = 0; i < static_cast<int>(refClouds.size()); ++i)
  1235. {
  1236. if (!refClouds[i])
  1237. continue;
  1238. assert(m_clouds[i].entity); //a valid ref. cloud must have a valid counterpart!
  1239. //extract part/section from each cloud
  1240. ccPointCloud* part = nullptr;
  1241. //if the cloud is a ccPointCloud, we can keep a lot more information
  1242. //when extracting the section cloud
  1243. ccPointCloud* pc = dynamic_cast<ccPointCloud*>(m_clouds[i].entity);
  1244. if (pc)
  1245. {
  1246. part = pc->partialClone(refClouds[i]);
  1247. }
  1248. else
  1249. {
  1250. part = ccPointCloud::From(refClouds[i], m_clouds[i].entity);
  1251. }
  1252. if (part)
  1253. {
  1254. if (i == 0)
  1255. {
  1256. //we simply use this 'part' cloud as the section cloud
  1257. sectionCloud = part;
  1258. }
  1259. else
  1260. {
  1261. assert(sectionCloud);
  1262. //fuse it with the global cloud
  1263. unsigned cloudSizeBefore = sectionCloud->size();
  1264. unsigned partSize = part->size();
  1265. sectionCloud->append(part, cloudSizeBefore, true);
  1266. //don't need it anymore
  1267. delete part;
  1268. part = nullptr;
  1269. //check that it actually worked!
  1270. if (sectionCloud->size() != cloudSizeBefore + partSize)
  1271. {
  1272. //not enough memory
  1273. ccLog::Warning("[ccSectionExtractionTool][extract cloud] Not enough memory");
  1274. delete sectionCloud;
  1275. return false;
  1276. }
  1277. }
  1278. }
  1279. else
  1280. {
  1281. //not enough memory
  1282. ccLog::Warning("[ccSectionExtractionTool][extract cloud] Not enough memory");
  1283. delete sectionCloud;
  1284. return false;
  1285. }
  1286. }
  1287. if (sectionCloud)
  1288. {
  1289. //create output group if necessary
  1290. ccHObject* destEntity = getExportGroup(s_cloudExportGroupID, "Extracted section clouds");
  1291. if (!destEntity)
  1292. {
  1293. assert(false);
  1294. return false;
  1295. }
  1296. sectionCloud->setName(QString("Section cloud #%1").arg(sectionIndex));
  1297. sectionCloud->setDisplay(destEntity->getDisplay());
  1298. //add to main DB
  1299. destEntity->addChild(sectionCloud);
  1300. MainWindow::TheInstance()->addToDB(sectionCloud, false, false);
  1301. cloudGenerated = true;
  1302. }
  1303. return true;
  1304. }
  1305. struct Segment
  1306. {
  1307. Segment()
  1308. : A(0, 0)
  1309. , B(0, 0)
  1310. , u(0, 0)
  1311. , d(0)
  1312. , curvPos(0)
  1313. {}
  1314. CCVector2 A, B, u;
  1315. PointCoordinateType d, curvPos;
  1316. };
  1317. void ccSectionExtractionTool::unfoldPoints()
  1318. {
  1319. if (!m_selectedPoly)
  1320. {
  1321. if (QMessageBox::No == QMessageBox::question(this, "No polyline selected", "Do you want to unfold all the polylines at once?"))
  1322. {
  1323. return;
  1324. }
  1325. }
  1326. else if (!m_selectedPoly->entity)
  1327. {
  1328. assert(false);
  1329. return;
  1330. }
  1331. //compute loaded clouds bounding-box
  1332. ccBBox box;
  1333. unsigned totalPointCount = 0;
  1334. {
  1335. for (auto & cloud : m_clouds)
  1336. {
  1337. if (cloud.entity)
  1338. {
  1339. box += cloud.entity->getOwnBB();
  1340. totalPointCount += cloud.entity->size();
  1341. }
  1342. }
  1343. }
  1344. static double s_defaultThickness = -1.0;
  1345. if (s_defaultThickness <= 0)
  1346. {
  1347. s_defaultThickness = box.getMaxBoxDim() / 10.0;
  1348. }
  1349. bool ok;
  1350. double thickness = QInputDialog::getDouble(MainWindow::TheInstance(), "Thickness", "Distance to polyline:", s_defaultThickness, 1.0e-6, 1.0e6, 6, &ok);
  1351. if (!ok)
  1352. return;
  1353. s_defaultThickness = thickness;
  1354. //projection direction
  1355. int vertDim = m_UI->vertAxisComboBox->currentIndex();
  1356. int xDim = (vertDim < 2 ? vertDim + 1 : 0);
  1357. int yDim = (xDim < 2 ? xDim + 1 : 0);
  1358. //we consider half of the total thickness as points can be on both sides!
  1359. double maxSquareDistToPolyline = (thickness / 2) * (thickness / 2);
  1360. std::vector<ccPolyline*> polylines;
  1361. try
  1362. {
  1363. if (m_selectedPoly)
  1364. {
  1365. polylines.reserve(1);
  1366. polylines.push_back(m_selectedPoly->entity);
  1367. }
  1368. else
  1369. {
  1370. polylines.reserve(m_sections.size());
  1371. for (const auto& section : m_sections)
  1372. {
  1373. polylines.push_back(section.entity);
  1374. }
  1375. }
  1376. }
  1377. catch (const std::bad_alloc&)
  1378. {
  1379. ccLog::Error("Not enough memory");
  1380. return;
  1381. }
  1382. ccProgressDialog pdlg(true);
  1383. CCCoreLib::NormalizedProgress nprogress(&pdlg, polylines.size() > 1 ? static_cast<unsigned>(polylines.size()) : totalPointCount);
  1384. pdlg.setMethodTitle(tr("Unfold cloud(s)"));
  1385. if (polylines.size() > 1)
  1386. {
  1387. pdlg.setInfo(tr("Number of polylines: %1\nNumber of points: %2").arg(polylines.size()).arg(totalPointCount));
  1388. }
  1389. pdlg.start();
  1390. QCoreApplication::processEvents();
  1391. unsigned exportedClouds = 0;
  1392. for (ccPolyline* poly : polylines)
  1393. {
  1394. unsigned polyVertCount = poly->size();
  1395. if (polyVertCount < 2)
  1396. {
  1397. assert(false);
  1398. ccLog::Warning("Invalid polyline (less than 2 vertices)");
  1399. continue;
  1400. }
  1401. //prepare the computation of 2D distances
  1402. std::vector<Segment> segments;
  1403. unsigned polySegmentCount = poly->isClosed() ? polyVertCount : polyVertCount - 1;
  1404. {
  1405. try
  1406. {
  1407. segments.reserve(polySegmentCount);
  1408. }
  1409. catch (const std::bad_alloc&)
  1410. {
  1411. //not enough memory
  1412. ccLog::Error("Not enough memory");
  1413. return;
  1414. }
  1415. PointCoordinateType curvPos = 0;
  1416. for (unsigned j = 0; j < polySegmentCount; ++j)
  1417. {
  1418. //current polyline segment
  1419. const CCVector3* A = poly->getPoint(j);
  1420. const CCVector3* B = poly->getPoint((j + 1) % polyVertCount);
  1421. Segment s;
  1422. {
  1423. s.A = CCVector2(A->u[xDim], A->u[yDim]);
  1424. s.B = CCVector2(B->u[xDim], B->u[yDim]);
  1425. s.u = s.B - s.A;
  1426. s.d = s.u.norm();
  1427. if (CCCoreLib::GreaterThanEpsilon(s.d))
  1428. {
  1429. s.curvPos = curvPos;
  1430. s.u /= s.d;
  1431. segments.push_back(s);
  1432. }
  1433. }
  1434. //update curvilinear pos
  1435. curvPos += (*B - *A).norm();
  1436. }
  1437. }
  1438. if (polylines.size() == 1)
  1439. {
  1440. pdlg.setInfo(tr("Number of segments: %1\nNumber of points: %2").arg(polySegmentCount).arg(totalPointCount));
  1441. }
  1442. QCoreApplication::processEvents();
  1443. //for each cloud
  1444. for (auto & pc : m_clouds)
  1445. {
  1446. ccGenericPointCloud* cloud = pc.entity;
  1447. if (!cloud)
  1448. {
  1449. assert(false);
  1450. continue;
  1451. }
  1452. CCCoreLib::ReferenceCloud unfoldedIndexes(cloud);
  1453. if (!unfoldedIndexes.reserve(cloud->size()))
  1454. {
  1455. ccLog::Error("Not enough memory");
  1456. return;
  1457. }
  1458. std::vector<CCVector3> unfoldedPoints;
  1459. try
  1460. {
  1461. unfoldedPoints.reserve(cloud->size());
  1462. }
  1463. catch (const std::bad_alloc&)
  1464. {
  1465. ccLog::Error("Not enough memory");
  1466. return;
  1467. }
  1468. //now test each point and see if it's close to the current polyline (in 2D)
  1469. for (unsigned i = 0; i < cloud->size(); ++i)
  1470. {
  1471. const CCVector3* P = cloud->getPoint(i);
  1472. CCVector2 P2D(P->u[xDim], P->u[yDim]);
  1473. //test each segment
  1474. int closestSegment = -1;
  1475. PointCoordinateType minSquareDist = -CCCoreLib::PC_ONE;
  1476. for (unsigned j = 0; j < polySegmentCount; ++j)
  1477. {
  1478. const Segment& s = segments[j];
  1479. CCVector2 AP2D = P2D - s.A;
  1480. //longitudinal 'distance'
  1481. PointCoordinateType dotprod = s.u.dot(AP2D);
  1482. PointCoordinateType squareDist = 0;
  1483. if (dotprod < 0.0f)
  1484. {
  1485. //dist to nearest vertex
  1486. squareDist = AP2D.norm2();
  1487. }
  1488. else if (dotprod > s.d)
  1489. {
  1490. //dist to nearest vertex
  1491. squareDist = (P2D - s.B).norm2();
  1492. }
  1493. else
  1494. {
  1495. //orthogonal distance
  1496. squareDist = (AP2D - s.u*dotprod).norm2();
  1497. }
  1498. if (squareDist <= maxSquareDistToPolyline)
  1499. {
  1500. if (closestSegment < 0 || squareDist < minSquareDist)
  1501. {
  1502. minSquareDist = squareDist;
  1503. closestSegment = static_cast<int>(j);
  1504. }
  1505. }
  1506. }
  1507. if (closestSegment >= 0)
  1508. {
  1509. const Segment& s = segments[closestSegment];
  1510. //we use the curvilinear position of the point in the X dimension (and Y is 0)
  1511. CCVector3 Q;
  1512. {
  1513. CCVector2 AP2D = P2D - s.A;
  1514. PointCoordinateType dotprod = s.u.dot(AP2D);
  1515. PointCoordinateType d = (AP2D - s.u*dotprod).norm();
  1516. //compute the sign of 'minDist'
  1517. PointCoordinateType crossprod = AP2D.y * s.u.x - AP2D.x * s.u.y;
  1518. Q.u[xDim] = s.curvPos + dotprod;
  1519. Q.u[yDim] = crossprod < 0 ? -d : d; //signed orthogonal distance to the polyline
  1520. Q.u[vertDim] = P->u[vertDim];
  1521. }
  1522. unfoldedIndexes.addPointIndex(i);
  1523. unfoldedPoints.push_back(Q);
  1524. }
  1525. if (polylines.size() == 1)
  1526. {
  1527. if (!nprogress.oneStep())
  1528. {
  1529. ccLog::Warning("[Unfold] Process cancelled by the user");
  1530. return;
  1531. }
  1532. }
  1533. else //multiple polylines: we don't display the progress, but we still check if the cancel button has been pressed
  1534. {
  1535. if (pdlg.isCancelRequested())
  1536. {
  1537. ccLog::Warning("[Unfold] Process cancelled by the user");
  1538. return;
  1539. }
  1540. }
  1541. } //for each point
  1542. if (unfoldedIndexes.size() != 0)
  1543. {
  1544. //assign the default global shift & scale info
  1545. ccPointCloud* unfoldedCloud = nullptr;
  1546. {
  1547. if (cloud->isA(CC_TYPES::POINT_CLOUD))
  1548. unfoldedCloud = static_cast<ccPointCloud*>(cloud)->partialClone(&unfoldedIndexes);
  1549. else
  1550. unfoldedCloud = ccPointCloud::From(&unfoldedIndexes, cloud);
  1551. }
  1552. if (!unfoldedCloud)
  1553. {
  1554. ccLog::Error("Not enough memory");
  1555. return;
  1556. }
  1557. assert(unfoldedCloud->size() == unfoldedPoints.size());
  1558. CCVector3 C = box.minCorner();
  1559. C.u[vertDim] = 0;
  1560. C.u[xDim] = box.minCorner().u[xDim]; //we start at the bounding-box limit
  1561. for (unsigned i = 0; i < unfoldedCloud->size(); ++i)
  1562. {
  1563. //update the points positions
  1564. *const_cast<CCVector3*>(unfoldedCloud->getPoint(i)) = unfoldedPoints[i] + C;
  1565. }
  1566. unfoldedCloud->invalidateBoundingBox();
  1567. unfoldedCloud->setName(cloud->getName() + ".unfolded");
  1568. unfoldedCloud->copyGlobalShiftAndScale(*cloud);
  1569. unfoldedCloud->shrinkToFit();
  1570. unfoldedCloud->setDisplay(pc.originalDisplay);
  1571. MainWindow::TheInstance()->addToDB(unfoldedCloud);
  1572. ++exportedClouds;
  1573. }
  1574. else
  1575. {
  1576. ccLog::Warning(QString("[Unfold] No point of the cloud '%1' were unfolded (check parameters)").arg(cloud->getName()));
  1577. }
  1578. } //for each cloud
  1579. if (polylines.size() != 1) //multiple polylines only
  1580. {
  1581. if (!nprogress.oneStep())
  1582. {
  1583. ccLog::Warning("[Unfold] Process cancelled by the user");
  1584. return;
  1585. }
  1586. }
  1587. }
  1588. ccLog::Print(QString("[Unfold] %1 cloud(s) exported").arg(exportedClouds));
  1589. }
  1590. struct Segment2D
  1591. {
  1592. Segment2D() : lAB(0), s(0) {}
  1593. CCVector2 A, B, uAB;
  1594. PointCoordinateType lAB;
  1595. PointCoordinateType s; //curvilinear coordinate
  1596. };
  1597. void ccSectionExtractionTool::extractPoints()
  1598. {
  1599. static double s_defaultSectionThickness = -1.0;
  1600. static double s_envelopeMaxEdgeLength = 0;
  1601. static bool s_extractSectionsAsClouds = false;
  1602. static bool s_extractSectionsAsEnvelopes = true;
  1603. static bool s_multiPass = false;
  1604. static bool s_splitEnvelope = false;
  1605. static ccEnvelopeExtractor::EnvelopeType s_extractSectionsType = ccEnvelopeExtractor::LOWER;
  1606. //number of eligible sections
  1607. unsigned sectionCount = 0;
  1608. {
  1609. for (auto & section : m_sections)
  1610. {
  1611. if (section.entity && section.entity->size() > 1)
  1612. ++sectionCount;
  1613. }
  1614. }
  1615. if (sectionCount == 0)
  1616. {
  1617. ccLog::Error("No (valid) section!");
  1618. return;
  1619. }
  1620. //compute loaded clouds bounding-box
  1621. ccBBox box;
  1622. unsigned pointCount = 0;
  1623. for (auto & cloud : m_clouds)
  1624. {
  1625. if (cloud.entity)
  1626. {
  1627. box += cloud.entity->getOwnBB();
  1628. pointCount += cloud.entity->size();
  1629. }
  1630. }
  1631. if (s_defaultSectionThickness <= 0)
  1632. {
  1633. s_defaultSectionThickness = box.getMaxBoxDim() / 500.0;
  1634. }
  1635. if (s_envelopeMaxEdgeLength <= 0)
  1636. {
  1637. s_envelopeMaxEdgeLength = box.getMaxBoxDim() / 500.0;
  1638. }
  1639. //show dialog
  1640. ccSectionExtractionSubDlg sesDlg(MainWindow::TheInstance());
  1641. sesDlg.setActiveSectionCount(sectionCount);
  1642. sesDlg.setSectionThickness(s_defaultSectionThickness);
  1643. sesDlg.setMaxEdgeLength(s_envelopeMaxEdgeLength);
  1644. sesDlg.doExtractClouds(s_extractSectionsAsClouds);
  1645. sesDlg.doExtractEnvelopes(s_extractSectionsAsEnvelopes, s_extractSectionsType);
  1646. sesDlg.doUseMultiPass(s_multiPass);
  1647. sesDlg.doSplitEnvelopes(s_splitEnvelope);
  1648. if (!sesDlg.exec())
  1649. return;
  1650. s_defaultSectionThickness = sesDlg.getSectionThickness();
  1651. s_envelopeMaxEdgeLength = sesDlg.getMaxEdgeLength();
  1652. s_extractSectionsAsClouds = sesDlg.extractClouds();
  1653. s_extractSectionsAsEnvelopes = sesDlg.extractEnvelopes();
  1654. s_extractSectionsType = sesDlg.getEnvelopeType();
  1655. s_multiPass = sesDlg.useMultiPass();
  1656. s_splitEnvelope = sesDlg.splitEnvelopes();
  1657. bool visualDebugMode = sesDlg.visualDebugMode();
  1658. //progress dialog
  1659. ccProgressDialog pdlg(true);
  1660. CCCoreLib::NormalizedProgress nprogress(&pdlg, static_cast<unsigned>(sectionCount));
  1661. if (!visualDebugMode)
  1662. {
  1663. pdlg.setMethodTitle(tr("Extract sections"));
  1664. pdlg.setInfo(tr("Number of sections: %1\nNumber of points: %2").arg(sectionCount).arg(pointCount));
  1665. pdlg.start();
  1666. QCoreApplication::processEvents();
  1667. }
  1668. int vertDim = m_UI->vertAxisComboBox->currentIndex();
  1669. int xDim = (vertDim < 2 ? vertDim + 1 : 0);
  1670. int yDim = (xDim < 2 ? xDim + 1 : 0);
  1671. //we consider half of the total thickness as points can be on both sides!
  1672. double sectionThicknessSq = std::pow(s_defaultSectionThickness / 2.0, 2.0);
  1673. bool error = false;
  1674. unsigned generatedEnvelopes = 0;
  1675. unsigned generatedClouds = 0;
  1676. try
  1677. {
  1678. //for each slice
  1679. for (int s = 0; s < m_sections.size(); ++s)
  1680. {
  1681. ccPolyline* poly = m_sections[s].entity;
  1682. if (poly)
  1683. {
  1684. unsigned polyVertCount = poly->size();
  1685. if (polyVertCount < 2)
  1686. {
  1687. assert(false);
  1688. continue;
  1689. }
  1690. unsigned polySegmentCount = poly->isClosed() ? polyVertCount : polyVertCount - 1;
  1691. //project the section in '2D'
  1692. std::vector<Segment2D> polySegments2D;
  1693. {
  1694. polySegments2D.reserve(polySegmentCount);
  1695. PointCoordinateType s = 0;
  1696. for (unsigned j = 0; j < polySegmentCount; ++j)
  1697. {
  1698. Segment2D seg2D;
  1699. const CCVector3* A = poly->getPoint(j);
  1700. const CCVector3* B = poly->getPoint((j + 1) % polyVertCount);
  1701. seg2D.A = CCVector2(A->u[xDim], A->u[yDim]);
  1702. seg2D.B = CCVector2(B->u[xDim], B->u[yDim]);
  1703. seg2D.uAB = seg2D.B - seg2D.A; //(unit) direction
  1704. seg2D.lAB = seg2D.uAB.norm(); //length
  1705. seg2D.s = s;
  1706. s += seg2D.lAB;
  1707. if (CCCoreLib::LessThanEpsilon(seg2D.lAB))
  1708. {
  1709. //ignore too small segments
  1710. continue;
  1711. }
  1712. seg2D.uAB /= seg2D.lAB;
  1713. polySegments2D.push_back(seg2D);
  1714. }
  1715. if (polySegments2D.empty())
  1716. {
  1717. assert(false);
  1718. continue;
  1719. }
  1720. }
  1721. int cloudCount = m_clouds.size();
  1722. std::vector<CCCoreLib::ReferenceCloud*> refClouds;
  1723. if (s_extractSectionsAsClouds)
  1724. {
  1725. refClouds.resize(cloudCount, nullptr);
  1726. }
  1727. //for envelope extraction as a polyline
  1728. ccPointCloud* originalSlicePoints = nullptr;
  1729. ccPointCloud* unrolledSlicePoints = nullptr;
  1730. if (s_extractSectionsAsEnvelopes)
  1731. {
  1732. originalSlicePoints = new ccPointCloud("section.orig");
  1733. unrolledSlicePoints = new ccPointCloud("section.unroll");
  1734. //assign them the default (first!) global shift & scale info
  1735. assert(!m_clouds.empty());
  1736. ccGenericPointCloud* cloud = m_clouds.front().entity;
  1737. originalSlicePoints->copyGlobalShiftAndScale(*cloud);
  1738. }
  1739. //for each cloud
  1740. for (int c = 0; c < cloudCount; ++c)
  1741. {
  1742. ccGenericPointCloud* cloud = m_clouds[c].entity;
  1743. if (cloud)
  1744. {
  1745. //for envelope extraction as a cloud
  1746. CCCoreLib::ReferenceCloud* refCloud = nullptr;
  1747. if (s_extractSectionsAsClouds)
  1748. {
  1749. refCloud = new CCCoreLib::ReferenceCloud(cloud);
  1750. }
  1751. //compute the distance of each point to the current polyline segment
  1752. for (unsigned i = 0; i < cloud->size(); ++i)
  1753. {
  1754. const CCVector3* P = cloud->getPoint(i);
  1755. CCVector2 P2D(P->u[xDim], P->u[yDim]);
  1756. //for each vertex
  1757. PointCoordinateType minSquareDist = -CCCoreLib::PC_ONE;
  1758. PointCoordinateType curvilinearPos = 0.0;
  1759. size_t minIndex = 0;
  1760. for (size_t j = 0; j < polySegments2D.size(); ++j)
  1761. {
  1762. const Segment2D& seg2D = polySegments2D[j];
  1763. CCVector2 AP2D = P2D - seg2D.A;
  1764. //square distance to the polyline
  1765. PointCoordinateType squareDist = 0;
  1766. //longitudinal 'distance'
  1767. double dotprod = seg2D.uAB.dot(AP2D);
  1768. if (dotprod < 0)
  1769. {
  1770. if (j == 0 && !poly->isClosed())
  1771. {
  1772. continue;
  1773. }
  1774. squareDist = AP2D.norm2();
  1775. }
  1776. else if (dotprod > seg2D.lAB)
  1777. {
  1778. if (j + 1 == polySegments2D.size() && !poly->isClosed())
  1779. {
  1780. continue;
  1781. }
  1782. squareDist = (P2D - seg2D.B).norm2();
  1783. }
  1784. else
  1785. {
  1786. //orthogonal distance
  1787. squareDist = (AP2D - seg2D.uAB * dotprod).norm2();
  1788. }
  1789. if (minSquareDist < 0 || squareDist < minSquareDist)
  1790. {
  1791. minSquareDist = squareDist;
  1792. curvilinearPos = dotprod;
  1793. minIndex = j;
  1794. }
  1795. }
  1796. //elligible point?
  1797. if (minSquareDist >= 0 && minSquareDist < sectionThicknessSq)
  1798. {
  1799. //if we extract the section as cloud(s), we add the point to the (current) ref. cloud
  1800. if (s_extractSectionsAsClouds)
  1801. {
  1802. assert(refCloud);
  1803. unsigned refCloudSize = refCloud->size();
  1804. if (refCloudSize == refCloud->capacity())
  1805. {
  1806. refCloudSize += (refCloudSize / 2 + 1);
  1807. if (!refCloud->reserve(refCloudSize))
  1808. {
  1809. //not enough memory
  1810. ccLog::Warning("[ccSectionExtractionTool] Not enough memory");
  1811. error = true;
  1812. break;
  1813. }
  1814. }
  1815. refCloud->addPointIndex(i);
  1816. }
  1817. //if we extract the section as envelope(s), we add it to the 2D points set
  1818. if (s_extractSectionsAsEnvelopes)
  1819. {
  1820. assert(originalSlicePoints && unrolledSlicePoints);
  1821. assert(originalSlicePoints->size() == unrolledSlicePoints->size());
  1822. unsigned cloudSize = originalSlicePoints->size();
  1823. if (cloudSize == originalSlicePoints->capacity())
  1824. {
  1825. cloudSize += (cloudSize / 2 + 1);
  1826. if (!originalSlicePoints->reserve(cloudSize)
  1827. || !unrolledSlicePoints->reserve(cloudSize))
  1828. {
  1829. //not enough memory
  1830. ccLog::Warning("[ccSectionExtractionTool] Not enough memory");
  1831. error = true;
  1832. break;
  1833. }
  1834. }
  1835. const Segment2D& seg2D = polySegments2D[minIndex];
  1836. //we project the 'real' 3D point in the section plane
  1837. CCVector3 Pproj3D;
  1838. {
  1839. Pproj3D.u[xDim] = seg2D.A.x + seg2D.uAB.x * curvilinearPos;
  1840. Pproj3D.u[yDim] = seg2D.A.y + seg2D.uAB.y * curvilinearPos;
  1841. Pproj3D.u[vertDim] = P->u[vertDim];
  1842. }
  1843. originalSlicePoints->addPoint(Pproj3D);
  1844. unrolledSlicePoints->addPoint(CCVector3(seg2D.s + curvilinearPos, P->u[vertDim], 0));
  1845. }
  1846. }
  1847. if (error)
  1848. {
  1849. break;
  1850. }
  1851. } //for each point
  1852. if (refCloud)
  1853. {
  1854. assert(s_extractSectionsAsClouds);
  1855. if (error || refCloud->size() == 0)
  1856. {
  1857. delete refCloud;
  1858. refCloud = nullptr;
  1859. }
  1860. else
  1861. {
  1862. refClouds[c] = refCloud;
  1863. }
  1864. }
  1865. }
  1866. if (error)
  1867. {
  1868. break;
  1869. }
  1870. } //for each cloud
  1871. if (!error)
  1872. {
  1873. //Extract sections as (polyline) envelopes
  1874. if (/*!error && */s_extractSectionsAsEnvelopes)
  1875. {
  1876. assert(originalSlicePoints && unrolledSlicePoints);
  1877. bool envelopeGenerated = false;
  1878. error = !extractSectionEnvelope(poly,
  1879. originalSlicePoints,
  1880. unrolledSlicePoints,
  1881. s + 1,
  1882. s_extractSectionsType,
  1883. s_envelopeMaxEdgeLength,
  1884. s_multiPass,
  1885. s_splitEnvelope,
  1886. envelopeGenerated,
  1887. visualDebugMode);
  1888. if (envelopeGenerated)
  1889. {
  1890. ++generatedEnvelopes;
  1891. }
  1892. }
  1893. //Extract sections as clouds
  1894. if (!error && s_extractSectionsAsClouds)
  1895. {
  1896. assert(static_cast<int>(refClouds.size()) == cloudCount);
  1897. bool cloudGenerated = false;
  1898. error = !extractSectionCloud(refClouds, s + 1, cloudGenerated);
  1899. if (cloudGenerated)
  1900. ++generatedClouds;
  1901. }
  1902. }
  1903. //release memory
  1904. for (auto & refCloud : refClouds)
  1905. {
  1906. delete refCloud;
  1907. refCloud = nullptr;
  1908. }
  1909. delete originalSlicePoints;
  1910. originalSlicePoints = nullptr;
  1911. delete unrolledSlicePoints;
  1912. unrolledSlicePoints = nullptr;
  1913. } //if (poly)
  1914. if (!nprogress.oneStep())
  1915. {
  1916. ccLog::Warning("[ccSectionExtractionTool] Canceled by user");
  1917. error = true;
  1918. }
  1919. if (error)
  1920. break;
  1921. } //for (int s=0; s<m_sections.size(); ++s)
  1922. }
  1923. catch (const std::bad_alloc&)
  1924. {
  1925. error = true;
  1926. }
  1927. if (error)
  1928. {
  1929. ccLog::Error("An error occurred (see console)");
  1930. }
  1931. else
  1932. {
  1933. ccLog::Print(QString("[ccSectionExtractionTool] Job done (%1 envelope(s) and %2 cloud(s) were generated)").arg(generatedEnvelopes).arg(generatedClouds));
  1934. }
  1935. }