TTK
Loading...
Searching...
No Matches
ContourForests.cpp
Go to the documentation of this file.
1/*
2 * file: ContourForestsTree.cpp
3 * description: ContourForestsTree processing package.
4 * author: Gueunet Charles
5 * date: Aout 2015
6 */
7
8#include <iterator>
9#include <list>
10
11#include "ContourForests.h"
12
13using namespace std;
14using namespace ttk;
15using namespace cf;
16
17Interface::Interface(const SimplexId &seed) : seed_(seed) {
18}
19
20// ------------------------- ContourForests
21
23 : ContourForestsTree(std::make_shared<Params>(), std::make_shared<Scalars>()),
24 parallelParams_(), parallelData_() {
25 this->setDebugMsgPrefix("ContourForests");
26 this->printWrn(
27 "DEPRECATED This module will be removed in a future release, please use "
28 "FTM instead for contour trees and FTR for Reeb graphs.");
29 params_->treeType = TreeType::Contour;
30}
31
33 params_.reset();
34 scalars_.reset();
35}
36
37// Get
38// {
39
41 const SimplexId &position = scalars_->sosOffsets[v];
42 idPartition partition = 0;
43 while(partition < parallelParams_.nbInterfaces
44 && scalars_->sosOffsets[parallelData_.interfaces[partition].getSeed()]
45 <= position) {
46 ++partition;
47 }
48
49 return partition;
50}
51
52// }
53// Init
54// {
55
57 // We have nbThread_ partition of the same size through all vertices
58 const size_t partitionSize = scalars_->size / parallelParams_.nbPartitions;
59
60 // ------------------
61 // Seeds
62 // ------------------
63 // {
64
65 // We initiate interface with their seed (isovalue) and their adjacent
66 // partition
67 // and each partition with it size and bounds.
68 for(idInterface i = 0; i < parallelParams_.nbInterfaces; ++i) {
69 // interfaces have their first vertex of the sorted array as seed
70 parallelData_.interfaces.emplace_back(
71 scalars_->sortedVertices[partitionSize * (i + 1)]);
72 }
73
74 // }
75 // ------------------
76 // Print Debug
77 // ------------------
78 // {
79
80 if(params_->debugLevel >= 4) {
81 stringstream partition;
82 partition << "seeds :";
83 for(const auto &i : parallelData_.interfaces) {
84 partition << i.getSeed() << " ";
85 }
86 this->printMsg(partition.str(), debug::Priority::DETAIL);
87 }
88
89 // }
90}
91
93 if(parallelParams_.lessPartition && parallelParams_.nbThreads >= 2) {
94 parallelParams_.nbPartitions = parallelParams_.nbThreads / 2;
95 } else {
96 parallelParams_.nbPartitions = parallelParams_.nbThreads;
97 }
98
99 parallelParams_.nbInterfaces = parallelParams_.nbPartitions - 1;
100}
101
102// }
103
104// Process
105// {
106
108 // We need goods information here before starting
109 // Get the arc/NODE corresponding to the seed + crossingEdges
110
111 if(params_->treeType == TreeType::Contour) {
112 stitchTree(2);
113 } else {
114 stitchTree(0);
115 stitchTree(1);
116 }
117}
118
119void ContourForests::stitchTree(const char treetype) {
120
121 const bool DEBUG = false;
122
123 // For the instance assume ct :
124 // insert node with true (jt / ct ok)
125 auto getTreePart = [&, treetype](const idPartition &i) -> MergeTree * {
126 if(treetype == 0) {
127 return parallelData_.trees[i].getJoinTree();
128 }
129 if(treetype == 1) {
130 return parallelData_.trees[i].getSplitTree();
131 }
132 return &parallelData_.trees[i];
133 };
134
135 vector<bool> seenSeed(parallelParams_.nbInterfaces, false);
136
137 // For each partition, we stich with above
138 for(idPartition i = 0; i < parallelParams_.nbPartitions - 1; i++) {
139 MergeTree *curTree = getTreePart(i);
140 const auto &seedPair
141 = make_pair(parallelData_.interfaces[i].getSeed(), false);
142
143 if(DEBUG) {
144 cout << "partition : " << static_cast<unsigned>(i);
145 cout << " seed is : " << seedPair.first << endl << endl;
146 }
147
148 // For each superarc crossing the upper boundary :
149 for(const auto &arc : curTree->treeData_.arcsCrossingAbove) {
150 SuperArc *crossing = curTree->getSuperArc(arc);
151
152 // Hidden arc, no need to stich (may have already been stitched)
153 if(!crossing->isVisible())
154 continue;
155
156 const idNode &downCrossingId = crossing->getDownNodeId();
157
158 // the down node of the arc is not on this partition, It should have
159 // already been processed
160 if(vertex2partition(curTree->getNode(downCrossingId)->getVertexId()) < i)
161 continue;
162
163 // Stitch vertex and insertion in current tree
164 const SimplexId &stitchVertex
165 = curTree->insertNodeAboveSeed(arc, seedPair);
166
167 // Opposite partition
168 const idPartition &otherPartition = vertex2partition(stitchVertex);
169 MergeTree *otherTree = getTreePart(otherPartition);
170
171 if(DEBUG) {
172 cout << "crossing arc is " << curTree->printArc(arc) << endl;
173 }
174
175 if(DEBUG) {
176 cout << "stitch vertex : " << stitchVertex << endl;
177 cout << "on partition " << static_cast<unsigned>(otherPartition)
178 << endl;
179 cout << "crossing arc is now " << curTree->printArc(arc) << endl;
180 }
181
182 const idNode &curTreeStitchNodeId
183 = curTree->getCorrespondingNodeId(stitchVertex);
184 Node *curTreeStitchNode = curTree->getNode(curTreeStitchNodeId);
185
186 bool otherTreeAlreadyHide = false;
187 if(otherTree->isCorrespondingArc(stitchVertex)) {
188 if(DEBUG) {
189 const idSuperArc &sa
190 = otherTree->getCorrespondingSuperArcId(stitchVertex);
191 cout << "other tree arc is : " << otherTree->printArc(sa) << endl;
192 }
193 const auto &arcToHide
194 = otherTree->reverseInsertNode(curTreeStitchNode, true);
195 curTreeStitchNode->setDownValence(1);
196 curTreeStitchNode->setUpValence(1);
197 otherTree->hideArc(arcToHide);
198 otherTreeAlreadyHide = true;
199
200 if(DEBUG) {
201 cout << "hide arc in other : " << otherTree->printArc(arcToHide)
202 << endl;
203 }
204 }
205
206 const idNode &otherTreeStitchNodeId
207 = otherTree->getCorrespondingNodeId(stitchVertex);
208 Node *otherTreeStitchNode = otherTree->getNode(otherTreeStitchNodeId);
209
210 if(DEBUG) {
211 cout << "Stitch nodes : " << endl;
212 cout << "current : " << curTree->printNode(curTreeStitchNodeId) << endl;
213 cout << "other : " << otherTree->printNode(otherTreeStitchNodeId)
214 << endl;
215 }
216
217 // Now we can remove all arc above the stitch vertex in the current tree
218 // (noise) When using debug, all arc are not hidden, they are just
219 // disconnected Look at the node information, not all the arcs
220
221 curTreeStitchNode->clearUpSuperArcs();
222
223 // for the other tree we need to replace arc (hide the one that is
224 // replaced) Exception : the seed may contain noise directly above: we
225 // clear its down arcs if it is the stitch node.
226 if(stitchVertex == seedPair.first) {
227 if(DEBUG) {
228 cout << "stitch vertex is seed" << endl;
229 }
230 // we have'nt clear it yet
231 if(!seenSeed[otherPartition]) {
232 seenSeed[otherPartition] = true;
233 otherTree->removeInternalDownArcs(otherTreeStitchNodeId);
234 if(DEBUG) {
235 cout << "clear below stitch vert " << endl;
236 }
237 }
238 } else if(!otherTreeAlreadyHide && crossing->getDownCT() == i) {
239 const SimplexId &currentBelowSeed = curTree->getVertBelowSeed(
240 arc, seedPair, otherTree->treeData_.vert2tree);
241 const idSuperArc &arcToHide = otherTree->hideAndClearLeadingTo(
242 otherTreeStitchNodeId, currentBelowSeed);
243
244 if(DEBUG) {
245 cout << "hide arc leading to: ";
246 if(arcToHide != nullSuperArc) {
247 cout << otherTree->printArc(arcToHide) << endl;
248 } else {
249 cout << "not found" << endl;
250 }
251 }
252 }
253
254 // Create an external arc to link both nodes on each tree
255 // The created arc may be a new crossing arc
256
257 curTree->treeData_.superArcs.emplace_back(curTreeStitchNodeId,
258 otherTreeStitchNodeId, false,
259 true, i, otherPartition);
260 curTreeStitchNode->addUpSuperArcId(curTree->getNumberOfSuperArcs() - 1);
261
262 // chk if cross the next interface to add it in the vector if crossing
263 // above
264 bool crossNextInterface = false;
265 if(otherPartition < parallelParams_.nbInterfaces) {
266 const SimplexId &nextSeed
267 = parallelData_.interfaces[otherPartition].getSeed();
268 crossNextInterface = isLower(nextSeed, stitchVertex);
269 }
270 otherTree->treeData_.superArcs.emplace_back(
271 curTreeStitchNodeId, otherTreeStitchNodeId, true, crossNextInterface, i,
272 otherPartition);
273 otherTreeStitchNode->addDownSuperArcId(otherTree->getNumberOfSuperArcs()
274 - 1);
275 if(crossNextInterface) {
276 otherTree->addCrossingAbove(otherTree->getNumberOfSuperArcs() - 1);
277 if(DEBUG) {
278 cout << "new crossing above" << endl;
279 }
280 }
281
282 if(DEBUG) {
283 cout << "arc added :" << endl;
284 cout << "current : "
285 << curTree->printArc(curTree->getNumberOfSuperArcs() - 1) << endl;
286 cout << "other : "
287 << otherTree->printArc(otherTree->getNumberOfSuperArcs() - 1)
288 << endl;
289 cout << endl << endl;
290 }
291
292 } // for each arc of this curTree
293
294 } // for each partition
295
296 if(DEBUG) {
297 printVectCT();
298 }
299}
300
302
303 if(params_->treeType == TreeType::Contour) {
304 unifyTree(2);
305 } else {
306 unifyTree(0);
307 unifyTree(1);
308 }
309}
310
311void ContourForests::unifyTree(const char treetype) {
312
313 const bool DEBUG = false;
314
315 // Get the good tree
316 auto getTreePart = [&, treetype](const idPartition &i) -> MergeTree * {
317 if(treetype == 0) {
318 return parallelData_.trees[i].getJoinTree();
319 }
320 if(treetype == 1) {
321 return parallelData_.trees[i].getSplitTree();
322 }
323 return &parallelData_.trees[i];
324 };
325
326 // this tree will receive the final tree
327 // all variables linked to tmpree have a "_tt" suffix
328 MergeTree tmpTree(params_, scalars_, params_->treeType);
329 // for vert2tree
330 tmpTree.flush();
331 // statistical reserves
332 tmpTree.treeData_.nodes.reserve(scalars_->size / 50);
333 tmpTree.treeData_.superArcs.reserve(scalars_->size / 50);
334
335 // partition, node in partition, is a leaf
336 queue<tuple<idInterface, idNode>> leavesNodes;
337 vector<unsigned> nbVisit(scalars_->size, 0);
338
339 // Unify by traversing leaves for each partition starting by the lowest
340 for(idPartition partition = 0; partition < parallelParams_.nbPartitions;
341 ++partition) {
342 MergeTree *currentTree = getTreePart(partition);
343
344 for(auto &l : currentTree->treeData_.leaves) {
345 Node *curNode = currentTree->getNode(l);
346 const SimplexId leafVert = curNode->getVertexId();
347
348 // Condition to keep
349
350 // if not in partition
351 if(partition != 0
352 && isLower(
353 leafVert, parallelData_.interfaces[partition - 1].getSeed()))
354 continue;
355 if(partition != parallelParams_.nbInterfaces
356 && isHigher(leafVert, parallelData_.interfaces[partition].getSeed()))
357 continue;
358
359 // if hidden
360 if(!curNode->isVisible())
361 continue;
362
363 // if max
364 if(!curNode->getNumberOfUpSuperArcs())
365 continue;
366
367 // this leaf have received an external arc during stitching, not a real
368 // leaf anymore
369 if(curNode->getNumberOfUpSuperArcs()
370 && curNode->getNumberOfDownSuperArcs())
371 continue;
372
373 // isolated node
374 if(!currentTree->getNumberOfVisibleArcs(l))
375 continue;
376
377 // Add the leave
378
379 if(!nbVisit[leafVert]) {
380 // will be processed
381 leavesNodes.emplace(partition, l);
382
383 if(DEBUG) {
384 cout << "will see : partition : " << static_cast<unsigned>(partition);
385 cout << " leaf node " << currentTree->printNode(l) << endl;
386 }
387
388 // seen once
389 ++nbVisit[leafVert];
390
391 // Create node in tmpTree and mark as leaf
392 const idNode &newNodeId_tt = tmpTree.makeNode(curNode);
393 tmpTree.treeData_.leaves.emplace_back(newNodeId_tt);
394 }
395 }
396 }
397
398 // cross node from min to max to construct the tree
399
400 while(!leavesNodes.empty()) {
401 // get the next node
402 idPartition currentPartition;
403 idNode currentNodeId;
404 tie(currentPartition, currentNodeId) = leavesNodes.front();
405 leavesNodes.pop();
406
407 // get tree and node
408 MergeTree *currentTree = getTreePart(currentPartition);
409 Node *currentNode = currentTree->getNode(currentNodeId);
410
411 if(DEBUG) {
412 cout << endl;
413 cout << "process : partition : "
414 << static_cast<unsigned>(currentPartition) << endl;
415 cout << " node " << currentTree->printNode(currentNodeId) << endl;
416 }
417
418 // create or recover in tmpTree
419 const idNode &baseNode_tt = tmpTree.makeNode(currentNode);
420
421 // Cross the ups arc of the node
422 const idPartition refPartition = currentPartition;
423 const idNode refNodeId = currentNodeId;
424 const idSuperArc &nbUpArc = currentNode->getNumberOfUpSuperArcs();
425
426 for(idSuperArc upArcPos = 0; upArcPos < nbUpArc; upArcPos++) {
427 // reset partition / node and tree
428 currentTree = getTreePart(refPartition);
429 currentNodeId = refNodeId;
430 currentNode = currentTree->getNode(currentNodeId);
431
432 const idSuperArc &upArcId = currentNode->getUpSuperArcId(upArcPos);
433 SuperArc *upArc = currentTree->getSuperArc(upArcId);
434
435 if(DEBUG) {
436 cout << " process arc " << currentTree->printArc(upArcId) << endl;
437 }
438
439 if(!upArc->isVisible()) {
440 if(DEBUG) {
441 cout << " - ignore not visible" << endl;
442 }
443 continue;
444 }
445
446 const idSuperArc &newArcId_tt
447 = tmpTree.openSuperArc(baseNode_tt, false, false);
448 SuperArc *newArc_tt = tmpTree.getSuperArc(newArcId_tt);
449
450 // segmentation related
451 list<pair<SimplexId, bool> *> listVertList;
452 list<SimplexId> listVertSize;
453 int totalSize = 0;
454
455 // add upArc until the upnode is not a regular one
456 do {
457 listVertList.push_back(upArc->getVertList());
458 listVertSize.push_back(upArc->getVertSize());
459 totalSize += upArc->getVertSize();
460
461 upArc->hide();
462
463 currentPartition = upArc->getUpCT();
464 currentTree = getTreePart(currentPartition);
465 currentNodeId = upArc->getUpNodeId();
466 currentNode = currentTree->getNode(currentNodeId);
467
468 if(currentNode->getNumberOfUpSuperArcs() == 1
469 && currentNode->getNumberOfDownSuperArcs() == 1) {
470 upArc = currentTree->getSuperArc(currentNode->getUpSuperArcId(0));
471 if(DEBUG) {
472 cout << " cross : "
473 << currentTree->printArc(currentNode->getUpSuperArcId(0));
474 cout << endl;
475 }
476 } else {
477 // no longer regular : stop here
478 if(DEBUG) {
479 cout << "stop at " << currentTree->printNode(currentNodeId) << endl;
480 }
481 break;
482 }
483
484 } while(true);
485
486 // Finish the current Arc (segmentation + close)
487 if(totalSize) {
488 newArc_tt->appendVertLists(
489 listVertList, listVertSize, this->storage_, totalSize);
490 }
491 const idNode &closingNode_tt = tmpTree.makeNode(currentNode);
492 tmpTree.closeSuperArc(newArcId_tt, closingNode_tt, false, false);
493
494 if(DEBUG) {
495 cout << " Create arc : " << tmpTree.printArc(newArcId_tt) << endl;
496 }
497
498 // push current vertex TODO
499 const SimplexId &closingVertex = currentNode->getVertexId();
500 ++nbVisit[closingVertex];
501
502 // get the down valence of this node in the partition where it have no
503 // noise
504 const idPartition &closingPartition = vertex2partition(closingVertex);
505 MergeTree *closingTree = getTreePart(closingPartition);
506 const idNode &closingNodeId
507 = closingTree->getCorrespondingNodeId(closingVertex);
508 const unsigned &downVal
509 = closingTree->getNode(closingNodeId)->getNumberOfDownSuperArcs();
510
511 if(nbVisit[closingVertex] == downVal) {
512 leavesNodes.emplace(closingPartition, closingNodeId);
513 if(DEBUG) {
514 cout << " push : partition : "
515 << static_cast<unsigned>(closingPartition) << endl;
516 cout << " push : node : " << closingTree->printNode(closingNodeId)
517 << endl;
518 }
519 } else if(DEBUG) {
520 cout << " visit : " << nbVisit[closingVertex] << endl;
521 cout << " downVal : " << downVal << endl;
522 }
523 } // end for each up arc
524 } // end while leavesNodes
525
526 tmpTree.treeData_.superArcs.shrink_to_fit();
527 tmpTree.treeData_.nodes.shrink_to_fit();
528
529 // could use swap, more efficient
530 if(treetype == 0) {
531 jt_.clone(&tmpTree);
532 } else if(treetype == 1) {
533 st_.clone(&tmpTree);
534 } else if(treetype == 2) {
535 clone(&tmpTree);
536 }
537}
538// }
539
540// Print
541// {
542void ContourForests::printDebug(DebugTimer &timer, const string &str) {
543 this->printMsg(str, 1.0, timer.getElapsedTime(), this->threadNumber_);
544}
545
547 int arcCTUp, arcCTDown;
548
549 for(idPartition nb = 0; nb < parallelParams_.nbPartitions; ++nb) {
550 cout << "CT " << nb << endl;
551 cout << "Nodes" << endl;
552
553 for(const auto &n : parallelData_.trees[nb].getNodes()) {
554 if(!n.isHidden()) {
555 cout << "Node " << n.getVertexId();
556
557 if(n.isHidden())
558 cout << " X ";
559
560 cout << endl;
561 cout << " arc up : ";
562
563 for(idSuperArc i = 0; i < n.getNumberOfUpSuperArcs(); ++i) {
564 cout << n.getUpSuperArcId(i) << " ";
565 }
566
567 cout << endl << " arc down : ";
568
569 for(idSuperArc i = 0; i < n.getNumberOfDownSuperArcs(); ++i) {
570 cout << n.getDownSuperArcId(i) << " ";
571 }
572
573 cout << endl;
574 }
575 }
576
577 cout << "Arcs" << endl;
578
579 for(const auto &sa : parallelData_.trees[nb].getSuperArc()) {
580 if(!sa.isHidden()) {
581 arcCTDown = sa.getDownCT();
582 arcCTUp = sa.getUpCT();
583
584 if(sa.getDownNodeId() == nullNodes) {
585 cout << "||";
586 } else {
587 cout << static_cast<unsigned>(arcCTDown) << ":";
588 cout << parallelData_.trees[arcCTDown]
589 .getNode(sa.getDownNodeId())
590 ->getVertexId();
591 }
592
593 if(sa.isHidden())
594 cout << " <X> ";
595 else if(!sa.isVisible())
596 cout << " <-> ";
597 else
598 cout << " <> ";
599
600 if(sa.getUpNodeId() == nullNodes) {
601 cout << "||";
602 } else {
603 cout << static_cast<unsigned>(arcCTUp) << ":";
604 cout << parallelData_.trees[arcCTUp]
605 .getNode(sa.getUpNodeId())
606 ->getVertexId();
607 }
608
609 cout << endl;
610 }
611 }
612
613 if(true) {
614 cout << "Leaves" << endl;
615
616 for(const auto &l : parallelData_.trees[nb].getLeaves())
617 cout << " " << l;
618
619 cout << endl;
620
621 cout << "Roots" << endl;
622
623 for(const auto &r : parallelData_.trees[nb].getRoots())
624 cout << " " << r;
625
626 cout << endl;
627 }
628 }
629}
630// }
#define DEBUG(msg)
Legacy backward compatibility.
Definition Debug.h:472
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
double getElapsedTime()
Definition Timer.h:15
void stitchTree(const char tree)
idPartition vertex2partition(const SimplexId &v)
void printDebug(DebugTimer &timer, const std::string &str)
void unifyTree(const char treetype)
Interface(const SimplexId &seed)
void hideArc(const idSuperArc &sa)
idSuperArc getNumberOfVisibleArcs() const
Definition MergeTree.h:187
idNode makeNode(const SimplexId &vertexId, const SimplexId &linked=nullVertex)
idSuperArc openSuperArc(const idNode &downNodeId, const bool overlapB, const bool overlapA)
std::string printArc(const idSuperArc &a)
Definition MergeTree.h:580
std::shared_ptr< Scalars > scalars_
Definition MergeTree.h:44
bool isCorrespondingArc(const SimplexId &val) const
Definition MergeTree.h:294
void removeInternalDownArcs(const idNode &node)
std::shared_ptr< MergeTree > clone() const
void addCrossingAbove(const idSuperArc &sa)
Definition MergeTree.h:225
idSuperArc getNumberOfSuperArcs() const
Definition MergeTree.h:183
idSuperArc reverseInsertNode(Node *node, const bool segment)
SimplexId getVertBelowSeed(const idSuperArc &arc, const std::pair< SimplexId, bool > &seed, const std::vector< idCorresp > &vert2treeOther)
std::string printNode(const idNode &n)
Definition MergeTree.h:598
bool isHigher(const SimplexId &a, const SimplexId &b) const
Definition MergeTree.h:649
idNode getCorrespondingNodeId(const SimplexId &val) const
Definition MergeTree.h:310
idSuperArc getCorrespondingSuperArcId(const SimplexId &val) const
Definition MergeTree.h:321
void closeSuperArc(const idSuperArc &superArcId, const idNode &upNodeId, const bool overlapB, const bool overlapA)
void flush()
clear local data for new computation
Definition MergeTree.h:87
TreeData treeData_
Definition MergeTree.h:47
bool isLower(const SimplexId &a, const SimplexId &b) const
Definition MergeTree.h:645
idSuperArc hideAndClearLeadingTo(const idNode &baseNode, const SimplexId &v)
SimplexId insertNodeAboveSeed(const idSuperArc &arc, const std::pair< SimplexId, bool > &seed)
const std::vector< SuperArc > & getSuperArc() const
Definition MergeTree.h:197
Node * getNode(const idNode &nodeId)
Definition MergeTree.h:244
std::shared_ptr< Params > params_
Definition MergeTree.h:43
SimplexId getVertexId() const
idSuperArc clearUpSuperArcs()
void addUpSuperArcId(const idSuperArc &upSuperArcId)
void addDownSuperArcId(const idSuperArc &downSuperArcId)
idSuperArc getNumberOfUpSuperArcs() const
void setUpValence(const idSuperArc &v)
bool isVisible() const
idSuperArc getUpSuperArcId(const idSuperArc &neighborId) const
void setDownValence(const idSuperArc &v)
idSuperArc getNumberOfDownSuperArcs() const
idPartition getDownCT() const
const idNode & getUpNodeId() const
const SimplexId & getVertSize()
std::pair< SimplexId, bool > * getVertList()
void appendVertLists(const std::list< std::pair< SimplexId, bool > * > &vertLists, std::list< SimplexId > vertSizes, std::list< std::vector< std::pair< SimplexId, bool > > > &storage, const SimplexId &totalSize)
const idNode & getDownNodeId() const
idPartition getUpCT() const
numThread idInterface
index of the interface/partition in vect_interfaces_
numThread idPartition
long unsigned int idSuperArc
SuperArc index in vect_superArcs_.
unsigned int idNode
Node index in vect_nodes_.
The Topology ToolKit.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
std::vector< Interface > interfaces
std::vector< ContourForestsTree > trees
std::vector< idSuperArc > arcsCrossingAbove
std::vector< idNode > leaves
std::vector< Node > nodes
std::vector< SuperArc > superArcs
std::vector< idCorresp > vert2tree
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)