DGtal 2.1.1
Loading...
Searching...
No Matches
Display3D.ih
1/**
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 */
15
16/*
17 * @file Display3D.ih
18 * @author Bastien Doignies <bastien.doignies@liris.cnrs.fr>
19 *
20 * @date 2025/05/11
21 *
22 * Implementation file for 3D Display
23 *
24 * This file is part of the DGtal library.
25 */
26
27namespace DGtal {
28 namespace drawutils {
29 template<size_t I>
30 std::vector<std::array<size_t, I>> makeIndices(size_t N) {
31 std::vector<std::array<size_t, I>> indices(N);
32
33 for (size_t i = 0; i < N; ++i) {
34 for (size_t j = 0; j < I; ++j) {
35 indices[i][j] = j + i * I;
36 }
37 }
38 return indices;
39 }
40
41 template<typename T>
42 std::array<T, 8> getCubeVertices(T center, double size) {
43 return {
44 center + 0.5 * size * T(-1, -1, -1),
45 center + 0.5 * size * T( 1, -1, -1),
46 center + 0.5 * size * T( 1, 1, -1),
47 center + 0.5 * size * T(-1, 1, -1),
48 center + 0.5 * size * T(-1, -1, 1),
49 center + 0.5 * size * T( 1, -1, 1),
50 center + 0.5 * size * T( 1, 1, 1),
51 center + 0.5 * size * T(-1, 1, 1)
52 };
53 }
54
55 template<typename T, typename U>
56 void insertCubeVertices(U& dest, T center, double scale) {
57 auto vertices = getCubeVertices(center, scale);
58 dest.insert(dest.end(), vertices.begin(), vertices.end());
59 }
60
61 template <typename T>
62 std::array<T, 4> getAASquareVertices(T center, int orientation, double size) {
63 switch(orientation) {
64 case 0: // Normal in x direction
65 return {
66 center + 0.5 * size * T(0, -1, -1),
67 center + 0.5 * size * T(0, -1, 1),
68 center + 0.5 * size * T(0, 1, 1),
69 center + 0.5 * size * T(0, 1, -1)
70 };
71 case 1: // Normal in y direction
72 return {
73 center + 0.5 * size * T(-1, 0, -1),
74 center + 0.5 * size * T(-1, 0, 1),
75 center + 0.5 * size * T( 1, 0, 1),
76 center + 0.5 * size * T( 1, 0, -1)
77 };
78 case 2: // Normal in z directionDisplay3
79 default:
80 return {
81 center + 0.5 * size * T(-1, -1, 0),
82 center + 0.5 * size * T(-1, 1, 0),
83 center + 0.5 * size * T( 1, 1, 0),
84 center + 0.5 * size * T( 1, -1, 0)
85 };
86 }
87 }
88
89 template<typename U, typename T>
90 void insertAASquare(U& dest, T center, int orientation, double size) {
91 auto vertices = getAASquareVertices(center, orientation, size);
92 dest.insert(dest.end(), vertices.begin(), vertices.end());
93 }
94
95 template<typename T>
96 std::array<T, 8> getPrism(
97 T center, int orientation,
98 double size1, double size2, double shift1, double shift2
99 ) {
100 T dir(0, 0, 0); dir[orientation] = 1;
101
102 std::array<T, 8> vertices;
103 auto fQuad = getAASquareVertices(center + shift1 * dir, orientation, size1);
104 auto sQuad = getAASquareVertices(center + shift2 * dir, orientation, size2);
105
106 std::copy(fQuad.begin(), fQuad.end(), vertices.begin());
107 std::copy(sQuad.begin(), sQuad.end(), vertices.begin() + fQuad.size());
108 return vertices;
109 }
110
111 template<typename T, typename U>
112 void insertPrism(U& dest, T center, int orientation,
113 double scale1, double scale2, double shift1, double shift2) {
114 auto vertices = getPrism(center, orientation, scale1, scale2, shift1, shift2);
115 dest.insert(dest.end(), vertices.begin(), vertices.end());
116 }
117 } // drawutils
118
119 ///////////////////////////////////////////////////////////////////////////
120 ///////////////////////////// GENERAL COMMANDS ////////////////////////////
121
122 template <typename Space, typename KSpace>
123 void Display3D<Space, KSpace>::clear() {
124 endCurrentGroup();
125
126 myToRender.clear();
127 planes.clear();
128 data.clear();
129
130 clearView();
131 }
132
133 template <typename Space, typename KSpace>
134 void Display3D<Space, KSpace>::setCallback(typename Display3D<Space, KSpace>::Callback* callback) {
135 this->myCallback = callback;
136 if (this->myCallback) {
137 this->myCallback->viewer = this;
138 this->myCallback->OnAttach(this);
139 }
140 }
141
142 ///////////////////////////// GENERAL COMMANDS ////////////////////////////
143 ///////////////////////////////////////////////////////////////////////////
144
145 ///////////////////////////////////////////////////////////////////////////
146 ///////////////////////////// GROUP MANAGEMENT ////////////////////////////
147
148 template <typename Space, typename KSpace>
149 std::string Display3D<Space, KSpace>::newList(const std::string& name, size_t eSize) {
150 static const std::string token = "{i}";
151
152 auto it = data.find(name);
153 std::string newName = name;
154
155 size_t idx = name.find(token);
156 if (it != data.end() || idx != std::string::npos) {
157 std::string prefix = name;
158
159 if (idx == std::string::npos) {
160 idx = prefix.size() + 1;
161 prefix = prefix + "_" + token;
162 }
163
164 size_t i = 1;
165 do {
166 std::string tmpPrefix = prefix;
167 newName = tmpPrefix.replace(idx, token.size(), std::to_string(i));
168
169 i += 1;
170 } while (data.find(newName) != data.end());
171 }
172
173 // Insert a new empty container
174 DisplayData<RealPoint> newData;
175
176 // Set properties
177 newData.style = currentStyle;
178 newData.elementSize = eSize;
179
180 myCurrentData = &data.emplace(newName, std::move(newData)).first->second;
181 myCurrentName = newName;
182 // Add to render queue
183 myToRender.push_back(myCurrentName);
184 // Return computed name
185 return newName;
186 }
187
188 template <typename Space, typename KSpace>
189 bool Display3D<Space, KSpace>::setCurrentList(const std::string& name) {
190 auto it = data.find(name);
191 if (it == data.end())
192 return false;
193
194 myCurrentData = &it->second;
195 myCurrentName = name;
196 return true;
197 }
198
199 template <typename Space, typename KSpace>
200 bool Display3D<Space, KSpace>::canCreateNewList(size_t elementSize) const {
201 if (!myCurrentData) return true;
202 if (myCurrentData->elementSize != elementSize) return true;
203 return !allowReuseList;
204 }
205
206 template <typename Space, typename KSpace>
207 std::string Display3D<Space, KSpace>::createOrReuseList(const std::string& name, size_t elementSize) {
208 if (canCreateNewList(elementSize)) {
209 return newList(name, elementSize);
210 }
211 return myCurrentName;
212 }
213
214 template <typename Space, typename KSpace>
215 void Display3D<Space, KSpace>::endCurrentGroup() {
216 myCurrentData = nullptr;
217 myCurrentName = "";
218 }
219
220 ///////////////////////////// GROUP MANAGEMENT /////////////////////////////
221 ////////////////////////////////////////////////////////////////////////////
222
223 ////////////////////////////////////////////////////////////////////////////
224 ////////////////////////////// DRAW MODIFIERS //////////////////////////////
225
226 template <typename Space, typename KSpace>
227 std::string Display3D<Space, KSpace>::draw(const DGtal::Color& color, const std::string& name) {
228 ((void) name);
229 drawColor(color);
230 return "";
231 }
232
233 template <typename Space, typename KSpace>
234 void Display3D<Space, KSpace>::drawColor(const DGtal::Color& color) {
235 currentStyle.color = color;
236 currentStyle.useDefaultColors = false;
237 }
238
239 template <typename Space, typename KSpace>
240 void Display3D<Space, KSpace>::setDefaultColors() {
241 currentStyle.useDefaultColors = true;
242 }
243
244 template <typename Space, typename KSpace>
245 void Display3D<Space, KSpace>::drawAdjacencies(bool toggle) {
246 if (toggle) currentStyle.mode |= DisplayStyle::ADJACENCIES;
247 else currentStyle.mode &= ~DisplayStyle::ADJACENCIES;
248 }
249
250 template <typename Space, typename KSpace>
251 void Display3D<Space, KSpace>::drawAsSimplified(bool toggle) {
252 if (toggle) currentStyle.mode |= DisplayStyle::SIMPLIFIED;
253 else currentStyle.mode &= ~DisplayStyle::SIMPLIFIED;
254 }
255
256 template <typename Space, typename KSpace>
257 void Display3D<Space, KSpace>::drawAsGrid(bool toggle) {
258 if (toggle) currentStyle.mode |= DisplayStyle::GRID;
259 else currentStyle.mode &= ~DisplayStyle::GRID;
260 }
261
262 template <typename Space, typename KSpace>
263 void Display3D<Space, KSpace>::defaultStyle() {
264 currentStyle.mode = DisplayStyle::DEFAULT;
265 }
266
267 template <typename Space, typename KSpace>
268 void Display3D<Space, KSpace>::drawAsPaving() {
269 currentStyle.mode &= ~DisplayStyle::BALLS;
270 currentStyle.mode |= DisplayStyle::PAVING;
271 }
272
273 template <typename Space, typename KSpace>
274 void Display3D<Space, KSpace>::drawAsBalls() {
275 currentStyle.mode &= ~DisplayStyle::PAVING;
276 currentStyle.mode |= DisplayStyle::BALLS;
277 }
278
279 ////////////////////////////// DRAW MODIFIERS //////////////////////////////
280 ////////////////////////////////////////////////////////////////////////////
281
282 ////////////////////////////////////////////////////////////////////////////
283 ////////////////////////////// DRAW COMMANDS ///////////////////////////////
284
285 template <typename Space, typename KSpace>
286 template<typename Obj>
287 Display3D<Space, KSpace>& Display3D<Space, KSpace>::operator<<(const Obj& obj) {
288 draw(obj);
289 return *this;
290 }
291
292 template <typename Space, typename KSpace>
293 std::string Display3D<Space, KSpace>::draw(const Point& p, const std::string& uname) {
294 return draw(myEmbedder.embed(p), uname);
295 }
296
297 template <typename Space, typename KSpace>
298 std::string Display3D<Space, KSpace>::draw(const RealPoint& rp, const std::string& uname) {
299 std::string name = createOrReuseList(uname, 1);
300 myCurrentData->vertices.push_back(rp);
301 return name;
302 }
303
304 template <typename Space, typename KSpace>
305 std::string Display3D<Space, KSpace>::draw(const std::pair<RealPoint, RealPoint>& arrow, const std::string& uname) {
306 // Warning, this function draw arrows, not lines !
307 std::string name = createOrReuseBallList(uname);
308 myCurrentData->vertices.push_back(arrow.first);
309 myCurrentData->vectorQuantities[QuantityScale::VERTEX]["value"].push_back(arrow.second);
310 return name;
311 }
312
313 template <typename Space, typename KSpace>
314 template<typename T>
315 std::string Display3D<Space, KSpace>::draw(const std::vector<T>& range, const std::string& uname) {
316 return drawGenericRange(range, uname);
317 }
318
319 template <typename Space, typename KSpace>
320 template<typename A, typename B, typename C>
321 std::string Display3D<Space, KSpace>::draw(const ConstRangeAdapter<A, B, C> range, const std::string& uname) {
322 return drawGenericRange(range, uname);
323 }
324
325 template <typename Space, typename KSpace>
326 template<typename A, typename B, typename C>
327 std::string Display3D<Space, KSpace>::draw(const ConstIteratorAdapter<A, B, C>& adapter, const std::string& uname) {
328 if (uname.empty()) {
329 // Use default value of draw
330 return draw(*adapter);
331 }
332 return draw(*adapter, uname);
333 }
334
335 template <typename Space, typename KSpace>
336 std::string Display3D<Space, KSpace>::draw(const GridCurve<KSpace>& curve, const std::string& uname) {
337 return draw(curve.getSCellsRange(), uname);
338 }
339
340 template <typename Space, typename KSpace>
341 std::string Display3D<Space, KSpace>::draw(const typename GridCurve<KSpace>::MidPointsRange& range, const std::string& uname) {
342 return drawGenericRange(range, uname);
343 }
344
345 template <typename Space, typename KSpace>
346 std::string Display3D<Space, KSpace>::draw(const typename GridCurve<KSpace>::ArrowsRange& range, const std::string& uname) {
347 return drawGenericRange(range, uname);
348 }
349
350 template <typename Space, typename KSpace>
351 template <DGtal::Dimension emb, DGtal::Dimension amb, typename Algebra, typename Int>
352 std::string Display3D<Space, KSpace>::draw(const DiscreteExteriorCalculus<emb, amb, Algebra, Int>& calc, const std::string& uname) {
353 bool save = allowReuseList;
354 allowReuseList = true;
355
356 // Maintains multiple names it contains any dimension type
357 std::string list0 = newBallList(uname + "_0d");
358 std::string list1 = newLineList(uname + "_1d");
359 std::string list2_1 = newQuadList(uname + "_2d");
360 std::string list2_2 = newVolumetricList(uname + "_2d_signed");
361 std::string list3 = newCubeList(uname + "_3d");
362
363 const std::string* const lists[4] = { &list0, &list1, &list2_2, &list3 };
364
365 for (auto it = calc.begin(); it != calc.end(); ++it) {
366 const auto& cell = it->first;
367 const bool& flip = it->second.flipped;
368
369 const SCell displayed = calc.myKSpace.signs(cell, flip ? KSpace::NEG : KSpace::POS);
370
371 const bool xodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(displayed.preCell().coordinates[0]) & 1);
372 const bool yodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(displayed.preCell().coordinates[1]) & 1);
373 const bool zodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(displayed.preCell().coordinates[2]) & 1);
374
375 const int dim = xodd + yodd + zodd;
376
377 setCurrentList(*lists[dim]);
378 if ((dim == 2) && (currentStyle.mode & DisplayStyle::SIMPLIFIED))
379 setCurrentList(list2_1);
380
381 const auto rp = mySCellEmbedder.embed(displayed);
382 drawKCell(*lists[dim], rp, xodd, yodd, zodd, true, displayed.preCell().positive);
383 }
384
385 allowReuseList = save;
386 endCurrentGroup();
387 return list2_2; // Return one of the name
388 }
389
390 template <typename Space, typename KSpace>
391 template <typename Calculus, DGtal::Order order, DGtal::Duality duality>
392 std::string Display3D<Space, KSpace>::draw(const KForm<Calculus, order, duality>& kform, const std::string& uname) {
393 bool save = allowReuseList;
394 allowReuseList = true;
395
396 // using CSCell = Calculus::SCell;
397 using Scalar = typename Calculus::Scalar;
398
399 std::string list0 = newBallList(uname + "_0d");
400 std::string list1 = newLineList(uname + "_1d");
401 std::string list2_1 = newQuadList(uname + "_2d");
402 std::string list2_2 = newVolumetricList(uname + "_2d_signed");
403 std::string list3 = newCubeList(uname + "_3d");
404
405 const std::string* lists[4] = { &list0, &list1, &list2_2, &list3 };
406
407 for (typename Calculus::Index i = 0; i < kform.length(); ++i) {
408 const SCell cell = kform.getSCell(i);
409 const Scalar val = kform.myContainer(i);
410
411 const bool xodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[0]) & 1);
412 const bool zodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[2]) & 1);
413 const bool yodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[1]) & 1);
414
415 const int dim = xodd + yodd + zodd;
416 if (!std::isfinite(val)) continue;
417
418 setCurrentList(*lists[dim]);
419 if ((dim == 2) && (currentStyle.mode & DisplayStyle::SIMPLIFIED))
420 setCurrentList(list2_1);
421
422 // Automatically decide the scalle of the quantity
423 draw(WithQuantity(cell, "value", val));
424 }
425
426 allowReuseList = save;
427 endCurrentGroup();
428 return list2_2; // Return one of the name
429 }
430
431
432 template <typename Space, typename KSpace>
433 template <typename Calculus, DGtal::Duality dual>
434 std::string Display3D<Space, KSpace>::draw(const VectorField<Calculus, dual>& field, const std::string& uname) {
435 std::string name = newBallList(uname);
436
437 myCurrentData->style.width = 0.; // Make ball diseapear
438 myCurrentData->vertices.reserve(field.length());
439 myCurrentData->vectorQuantities[QuantityScale::VERTEX]["value"].reserve(field.length());
440
441 for (typename Calculus::Index i = 0; i < field.length(); ++i) {
442 const auto& origin = mySCellEmbedder.embed(field.getSCell(i));
443 const auto vector = field.getVector(i);
444
445 if (std::isfinite(vector[0]) && std::isfinite(vector[1]) && std::isfinite(vector[2])) {
446 myCurrentData->vertices.push_back(origin);
447 myCurrentData->vectorQuantities[QuantityScale::VERTEX]["value"].push_back(vector);
448 }
449 }
450
451 endCurrentGroup();
452 return name;
453 }
454
455 template <typename Space, typename KSpace>
456 std::string Display3D<Space, KSpace>::draw(const KCell& cell, const std::string& name) {
457 const RealPoint rp = myCellEmbedder.embed(cell);
458
459 const bool xodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[0]) & 1);
460 const bool yodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[1]) & 1);
461 const bool zodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[2]) & 1);
462
463 return drawKCell(name, rp, xodd, yodd, zodd, false, false);
464 }
465
466 template <typename Space, typename KSpace>
467 std::string Display3D<Space, KSpace>::draw(const SCell& cell, const std::string& name) {
468 const RealPoint rp = mySCellEmbedder.embed(cell);
469
470 const bool xodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[0]) & 1);
471 const bool yodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[1]) & 1);
472 const bool zodd = (NumberTraits<typename KSpace::Integer>::castToInt64_t(cell.preCell().coordinates[2]) & 1);
473
474 return drawKCell(name, rp, xodd, yodd, zodd, true, cell.preCell().positive);
475 }
476
477 template <typename Space, typename KSpace>
478 std::string Display3D<Space, KSpace>::draw(const HyperRectDomain<Space>& domain, const std::string& uname) {
479
480 std::string name;
481
482 if (currentStyle.mode & DisplayStyle::GRID) {
483 name = newLineList(uname);
484
485 // Faces YX
486 for (auto z = domain.myLowerBound[2]; z <= domain.myUpperBound[2]; z++) {
487 for (auto x = domain.myLowerBound[0]; x <= domain.myUpperBound[0]; x++) {
488 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(x, domain.myLowerBound[1], z) );
489 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(x, domain.myUpperBound[1], z) );
490 myCurrentData->vertices.push_back(rp1);
491 myCurrentData->vertices.push_back(rp2);
492 }
493
494 for (auto y = domain.myLowerBound[1]; y <= domain.myUpperBound[1]; y++) {
495 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(domain.myLowerBound[0], y, z) );
496 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(domain.myUpperBound[0], y, z) );
497 myCurrentData->vertices.push_back(rp1);
498 myCurrentData->vertices.push_back(rp2);
499 }
500 }
501
502 // Faces XZ
503 for (auto y = domain.myLowerBound[1]; y <= domain.myUpperBound[1]; y++) {
504 for (auto x = domain.myLowerBound[0]; x <= domain.myUpperBound[0]; x++) {
505 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(x, y, domain.myLowerBound[2]) );
506 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(x, y, domain.myLowerBound[2]) );
507
508 myCurrentData->vertices.push_back(rp1);
509 myCurrentData->vertices.push_back(rp2);
510 }
511 for (auto z = domain.myLowerBound[2]; z <= domain.myUpperBound[2]; z++) {
512 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(domain.myLowerBound[0], y, z) );
513 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(domain.myUpperBound[0], y, z) );
514
515 myCurrentData->vertices.push_back(rp1);
516 myCurrentData->vertices.push_back(rp2);
517 }
518 }
519
520 // Faces YZ
521 for (auto x = domain.myLowerBound[0]; x <= domain.myUpperBound[0]; x++) {
522 for (auto y = domain.myLowerBound[1]; y <= domain.myUpperBound[1]; y++) {
523 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(x, y, domain.myLowerBound[2]) );
524 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(x, y, domain.myUpperBound[2]) );
525
526 myCurrentData->vertices.push_back(rp1);
527 myCurrentData->vertices.push_back(rp2);
528 }
529 for (auto z = domain.myLowerBound[2]; z <= domain.myUpperBound[2]; z++) {
530 DGtal::Z3i::RealPoint rp1 = myEmbedder.embed( DGtal::Z3i::Point(x, domain.myLowerBound[1], z) );
531 DGtal::Z3i::RealPoint rp2 = myEmbedder.embed( DGtal::Z3i::Point(x, domain.myLowerBound[1], z) );
532
533 myCurrentData->vertices.push_back(rp1);
534 myCurrentData->vertices.push_back(rp2);
535 }
536 }
537 } else if (currentStyle.mode == DisplayStyle::DEFAULT) {
538 name = newQuadList(uname);
539
540 const Point lb = domain.lowerBound();
541 const Point ub = domain.upperBound() + Point(1, 1, 1);
542
543 const RealPoint a = RealPoint(lb[0], lb[1], lb[2]);
544 const RealPoint b = RealPoint(ub[0], lb[1], lb[2]);
545 const RealPoint c = RealPoint(ub[0], ub[1], lb[2]);
546 const RealPoint d = RealPoint(lb[0], ub[1], lb[2]);
547
548 const RealPoint e = RealPoint(lb[0], lb[1], ub[2]);
549 const RealPoint f = RealPoint(ub[0], lb[1], ub[2]);
550 const RealPoint g = RealPoint(ub[0], ub[1], ub[2]);
551 const RealPoint h = RealPoint(lb[0], ub[1], ub[2]);
552
553 myCurrentData->vertices = {
554 a, b, c, d, b, c, g, f,
555 d, c, g, h, a, d, h, e,
556 a, b, f, e, e, f, g, h
557 };
558
559 data[name].style.useDefaultColors=false;
560 data[name].style.color.alpha(64);
561 } else {
562 name = drawGenericRange(domain, uname);
563 if (!(currentStyle.mode & DisplayStyle::BALLS))
564 {
565 data[name].style.useDefaultColors=false;
566 data[name].style.color.alpha(64);
567 }
568 }
569
570 endCurrentGroup();
571 return name;
572 }
573
574 template <typename Space, typename KSpace>
575 template <typename Vec>
576 std::string Display3D<Space, KSpace>::drawPolygon(const std::vector<Vec>& vertices, const std::string& uname) {
577 std::string name = createOrReusePolygonList(uname);
578
579 std::vector<unsigned> indices;
580 indices.reserve(vertices.size());
581
582 size_t count = myCurrentData->vertices.size();
583 for (const auto& vert : vertices) {
584 myCurrentData->vertices.push_back(vert);
585 indices.push_back(count++);
586 }
587 myCurrentData->indices.push_back(std::move(indices));
588 return name;
589 }
590
591 template <typename Space, typename KSpace>
592 std::string Display3D<Space, KSpace>::drawBall(const RealPoint& c, const std::string& uname) {
593 std::string name = createOrReuseBallList(uname);
594 myCurrentData->vertices.push_back(c);
595 return name;
596 }
597
598 template <typename Space, typename KSpace>
599 std::string Display3D<Space, KSpace>::drawLine(const RealPoint& a, const RealPoint& b, const std::string& uname) {
600 std::string name = createOrReuseLineList(uname);
601
602 myCurrentData->vertices.push_back(a);
603 myCurrentData->vertices.push_back(b);
604 return name;
605 }
606
607 template <typename Space, typename KSpace>
608 std::string Display3D<Space, KSpace>::drawQuad(const RealPoint& a, const RealPoint& b, const RealPoint& c, const RealPoint& d, const std::string& uname) {
609 // Not a draw specialization as it would be confusing with a drawing call of
610 // an array of points, or other primitives
611 std::string name = createOrReuseQuadList(uname);
612
613 myCurrentData->vertices.push_back(a);
614 myCurrentData->vertices.push_back(b);
615 myCurrentData->vertices.push_back(c);
616 myCurrentData->vertices.push_back(d);
617
618 return name;
619 }
620
621 template <typename Space, typename KSpace>
622 template <typename Obj, typename Cont>
623 std::string Display3D<Space, KSpace>::draw(const DigitalSetByAssociativeContainer<Obj, Cont>& set, const std::string& name) {
624 return drawGenericRange(set, name);
625 }
626
627 template <typename Space, typename KSpace>
628 template <typename D, typename T>
629 std::string Display3D<Space, KSpace>::draw(const ImageContainerBySTLVector<D, T>& image, const std::string& name) {
630 return drawImage(name, image);
631 }
632
633 template <typename Space, typename KSpace>
634 template <typename TImageContainer,
635 typename TNewDomain,
636 typename TFunctorD,
637 typename TNewValue,
638 typename TFunctorV,
639 typename TFunctorVm1>
640 std::string Display3D<Space, KSpace>::draw(const ImageAdapter<TImageContainer, TNewDomain, TFunctorD, TNewValue, TFunctorV, TFunctorVm1>& adapter, const std::string& name) {
641 return drawImageAdaptDom(name, adapter);
642 }
643
644 template <typename Space, typename KSpace>
645 template <typename TImageContainer,
646 typename TNewDomain,
647 typename TFunctorD,
648 typename TNewValue,
649 typename TFunctorV>
650 std::string Display3D<Space, KSpace>::draw(const ConstImageAdapter<TImageContainer, TNewDomain, TFunctorD, TNewValue, TFunctorV>& adapter, const std::string& name) {
651 return drawImageAdaptDom(name, adapter);
652 }
653
654 template <typename Space, typename KSpace>
655 template <typename Adj, typename Set>
656 std::string Display3D<Space, KSpace>::draw(const DGtal::Object<Adj, Set>& obj, const std::string& uname) {
657 std::string name = drawGenericRange(obj, uname);
658
659 // Draw adjacency if needed
660 if (currentStyle.mode & DisplayStyle::ADJACENCIES) {
661 newLineList(name + "_adj");
662
663 for (auto it = obj.begin(); it != obj.end(); ++it) {
664 auto neig = obj.properNeighborhood(*it);
665
666 const RealPoint p = myEmbedder.embed(*it);
667 for (auto it2 = neig.begin(); it2 != neig.end(); ++it2) {
668 auto p2 = myEmbedder.embed(*it2);
669
670 myCurrentData->vertices.push_back(p);
671 myCurrentData->vertices.push_back(p2);
672 }
673 }
674 }
675 endCurrentGroup();
676 return name;
677 }
678
679 template <typename Space, typename KSpace>
680 template <typename T, typename Type>
681 std::string Display3D<Space, KSpace>::draw(const WithQuantity<T, Type>& props, const std::string& uname) {
682 std::string name;
683 if (uname.empty())
684 name = draw(props.object);
685 else
686 name = draw(props.object, uname);
687
688 QuantityScale sloc = props.scale;
689
690 // Tries to find an appropriate scale
691 if (sloc == QuantityScale::UNKNOWN)
692 sloc = data[name].getDefaultQuantityLevel(data[name].elementSize);
693
694 if (sloc == QuantityScale::UNKNOWN) {
695 trace.error() << "Unable to find suitable quantity scale, defaulting to vertex (for: '" << name << "')\n";
696 sloc = QuantityScale::VERTEX;
697 }
698
699 addQuantity(name, props.name, props.values, sloc);
700 return name;
701 }
702
703 template <typename Space, typename KSpace>
704 template <typename Type>
705 void Display3D<Space, KSpace>::addQuantity(const std::string& oName, const std::string& qName, const Type& value, QuantityScale scale) {
706 std::vector<Type> values = {value};
707 addQuantity(oName, qName, values, scale);
708 }
709
710 template <typename Space, typename KSpace>
711 template <typename Type>
712 void Display3D<Space, KSpace>::addQuantity(const std::string& oName, const std::string& qName, const std::vector<Type>& values, QuantityScale scale) {
713 if (scale == QuantityScale::UNKNOWN)
714 scale = data[oName].getDefaultQuantityLevel(data[oName].elementSize);
715
716 if (scale == QuantityScale::UNKNOWN) {
717 trace.error() << "Unable to find suitable quantity scale, defaulting to vertex (for: '" << oName << "')\n";
718 scale = QuantityScale::VERTEX;
719 }
720
721 if constexpr (std::is_scalar_v<Type>) {
722 auto& loc = data[oName].scalarQuantities[scale][qName];
723 loc.insert(loc.end(), values.begin(), values.end());
724 }
725 else if constexpr(std::is_same_v<RealPoint, Type>) {
726 auto& loc = data[oName].vectorQuantities[scale][qName];
727 loc.insert(loc.end(), values.begin(), values.end());
728 }
729 else if constexpr(std::is_same_v<Color , Type>) {
730 auto& loc = data[oName].colorQuantities[scale][qName];
731 loc.insert(loc.end(), values.begin(), values.end());
732 }
733 else {
734 trace.error() << "Unknown quantity type (for: '" << oName << "')\n";
735 }
736
737 }
738
739 template <typename Space, typename KSpace>
740 template <typename Pt>
741 std::string Display3D<Space, KSpace>::draw(const Mesh<Pt>& mesh, const std::string& uname) {
742 // A mesh may have quad faces, therefore we render it as a polygonal mesh
743 std::string name = newPolygonList(uname);
744
745 myCurrentData->vertices.reserve(mesh.nbVertex());
746
747 myCurrentData->indices.reserve(mesh.nbFaces());
748 myCurrentData->colorQuantities[QuantityScale::FACE]["color"].reserve(mesh.nbFaces());
749
750 // Can not insert directly vectors because of type mismatch
751 for (auto it = mesh.vertexBegin(); it != mesh.vertexEnd(); ++it) {
752 myCurrentData->vertices.push_back(*it);
753 }
754 for (size_t i = 0; i < mesh.nbFaces(); ++i) {
755 const auto& face = mesh.getFace(i);
756 std::vector<unsigned int> fIdx;
757 fIdx.reserve(face.size());
758 for (auto j : face) {
759 fIdx.push_back(j);
760 }
761 myCurrentData->indices.push_back(std::move(fIdx));
762 myCurrentData->colorQuantities[QuantityScale::FACE]["color"].push_back(mesh.getFaceColor(i));
763 }
764 endCurrentGroup();
765 return name;
766 }
767
768 template <typename Space, typename KSpace>
769 template <typename It, typename Int, int Con>
770 std::string Display3D<Space, KSpace>::draw(const StandardDSS6Computer<It, Int, Con>& computer, const std::string& uname) {
771 std::string name;
772 if (currentStyle.mode & DisplayStyle::BALLS) {
773 name = newBallList(uname);
774
775 for (auto it = computer.begin(); it != computer.end(); ++it) {
776 const auto rp = myEmbedder.embed(*it);
777 myCurrentData->vertices.push_back(rp);
778 }
779 } else { // Default mode
780 name = newLineList(uname);
781
782 auto it = computer.begin();
783 RealPoint a = myEmbedder.embed(*it);
784 RealPoint b = a;
785
786 for (++it; it != computer.end(); ++it) {
787 b = myEmbedder.embed(*it);
788 myCurrentData->vertices.push_back(a);
789 myCurrentData->vertices.push_back(b);
790
791 std::swap(a, b);
792 }
793 }
794
795 endCurrentGroup();
796 return name;
797 }
798
799 template <typename Space, typename KSpace>
800 template<typename It, typename Int, int Con>
801 std::string Display3D<Space, KSpace>::draw(const Naive3DDSSComputer<It, Int, Con>& computer, const std::string& uname) {
802 std::string name = drawGenericRange(computer, uname);
803 endCurrentGroup();
804
805 if (currentStyle.mode & DisplayStyle::ADJACENCIES) {
806 newLineList(uname + "_links");
807
808 auto it = computer.begin();
809 auto prev = myEmbedder(*it);
810 ++it;
811
812 for (; it != computer.end(); ++it) {
813 auto p = myEmbedder(*it);
814 myCurrentData->vertices.push_back(prev);
815 myCurrentData->vertices.push_back(p);
816 prev = p;
817 }
818
819 endCurrentGroup();
820 }
821 return name;
822 }
823
824 template <typename Space, typename KSpace>
825 std::string Display3D<Space, KSpace>::draw(const ClippingPlane& plane, const std::string& uname) {
826 ((void) uname);
827 planes.push_back(plane);
828 planes.back().style = currentStyle;
829 return "";
830 }
831
832 template <typename Space, typename KSpace>
833 template <typename T>
834 std::string Display3D<Space, KSpace>::draw(const SphericalAccumulator<T> accumulator, const std::string& uname) {
835 std::string name = newQuadList(uname);
836
837 typedef typename SphericalAccumulator<T>::Size Size;
838 typedef typename SphericalAccumulator<T>::RealVector Vec;
839
840 Size i, j;
841 Vec a, b, c, d;
842 for (auto it = accumulator.begin(); it != accumulator.end(); ++it) {
843 accumulator.binCoordinates(it, i, j);
844
845 if (accumulator.isValidBin(i, j)) {
846 accumulator.getBinGeometry(i, j, a, b, c, d);
847
848 myCurrentData->vertices.push_back(a);
849 myCurrentData->vertices.push_back(b);
850 myCurrentData->vertices.push_back(c);
851 myCurrentData->vertices.push_back(d);
852 myCurrentData->scalarQuantities[QuantityScale::FACE]["value"].push_back(accumulator.count(i, j));
853 }
854 }
855 return name;
856 }
857
858 // Generic functions
859 template <typename Space, typename KSpace>
860 template <typename Range>
861 std::string Display3D<Space, KSpace>::drawGenericRange(const Range& range, const std::string& uname) {
862 bool save = allowReuseList;
863
864 endCurrentGroup();
865 allowReuseList = true;
866
867 auto it = range.begin();
868
869 std::string name = myCurrentName;
870 if (uname.empty()) {
871 name = draw(*it);
872 } else {
873 name = draw(*it, uname);
874 }
875
876 myCurrentData->vertices.reserve(
877 myCurrentData->vertices.size() +
878 myCurrentData->elementSize * std::distance(range.begin(), range.end())
879 );
880
881 for (++it; it != range.end(); ++it) {
882 draw(*it, name);
883 }
884
885 endCurrentGroup();
886 allowReuseList = save;
887 return name;
888 }
889
890 template <typename Space, typename KSpace>
891 template <typename T>
892 std::string Display3D<Space, KSpace>::drawImage(const std::string& uname, const T& image) {
893 std::string name = newCubeList(uname);
894
895 size_t total = image.domain().size();
896
897 auto it = image.domain().begin();
898 auto itend = image.domain().end();
899 constexpr size_t dim = T::Domain::Space::dimension;
900
901 myCurrentData->vertices.reserve(8 * total);
902 myCurrentData->scalarQuantities[QuantityScale::CELL]["value"].reserve(total);
903 for(; it != itend; ++it) {
904 RealPoint rp;
905 if constexpr (dim == 3) {
906 rp = myEmbedder.embed(*it);
907 } else {
908 // We accept to draw these 2D image, do ask to parametrize to also the myEmbedder...
909 rp = myEmbedder.embed(Z3i::Point((*it)[0], (*it)[1], 0));
910 }
911 myCurrentData->scalarQuantities[QuantityScale::CELL]["value"].push_back(image(*it));
912 myCurrentData->vertices.push_back(rp);
913 }
914 return name;
915 }
916
917 template <typename Space, typename KSpace>
918 template <typename T>
919 std::string Display3D<Space, KSpace>::drawImageAdaptDom(const std::string& uname, const T& image) {
920 std::string name = newCubeList(uname);
921
922 size_t total = image.domain().size();
923
924 auto it = image.domain().begin();
925 auto itend = image.domain().end();
926 constexpr size_t dim = T::Domain::Space::dimension;
927
928 myCurrentData->vertices.reserve(8 * total);
929 myCurrentData->scalarQuantities[QuantityScale::CELL]["value"].reserve(total);
930 for(; it != itend; ++it) {
931 RealPoint rp;
932 if constexpr (dim == 3) {
933 rp = myEmbedder.embed(*it);
934 } else {
935 // We accept to draw these 2D image, do ask to parametrize to also the myEmbedder...
936 rp = myEmbedder.embed(image.sourceDomainPoint(Z2i::Point((*it)[0], (*it)[1])));
937 }
938 myCurrentData->scalarQuantities[QuantityScale::CELL]["value"].push_back(image(*it));
939 myCurrentData->vertices.push_back(rp);
940 }
941 return name;
942 }
943
944
945 template <typename Space, typename KSpace>
946 std::string Display3D<Space, KSpace>::drawKCell(std::string uname, const RealPoint& rp, bool xodd, bool yodd, bool zodd, bool hasSign, bool sign) {
947 std::string name = myCurrentName;
948 static const std::string TOKEN = "{d}";
949 static const double scale = 0.9;
950 static const double shift = 0.05;
951 static const double smallScale = 0.3;
952 static const double smallShift = 0.15;
953 // For 2D cell, this indicates if the big quad is
954 // inside the cell or outside
955 static const int orientationPermut[3][2] = {
956 {1, 0}, {0, 1}, {1, 0}
957 };
958
959 const unsigned int dim = xodd + yodd + zodd;
960
961 auto tokenPos = uname.find(TOKEN);
962 if (tokenPos != std::string::npos)
963 uname.replace(uname.find(TOKEN), TOKEN.size(), std::to_string(dim));
964
965 switch(dim) {
966 case 0: {
967 name = createOrReuseBallList(uname);
968 if (myCurrentData->vertices.size() == 0)
969 myCurrentData->style.width *= scale;
970
971 myCurrentData->vertices.push_back(rp);
972 }
973 break;
974 case 1: {
975 name = createOrReuseLineList(uname);
976
977 const RealPoint newshift(xodd, yodd, zodd);
978 myCurrentData->vertices.push_back(rp - 0.5 * newshift);
979 myCurrentData->vertices.push_back(rp + 0.5 * newshift);
980 }
981 break;
982 case 2: {
983 const unsigned int orientation = (!xodd ? 0 : (!yodd ? 1 : 2));
984 if (currentStyle.mode & DisplayStyle::SIMPLIFIED || !hasSign) {
985 name = createOrReuseQuadList(uname);
986
987 const double scale1 = myCurrentData->style.width * scale;
988
989 drawutils::insertAASquare(myCurrentData->vertices, rp, orientation, scale1);
990 } else {
991 name = createOrReuseVolumetricList(uname);
992
993 const double scales[2] = {
994 scale * myCurrentData->style.width,
995 smallScale * myCurrentData->style.width
996 };
997
998 // Decide where the big quad goes, in the interior or the exterior
999 // of the cell depending on sign and the orientation
1000 int permut = orientationPermut[orientation][sign];
1001 double scale1 = scales[ permut];
1002 double scale2 = scales[1 - permut];
1003 double shift1 = shift;
1004 double shift2 = smallShift;
1005
1006 drawutils::insertPrism(myCurrentData->vertices, rp, orientation, scale1, scale2, shift1, shift2);
1007 }
1008 }
1009 break;
1010 case 3: {
1011 name = createOrReuseCubeList(uname);
1012 myCurrentData->vertices.push_back(rp);
1013 };
1014 break;
1015 };
1016
1017 return name;
1018 }
1019
1020
1021} // DGtal