ccDBRoot.cpp 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342
  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 "ccDBRoot.h"
  18. //Local
  19. #include "ccGLWindowInterface.h"
  20. //Qt
  21. #include <QApplication>
  22. #include <QHeaderView>
  23. #include <QInputDialog>
  24. #include <QMenu>
  25. #include <QMessageBox>
  26. #include <QMimeData>
  27. #include <QRegExp>
  28. #include <QStandardItemModel>
  29. #include <QTreeView>
  30. //qCC_db
  31. #include <cc2DLabel.h>
  32. #include <ccFacet.h>
  33. #include <ccGBLSensor.h>
  34. #include <ccGenericPointCloud.h>
  35. #include <ccGenericPrimitive.h>
  36. #include <ccHObject.h>
  37. #include <ccLog.h>
  38. #include <ccMaterialSet.h>
  39. #include <ccMesh.h>
  40. #include <ccPlane.h>
  41. #include <ccPointCloud.h>
  42. #include <ccPolyline.h>
  43. #include <ccScalarField.h>
  44. //CClib
  45. #include <CCMiscTools.h>
  46. //common
  47. #include <ccPickOneElementDlg.h>
  48. //local
  49. #include "ccPropertiesTreeDelegate.h"
  50. #include "ccSelectChildrenDlg.h"
  51. #include "mainwindow.h"
  52. //system
  53. #include <algorithm>
  54. #include <cassert>
  55. #include <cstring>
  56. //Minimum width of the left column of the properties tree view
  57. static const int c_propViewLeftColumnWidth = 115;
  58. //test whether a cloud can be deleted or moved
  59. static bool CanDetachCloud(const ccHObject* obj)
  60. {
  61. if (!obj)
  62. {
  63. assert(false);
  64. return false;
  65. }
  66. ccHObject* parent = obj->getParent();
  67. if (!parent)
  68. {
  69. assert(false);
  70. return true;
  71. }
  72. //can't delete the vertices of a mesh or the verties of a polyline
  73. bool blocked = ( (parent->isKindOf(CC_TYPES::MESH) && (ccHObjectCaster::ToGenericMesh(parent)->getAssociatedCloud() == obj))
  74. || (parent->isKindOf(CC_TYPES::POLY_LINE) && (dynamic_cast<ccPointCloud*>(ccHObjectCaster::ToPolyline(parent)->getAssociatedCloud()) == obj)) );
  75. return !blocked;
  76. }
  77. class DBRootIcons
  78. {
  79. public:
  80. const QIcon &icon( CC_CLASS_ENUM id, bool locked )
  81. {
  82. if ( mIconMap.isEmpty() )
  83. {
  84. init();
  85. }
  86. if ( !mIconMap.contains( id ) )
  87. {
  88. return (locked ? mDefaultIcons.second : mDefaultIcons.first);
  89. }
  90. const int index = mIconMap[id];
  91. const IconPair &icons = mIconList[index];
  92. if ( !locked )
  93. {
  94. return icons.first;
  95. }
  96. // If we don't have a locked icon for this class, return the unlocked one
  97. return (icons.second.isNull() ? icons.first : icons.second);
  98. }
  99. private:
  100. using IconPair = QPair<QIcon, QIcon>; // unlocked icon, locked icon (if any)
  101. using IconPairList = QVector<IconPair>;
  102. using IconMap = QMap<CC_CLASS_ENUM, int>;
  103. void init()
  104. {
  105. // Special case for default - no icon in general, but a lock if locked
  106. mDefaultIcons = { {}, QIcon(QStringLiteral(":/CC/images/dbLockSymbol.png")) };
  107. const int hObjectIndex = mIconList.count();
  108. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbHObjectSymbol.png")),
  109. QIcon(QStringLiteral(":/CC/images/dbHObjectSymbolLocked.png")) } );
  110. const int cloudIndex = mIconList.count();
  111. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbCloudSymbol.png")),
  112. QIcon(QStringLiteral(":/CC/images/dbCloudSymbolLocked.png")) } );
  113. const int geomIndex = mIconList.count();
  114. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbMiscGeomSymbol.png")),
  115. QIcon(QStringLiteral(":/CC/images/dbMiscGeomSymbolLocked.png")) } );
  116. const int meshIndex = mIconList.count();
  117. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbMeshSymbol.png")),
  118. QIcon(QStringLiteral(":/CC/images/dbMeshSymbolLocked.png")) } );
  119. const int subMeshIndex = mIconList.count();
  120. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbSubMeshSymbol.png")),
  121. QIcon(QStringLiteral(":/CC/images/dbSubMeshSymbolLocked.png")) } );
  122. const int polyLineIndex = mIconList.count();
  123. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbPolylineSymbol.png")),
  124. {} } );
  125. const int octreeIndex = mIconList.count();
  126. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbOctreeSymbol.png")),
  127. QIcon(QStringLiteral(":/CC/images/dbOctreeSymbolLocked.png")) } );
  128. const int calibratedImageIndex = mIconList.count();
  129. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbCalibratedImageSymbol.png")),
  130. {} } );
  131. const int imageIndex = mIconList.count();
  132. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbImageSymbol.png")),
  133. {} } );
  134. const int sensorIndex = mIconList.count();
  135. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbGBLSensorSymbol.png")),
  136. {} } );
  137. const int cameraSensorIndex = mIconList.count();
  138. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbCamSensorSymbol.png")),
  139. {} } );
  140. const int materialSetIndex = mIconList.count();
  141. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbMaterialSymbol.png")),
  142. {} } );
  143. const int containerIndex = mIconList.count();
  144. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbContainerSymbol.png")),
  145. QIcon(QStringLiteral(":/CC/images/dbContainerSymbolLocked.png")) } );
  146. const int labelIndex = mIconList.count();
  147. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbLabelSymbol.png")),
  148. {} } );
  149. const int viewportObjIndex = mIconList.count();
  150. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbViewportSymbol.png")),
  151. {} } );
  152. const int viewportLabelIndex = mIconList.count();
  153. mIconList.append( { QIcon(QStringLiteral(":/CC/images/dbAreaLabelSymbol.png")),
  154. {} } );
  155. mIconMap = {
  156. { CC_TYPES::HIERARCHY_OBJECT, hObjectIndex },
  157. { CC_TYPES::POINT_CLOUD, cloudIndex },
  158. { CC_TYPES::PLANE, geomIndex },
  159. { CC_TYPES::SPHERE, geomIndex },
  160. { CC_TYPES::TORUS, geomIndex },
  161. { CC_TYPES::CYLINDER, geomIndex },
  162. { CC_TYPES::CONE, geomIndex },
  163. { CC_TYPES::BOX, geomIndex },
  164. { CC_TYPES::DISH, geomIndex },
  165. { CC_TYPES::EXTRU, geomIndex },
  166. { CC_TYPES::FACET, geomIndex },
  167. { CC_TYPES::QUADRIC, geomIndex },
  168. { CC_TYPES::MESH, meshIndex },
  169. { CC_TYPES::MESH_GROUP, subMeshIndex },
  170. { CC_TYPES::SUB_MESH, subMeshIndex },
  171. { CC_TYPES::POLY_LINE, polyLineIndex },
  172. { CC_TYPES::POINT_OCTREE, octreeIndex },
  173. { CC_TYPES::CALIBRATED_IMAGE, calibratedImageIndex },
  174. { CC_TYPES::IMAGE, imageIndex },
  175. { CC_TYPES::SENSOR, sensorIndex },
  176. { CC_TYPES::GBL_SENSOR, sensorIndex },
  177. { CC_TYPES::CAMERA_SENSOR, cameraSensorIndex },
  178. { CC_TYPES::MATERIAL_SET, materialSetIndex },
  179. { CC_TYPES::NORMALS_ARRAY, containerIndex },
  180. { CC_TYPES::NORMAL_INDEXES_ARRAY, containerIndex },
  181. { CC_TYPES::RGB_COLOR_ARRAY, containerIndex },
  182. { CC_TYPES::RGBA_COLOR_ARRAY, containerIndex },
  183. { CC_TYPES::TEX_COORDS_ARRAY, containerIndex },
  184. { CC_TYPES::TRANS_BUFFER, containerIndex },
  185. { CC_TYPES::LABEL_2D, labelIndex },
  186. { CC_TYPES::VIEWPORT_2D_OBJECT, viewportObjIndex },
  187. { CC_TYPES::VIEWPORT_2D_LABEL, viewportLabelIndex },
  188. { CC_TYPES::COORDINATESYSTEM, geomIndex }
  189. };
  190. }
  191. IconPair mDefaultIcons;
  192. IconPairList mIconList;
  193. IconMap mIconMap;
  194. };
  195. Q_GLOBAL_STATIC( DBRootIcons, gDBRootIcons )
  196. ccDBRoot::ccDBRoot(ccCustomQTreeView* dbTreeWidget, QTreeView* propertiesTreeWidget, QObject* parent) : QAbstractItemModel(parent)
  197. {
  198. m_treeRoot = new ccHObject("DB Tree");
  199. //DB Tree
  200. assert(dbTreeWidget);
  201. m_dbTreeWidget = dbTreeWidget;
  202. m_dbTreeWidget->setModel(this);
  203. m_dbTreeWidget->header()->hide();
  204. //drag & drop support
  205. m_dbTreeWidget->setDragEnabled(true);
  206. m_dbTreeWidget->setAcceptDrops(true);
  207. m_dbTreeWidget->setDropIndicatorShown(true);
  208. //context menu on DB tree elements
  209. m_dbTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
  210. m_expandBranch = new QAction("Expand branch", this);
  211. m_collapseBranch = new QAction("Collapse branch", this);
  212. m_gatherInformation = new QAction("Information (recursive)", this);
  213. m_sortChildrenType = new QAction("Sort children by type", this);
  214. m_sortChildrenAZ = new QAction("Sort children by name (A-Z)", this);
  215. m_sortChildrenZA = new QAction("Sort children by name (Z-A)", this);
  216. m_selectByTypeAndName = new QAction("Select children by type and/or name", this);
  217. m_deleteSelectedEntities = new QAction("Delete", this);
  218. m_toggleSelectedEntities = new QAction("Toggle", this);
  219. m_toggleSelectedEntitiesVisibility = new QAction("Toggle visibility", this);
  220. m_toggleSelectedEntitiesColor = new QAction("Toggle color", this);
  221. m_toggleSelectedEntitiesNormals = new QAction("Toggle normals", this);
  222. m_toggleSelectedEntitiesMat = new QAction("Toggle materials/textures", this);
  223. m_toggleSelectedEntitiesSF = new QAction("Toggle SF", this);
  224. m_toggleSelectedEntities3DName = new QAction("Toggle 3D name", this);
  225. m_addEmptyGroup = new QAction("Add empty group", this);
  226. m_alignCameraWithEntity = new QAction("Align camera", this);
  227. m_alignCameraWithEntityReverse = new QAction("Align camera (reverse)", this);
  228. m_enableBubbleViewMode = new QAction("Bubble-view", this);
  229. m_editLabelScalarValue = new QAction("Edit scalar value", this);
  230. m_contextMenuPos = QPoint(-1,-1);
  231. //connect custom context menu actions
  232. connect(m_dbTreeWidget, &QWidget::customContextMenuRequested, this, &ccDBRoot::showContextMenu);
  233. connect(m_expandBranch, &QAction::triggered, this, &ccDBRoot::expandBranch);
  234. connect(m_collapseBranch, &QAction::triggered, this, &ccDBRoot::collapseBranch);
  235. connect(m_gatherInformation, &QAction::triggered, this, &ccDBRoot::gatherRecursiveInformation);
  236. connect(m_sortChildrenAZ, &QAction::triggered, this, &ccDBRoot::sortChildrenAZ);
  237. connect(m_sortChildrenZA, &QAction::triggered, this, &ccDBRoot::sortChildrenZA);
  238. connect(m_sortChildrenType, &QAction::triggered, this, &ccDBRoot::sortChildrenType);
  239. connect(m_selectByTypeAndName, &QAction::triggered, this, &ccDBRoot::selectByTypeAndName);
  240. connect(m_deleteSelectedEntities, &QAction::triggered, this, &ccDBRoot::deleteSelectedEntities);
  241. connect(m_toggleSelectedEntities, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntities);
  242. connect(m_toggleSelectedEntitiesVisibility, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntitiesVisibility);
  243. connect(m_toggleSelectedEntitiesColor, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntitiesColor);
  244. connect(m_toggleSelectedEntitiesNormals, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntitiesNormals);
  245. connect(m_toggleSelectedEntitiesMat, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntitiesMat);
  246. connect(m_toggleSelectedEntitiesSF, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntitiesSF);
  247. connect(m_toggleSelectedEntities3DName, &QAction::triggered, this, &ccDBRoot::toggleSelectedEntities3DName);
  248. connect(m_addEmptyGroup, &QAction::triggered, this, &ccDBRoot::addEmptyGroup);
  249. connect(m_alignCameraWithEntity, &QAction::triggered, this, &ccDBRoot::alignCameraWithEntityDirect);
  250. connect(m_alignCameraWithEntityReverse, &QAction::triggered, this, &ccDBRoot::alignCameraWithEntityIndirect);
  251. connect(m_enableBubbleViewMode, &QAction::triggered, this, &ccDBRoot::enableBubbleViewMode);
  252. connect(m_editLabelScalarValue, &QAction::triggered, this, &ccDBRoot::editLabelScalarValue);
  253. //other DB tree signals/slots connection
  254. connect(m_dbTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ccDBRoot::changeSelection);
  255. //Properties Tree
  256. assert(propertiesTreeWidget);
  257. m_propertiesTreeWidget = propertiesTreeWidget;
  258. m_propertiesModel = new QStandardItemModel(0, 2, parent);
  259. m_ccPropDelegate = new ccPropertiesTreeDelegate(m_propertiesModel, m_propertiesTreeWidget);
  260. m_propertiesTreeWidget->setItemDelegate(m_ccPropDelegate);
  261. m_propertiesTreeWidget->setModel(m_propertiesModel);
  262. m_propertiesTreeWidget->header()->setSectionResizeMode(QHeaderView::Interactive);
  263. m_propertiesTreeWidget->setEnabled(false);
  264. //Properties tree signals/slots connection
  265. connect(m_ccPropDelegate, &ccPropertiesTreeDelegate::ccObjectPropertiesChanged, this, &ccDBRoot::updateCCObject);
  266. connect(m_ccPropDelegate, &ccPropertiesTreeDelegate::ccObjectAppearanceChanged, this, &ccDBRoot::redrawCCObject);
  267. connect(m_ccPropDelegate, &ccPropertiesTreeDelegate::ccObjectAndChildrenAppearanceChanged, this, &ccDBRoot::redrawCCObjectAndChildren);
  268. }
  269. ccDBRoot::~ccDBRoot()
  270. {
  271. delete m_ccPropDelegate;
  272. delete m_propertiesModel;
  273. delete m_treeRoot;
  274. }
  275. void ccDBRoot::unloadAll()
  276. {
  277. if (!m_treeRoot)
  278. {
  279. return;
  280. }
  281. while (m_treeRoot->getChildrenNumber() > 0)
  282. {
  283. int i = static_cast<int>(m_treeRoot->getChildrenNumber()) - 1;
  284. ccHObject* object = m_treeRoot->getChild(i);
  285. assert(object);
  286. object->prepareDisplayForRefresh_recursive();
  287. beginRemoveRows(index(object).parent(), i, i);
  288. m_treeRoot->removeChild(i);
  289. endRemoveRows();
  290. }
  291. Q_EMIT dbIsEmpty();
  292. updatePropertiesView();
  293. MainWindow::RefreshAllGLWindow(false);
  294. }
  295. ccHObject* ccDBRoot::getRootEntity()
  296. {
  297. return m_treeRoot;
  298. }
  299. void ccDBRoot::addElement(ccHObject* object, bool autoExpand/*=true*/)
  300. {
  301. if (!m_treeRoot)
  302. {
  303. assert(false);
  304. return;
  305. }
  306. if (!object)
  307. {
  308. assert(false);
  309. return;
  310. }
  311. bool wasEmpty = (m_treeRoot->getChildrenNumber() == 0);
  312. //look for object's parent
  313. ccHObject* parentObject = object->getParent();
  314. if (!parentObject)
  315. {
  316. //if the object has no parent, it will be inserted at tree root
  317. parentObject = m_treeRoot;
  318. m_treeRoot->addChild(object);
  319. }
  320. else
  321. {
  322. //DGM TODO: how could we check that the object is not already inserted in the DB tree?
  323. //The double insertion can cause serious damage to it (not sure why excatly though).
  324. //The code below doesn't work because the 'index' method will always return a valid index
  325. //as soon as the object has a parent (index creation is a purely 'logical' approach)
  326. //QModelIndex nodeIndex = index(object);
  327. //if (nodeIndex.isValid())
  328. // return;
  329. }
  330. //look for insert node index in tree
  331. QModelIndex insertNodeIndex = index(parentObject);
  332. int childPos = parentObject->getChildIndex(object);
  333. //row insertion operation (start)
  334. beginInsertRows(insertNodeIndex, childPos, childPos);
  335. //row insertion operation (end)
  336. endInsertRows();
  337. if (autoExpand)
  338. {
  339. //expand the parent (just in case)
  340. m_dbTreeWidget->expand(index(parentObject));
  341. //and the child
  342. m_dbTreeWidget->expand(index(object));
  343. }
  344. else //if (parentObject)
  345. {
  346. m_dbTreeWidget->expand(insertNodeIndex);
  347. }
  348. if (wasEmpty && m_treeRoot->getChildrenNumber() != 0)
  349. {
  350. Q_EMIT dbIsNotEmptyAnymore();
  351. }
  352. }
  353. void ccDBRoot::expandElement(ccHObject* object, bool state)
  354. {
  355. if (!object || !m_dbTreeWidget)
  356. {
  357. return;
  358. }
  359. m_dbTreeWidget->setExpanded(index(object), state);
  360. }
  361. void ccDBRoot::removeElements(ccHObject::Container& objects)
  362. {
  363. if (objects.empty())
  364. {
  365. assert(false);
  366. return;
  367. }
  368. //we hide properties view in case this is the deleted object that is currently selected
  369. hidePropertiesView();
  370. //every object in tree must have a parent!
  371. for (ccHObject* object : objects)
  372. {
  373. ccHObject* parent = object->getParent();
  374. if (!parent)
  375. {
  376. ccLog::Warning(QString("[ccDBRoot::removeElements] Internal error: object '%1' has no parent").arg(object->getName()));
  377. continue;
  378. }
  379. //just in case
  380. object->prepareDisplayForRefresh();
  381. int childPos = parent->getChildIndex(object);
  382. assert(childPos >= 0);
  383. {
  384. //row removal operation (start)
  385. beginRemoveRows(index(parent), childPos, childPos);
  386. parent->removeChild(childPos);
  387. //row removal operation (end)
  388. endRemoveRows();
  389. }
  390. }
  391. //we restore properties view
  392. updatePropertiesView();
  393. if (m_treeRoot->getChildrenNumber() == 0)
  394. {
  395. Q_EMIT dbIsEmpty();
  396. }
  397. }
  398. void ccDBRoot::removeElement(ccHObject* object)
  399. {
  400. if (!object)
  401. {
  402. assert(false);
  403. return;
  404. }
  405. //we hide properties view in case this is the deleted object that is currently selected
  406. hidePropertiesView();
  407. //every object in tree must have a parent!
  408. ccHObject* parent = object->getParent();
  409. if (!parent)
  410. {
  411. ccLog::Warning("[ccDBRoot::removeElement] Internal error: object has no parent");
  412. return;
  413. }
  414. //just in case
  415. object->prepareDisplayForRefresh();
  416. int childPos = parent->getChildIndex(object);
  417. assert(childPos >= 0);
  418. {
  419. //row removal operation (start)
  420. beginRemoveRows(index(parent), childPos, childPos);
  421. parent->removeChild(childPos);
  422. //row removal operation (end)
  423. endRemoveRows();
  424. }
  425. //we restore properties view
  426. updatePropertiesView();
  427. if (m_treeRoot->getChildrenNumber() == 0)
  428. {
  429. Q_EMIT dbIsEmpty();
  430. }
  431. }
  432. void ccDBRoot::deleteSelectedEntities()
  433. {
  434. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  435. QModelIndexList selectedIndexes = qism->selectedIndexes();
  436. if (selectedIndexes.empty())
  437. {
  438. return;
  439. }
  440. unsigned selCount = static_cast<unsigned>(selectedIndexes.size());
  441. hidePropertiesView();
  442. bool verticesWarningIssued = false;
  443. //we remove all objects that are children of other deleted ones!
  444. //(otherwise we may delete the parent before the child!)
  445. //TODO DGM: not sure this is still necessary with the new dependency mechanism
  446. std::vector<ccHObject*> toBeDeleted;
  447. for (unsigned i = 0; i < selCount; ++i)
  448. {
  449. ccHObject* obj = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  450. //we don't take care of parent-less objects (i.e. the tree root)
  451. if (!obj->getParent() || obj->isLocked())
  452. {
  453. ccLog::Warning(QString("Object '%1' can't be deleted this way (locked)").arg(obj->getName()));
  454. continue;
  455. }
  456. //we don't consider objects that are 'descendent' of others in the selection
  457. bool isDescendent = false;
  458. for (unsigned j = 0; j < selCount; ++j)
  459. {
  460. if (i != j)
  461. {
  462. ccHObject* otherObj = static_cast<ccHObject*>(selectedIndexes[j].internalPointer());
  463. if (otherObj->isAncestorOf(obj))
  464. {
  465. isDescendent = true;
  466. break;
  467. }
  468. }
  469. }
  470. if (!isDescendent)
  471. {
  472. //last check: mesh vertices
  473. if (obj->isKindOf(CC_TYPES::POINT_CLOUD) && !CanDetachCloud(obj))
  474. {
  475. if (!verticesWarningIssued)
  476. {
  477. ccLog::Warning("Vertices can't be deleted without their parent mesh");
  478. verticesWarningIssued = true;
  479. }
  480. continue;
  481. }
  482. toBeDeleted.push_back(obj);
  483. }
  484. }
  485. qism->clear();
  486. while (!toBeDeleted.empty())
  487. {
  488. ccHObject* object = toBeDeleted.back();
  489. assert(object);
  490. toBeDeleted.pop_back();
  491. object->prepareDisplayForRefresh_recursive();
  492. if (object->isKindOf(CC_TYPES::MESH))
  493. {
  494. //specific case: the object is a mesh and its parent is its vertices!
  495. //(can happen if a Delaunay mesh is computed directly in CC)
  496. if (object->getParent() && object->getParent() == ccHObjectCaster::ToGenericMesh(object)->getAssociatedCloud())
  497. {
  498. object->getParent()->setVisible(true);
  499. }
  500. }
  501. ccHObject* parent = object->getParent();
  502. int childPos = parent->getChildIndex(object);
  503. assert(childPos >= 0);
  504. beginRemoveRows(index(object).parent(), childPos, childPos);
  505. parent->removeChild(childPos);
  506. endRemoveRows();
  507. }
  508. updatePropertiesView();
  509. if (m_treeRoot->getChildrenNumber() == 0)
  510. {
  511. Q_EMIT dbIsEmpty();
  512. }
  513. MainWindow::RefreshAllGLWindow(false);
  514. }
  515. QVariant ccDBRoot::data(const QModelIndex& idx, int role) const
  516. {
  517. if (!idx.isValid())
  518. {
  519. return QVariant();
  520. }
  521. const ccHObject *item = static_cast<const ccHObject*>(idx.internalPointer());
  522. assert(item);
  523. if (!item)
  524. {
  525. return QVariant();
  526. }
  527. switch (role)
  528. {
  529. case Qt::DisplayRole:
  530. {
  531. QString baseName(item->getName());
  532. if (baseName.isEmpty())
  533. baseName = QStringLiteral("no name");
  534. //specific case
  535. if (item->isA(CC_TYPES::LABEL_2D))
  536. baseName = QStringLiteral("2D label: ")+baseName;
  537. else if (item->isA(CC_TYPES::VIEWPORT_2D_LABEL))
  538. baseName = QStringLiteral("2D area label: ")+baseName;
  539. return baseName;
  540. }
  541. case Qt::EditRole:
  542. {
  543. return item->getName();
  544. }
  545. case Qt::DecorationRole:
  546. {
  547. // does the object have an "embedded icon"? - It may be the case for ccHObject defined in plugins
  548. QIcon icon = item->getIcon();
  549. if (!icon.isNull())
  550. {
  551. return icon;
  552. }
  553. const bool locked = item->isLocked();
  554. switch (item->getClassID())
  555. {
  556. case CC_TYPES::HIERARCHY_OBJECT:
  557. if ( item->getChildrenNumber() )
  558. {
  559. return gDBRootIcons->icon( item->getClassID(), locked );
  560. }
  561. return {};
  562. default:
  563. {
  564. return gDBRootIcons->icon( item->getClassID(), locked );
  565. }
  566. }
  567. }
  568. case Qt::CheckStateRole:
  569. {
  570. // Don't include checkboxes for hierarchy objects if they have no children or only contain hierarchy objects (recursively)
  571. if ( item->getClassID() == CC_TYPES::HIERARCHY_OBJECT )
  572. {
  573. if ( item->getChildrenNumber() == 0 )
  574. {
  575. return {};
  576. }
  577. ccHObject::Container drawableObjects;
  578. unsigned int count = item->filterChildren( drawableObjects, true, CC_TYPES::HIERARCHY_OBJECT, true );
  579. if ( item->getChildCountRecursive() == count )
  580. {
  581. return {};
  582. }
  583. }
  584. if (item->isEnabled())
  585. return Qt::Checked;
  586. else
  587. return Qt::Unchecked;
  588. }
  589. default:
  590. //unhandled role
  591. break;
  592. }
  593. return QVariant();
  594. }
  595. bool ccDBRoot::setData(const QModelIndex& idx, const QVariant& value, int role)
  596. {
  597. if (idx.isValid())
  598. {
  599. if (role == Qt::EditRole)
  600. {
  601. if (value.toString().isEmpty())
  602. {
  603. return false;
  604. }
  605. ccHObject *item = static_cast<ccHObject*>(idx.internalPointer());
  606. assert(item);
  607. if (item)
  608. {
  609. item->setName(value.toString());
  610. //particular cases:
  611. // - labels name is their title (so we update them)
  612. // - name might be displayed in 3D
  613. if (item->nameShownIn3D() || item->isKindOf(CC_TYPES::LABEL_2D))
  614. if (item->isEnabled() && item->isVisible() && item->getDisplay())
  615. item->getDisplay()->redraw();
  616. reflectObjectPropChange(item);
  617. Q_EMIT dataChanged(idx, idx);
  618. }
  619. return true;
  620. }
  621. else if (role == Qt::CheckStateRole)
  622. {
  623. ccHObject *item = static_cast<ccHObject*>(idx.internalPointer());
  624. assert(item);
  625. if (item)
  626. {
  627. if (value == Qt::Checked)
  628. item->setEnabled(true);
  629. else
  630. item->setEnabled(false);
  631. redrawCCObjectAndChildren(item);
  632. //reflectObjectPropChange(item);
  633. }
  634. return true;
  635. }
  636. }
  637. return false;
  638. }
  639. QModelIndex ccDBRoot::index(int row, int column, const QModelIndex &parentIndex) const
  640. {
  641. if (!hasIndex(row, column, parentIndex))
  642. {
  643. return QModelIndex();
  644. }
  645. ccHObject *parent = (parentIndex.isValid() ? static_cast<ccHObject*>(parentIndex.internalPointer()) : m_treeRoot);
  646. assert(parent);
  647. if (!parent)
  648. {
  649. return QModelIndex();
  650. }
  651. ccHObject *child = parent->getChild(row);
  652. return child ? createIndex(row, column, child) : QModelIndex();
  653. }
  654. QModelIndex ccDBRoot::index(ccHObject* object)
  655. {
  656. assert(object);
  657. if (object == m_treeRoot)
  658. {
  659. return QModelIndex();
  660. }
  661. ccHObject* parent = object->getParent();
  662. if (!parent)
  663. {
  664. //DGM: actually, it can happen (for instance if the entity is displayed in the local DB of a 3D view)
  665. //ccLog::Error(QString("An error occurred while creating DB tree index: object '%1' has no parent").arg(object->getName()));
  666. return QModelIndex();
  667. }
  668. int pos = parent->getChildIndex(object);
  669. assert(pos >= 0);
  670. return createIndex(pos, 0, object);
  671. }
  672. QModelIndex ccDBRoot::parent(const QModelIndex& idx) const
  673. {
  674. if (!idx.isValid())
  675. {
  676. return QModelIndex();
  677. }
  678. ccHObject *childItem = static_cast<ccHObject*>(idx.internalPointer());
  679. if (!childItem)
  680. {
  681. assert(false);
  682. return QModelIndex();
  683. }
  684. ccHObject *parentItem = childItem->getParent();
  685. assert(parentItem);
  686. if (!parentItem || parentItem == m_treeRoot)
  687. {
  688. return QModelIndex();
  689. }
  690. return createIndex(parentItem->getIndex(), 0, parentItem);
  691. }
  692. int ccDBRoot::rowCount(const QModelIndex &parent) const
  693. {
  694. ccHObject *parentItem = nullptr;
  695. if (!parent.isValid())
  696. parentItem = m_treeRoot;
  697. else
  698. parentItem = static_cast<ccHObject*>(parent.internalPointer());
  699. assert(parentItem);
  700. return (parentItem ? parentItem->getChildrenNumber() : 0);
  701. }
  702. int ccDBRoot::columnCount(const QModelIndex &parent) const
  703. {
  704. return 1;
  705. }
  706. void ccDBRoot::changeSelection(const QItemSelection & selected, const QItemSelection & deselected)
  707. {
  708. //first unselect
  709. QModelIndexList deselectedItems = deselected.indexes();
  710. {
  711. for (int i = 0; i < deselectedItems.count(); ++i)
  712. {
  713. ccHObject* element = static_cast<ccHObject*>(deselectedItems.at(i).internalPointer());
  714. assert(element);
  715. if (element)
  716. {
  717. element->setSelected(false);
  718. element->prepareDisplayForRefresh();
  719. }
  720. }
  721. }
  722. //then select
  723. QModelIndexList selectedItems = selected.indexes();
  724. {
  725. for (int i = 0; i < selectedItems.count(); ++i)
  726. {
  727. ccHObject* element = static_cast<ccHObject*>(selectedItems.at(i).internalPointer());
  728. assert(element);
  729. if (element)
  730. {
  731. element->setSelected(true);
  732. element->prepareDisplayForRefresh();
  733. }
  734. }
  735. }
  736. updatePropertiesView();
  737. MainWindow::RefreshAllGLWindow();
  738. Q_EMIT selectionChanged();
  739. }
  740. void ccDBRoot::unselectEntity(ccHObject* obj)
  741. {
  742. if (obj && obj->isSelected())
  743. {
  744. QModelIndex objIndex = index(obj);
  745. if (objIndex.isValid())
  746. {
  747. QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
  748. assert(selectionModel);
  749. selectionModel->select(objIndex, QItemSelectionModel::Deselect);
  750. }
  751. }
  752. }
  753. void ccDBRoot::unselectAllEntities()
  754. {
  755. QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
  756. assert(selectionModel);
  757. selectionModel->clear();
  758. QCoreApplication::processEvents();
  759. }
  760. void ccDBRoot::selectEntity(ccHObject* obj, bool forceAdditiveSelection/*=false*/)
  761. {
  762. bool additiveSelection = forceAdditiveSelection || (QApplication::keyboardModifiers () & Qt::ControlModifier);
  763. QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
  764. assert(selectionModel);
  765. //valid object? then we will try to select (or toggle) it
  766. if (obj)
  767. {
  768. QModelIndex selectedIndex = index(obj);
  769. if (selectedIndex.isValid())
  770. {
  771. //if CTRL is pushed (or additive selection is forced)
  772. if (additiveSelection)
  773. {
  774. //default case: toggle current item selection state
  775. if (!obj->isSelected())
  776. {
  777. QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
  778. if (!selectedIndexes.empty())
  779. {
  780. //special case: labels can only be merged with labels!
  781. if (obj->isA(CC_TYPES::LABEL_2D) != static_cast<ccHObject*>(selectedIndexes[0].internalPointer())->isA(CC_TYPES::LABEL_2D))
  782. {
  783. ccLog::Warning("[Selection] Labels and other entities can't be mixed (release the CTRL key to start a new selection)");
  784. return;
  785. }
  786. }
  787. }
  788. selectionModel->select(selectedIndex, QItemSelectionModel::Toggle);
  789. obj->setSelected(selectionModel->isSelected(selectedIndex));
  790. }
  791. else
  792. {
  793. if (selectionModel->isSelected(selectedIndex)) //nothing to do
  794. return;
  795. selectionModel->select(selectedIndex, QItemSelectionModel::ClearAndSelect);
  796. obj->setSelected(true);
  797. }
  798. //hack: auto-scroll to selected element
  799. if (obj->isSelected() && !additiveSelection)
  800. m_dbTreeWidget->scrollTo(selectedIndex);
  801. }
  802. }
  803. //otherwise we clear current selection (if CTRL is not pushed)
  804. else if (!additiveSelection)
  805. {
  806. selectionModel->clear();
  807. }
  808. }
  809. void ccDBRoot::selectEntities(std::unordered_set<int> entIDs)
  810. {
  811. bool ctrlPushed = (QApplication::keyboardModifiers () & Qt::ControlModifier);
  812. //convert input list of IDs to proper entities
  813. ccHObject::Container entities;
  814. try
  815. {
  816. entities.reserve(entIDs.size());
  817. }
  818. catch (const std::bad_alloc&)
  819. {
  820. ccLog::Warning("[ccDBRoot::selectEntities] Not enough memory");
  821. return;
  822. }
  823. for (auto entID : entIDs)
  824. {
  825. ccHObject* obj = find(entID);
  826. if (obj != nullptr)
  827. {
  828. entities.push_back(obj);
  829. }
  830. }
  831. selectEntities(entities, ctrlPushed);
  832. }
  833. void ccDBRoot::selectEntities(const ccHObject::Container& entities, bool incremental/*=false*/)
  834. {
  835. //selection model
  836. QItemSelectionModel* selectionModel = m_dbTreeWidget->selectionModel();
  837. assert(selectionModel);
  838. //count the number of lables
  839. size_t labelCount = 0;
  840. for (auto entity : entities)
  841. {
  842. if (entity == nullptr)
  843. {
  844. assert(false);
  845. continue;
  846. }
  847. if (entity->isA(CC_TYPES::LABEL_2D))
  848. {
  849. ++labelCount;
  850. }
  851. }
  852. //create new selection structure
  853. QItemSelection newSelection;
  854. //shall we keep labels?
  855. bool keepLabels = false;
  856. {
  857. QModelIndexList formerSelectedIndexes = selectionModel->selectedIndexes();
  858. if (formerSelectedIndexes.isEmpty() || !incremental)
  859. keepLabels = (labelCount == entities.size()); //yes if they are the only selected entities
  860. else if (incremental)
  861. keepLabels = static_cast<ccHObject*>(formerSelectedIndexes[0].internalPointer())->isA(CC_TYPES::LABEL_2D); //yes if previously selected entities were already labels
  862. }
  863. for (auto entity : entities)
  864. {
  865. if (entity == nullptr)
  866. {
  867. continue;
  868. }
  869. //filter input selection (can't keep both labels and standard entities --> we can't mix them!)
  870. bool isLabel = entity->isA(CC_TYPES::LABEL_2D);
  871. if (isLabel == keepLabels && (!incremental || !entity->isSelected()))
  872. {
  873. QModelIndex selectedIndex = index(entity);
  874. if (selectedIndex.isValid())
  875. {
  876. newSelection.merge(QItemSelection(selectedIndex,selectedIndex),QItemSelectionModel::Select);
  877. }
  878. }
  879. }
  880. //default behavior: clear previous selection if CTRL is not pushed
  881. selectionModel->select(newSelection,incremental ? QItemSelectionModel::Select : QItemSelectionModel::ClearAndSelect);
  882. }
  883. ccHObject* ccDBRoot::find(int uniqueID) const
  884. {
  885. return m_treeRoot->find(uniqueID);
  886. }
  887. void ccDBRoot::showPropertiesView(ccHObject* obj)
  888. {
  889. m_ccPropDelegate->fillModel(obj);
  890. m_propertiesTreeWidget->setEnabled(true);
  891. m_propertiesTreeWidget->setColumnWidth(0, c_propViewLeftColumnWidth);
  892. //m_propertiesTreeWidget->setColumnWidth(1, m_propertiesTreeWidget->width() - c_propViewLeftColumnWidth);
  893. }
  894. void ccDBRoot::hidePropertiesView()
  895. {
  896. m_ccPropDelegate->unbind();
  897. m_propertiesModel->clear();
  898. m_propertiesTreeWidget->setEnabled(false);
  899. }
  900. void ccDBRoot::reflectObjectPropChange(ccHObject* obj)
  901. {
  902. assert(m_ccPropDelegate);
  903. assert(m_propertiesTreeWidget);
  904. if (!m_propertiesTreeWidget->isEnabled() || m_ccPropDelegate->getCurrentObject() != obj)
  905. {
  906. showPropertiesView(obj);
  907. }
  908. }
  909. void ccDBRoot::updatePropertiesView()
  910. {
  911. assert(m_dbTreeWidget);
  912. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  913. QModelIndexList selectedIndexes = qism->selectedIndexes();
  914. if (selectedIndexes.size() == 1)
  915. {
  916. showPropertiesView(static_cast<ccHObject*>(selectedIndexes[0].internalPointer()));
  917. }
  918. else
  919. {
  920. hidePropertiesView();
  921. }
  922. for (const QModelIndex& idx : selectedIndexes)
  923. {
  924. Q_EMIT dataChanged(idx, idx);
  925. }
  926. }
  927. void ccDBRoot::updateCCObject(ccHObject* object)
  928. {
  929. assert(object);
  930. QModelIndex idx = index(object);
  931. if (idx.isValid())
  932. Q_EMIT dataChanged(idx, idx);
  933. }
  934. void ccDBRoot::redrawCCObject(ccHObject* object)
  935. {
  936. assert(object);
  937. object->redrawDisplay();
  938. }
  939. void ccDBRoot::redrawCCObjectAndChildren(ccHObject* object)
  940. {
  941. assert(object);
  942. object->prepareDisplayForRefresh_recursive();
  943. object->refreshDisplay_recursive(/*only2D=*/false);
  944. }
  945. int ccDBRoot::countSelectedEntities(CC_CLASS_ENUM filter)
  946. {
  947. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  948. QModelIndexList selectedIndexes = qism->selectedIndexes();
  949. int selCount = selectedIndexes.size();
  950. if (selCount == 0 || filter == CC_TYPES::OBJECT)
  951. return selCount;
  952. int realCount = 0;
  953. for (int i = 0; i < selCount; ++i)
  954. {
  955. ccHObject* object = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  956. if (object && object->isKindOf(filter))
  957. ++realCount;
  958. }
  959. return realCount;
  960. }
  961. size_t ccDBRoot::getSelectedEntities( ccHObject::Container& selectedEntities,
  962. CC_CLASS_ENUM filter/*=CC_TYPES::OBJECT*/,
  963. dbTreeSelectionInfo* info/*=nullptr*/ )
  964. {
  965. selectedEntities.clear();
  966. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  967. QModelIndexList selectedIndexes = qism->selectedIndexes();
  968. try
  969. {
  970. int selCount = selectedIndexes.size();
  971. for (int i = 0; i < selCount; ++i)
  972. {
  973. ccHObject* object = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  974. if (object && object->isKindOf(filter))
  975. selectedEntities.push_back(object);
  976. }
  977. }
  978. catch (const std::bad_alloc&)
  979. {
  980. //not enough memory!
  981. }
  982. if (info)
  983. {
  984. *info = {};
  985. info->selCount = selectedIndexes.size();
  986. for (size_t i = 0; i < info->selCount; ++i)
  987. {
  988. ccHObject* obj = selectedEntities[i];
  989. info->sfCount += obj->hasScalarFields() ? 1 : 0;
  990. info->colorCount += obj->hasColors() ? 1 : 0;
  991. info->normalsCount += obj->hasNormals() ? 1 : 0;
  992. if (obj->isA(CC_TYPES::HIERARCHY_OBJECT))
  993. {
  994. info->groupCount++;
  995. }
  996. else if(obj->isKindOf(CC_TYPES::POINT_CLOUD))
  997. {
  998. ccGenericPointCloud* genericCloud = ccHObjectCaster::ToGenericPointCloud(obj);
  999. info->cloudCount++;
  1000. info->octreeCount += genericCloud->getOctree() != nullptr ? 1 : 0;
  1001. ccPointCloud* qccCloud = ccHObjectCaster::ToPointCloud(obj);
  1002. info->gridCound += qccCloud->gridCount();
  1003. }
  1004. else if (obj->isKindOf(CC_TYPES::MESH))
  1005. {
  1006. info->meshCount++;
  1007. if (obj->isKindOf(CC_TYPES::PRIMITIVE))
  1008. {
  1009. info->primitiveCount++;
  1010. if (obj->isKindOf(CC_TYPES::PLANE))
  1011. info->planeCount++;
  1012. }
  1013. }
  1014. else if (obj->isKindOf(CC_TYPES::POLY_LINE))
  1015. {
  1016. info->polylineCount++;
  1017. }
  1018. else if(obj->isKindOf(CC_TYPES::SENSOR))
  1019. {
  1020. info->sensorCount++;
  1021. if (obj->isKindOf(CC_TYPES::GBL_SENSOR))
  1022. info->gblSensorCount++;
  1023. if (obj->isKindOf(CC_TYPES::CAMERA_SENSOR))
  1024. info->cameraSensorCount++;
  1025. }
  1026. else if (obj->isKindOf(CC_TYPES::POINT_KDTREE))
  1027. {
  1028. info->kdTreeCount++;
  1029. }
  1030. }
  1031. }
  1032. return selectedEntities.size();
  1033. }
  1034. Qt::DropActions ccDBRoot::supportedDropActions() const
  1035. {
  1036. return Qt::MoveAction;
  1037. }
  1038. Qt::ItemFlags ccDBRoot::flags(const QModelIndex& idx) const
  1039. {
  1040. if (!idx.isValid())
  1041. return Qt::NoItemFlags;
  1042. Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(idx);
  1043. //common flags
  1044. defaultFlags |= (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
  1045. //class type based filtering
  1046. const ccHObject *item = static_cast<const ccHObject*>(idx.internalPointer());
  1047. assert(item);
  1048. if (item && !item->isLocked()) //locked items cannot be drag-dropped
  1049. {
  1050. if (item->isA(CC_TYPES::HIERARCHY_OBJECT) ||
  1051. (item->isKindOf(CC_TYPES::POINT_CLOUD) && CanDetachCloud(item)) || //vertices can't be displaced
  1052. (item->isKindOf(CC_TYPES::MESH) && !item->isA(CC_TYPES::SUB_MESH)) || //a sub-mesh can't leave its parent mesh
  1053. item->isKindOf(CC_TYPES::IMAGE) ||
  1054. item->isKindOf(CC_TYPES::LABEL_2D) ||
  1055. item->isKindOf(CC_TYPES::CAMERA_SENSOR) ||
  1056. item->isKindOf(CC_TYPES::PRIMITIVE) ||
  1057. item->isKindOf(CC_TYPES::FACET) ||
  1058. item->isKindOf(CC_TYPES::CUSTOM_H_OBJECT))
  1059. {
  1060. defaultFlags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
  1061. }
  1062. else if (item->isKindOf(CC_TYPES::POLY_LINE))
  1063. {
  1064. const ccPolyline* poly = static_cast<const ccPolyline*>(item);
  1065. //we can only displace a polyline if it is not dependent on it's father!
  1066. const ccHObject* polyVertices = dynamic_cast<const ccHObject*>(poly->getAssociatedCloud());
  1067. if (polyVertices != poly->getParent())
  1068. {
  1069. defaultFlags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
  1070. }
  1071. }
  1072. else if (item->isKindOf(CC_TYPES::VIEWPORT_2D_OBJECT))
  1073. {
  1074. defaultFlags |= Qt::ItemIsDragEnabled;
  1075. }
  1076. }
  1077. return defaultFlags;
  1078. }
  1079. QMap<int,QVariant> ccDBRoot::itemData(const QModelIndex& idx) const
  1080. {
  1081. QMap<int,QVariant> map = QAbstractItemModel::itemData(idx);
  1082. if (idx.isValid())
  1083. {
  1084. const ccHObject* object = static_cast<const ccHObject*>(idx.internalPointer());
  1085. if (object)
  1086. map.insert(Qt::UserRole,QVariant(object->getUniqueID()));
  1087. }
  1088. return map;
  1089. }
  1090. bool ccDBRoot::dropMimeData(const QMimeData* data, Qt::DropAction action, int destRow, int destColumn, const QModelIndex& destParent)
  1091. {
  1092. if (action != Qt::MoveAction)
  1093. {
  1094. return false;
  1095. }
  1096. //default mime type for QAbstractItemModel items)
  1097. if (!data->hasFormat("application/x-qabstractitemmodeldatalist"))
  1098. {
  1099. return false;
  1100. }
  1101. //new parent (can't be a leaf object!)
  1102. ccHObject* newParent = destParent.isValid() ? static_cast<ccHObject*>(destParent.internalPointer()) : m_treeRoot;
  1103. if (newParent && newParent->isLeaf())
  1104. {
  1105. return false;
  1106. }
  1107. //decode data
  1108. QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist");
  1109. QDataStream stream(&encoded, QIODevice::ReadOnly);
  1110. while (!stream.atEnd())
  1111. {
  1112. //decode current item index data (row, col, data 'roles' map)
  1113. int srcRow = 0;
  1114. int srcCol = 0;
  1115. QMap<int, QVariant> roleDataMap;
  1116. stream >> srcRow >> srcCol >> roleDataMap;
  1117. if (!roleDataMap.contains(Qt::UserRole))
  1118. continue;
  1119. //selected item
  1120. int uniqueID = roleDataMap.value(Qt::UserRole).toInt();
  1121. ccHObject *item = m_treeRoot->find(uniqueID);
  1122. if (!item)
  1123. continue;
  1124. //ccLog::Print(QString("[Drag & Drop] Source: %1").arg(item->getName()));
  1125. //old parent
  1126. ccHObject* oldParent = item->getParent();
  1127. //ccLog::Print(QString("[Drag & Drop] Parent: %1").arg(oldParent ? oldParent->getName() : "none")));
  1128. //let's check if we can actually move the entity
  1129. if (oldParent)
  1130. {
  1131. if (item->isKindOf(CC_TYPES::POINT_CLOUD))
  1132. {
  1133. //point cloud == mesh vertices?
  1134. if (oldParent->isKindOf(CC_TYPES::MESH) && ccHObjectCaster::ToGenericMesh(oldParent)->getAssociatedCloud() == item)
  1135. {
  1136. if (oldParent != newParent)
  1137. {
  1138. ccLog::Error("Vertices can't leave their parent mesh");
  1139. return false;
  1140. }
  1141. }
  1142. }
  1143. else if (item->isKindOf(CC_TYPES::MESH))
  1144. {
  1145. //a sub-mesh can't leave its parent mesh
  1146. if (item->isA(CC_TYPES::SUB_MESH))
  1147. {
  1148. assert(false);
  1149. ccLog::Error("Sub-meshes can't leave their mesh group");
  1150. return false;
  1151. }
  1152. //a mesh can't leave its associated cloud
  1153. else if (oldParent->isKindOf(CC_TYPES::POINT_CLOUD) && ccHObjectCaster::ToGenericMesh(item)->getAssociatedCloud() == oldParent)
  1154. {
  1155. if (oldParent != newParent)
  1156. {
  1157. ccLog::Error("Meshes can't leave their associated cloud (vertices set)");
  1158. return false;
  1159. }
  1160. }
  1161. }
  1162. else if (/*item->isKindOf(CC_TYPES::PRIMITIVE) || */item->isKindOf(CC_TYPES::IMAGE))
  1163. {
  1164. if (oldParent != newParent)
  1165. {
  1166. ccLog::Error("This kind of entity can't leave their parent");
  1167. return false;
  1168. }
  1169. }
  1170. else if (oldParent != newParent)
  1171. {
  1172. //a label or a group of labels can't be moved to another cloud!
  1173. //ccHObject::Container labels;
  1174. //if (item->isA(CC_TYPES::LABEL_2D))
  1175. // labels.push_back(item);
  1176. //else
  1177. // item->filterChildren(labels, true, CC_TYPES::LABEL_2D);
  1178. ////for all labels in the sub-tree
  1179. //for (ccHObject::Container::const_iterator it = labels.begin(); it != labels.end(); ++it)
  1180. //{
  1181. // if ((*it)->isA(CC_TYPES::LABEL_2D)) //Warning: cc2DViewportLabel is also a kind of 'CC_TYPES::LABEL_2D'!
  1182. // {
  1183. // cc2DLabel* label = static_cast<cc2DLabel*>(*it);
  1184. // bool canMove = false;
  1185. // for (unsigned j = 0; j < label->size(); ++j)
  1186. // {
  1187. // assert(label->getPoint(j).cloud);
  1188. // //3 options to allow moving a label:
  1189. // if (item->isAncestorOf(label->getPoint(j).cloud) //label's cloud is inside sub-tree
  1190. // || newParent == label->getPoint(j).cloud //destination is label's cloud
  1191. // || label->getPoint(j).cloud->isAncestorOf(newParent)) //destination is below label's cloud
  1192. // {
  1193. // canMove = true;
  1194. // break;
  1195. // }
  1196. // }
  1197. // if (!canMove)
  1198. // {
  1199. // ccLog::Error("Labels (or group of) can't leave their parent");
  1200. // return false;
  1201. // }
  1202. // }
  1203. //}
  1204. }
  1205. }
  1206. //special case: moving an item inside the same 'parent'
  1207. if (oldParent && newParent == oldParent)
  1208. {
  1209. int oldRow = oldParent->getChildIndex(item);
  1210. if (destRow < 0)
  1211. {
  1212. assert(oldParent->getChildrenNumber() != 0);
  1213. destRow = static_cast<int>(oldParent->getChildrenNumber())-1;
  1214. }
  1215. else if (oldRow < destRow)
  1216. {
  1217. assert(destRow > 0);
  1218. --destRow;
  1219. }
  1220. else if (oldRow == destRow)
  1221. {
  1222. return false; //nothing to do
  1223. }
  1224. }
  1225. //remove link with old parent (only CHILD/PARENT related flags!)
  1226. int itemDependencyFlags = item->getDependencyFlagsWith(oldParent); //works even with nullptr
  1227. int fatherDependencyFlags = oldParent ? oldParent->getDependencyFlagsWith(item) : 0;
  1228. if (oldParent)
  1229. {
  1230. oldParent->removeDependencyFlag(item,ccHObject::DP_PARENT_OF_OTHER);
  1231. item->removeDependencyFlag(oldParent,ccHObject::DP_PARENT_OF_OTHER);
  1232. }
  1233. //remove item from current position
  1234. removeElement(item);
  1235. //sets new parent
  1236. assert(newParent);
  1237. newParent->addChild(item,fatherDependencyFlags & ccHObject::DP_PARENT_OF_OTHER,destRow);
  1238. item->addDependency(newParent,itemDependencyFlags & ccHObject::DP_PARENT_OF_OTHER);
  1239. //restore other flags on old parent (as all flags have been removed when calling removeElement!)
  1240. if (oldParent)
  1241. {
  1242. oldParent->addDependency(item,fatherDependencyFlags & (~ccHObject::DP_PARENT_OF_OTHER));
  1243. item->addDependency(oldParent,itemDependencyFlags & (~ccHObject::DP_PARENT_OF_OTHER));
  1244. }
  1245. if (newParent->getDisplay() == nullptr)
  1246. newParent->setDisplay(item->getDisplay());
  1247. else if (item->getDisplay() == nullptr)
  1248. item->setDisplay(newParent->getDisplay());
  1249. //add item back
  1250. addElement(item,false);
  1251. item->prepareDisplayForRefresh();
  1252. }
  1253. MainWindow::RefreshAllGLWindow(false);
  1254. return true;
  1255. }
  1256. void ccDBRoot::expandBranch()
  1257. {
  1258. expandOrCollapseHoveredBranch(true);
  1259. }
  1260. void ccDBRoot::collapseBranch()
  1261. {
  1262. expandOrCollapseHoveredBranch(false);
  1263. }
  1264. void ccDBRoot::expandOrCollapseHoveredBranch(bool expand)
  1265. {
  1266. //not initialized?
  1267. if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0)
  1268. return;
  1269. QModelIndex clickIndex = m_dbTreeWidget->indexAt(m_contextMenuPos);
  1270. if (!clickIndex.isValid())
  1271. return;
  1272. ccHObject* item = static_cast<ccHObject*>(clickIndex.internalPointer());
  1273. assert(item);
  1274. if (!item || item->getChildrenNumber() == 0)
  1275. return;
  1276. //we recursively expand sub-branches
  1277. ccHObject::Container toExpand;
  1278. try
  1279. {
  1280. toExpand.push_back(item);
  1281. while (!toExpand.empty())
  1282. {
  1283. item = toExpand.back();
  1284. toExpand.pop_back();
  1285. QModelIndex itemIndex = index(item);
  1286. if (itemIndex.isValid())
  1287. {
  1288. if (expand)
  1289. m_dbTreeWidget->expand(itemIndex);
  1290. else
  1291. m_dbTreeWidget->collapse(itemIndex);
  1292. }
  1293. assert(item->getChildrenNumber() != 0);
  1294. for (unsigned i=0; i<item->getChildrenNumber(); ++i)
  1295. {
  1296. if (item->getChild(i)->getChildrenNumber() != 0)
  1297. toExpand.push_back(item->getChild(i));
  1298. }
  1299. }
  1300. }
  1301. catch (const std::bad_alloc&)
  1302. {
  1303. //not enough memory!
  1304. }
  1305. }
  1306. void ccDBRoot::alignCameraWithEntity(bool reverse)
  1307. {
  1308. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1309. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1310. int selCount = selectedIndexes.size();
  1311. if (selCount == 0)
  1312. return;
  1313. ccHObject* obj = static_cast<ccHObject*>(selectedIndexes[0].internalPointer());
  1314. if (!obj)
  1315. return;
  1316. ccGenericGLDisplay* display = obj->getDisplay();
  1317. if (!display)
  1318. {
  1319. ccLog::Warning("[alignCameraWithEntity] Selected entity has no associated display");
  1320. return;
  1321. }
  1322. ccGLWindowInterface* win = static_cast<ccGLWindowInterface*>(display);
  1323. assert(win);
  1324. //plane normal
  1325. CCVector3d planeNormal;
  1326. CCVector3d planeVertDir;
  1327. CCVector3 center;
  1328. if (obj->isA(CC_TYPES::LABEL_2D)) //2D label with 3 points?
  1329. {
  1330. cc2DLabel* label = static_cast<cc2DLabel*>(obj);
  1331. //work only with labels with 3 points or labels picked on a triangle!
  1332. CCVector3 A;
  1333. CCVector3 B;
  1334. CCVector3 C;
  1335. if (label->size() == 3)
  1336. {
  1337. A = label->getPickedPoint(0).getPointPosition();
  1338. B = label->getPickedPoint(1).getPointPosition();
  1339. C = label->getPickedPoint(2).getPointPosition();
  1340. }
  1341. else if (label->size() == 1)
  1342. {
  1343. const cc2DLabel::PickedPoint& pp = label->getPickedPoint(0);
  1344. if (pp._mesh)
  1345. {
  1346. pp._mesh->getTriangleVertices(pp.index, A, B, C);
  1347. }
  1348. else
  1349. {
  1350. ccLog::Error("Works only with 3-points labels or labels picked on a triangle");
  1351. return;
  1352. }
  1353. }
  1354. else
  1355. {
  1356. ccLog::Error("Works only with 3-points labels or labels picked on a triangle");
  1357. return;
  1358. }
  1359. CCVector3 N = (B - A).cross(C - A);
  1360. planeNormal = N;
  1361. planeVertDir = win->getViewportParameters().getUpDir();
  1362. center = (A + B + C) / 3;
  1363. }
  1364. else if (obj->isA(CC_TYPES::PLANE)) //plane
  1365. {
  1366. ccPlane* plane = static_cast<ccPlane*>(obj);
  1367. //3rd column = plane normal!
  1368. planeNormal = plane->getNormal();
  1369. planeVertDir = plane->getTransformation().getColumnAsVec3D(1);
  1370. center = plane->getOwnBB().getCenter();
  1371. }
  1372. else if (obj->isA(CC_TYPES::FACET)) //facet
  1373. {
  1374. ccFacet* facet = static_cast<ccFacet*>(obj);
  1375. planeNormal = facet->getNormal();
  1376. CCVector3d planeHorizDir(0, 1, 0);
  1377. CCCoreLib::CCMiscTools::ComputeBaseVectors(planeNormal,planeHorizDir,planeVertDir);
  1378. center = facet->getBB_recursive(false,false).getCenter();
  1379. }
  1380. else
  1381. {
  1382. assert(false);
  1383. return;
  1384. }
  1385. //we can now make the camera look in the direction of the normal
  1386. if (!reverse)
  1387. planeNormal *= -1;
  1388. win->setCustomView(planeNormal,planeVertDir);
  1389. //output the transformation matrix that would make this normal points towards +Z
  1390. {
  1391. ccGLMatrixd transMat;
  1392. transMat.setTranslation(-center);
  1393. ccGLMatrixd viewMat = win->getViewportParameters().viewMat;
  1394. viewMat = viewMat * transMat;
  1395. viewMat.setTranslation(viewMat.getTranslationAsVec3D() + center);
  1396. ccLog::Print("[Align camera] Corresponding view matrix:");
  1397. ccLog::Print(viewMat.toString(12,' ')); //full precision
  1398. ccLog::Print("[Orientation] You can copy this matrix values (CTRL+C) and paste them in the 'Apply transformation tool' dialog");
  1399. }
  1400. }
  1401. void ccDBRoot::gatherRecursiveInformation()
  1402. {
  1403. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1404. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1405. int selCount = selectedIndexes.size();
  1406. if (selCount == 0)
  1407. return;
  1408. struct GlobalInfo
  1409. {
  1410. //properties
  1411. unsigned pointCount = 0;
  1412. unsigned triangleCount = 0;
  1413. unsigned colorCount = 0;
  1414. unsigned normalCount = 0;
  1415. unsigned materialCount = 0;
  1416. unsigned scalarFieldCount = 0;
  1417. //entities
  1418. unsigned cloudCount = 0;
  1419. unsigned meshCount = 0;
  1420. unsigned primitiveCount = 0;
  1421. unsigned octreeCount = 0;
  1422. unsigned imageCount = 0;
  1423. unsigned sensorCount = 0;
  1424. unsigned labelCount = 0;
  1425. } info;
  1426. //init the list of entities to process
  1427. ccHObject::Container toProcess;
  1428. try
  1429. {
  1430. toProcess.resize(selCount);
  1431. }
  1432. catch (const std::bad_alloc&)
  1433. {
  1434. ccLog::Error("Not engough memory");
  1435. return;
  1436. }
  1437. for (int i = 0; i < selCount; ++i)
  1438. {
  1439. toProcess[i] = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  1440. }
  1441. ccHObject::Container alreadyProcessed;
  1442. while (!toProcess.empty())
  1443. {
  1444. ccHObject* ent = toProcess.back();
  1445. toProcess.pop_back();
  1446. //we don't process entities twice!
  1447. if (std::find(alreadyProcessed.begin(), alreadyProcessed.end(), ent) != alreadyProcessed.end())
  1448. {
  1449. continue;
  1450. }
  1451. //gather information from current entity
  1452. if (ent->isA(CC_TYPES::POINT_CLOUD))
  1453. {
  1454. ccPointCloud* cloud = static_cast<ccPointCloud*>(ent);
  1455. info.cloudCount++;
  1456. unsigned cloudSize = cloud->size();
  1457. info.pointCount += cloudSize;
  1458. info.colorCount += (cloud->hasColors() ? cloudSize : 0);
  1459. info.normalCount += (cloud->hasNormals() ? cloudSize : 0);
  1460. info.scalarFieldCount += cloud->getNumberOfScalarFields();
  1461. }
  1462. else if (ent->isKindOf(CC_TYPES::MESH))
  1463. {
  1464. ccMesh* mesh = static_cast<ccMesh*>(ent);
  1465. info.meshCount++;
  1466. unsigned meshSize = mesh->size();
  1467. info.triangleCount += meshSize;
  1468. info.normalCount += (mesh->hasTriNormals() ? meshSize : 0);
  1469. info.materialCount += (mesh->getMaterialSet() ? static_cast<unsigned>(mesh->getMaterialSet()->size()) : 0);
  1470. if (ent->isKindOf(CC_TYPES::PRIMITIVE))
  1471. {
  1472. info.primitiveCount++;
  1473. }
  1474. }
  1475. else if (ent->isKindOf(CC_TYPES::LABEL_2D))
  1476. {
  1477. info.labelCount++;
  1478. }
  1479. else if (ent->isKindOf(CC_TYPES::SENSOR))
  1480. {
  1481. info.sensorCount++;
  1482. }
  1483. else if (ent->isKindOf(CC_TYPES::POINT_OCTREE))
  1484. {
  1485. info.octreeCount++;
  1486. }
  1487. else if (ent->isKindOf(CC_TYPES::IMAGE))
  1488. {
  1489. info.imageCount++;
  1490. }
  1491. //we can add its children to the 'toProcess' list and itself to the 'processed' list
  1492. try
  1493. {
  1494. for (unsigned i = 0; i < ent->getChildrenNumber(); ++i)
  1495. {
  1496. toProcess.push_back(ent->getChild(i));
  1497. }
  1498. alreadyProcessed.push_back(ent);
  1499. }
  1500. catch (const std::bad_alloc&)
  1501. {
  1502. ccLog::Error("Not engough memory");
  1503. return;
  1504. }
  1505. }
  1506. //output information
  1507. {
  1508. QStringList infoStr;
  1509. const QString separator("--------------------------");
  1510. infoStr << QString("Point(s):\t\t%L1").arg(info.pointCount);
  1511. infoStr << QString("Triangle(s):\t\t%L1").arg(info.triangleCount);
  1512. infoStr << separator;
  1513. if (info.colorCount)
  1514. infoStr << QString("Color(s):\t\t%L1").arg(info.colorCount);
  1515. if (info.normalCount)
  1516. infoStr << QString("Normal(s):\t\t%L1").arg(info.normalCount);
  1517. if (info.scalarFieldCount)
  1518. infoStr << QString("Scalar field(s):\t\t%L1").arg(info.scalarFieldCount);
  1519. if (info.materialCount)
  1520. infoStr << QString("Material(s):\t\t%L1").arg(info.materialCount);
  1521. infoStr << separator;
  1522. infoStr << QString("Cloud(s):\t\t%L1").arg(info.cloudCount);
  1523. infoStr << QString("Mesh(es):\t\t%L1").arg(info.meshCount);
  1524. infoStr << QString(" - Pritmitive(s):\t%L1").arg(info.primitiveCount);
  1525. if (info.octreeCount)
  1526. infoStr << QString("Octree(s):\t\t%L1").arg(info.octreeCount);
  1527. if (info.imageCount)
  1528. infoStr << QString("Image(s):\t\t%L1").arg(info.imageCount);
  1529. if (info.labelCount)
  1530. infoStr << QString("Label(s):\t\t%L1").arg(info.labelCount);
  1531. if (info.sensorCount)
  1532. infoStr << QString("Sensor(s):\t\t%L1").arg(info.sensorCount);
  1533. //display info box
  1534. QMessageBox::information(MainWindow::TheInstance(),
  1535. "Information",
  1536. infoStr.join("\n"));
  1537. }
  1538. }
  1539. void ccDBRoot::sortChildrenAZ()
  1540. {
  1541. sortSelectedEntitiesChildren(SORT_A2Z);
  1542. }
  1543. void ccDBRoot::sortChildrenZA()
  1544. {
  1545. sortSelectedEntitiesChildren(SORT_Z2A);
  1546. }
  1547. void ccDBRoot::sortChildrenType()
  1548. {
  1549. sortSelectedEntitiesChildren(SORT_BY_TYPE);
  1550. }
  1551. void ccDBRoot::sortSelectedEntitiesChildren(SortRules sortRule)
  1552. {
  1553. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1554. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1555. int selCount = selectedIndexes.size();
  1556. if (selCount == 0)
  1557. return;
  1558. for (int i = 0; i < selCount; ++i)
  1559. {
  1560. ccHObject* item = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  1561. unsigned childCount = (item ? item->getChildrenNumber() : 0);
  1562. if (childCount > 1)
  1563. {
  1564. //remove all children from DB tree
  1565. beginRemoveRows(selectedIndexes[i], 0, childCount - 1);
  1566. //row removal operation (end)
  1567. endRemoveRows();
  1568. //sort
  1569. for (unsigned k = 0; k < childCount - 1; ++k)
  1570. {
  1571. unsigned firstChildIndex = k;
  1572. ccHObject* firstChild = item->getChild(k);
  1573. QString firstChildName = firstChild->getName().toUpper();
  1574. for (unsigned j = k + 1; j < childCount; ++j)
  1575. {
  1576. bool swap = false;
  1577. QString currentName = item->getChild(j)->getName().toUpper();
  1578. switch (sortRule)
  1579. {
  1580. case SORT_A2Z:
  1581. swap = (firstChildName.compare(currentName) > 0);
  1582. break;
  1583. case SORT_Z2A:
  1584. swap = (firstChildName.compare(currentName) < 0);
  1585. break;
  1586. case SORT_BY_TYPE:
  1587. if (firstChild->getClassID() == item->getChild(j)->getClassID())
  1588. swap = (firstChildName.compare(currentName) > 0); //A2Z in second choice
  1589. else
  1590. swap = (firstChild->getClassID() > item->getChild(j)->getClassID());
  1591. break;
  1592. }
  1593. if (swap)
  1594. {
  1595. firstChildIndex = j;
  1596. firstChildName = currentName;
  1597. }
  1598. }
  1599. if (k != firstChildIndex)
  1600. item->swapChildren(k, firstChildIndex);
  1601. }
  1602. //add children back
  1603. beginInsertRows(selectedIndexes[i], 0, childCount - 1);
  1604. //row insertion operation (end)
  1605. endInsertRows();
  1606. }
  1607. }
  1608. }
  1609. void ccDBRoot::selectByTypeAndName()
  1610. {
  1611. ccSelectChildrenDlg scDlg(MainWindow::TheInstance());
  1612. scDlg.addType("Point cloud", CC_TYPES::POINT_CLOUD);
  1613. scDlg.addType("Poly-line", CC_TYPES::POLY_LINE);
  1614. scDlg.addType("Mesh", CC_TYPES::MESH);
  1615. scDlg.addType(" Sub-mesh", CC_TYPES::SUB_MESH);
  1616. scDlg.addType(" Primitive", CC_TYPES::PRIMITIVE);
  1617. scDlg.addType(" Plane", CC_TYPES::PLANE);
  1618. scDlg.addType(" Sphere", CC_TYPES::SPHERE);
  1619. scDlg.addType(" Torus", CC_TYPES::TORUS);
  1620. scDlg.addType(" Cylinder", CC_TYPES::CYLINDER);
  1621. scDlg.addType(" Cone", CC_TYPES::CONE);
  1622. scDlg.addType(" Box", CC_TYPES::BOX);
  1623. scDlg.addType(" Dish", CC_TYPES::DISH);
  1624. scDlg.addType(" Extrusion", CC_TYPES::EXTRU);
  1625. scDlg.addType("Sensor", CC_TYPES::SENSOR);
  1626. scDlg.addType(" GBL/TLS sensor", CC_TYPES::GBL_SENSOR);
  1627. scDlg.addType(" Camera sensor", CC_TYPES::CAMERA_SENSOR);
  1628. scDlg.addType("Image", CC_TYPES::IMAGE);
  1629. scDlg.addType("Facet", CC_TYPES::FACET);
  1630. scDlg.addType("Label", CC_TYPES::LABEL_2D);
  1631. scDlg.addType("Area label", CC_TYPES::VIEWPORT_2D_LABEL);
  1632. scDlg.addType("Octree", CC_TYPES::POINT_OCTREE);
  1633. scDlg.addType("Kd-tree", CC_TYPES::POINT_KDTREE);
  1634. scDlg.addType("Viewport", CC_TYPES::VIEWPORT_2D_OBJECT);
  1635. scDlg.addType("Custom Types", CC_TYPES::CUSTOM_H_OBJECT);
  1636. if (!scDlg.exec())
  1637. return;
  1638. // for type checking
  1639. CC_CLASS_ENUM type = CC_TYPES::OBJECT; // all objects are matched by def
  1640. bool exclusive = false;
  1641. if (scDlg.getTypeIsUsed()) // we are using type checking
  1642. {
  1643. type = scDlg.getSelectedType();
  1644. //some types are exclusive, some are generic, and some can be both
  1645. //(e.g. Meshes)
  1646. //
  1647. //For generic-only types the match type gets overridden and forced to
  1648. //false because exclusive match makes no sense!
  1649. switch (type)
  1650. {
  1651. case CC_TYPES::HIERARCHY_OBJECT: //returned if no type is selected (i.e. all objects are selected!)
  1652. case CC_TYPES::PRIMITIVE:
  1653. case CC_TYPES::SENSOR:
  1654. case CC_TYPES::IMAGE:
  1655. exclusive = false;
  1656. break;
  1657. default:
  1658. exclusive = scDlg.getStrictMatchState();
  1659. break;
  1660. }
  1661. }
  1662. // for name matching - def values
  1663. bool regex = false;
  1664. QString name; // an empty string by default
  1665. if (scDlg.getNameMatchIsUsed())
  1666. {
  1667. regex = scDlg.getNameIsRegex();
  1668. name = scDlg.getSelectedName();
  1669. }
  1670. selectChildrenByTypeAndName(type, exclusive, name, regex);
  1671. }
  1672. /* name is optional, if passed it is used to restrict the selection by type */
  1673. void ccDBRoot::selectChildrenByTypeAndName(CC_CLASS_ENUM type,
  1674. bool typeIsExclusive/*=true*/,
  1675. QString name/*=QString()*/,
  1676. bool nameIsRegex/*= false*/)
  1677. {
  1678. //not initialized?
  1679. if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0)
  1680. return;
  1681. QModelIndex clickIndex = m_dbTreeWidget->indexAt(m_contextMenuPos);
  1682. if (!clickIndex.isValid())
  1683. return;
  1684. ccHObject* item = static_cast<ccHObject*>(clickIndex.internalPointer());
  1685. assert(item);
  1686. if (!item || item->getChildrenNumber() == 0)
  1687. return;
  1688. ccHObject::Container filteredByType;
  1689. item->filterChildren(filteredByType, true, type, typeIsExclusive);
  1690. // The case of an empty filteredByType is handled implicitly, to make
  1691. // the ctrlPushed behavior below more consistent (i.e. when no object
  1692. // is found and Control was NOT pressed the selection will still be
  1693. // cleared).
  1694. ccHObject::Container toSelect;
  1695. try
  1696. {
  1697. if (name.isEmpty())
  1698. {
  1699. toSelect = filteredByType;
  1700. }
  1701. else
  1702. {
  1703. for (size_t i=0; i<filteredByType.size(); ++i)
  1704. {
  1705. ccHObject* child = filteredByType[i];
  1706. if (nameIsRegex) // regex matching
  1707. {
  1708. QRegularExpression re(name);
  1709. QRegularExpressionMatch match = re.match(child->getName());
  1710. bool hasMatch = match.hasMatch(); // true
  1711. if (hasMatch)
  1712. toSelect.push_back(child);
  1713. }
  1714. else if (child->getName().compare(name) == 0) // simple comparison
  1715. toSelect.push_back(child);
  1716. }
  1717. }
  1718. }
  1719. catch (const std::bad_alloc&)
  1720. {
  1721. ccLog::Warning("[selectChildrenByTypeAndName] Not enough memory");
  1722. return;
  1723. }
  1724. bool ctrlPushed = (QApplication::keyboardModifiers () & Qt::ControlModifier);
  1725. selectEntities(toSelect, ctrlPushed);
  1726. }
  1727. void ccDBRoot::toggleSelectedEntitiesProperty(TOGGLE_PROPERTY prop)
  1728. {
  1729. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1730. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1731. int selCount = selectedIndexes.size();
  1732. if (selCount == 0)
  1733. return;
  1734. //hide properties view
  1735. hidePropertiesView();
  1736. for (int i = 0; i < selCount; ++i)
  1737. {
  1738. ccHObject* item = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  1739. if (!item)
  1740. {
  1741. assert(false);
  1742. continue;
  1743. }
  1744. switch (prop)
  1745. {
  1746. case TG_ENABLE: //enable state
  1747. item->setEnabled(!item->isEnabled());
  1748. break;
  1749. case TG_VISIBLE: //visibility
  1750. item->toggleVisibility();
  1751. break;
  1752. case TG_COLOR: //color
  1753. item->toggleColors();
  1754. break;
  1755. case TG_NORMAL: //normal
  1756. item->toggleNormals();
  1757. break;
  1758. case TG_SF: //SF
  1759. item->toggleSF();
  1760. break;
  1761. case TG_MATERIAL: //Materials/textures
  1762. item->toggleMaterials();
  1763. break;
  1764. case TG_3D_NAME: //3D name
  1765. item->toggleShowName();
  1766. break;
  1767. }
  1768. item->prepareDisplayForRefresh();
  1769. }
  1770. //we restablish properties view
  1771. updatePropertiesView();
  1772. MainWindow::RefreshAllGLWindow(false);
  1773. }
  1774. void ccDBRoot::addEmptyGroup()
  1775. {
  1776. //not initialized?
  1777. if (m_contextMenuPos.x() < 0 || m_contextMenuPos.y() < 0)
  1778. return;
  1779. QModelIndex idx = m_dbTreeWidget->indexAt(m_contextMenuPos);
  1780. ccHObject* newGroup = new ccHObject("Group");
  1781. if (idx.isValid())
  1782. {
  1783. ccHObject* parent = static_cast<ccHObject*>(idx.internalPointer());
  1784. if (parent)
  1785. parent->addChild(newGroup);
  1786. }
  1787. addElement(newGroup);
  1788. }
  1789. void ccDBRoot::enableBubbleViewMode()
  1790. {
  1791. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1792. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1793. int selCount = selectedIndexes.size();
  1794. if (selCount == 0)
  1795. return;
  1796. for (int i = 0; i < selCount; ++i)
  1797. {
  1798. ccHObject* item = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  1799. if (item &&item->isA(CC_TYPES::GBL_SENSOR))
  1800. {
  1801. static_cast<ccGBLSensor*>(item)->applyViewport();
  1802. }
  1803. }
  1804. MainWindow::RefreshAllGLWindow(false);
  1805. }
  1806. void ccDBRoot::editLabelScalarValue()
  1807. {
  1808. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1809. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1810. int selCount = selectedIndexes.size();
  1811. if (selCount == 0)
  1812. {
  1813. return;
  1814. }
  1815. ccHObject* obj = static_cast<ccHObject*>(selectedIndexes[0].internalPointer());
  1816. cc2DLabel* label = ccHObjectCaster::To2DLabel(obj);
  1817. if (!label || label->size() != 1)
  1818. {
  1819. return;
  1820. }
  1821. const cc2DLabel::PickedPoint& P = label->getPickedPoint(0);
  1822. if (!P._cloud)
  1823. {
  1824. assert(false);
  1825. return;
  1826. }
  1827. if (!P._cloud->isA(CC_TYPES::POINT_CLOUD) || !P._cloud->hasScalarFields())
  1828. {
  1829. return;
  1830. }
  1831. ccPointCloud* pc = static_cast<ccPointCloud*>(P._cloud);
  1832. ccScalarField* sf = pc->getCurrentDisplayedScalarField();
  1833. if (!sf)
  1834. {
  1835. ccLog::Warning("[editLabelScalarValue] No active scalar field");
  1836. return;
  1837. }
  1838. ScalarType s = sf->getValue(P.index);
  1839. bool ok = false;
  1840. double newValue = QInputDialog::getDouble(MainWindow::TheInstance(), "Edit scalar value", QString("%1 (%2) =").arg(QString::fromStdString(sf->getName())).arg(P.index), s, -2147483647, 2147483647, 6, &ok);
  1841. if (!ok)
  1842. {
  1843. //process cancelled by the user
  1844. return;
  1845. }
  1846. ScalarType newS = static_cast<ScalarType>(newValue);
  1847. if (s != newS)
  1848. {
  1849. //update the value and update the display
  1850. sf->setValue(P.index, newS);
  1851. sf->computeMinAndMax();
  1852. pc->redrawDisplay();
  1853. }
  1854. }
  1855. void ccDBRoot::showContextMenu(const QPoint& menuPos)
  1856. {
  1857. m_contextMenuPos = menuPos;
  1858. //build custom context menu
  1859. QMenu menu;
  1860. QModelIndex idx = m_dbTreeWidget->indexAt(menuPos);
  1861. if (idx.isValid())
  1862. {
  1863. QItemSelectionModel* qism = m_dbTreeWidget->selectionModel();
  1864. //selected items?
  1865. QModelIndexList selectedIndexes = qism->selectedIndexes();
  1866. int selCount = selectedIndexes.size();
  1867. if (selCount)
  1868. {
  1869. bool toggleVisibility = false;
  1870. bool toggleOtherProperties = false;
  1871. bool toggleMaterials = false;
  1872. bool hasMoreThanOneChild = false;
  1873. bool leafObject = false;
  1874. bool hasExacltyOneGBLSenor = false;
  1875. bool hasExactlyOnePlane = false;
  1876. bool canEditLabelScalarValue = false;
  1877. unsigned planarEntityCount = 0;
  1878. for (int i = 0; i < selCount; ++i)
  1879. {
  1880. ccHObject* item = static_cast<ccHObject*>(selectedIndexes[i].internalPointer());
  1881. if (!item)
  1882. {
  1883. assert(false);
  1884. continue;
  1885. }
  1886. if (item->getChildrenNumber() > 1)
  1887. {
  1888. hasMoreThanOneChild = true;
  1889. }
  1890. leafObject |= item->isLeaf();
  1891. if (!item->isA(CC_TYPES::HIERARCHY_OBJECT))
  1892. {
  1893. toggleVisibility = true;
  1894. if (item->isKindOf(CC_TYPES::POINT_CLOUD))
  1895. {
  1896. toggleOtherProperties = true;
  1897. }
  1898. else if (item->isKindOf(CC_TYPES::MESH))
  1899. {
  1900. toggleMaterials = true;
  1901. toggleOtherProperties = true;
  1902. }
  1903. if (selCount == 1)
  1904. {
  1905. if (item->isA(CC_TYPES::LABEL_2D))
  1906. {
  1907. cc2DLabel* label = ccHObjectCaster::To2DLabel(item);
  1908. if (label->size() == 3)
  1909. ++planarEntityCount;
  1910. else if (label->size() == 1 && label->getPickedPoint(0)._mesh)
  1911. ++planarEntityCount;
  1912. if (label->size() == 1)
  1913. {
  1914. const cc2DLabel::PickedPoint& pp = label->getPickedPoint(0);
  1915. canEditLabelScalarValue = ( pp._cloud
  1916. && pp._cloud->hasScalarFields()
  1917. && pp._cloud->isA(CC_TYPES::POINT_CLOUD)
  1918. && static_cast<ccPointCloud*>(pp._cloud)->getCurrentDisplayedScalarField() != nullptr
  1919. );
  1920. }
  1921. }
  1922. else if (item->isA(CC_TYPES::PLANE) || item->isA(CC_TYPES::FACET))
  1923. {
  1924. ++planarEntityCount;
  1925. hasExactlyOnePlane = item->isKindOf(CC_TYPES::PLANE);
  1926. }
  1927. else if (item->isA(CC_TYPES::GBL_SENSOR))
  1928. {
  1929. hasExacltyOneGBLSenor = true;
  1930. }
  1931. }
  1932. }
  1933. }
  1934. if (planarEntityCount == 1)
  1935. {
  1936. menu.addAction(m_alignCameraWithEntity);
  1937. menu.addAction(m_alignCameraWithEntityReverse);
  1938. menu.addSeparator();
  1939. }
  1940. if (hasExactlyOnePlane)
  1941. {
  1942. MainWindow::TheInstance()->addEditPlaneAction( menu );
  1943. }
  1944. if (hasExacltyOneGBLSenor)
  1945. {
  1946. menu.addAction(m_enableBubbleViewMode);
  1947. }
  1948. menu.addAction(m_gatherInformation);
  1949. menu.addSeparator();
  1950. menu.addAction(m_toggleSelectedEntities);
  1951. if (toggleVisibility)
  1952. {
  1953. menu.addAction(m_toggleSelectedEntitiesVisibility);
  1954. }
  1955. if (toggleOtherProperties)
  1956. {
  1957. menu.addAction(m_toggleSelectedEntitiesColor);
  1958. menu.addAction(m_toggleSelectedEntitiesNormals);
  1959. menu.addAction(m_toggleSelectedEntitiesSF);
  1960. }
  1961. if (toggleMaterials)
  1962. {
  1963. menu.addAction(m_toggleSelectedEntitiesMat);
  1964. }
  1965. menu.addAction(m_toggleSelectedEntities3DName);
  1966. menu.addSeparator();
  1967. menu.addAction(m_deleteSelectedEntities);
  1968. if (selCount == 1 && hasMoreThanOneChild)
  1969. {
  1970. menu.addSeparator();
  1971. menu.addAction(m_sortChildrenAZ);
  1972. menu.addAction(m_sortChildrenZA);
  1973. menu.addAction(m_sortChildrenType);
  1974. }
  1975. if (selCount == 1 && !leafObject)
  1976. {
  1977. menu.addSeparator();
  1978. menu.addAction(m_selectByTypeAndName);
  1979. menu.addSeparator();
  1980. menu.addAction(m_addEmptyGroup);
  1981. }
  1982. if (canEditLabelScalarValue)
  1983. {
  1984. menu.addSeparator();
  1985. menu.addAction(m_editLabelScalarValue);
  1986. }
  1987. menu.addSeparator();
  1988. }
  1989. menu.addAction(m_expandBranch);
  1990. menu.addAction(m_collapseBranch);
  1991. }
  1992. else
  1993. {
  1994. menu.addSeparator();
  1995. menu.addAction(m_addEmptyGroup);
  1996. }
  1997. menu.exec(m_dbTreeWidget->mapToGlobal(menuPos));
  1998. }
  1999. QItemSelectionModel::SelectionFlags ccCustomQTreeView::selectionCommand(const QModelIndex& idx, const QEvent* event/*=nullptr*/) const
  2000. {
  2001. if (idx.isValid())
  2002. {
  2003. //special case: labels can only be merged with labels!
  2004. QModelIndexList selectedIndexes = selectionModel()->selectedIndexes();
  2005. if (!selectedIndexes.empty() && !selectionModel()->isSelected(idx))
  2006. {
  2007. ccHObject* selectedItem = static_cast<ccHObject*>(idx.internalPointer());
  2008. if (selectedItem && selectedItem->isA(CC_TYPES::LABEL_2D) != static_cast<ccHObject*>(selectedIndexes[0].internalPointer())->isA(CC_TYPES::LABEL_2D))
  2009. return QItemSelectionModel::ClearAndSelect;
  2010. }
  2011. }
  2012. return QTreeView::selectionCommand(idx, event);
  2013. }