TTK
Loading...
Searching...
No Matches
MeshGraph.h
Go to the documentation of this file.
1
38
39#pragma once
40
41// base code includes
42#include <Wrapper.h>
43
44namespace ttk {
45
46 class MeshGraph : virtual public Debug {
47
48 public:
50 this->setDebugMsgPrefix("MeshGraph");
51 }
52 ~MeshGraph() override = default;
53
54 inline size_t computeNumberOfOutputPoints(const size_t &nInputPoints,
55 const size_t &nInputCells,
56 const bool &useQuadraticCells,
57 const size_t &nSubdivisions
58 = 0) const {
59 return useQuadraticCells
60 ? nInputPoints * 3
61 + nInputCells * 7 // 3 per input point (a -> a,a0,a1) + 7 per
62 // input cell (a0m0,m0,m0b0,b1m1,m1,m1a1,c)
63 : nInputPoints * 2
64 + nInputCells
65 * (nSubdivisions
66 * 2); // 2 per input point (a -> a0,a1) + 2 per cell
67 // subdivision (sIu+sId)
68 }
69
70 inline size_t
71 computeNumberOfOutputCells(const size_t &nInputCells,
72 const bool &useQuadraticCells) const {
73 return useQuadraticCells
74 ? nInputCells
75 * 2 // each cell gets converted into two quadratic quads
76 : nInputCells; // each cell gets converted into a one cell with
77 // multiple points
78 }
79
80 inline size_t computeOutputCellSize(const size_t &nSubdivisions) const {
81 return 4 + nSubdivisions * 2; // 4 corners + 2 for each subdivision
82 }
83
84 inline size_t
85 computeOutputConnectivityArraySize(const size_t &nInputCells,
86 const bool &useQuadraticCells,
87 const size_t &nSubdivisions
88 = 0) const {
89 return useQuadraticCells
90 ? nInputCells
91 * 16 // 8 corners (a0,m0,m1,a1, m0,b0,b1,m1) + 8
92 // mid-edge nodes (a0m0,c,m1a1,a, m0b0,b,b1m1,c)
93 : nInputCells * this->computeOutputCellSize(nSubdivisions);
94 }
95
96 // Mesh graph with quadratic quads
97 template <typename IT, typename CT, typename DT>
98 int execute(
99 // Output
100 CT *outputPoints,
101 IT *outputConnectivityArray,
102 IT *outputOffsetArray,
103
104 // Input
105 const CT *inputPoints,
106 const IT *inputConnectivityArray,
107 const size_t &nInputPoints,
108 const size_t &nInputCells,
109
110 const DT *inputPointSizes,
111 const CT &sizeScale,
112 const size_t &sizeAxis) const;
113
114 // Mesh graph with linear polygon
115 template <typename IT, typename CT, typename DT>
116 int execute2(
117 // Output
118 CT *outputPoints,
119 IT *outputConnectivityArray,
120 IT *outputOffsetArray,
121
122 // Input
123 const CT *inputPoints,
124 const IT *inputConnectivityArray,
125 const size_t nInputPoints,
126 const size_t nInputCells,
127 const size_t nSubdivisions,
128
129 const DT *inputPointSizes,
130 const CT sizeScale,
131 const size_t sizeAxis) const;
132
133 // Map input point data to output point data
134 template <typename DT, typename IT>
135 int mapInputPointDataToOutputPointData(DT *outputPointData,
136 const size_t &nInputPoints,
137 const size_t &nInputCells,
138 const IT *inputConnectivityArray,
139 const DT *inputPointData,
140 const bool &useQuadraticCells,
141 const size_t &nSubdivisions
142 = 0) const;
143
144 // Map input point data to output point data
145 template <typename DT>
146 int mapInputCellDataToOutputCellData(DT *outputCellData,
147 const size_t &nInputCells,
148 const DT *inputCellData,
149 const bool &useQuadraticCells) const;
150 };
151} // namespace ttk
152
153// =============================================================================
154// Version 1: Mesh Graph with Quadratic Quads
155// =============================================================================
156template <typename IT, typename CT, typename DT>
158 // Output
159 CT *outputPoints,
160 IT *outputConnectivityArray,
161 IT *outputOffsetArray,
162
163 // Input
164 const CT *inputPoints,
165 const IT *inputConnectivityArray,
166 const size_t &nInputPoints,
167 const size_t &nInputCells,
168 const DT *inputPointSizes,
169 const CT &sizeScale,
170 const size_t &sizeAxis) const {
171
172 // Print Input
174 this->printMsg({{"Mode", "Quadratic Quads"},
175 {"#Nodes", std::to_string(nInputPoints)},
176 {"#Edges", std::to_string(nInputCells)}});
178
179 const size_t edgePointOffset = nInputPoints * 3;
180 // ---------------------------------------------------------------------------
181 // Compute Output Point Locations
182 // ---------------------------------------------------------------------------
183 // outputPoints: [
184 // 3 per input point (a,a0,a1,b,b0,b1,...),
185 // 7 per input cell (m0,m1,a0m0,m0b0,b1m1,m1a1,c...)
186 // ]
187 {
188 Timer t;
189 this->printMsg("Computing node locations", 0, debug::LineMode::REPLACE);
190
191// -----------------------------------------------------------------------------
192// Compute points that result from input points
193// -----------------------------------------------------------------------------
194#ifdef TTK_ENABLE_OPENMP
195#pragma omp parallel for num_threads(threadNumber_)
196#endif
197 for(size_t i = 0; i < nInputPoints; i++) {
198 const CT *coord = &inputPoints[i * 3];
199
200 size_t q = i * 9; // i*3*3
201 // a
202 outputPoints[q] = coord[0];
203 outputPoints[q + 1] = coord[1];
204 outputPoints[q + 2] = coord[2];
205
206 // a0
207 outputPoints[q + 3] = coord[0];
208 outputPoints[q + 4] = coord[1];
209 outputPoints[q + 5] = coord[2];
210
211 // a1
212 outputPoints[q + 6] = coord[0];
213 outputPoints[q + 7] = coord[1];
214 outputPoints[q + 8] = coord[2];
215
216 const CT &size = ((CT)inputPointSizes[i]) * sizeScale;
217
218 // Move a0 and a1 along size axis
219 outputPoints[q + 3 + sizeAxis] += size / 2;
220 outputPoints[q + 6 + sizeAxis] -= size / 2;
221 }
222
223 // -------------------------------------------------------------------------
224 // Compute points that result from edges
225 // -------------------------------------------------------------------------
226
227 // Lambda function that linearly interpolates two point locations
228 auto getMidPoint = [&](const size_t &m, const size_t &i, const size_t &j) {
229 size_t mp = m * 3;
230 size_t ip = i * 3;
231 size_t jp = j * 3;
232 for(size_t k = 0; k < 3; k++)
233 outputPoints[mp + k]
234 = (outputPoints[ip + k] + outputPoints[jp + k]) / 2;
235 };
236
237 // Lambda function that computes the output location of a mid point on a
238 // bezier curve
239 auto getMidPoint2
240 = [&](const size_t &m, const size_t &p0, const size_t &p1) {
241 auto bezierPoint = [](CT &n, const CT &q0, const CT &q2) {
242 n = 0.5 * (0.5 * q0 + 0.5 * n) + 0.5 * (0.5 * n + 0.5 * q2);
243 };
244
245 size_t mi = m * 3;
246 size_t p0i = p0 * 3; // first point
247 size_t p1i = p1 * 3; // second point
248
249 for(size_t i = 0; i < 3; i++)
250 outputPoints[mi + i]
251 = (outputPoints[p0i + i] + outputPoints[p1i + i]) / 2;
252 outputPoints[mi + sizeAxis] = outputPoints[p0i + sizeAxis];
253
254 for(size_t i = 0; i < 3; i++)
255 bezierPoint(outputPoints[mi + i], outputPoints[p0i + i],
256 outputPoints[p1i + i]);
257 };
258
259// Iterate over input cells and generate new points
260#ifdef TTK_ENABLE_OPENMP
261#pragma omp parallel for num_threads(threadNumber_)
262#endif
263 for(size_t i = 0; i < nInputCells; i++) {
264 size_t temp = i * 2;
265 size_t aInputIndex = (size_t)inputConnectivityArray[temp++];
266 size_t bInputIndex = (size_t)inputConnectivityArray[temp];
267
268 // already computed points
269 size_t a = aInputIndex * 3;
270 size_t a0 = a + 1;
271 size_t a1 = a + 2;
272 size_t b = bInputIndex * 3;
273 size_t b0 = b + 1;
274 size_t b1 = b + 2;
275
276 // points to compute
277 size_t offset = edgePointOffset + i * 7;
278 size_t m0 = offset;
279 size_t m1 = offset + 1;
280 size_t a0m0 = offset + 2;
281 size_t m0b0 = offset + 3;
282 size_t b1m1 = offset + 4;
283 size_t m1a1 = offset + 5;
284 size_t c = offset + 6;
285
286 getMidPoint(m0, a0, b0);
287 getMidPoint(m1, a1, b1);
288
289 getMidPoint(c, m0, m1);
290
291 getMidPoint2(a0m0, a0, m0);
292 getMidPoint2(m0b0, b0, m0);
293
294 getMidPoint2(b1m1, b1, m1);
295 getMidPoint2(m1a1, a1, m1);
296 }
297
298 // Print Status
299 this->printMsg(
300 "Computing mesh vertices", 1, t.getElapsedTime(), this->threadNumber_);
301 }
302
303 // ---------------------------------------------------------------------------
304 // Compute Output Cells
305 // ---------------------------------------------------------------------------
306 {
307 Timer t;
308 this->printMsg("Computing mesh cells", 0, debug::LineMode::REPLACE);
309
310 IT edgePointOffset_ = (IT)edgePointOffset;
311
312#ifdef TTK_ENABLE_OPENMP
313#pragma omp parallel for num_threads(threadNumber_)
314#endif
315 for(size_t i = 0; i < nInputCells; i++) {
316 size_t temp = i * 2;
317 IT aInputIndex = inputConnectivityArray[temp++];
318 IT bInputIndex = inputConnectivityArray[temp];
319
320 // get point indices
321 IT a = aInputIndex * 3;
322 IT a0 = a + 1;
323 IT a1 = a + 2;
324 IT b = bInputIndex * 3;
325 IT b0 = b + 1;
326 IT b1 = b + 2;
327
328 IT i_ = (IT)i;
329 IT offset = edgePointOffset_ + i_ * 7;
330 IT m0 = offset;
331 IT m1 = offset + 1;
332 IT a0m0 = offset + 2;
333 IT m0b0 = offset + 3;
334 IT b1m1 = offset + 4;
335 IT m1a1 = offset + 5;
336 IT c = offset + 6;
337
338 // output cell offset
339 size_t q = i * 16;
340
341 // first quadratic quad
342 outputConnectivityArray[q++] = a0;
343 outputConnectivityArray[q++] = m0;
344 outputConnectivityArray[q++] = m1;
345 outputConnectivityArray[q++] = a1;
346
347 outputConnectivityArray[q++] = a0m0;
348 outputConnectivityArray[q++] = c;
349 outputConnectivityArray[q++] = m1a1;
350 outputConnectivityArray[q++] = a;
351
352 // second quadratic quad
353 outputConnectivityArray[q++] = m0;
354 outputConnectivityArray[q++] = b0;
355 outputConnectivityArray[q++] = b1;
356 outputConnectivityArray[q++] = m1;
357
358 outputConnectivityArray[q++] = m0b0;
359 outputConnectivityArray[q++] = b;
360 outputConnectivityArray[q++] = b1m1;
361 outputConnectivityArray[q++] = c;
362 }
363
364 for(size_t i = 0; i <= 2 * nInputCells; i++) {
365 outputOffsetArray[i] = i * 8;
366 }
367
368 this->printMsg(
369 "Computing mesh cells", 1, t.getElapsedTime(), this->threadNumber_);
370 }
371
372 return 1;
373}
374
375// =============================================================================
376// Version 2: Mesh Graph with Linear Polygon
377// =============================================================================
378template <typename IT, typename CT, typename DT>
380 // Output
381 CT *outputPoints,
382 IT *outputConnectivityArray,
383 IT *outputOffsetArray,
384
385 // Input
386 const CT *inputPoints,
387 const IT *inputConnectivityArray,
388 const size_t nInputPoints,
389 const size_t nInputCells,
390 const size_t nSubdivisions,
391 const DT *inputPointSizes,
392 const CT sizeScale,
393 const size_t sizeAxis) const {
394
396 this->printMsg({{"Mode", "Linear Polygon"},
397 {"#Nodes", std::to_string(nInputPoints)},
398 {"#Edges", std::to_string(nInputCells)},
399 {"#Subdivisions", std::to_string(nSubdivisions)}});
401
402 size_t subdivisionOffset = nInputPoints * 2;
403 size_t nSubdivisionPoints = nSubdivisions * 2;
404 size_t outputPointsSubdivisonOffset = nSubdivisionPoints * 3;
405
406 // ---------------------------------------------------------------------------
407 // Compute Output Point Locations
408 // ---------------------------------------------------------------------------
409 // outputPoints: [
410 // corners: 2*inputPoints in inputPoints order;
411 // SubPoints: 2 per subdivison in cell order]
412 // ]
413 {
414 Timer t;
415 this->printMsg("Computing mesh vertices", 0, debug::LineMode::REPLACE);
416
417// -----------------------------------------------------------------------------
418// Compute Corners
419// -----------------------------------------------------------------------------
420#ifdef TTK_ENABLE_OPENMP
421#pragma omp parallel for num_threads(threadNumber_)
422#endif
423 for(size_t i = 0; i < nInputPoints; i++) {
424 const CT *coords = &inputPoints[i * 3];
425
426 size_t q = i * 6;
427 outputPoints[q] = coords[0];
428 outputPoints[q + 1] = coords[1];
429 outputPoints[q + 2] = coords[2];
430
431 outputPoints[q + 3] = coords[0];
432 outputPoints[q + 4] = coords[1];
433 outputPoints[q + 5] = coords[2];
434
435 const CT &size = ((CT)inputPointSizes[i]) * sizeScale;
436
437 outputPoints[q + sizeAxis] += size / 2;
438 outputPoints[q + 3 + sizeAxis] -= size / 2;
439 }
440
441 // -------------------------------------------------------------------------
442 // Compute SubPoints
443 // -------------------------------------------------------------------------
444 size_t q = subdivisionOffset * 3;
445 float nSubdivisionsP1 = nSubdivisions + 1;
446
447 auto computeBezierPoint = [&](const size_t &no0, const size_t &no1,
448 const size_t &subdOffset,
449 const float lambda) {
450 float lambdaI = 1 - lambda;
451
452 float lambda_2 = lambda * lambda;
453 float lambda_3 = lambda * lambda_2;
454
455 float lambdaI_2 = lambdaI * lambdaI;
456 float lambdaI_3 = lambdaI * lambdaI_2;
457
458 float m0[3];
459 float m1[3];
460 for(size_t i = 0; i < 3; i++) {
461 m0[i] = (outputPoints[no0 + i] + outputPoints[no1 + i]) / 2;
462 m1[i] = m0[i];
463 }
464 m0[sizeAxis] = outputPoints[no0 + sizeAxis];
465 m1[sizeAxis] = outputPoints[no1 + sizeAxis];
466
467 for(size_t i = 0; i < 3; i++)
468 outputPoints[subdOffset + i]
469 = lambdaI_3 * outputPoints[no0 + i] + 3 * lambdaI_2 * lambda * m0[i]
470 + 3 * lambdaI * lambda_2 * m1[i] + lambda_3 * outputPoints[no1 + i];
471 };
472
473#ifdef TTK_ENABLE_OPENMP
474#pragma omp parallel for num_threads(threadNumber_)
475#endif
476 for(size_t i = 0; i < nInputCells; i++) {
477 size_t temp = i * 2;
478 IT n0 = inputConnectivityArray[temp++];
479 IT n1 = inputConnectivityArray[temp];
480
481 IT no0 = n0 * 6;
482 IT no1 = n1 * 6;
483
484 size_t q2 = q + i * outputPointsSubdivisonOffset;
485 for(size_t j = 1; j <= nSubdivisions; j++) {
486 computeBezierPoint(no0, no1, q2, j / nSubdivisionsP1);
487 computeBezierPoint(no0 + 3, no1 + 3, q2 + 3, j / nSubdivisionsP1);
488
489 q2 += 6;
490 }
491 }
492
493 this->printMsg(
494 "Computing mesh vertices", 1, t.getElapsedTime(), this->threadNumber_);
495 }
496
497 // ---------------------------------------------------------------------------
498 // Compute Output Cells
499 // ---------------------------------------------------------------------------
500 {
501 Timer t;
502 this->printMsg("Computing mesh cells", 0, debug::LineMode::REPLACE);
503
504 size_t cellSize = this->computeOutputCellSize(nSubdivisions);
505
506#ifdef TTK_ENABLE_OPENMP
507#pragma omp parallel for num_threads(threadNumber_)
508#endif
509 for(size_t i = 0; i < nInputCells; i++) {
510 size_t q = i * 2;
511 IT in0 = inputConnectivityArray[q++] * 2;
512 IT in1 = inputConnectivityArray[q] * 2;
513
514 IT c0 = in0;
515 IT c1 = in0 + 1;
516 IT c2 = in1 + 1;
517 IT c3 = in1;
518
519 size_t q2 = cellSize * i;
520
521 outputConnectivityArray[q2++] = c0;
522 outputConnectivityArray[q2++] = c1;
523
524 IT temp = subdivisionOffset + i * nSubdivisionPoints;
525
526 for(size_t j = 0; j < nSubdivisions; j++)
527 outputConnectivityArray[q2++] = temp + j * 2 + 1;
528
529 outputConnectivityArray[q2++] = c2;
530 outputConnectivityArray[q2++] = c3;
531
532 for(int j = nSubdivisions - 1; j >= 0; j--)
533 outputConnectivityArray[q2++] = temp + j * 2;
534 }
535
536 for(size_t i = 0; i <= nInputCells; i++)
537 outputOffsetArray[i] = i * cellSize;
538
539 this->printMsg(
540 "Computing mesh cells", 1, t.getElapsedTime(), this->threadNumber_);
541 }
542
543 return 1;
544}
545
546// =============================================================================
547// Map input point data to output point data
548// =============================================================================
549template <typename DT, typename IT>
551 DT *outputPointData,
552
553 const size_t &nInputPoints,
554 const size_t &nInputCells,
555 const IT *inputConnectivityArray,
556 const DT *inputPointData,
557 const bool &useQuadraticCells,
558 const size_t &nSubdivisions) const {
559
560 if(useQuadraticCells) {
561#ifdef TTK_ENABLE_OPENMP
562#pragma omp parallel for num_threads(threadNumber_)
563#endif
564 for(size_t i = 0; i < nInputPoints; i++) {
565 size_t q = i * 3;
566 // a, a0, a1
567 outputPointData[q] = inputPointData[i];
568 outputPointData[q + 1] = inputPointData[i];
569 outputPointData[q + 2] = inputPointData[i];
570 }
571
572 size_t edgePointOffset = nInputPoints * 3;
573
574// Iterate over input cells and assign point data of intermediate points
575#ifdef TTK_ENABLE_OPENMP
576#pragma omp parallel for num_threads(threadNumber_)
577#endif
578 for(size_t i = 0; i < nInputCells; i++) {
579 size_t temp = i * 2;
580 size_t aInputIndex = (size_t)inputConnectivityArray[temp++];
581 size_t bInputIndex = (size_t)inputConnectivityArray[temp];
582
583 size_t a = aInputIndex * 3;
584 size_t b = bInputIndex * 3;
585
586 size_t offset = edgePointOffset + i * 7;
587 size_t m0 = offset;
588 size_t m1 = offset + 1;
589 size_t a0m0 = offset + 2;
590 size_t m0b0 = offset + 3;
591 size_t b1m1 = offset + 4;
592 size_t m1a1 = offset + 5;
593 size_t c = offset + 6;
594
595 outputPointData[c] = (DT)((outputPointData[a] + outputPointData[b]) / 2);
596 outputPointData[m0] = outputPointData[c];
597 outputPointData[m1] = outputPointData[c];
598
599 outputPointData[a0m0]
600 = (DT)((outputPointData[a] + outputPointData[c]) / 2);
601 outputPointData[m1a1] = outputPointData[a0m0];
602
603 outputPointData[m0b0]
604 = (DT)((outputPointData[c] + outputPointData[b]) / 2);
605 outputPointData[b1m1] = outputPointData[m0b0];
606 }
607 } else {
608
609// Corners
610#ifdef TTK_ENABLE_OPENMP
611#pragma omp parallel for num_threads(threadNumber_)
612#endif
613 for(size_t i = 0; i < nInputPoints; i++) {
614 size_t offset = i * 2;
615 auto &v = inputPointData[i];
616 outputPointData[offset] = v;
617 outputPointData[offset + 1] = v;
618 }
619
620 // Intermediate Points
621 size_t subdivisionOffset = nInputPoints * 2;
622 size_t nSubdivisionPoints = nSubdivisions * 2;
623
624#ifdef TTK_ENABLE_OPENMP
625#pragma omp parallel for num_threads(threadNumber_)
626#endif
627 for(size_t i = 0; i < nInputCells; i++) {
628 size_t q = i * 2;
629 IT c0 = inputConnectivityArray[q];
630 DT c0V = inputPointData[c0];
631
632 size_t temp = subdivisionOffset + i * nSubdivisionPoints;
633 for(size_t j = 0; j < nSubdivisions; j++) {
634 size_t q2 = temp + j * 2;
635 outputPointData[q2] = c0V;
636 outputPointData[q2 + 1] = c0V;
637 }
638 }
639 }
640
641 return 1;
642}
643
644// =============================================================================
645// Map input cell data to output cell data
646// =============================================================================
647template <typename DT>
649 DT *outputCellData,
650 const size_t &nInputCells,
651 const DT *inputCellData,
652 const bool &useQuadraticCells) const {
653
654 if(useQuadraticCells) {
655#ifdef TTK_ENABLE_OPENMP
656#pragma omp parallel for num_threads(threadNumber_)
657#endif
658 for(size_t i = 0; i < nInputCells; i++) {
659 size_t offset = i * 2;
660 outputCellData[offset] = inputCellData[i];
661 outputCellData[offset + 1] = inputCellData[i];
662 }
663 } else {
664#ifdef TTK_ENABLE_OPENMP
665#pragma omp parallel for num_threads(threadNumber_)
666#endif
667 for(size_t i = 0; i < nInputCells; i++) {
668 outputCellData[i] = inputCellData[i];
669 }
670 }
671
672 return 1;
673}
Minimalist debugging class.
Definition: Debug.h:88
void setDebugMsgPrefix(const std::string &prefix)
Definition: Debug.h:364
int printMsg(const std::string &msg, const debug::Priority &priority=debug::Priority::INFO, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cout) const
Definition: Debug.h:118
TTK meshGraph processing package.
Definition: MeshGraph.h:46
int mapInputPointDataToOutputPointData(DT *outputPointData, const size_t &nInputPoints, const size_t &nInputCells, const IT *inputConnectivityArray, const DT *inputPointData, const bool &useQuadraticCells, const size_t &nSubdivisions=0) const
Definition: MeshGraph.h:550
size_t computeOutputCellSize(const size_t &nSubdivisions) const
Definition: MeshGraph.h:80
int mapInputCellDataToOutputCellData(DT *outputCellData, const size_t &nInputCells, const DT *inputCellData, const bool &useQuadraticCells) const
Definition: MeshGraph.h:648
size_t computeOutputConnectivityArraySize(const size_t &nInputCells, const bool &useQuadraticCells, const size_t &nSubdivisions=0) const
Definition: MeshGraph.h:85
~MeshGraph() override=default
int execute(CT *outputPoints, IT *outputConnectivityArray, IT *outputOffsetArray, const CT *inputPoints, const IT *inputConnectivityArray, const size_t &nInputPoints, const size_t &nInputCells, const DT *inputPointSizes, const CT &sizeScale, const size_t &sizeAxis) const
Definition: MeshGraph.h:157
int execute2(CT *outputPoints, IT *outputConnectivityArray, IT *outputOffsetArray, const CT *inputPoints, const IT *inputConnectivityArray, const size_t nInputPoints, const size_t nInputCells, const size_t nSubdivisions, const DT *inputPointSizes, const CT sizeScale, const size_t sizeAxis) const
Definition: MeshGraph.h:379
size_t computeNumberOfOutputPoints(const size_t &nInputPoints, const size_t &nInputCells, const bool &useQuadraticCells, const size_t &nSubdivisions=0) const
Definition: MeshGraph.h:54
size_t computeNumberOfOutputCells(const size_t &nInputCells, const bool &useQuadraticCells) const
Definition: MeshGraph.h:71
double getElapsedTime()
Definition: Timer.h:15
The Topology ToolKit.
printMsg(debug::output::GREEN+"                           "+debug::output::ENDCOLOR+debug::output::GREEN+"▒"+debug::output::ENDCOLOR+debug::output::GREEN+"▒▒▒▒▒▒▒▒▒▒▒▒▒░"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)