TTK
Loading...
Searching...
No Matches
Icosphere.h
Go to the documentation of this file.
1
8
9#pragma once
10
11// base code includes
12#include <Debug.h>
13
14// std includes
15#include <boost/functional/hash.hpp>
16#include <unordered_map>
17#include <vector>
18
19namespace ttk {
20
21 class Icosphere : virtual public Debug {
22
23 public:
25 this->setDebugMsgPrefix("Icosphere");
26 }
27 ~Icosphere() override = default;
28
34 size_t &nTriangles,
35 const size_t nSubdivisions) const {
36 nVertices = 12;
37 nTriangles = 20;
38
39 for(size_t i = 0; i < nSubdivisions; i++) {
40 nVertices += (nTriangles * 3) / 2;
41 nTriangles = nTriangles * 4;
42 }
43
44 return 1;
45 }
46
50 template <typename DT, typename IT>
52 // Output
53 DT *vertexCoords,
54 IT *connectivityList,
55
56 // Input
57 const size_t &icosphereIndex,
58 const size_t &nVerticesPerIcosphere,
59 const size_t &nTrianglesPerIcosphere,
60 const DT *centers) const;
61
65 template <typename DT, typename IT>
67 // Output
68 DT *vertexCoords,
69 IT *connectivityList,
70
71 // Input
72 const size_t &nSubdivisions) const;
73
77 template <typename DT, typename IT>
79 // Output
80 DT *vertexCoords,
81 IT *connectivityList,
82
83 // Input
84 const size_t &nSpheres,
85 const size_t &nSubdivisions,
86 const DT &radius,
87 const DT *center,
88
89 // Optional Output
90 DT *normals = nullptr) const;
91
92 private:
98 template <typename DT, typename IT>
99 IT addVertex(const DT &x,
100 const DT &y,
101 const DT &z,
102 DT *vertexCoords,
103 IT &vertexIndex) const {
104 IT cIndex = vertexIndex * 3;
105
106 DT length = std::sqrt(x * x + y * y + z * z);
107 vertexCoords[cIndex] = x / length;
108 vertexCoords[cIndex + 1] = y / length;
109 vertexCoords[cIndex + 2] = z / length;
110
111 return vertexIndex++;
112 }
113
119 template <class IT>
120 IT addTriangle(const IT &i,
121 const IT &j,
122 const IT &k,
123 IT *connectivityList,
124 IT &triangleIndex) const {
125 IT cIndex = triangleIndex * 3;
126 connectivityList[cIndex + 0] = i;
127 connectivityList[cIndex + 1] = j;
128 connectivityList[cIndex + 2] = k;
129 return triangleIndex++;
130 }
131
138 template <typename DT, typename IT>
139 IT addMidVertex(
140 const IT &i,
141 const IT &j,
142 std::unordered_map<std::pair<IT, IT>, IT, boost::hash<std::pair<IT, IT>>>
143 &processedEdges,
144 DT *vertexCoords,
145 IT &vertexIndex) const {
146 bool firstIsSmaller = i < j;
147 IT a = firstIsSmaller ? i : j;
148 IT b = firstIsSmaller ? j : i;
149
150 // Check if edge was already processed
151 {
152 auto it = processedEdges.find({a, b});
153 if(it != processedEdges.end())
154 return it->second;
155 }
156
157 // Otherwise add mid vertex
158 {
159 IT aOffset = a * 3;
160 IT bOffset = b * 3;
161 DT mx = (vertexCoords[aOffset] + vertexCoords[bOffset]) / 2.0;
162 DT my = (vertexCoords[aOffset + 1] + vertexCoords[bOffset + 1]) / 2.0;
163 DT mz = (vertexCoords[aOffset + 2] + vertexCoords[bOffset + 2]) / 2.0;
164
165 IT mIndex
166 = this->addVertex<DT, IT>(mx, my, mz, vertexCoords, vertexIndex);
167 processedEdges.insert({{a, b}, mIndex});
168 return mIndex;
169 }
170 }
171 };
172} // namespace ttk
173
174template <typename DT, typename IT>
176 // Output
177 DT *vertexCoords,
178 IT *connectivityList,
179
180 // Input
181 const size_t &nSubdivisions) const {
182
183 // initialize timer and memory
184 Timer timer;
185
186 // print status
187 const std::string msg
188 = "Computing Icosphere (S: " + std::to_string(nSubdivisions) + ")";
190
191 IT vertexIndex = 0;
192 IT triangleIndex = 0;
193
194 // build icosahedron
195 {
196 // create 12 vertices
197 DT t = (1.0 + sqrt(5.0)) / 2.0;
198 this->addVertex<DT, IT>(-1, t, 0, vertexCoords, vertexIndex);
199 this->addVertex<DT, IT>(1, t, 0, vertexCoords, vertexIndex);
200 this->addVertex<DT, IT>(-1, -t, 0, vertexCoords, vertexIndex);
201 this->addVertex<DT, IT>(1, -t, 0, vertexCoords, vertexIndex);
202
203 this->addVertex<DT, IT>(0, -1, t, vertexCoords, vertexIndex);
204 this->addVertex<DT, IT>(0, 1, t, vertexCoords, vertexIndex);
205 this->addVertex<DT, IT>(0, -1, -t, vertexCoords, vertexIndex);
206 this->addVertex<DT, IT>(0, 1, -t, vertexCoords, vertexIndex);
207
208 this->addVertex<DT, IT>(t, 0, -1, vertexCoords, vertexIndex);
209 this->addVertex<DT, IT>(t, 0, 1, vertexCoords, vertexIndex);
210 this->addVertex<DT, IT>(-t, 0, -1, vertexCoords, vertexIndex);
211 this->addVertex<DT, IT>(-t, 0, 1, vertexCoords, vertexIndex);
212
213 // create 20 triangles
214 this->addTriangle<IT>(0, 11, 5, connectivityList, triangleIndex);
215 this->addTriangle<IT>(0, 5, 1, connectivityList, triangleIndex);
216 this->addTriangle<IT>(0, 1, 7, connectivityList, triangleIndex);
217 this->addTriangle<IT>(0, 7, 10, connectivityList, triangleIndex);
218 this->addTriangle<IT>(0, 10, 11, connectivityList, triangleIndex);
219 this->addTriangle<IT>(1, 5, 9, connectivityList, triangleIndex);
220 this->addTriangle<IT>(5, 11, 4, connectivityList, triangleIndex);
221 this->addTriangle<IT>(11, 10, 2, connectivityList, triangleIndex);
222 this->addTriangle<IT>(10, 7, 6, connectivityList, triangleIndex);
223 this->addTriangle<IT>(7, 1, 8, connectivityList, triangleIndex);
224 this->addTriangle<IT>(3, 9, 4, connectivityList, triangleIndex);
225 this->addTriangle<IT>(3, 4, 2, connectivityList, triangleIndex);
226 this->addTriangle<IT>(3, 2, 6, connectivityList, triangleIndex);
227 this->addTriangle<IT>(3, 6, 8, connectivityList, triangleIndex);
228 this->addTriangle<IT>(3, 8, 9, connectivityList, triangleIndex);
229 this->addTriangle<IT>(4, 9, 5, connectivityList, triangleIndex);
230 this->addTriangle<IT>(2, 4, 11, connectivityList, triangleIndex);
231 this->addTriangle<IT>(6, 2, 10, connectivityList, triangleIndex);
232 this->addTriangle<IT>(8, 6, 7, connectivityList, triangleIndex);
233 this->addTriangle<IT>(9, 8, 1, connectivityList, triangleIndex);
234 }
235
236 // refine icosahedron
237 if(nSubdivisions > 0) {
238 // create temporary connectivityList
239 size_t nVertices = 0;
240 size_t nTriangles = 0;
242 nVertices, nTriangles, nSubdivisions);
243
244 std::vector<IT> connectivityListTemp(nTriangles * 3, 0);
245
246 // cache to store processed edges
247 std::unordered_map<std::pair<IT, IT>, IT, boost::hash<std::pair<IT, IT>>>
248 processedEdges;
249
250 // iterate over nSubdivisions
251 for(size_t s = 0; s < nSubdivisions; s++) {
252 // swap lists
253 const IT *oldList
254 = s % 2 == 0 ? connectivityList : connectivityListTemp.data();
255 IT *newList = s % 2 != 0 ? connectivityList : connectivityListTemp.data();
256
257 // reset indices
258 const size_t nOldTriangles = triangleIndex;
259 triangleIndex = 0;
260
261 for(size_t i = 0; i < nOldTriangles; i++) {
262 const IT offset = i * 3;
263
264 // compute mid points
265 const IT a
266 = this->addMidVertex(oldList[offset + 0], oldList[offset + 1],
267 processedEdges, vertexCoords, vertexIndex);
268 const IT b
269 = this->addMidVertex(oldList[offset + 1], oldList[offset + 2],
270 processedEdges, vertexCoords, vertexIndex);
271 const IT c
272 = this->addMidVertex(oldList[offset + 2], oldList[offset + 0],
273 processedEdges, vertexCoords, vertexIndex);
274
275 // replace triangle by 4 triangles
276 this->addTriangle(oldList[offset + 0], a, c, newList, triangleIndex);
277 this->addTriangle(oldList[offset + 1], b, a, newList, triangleIndex);
278 this->addTriangle(oldList[offset + 2], c, b, newList, triangleIndex);
279 this->addTriangle(a, b, c, newList, triangleIndex);
280 }
281
282 // print progress
283 this->printMsg(
284 msg, ((DT)s) / nSubdivisions, ttk::debug::LineMode::REPLACE);
285 }
286
287 // if uneven number of nSubdivisions then copy temp buffer to output buffer
288 if(nSubdivisions > 0 && nSubdivisions % 2 != 0) {
289 size_t n = nTriangles * 3;
290 for(size_t i = 0; i < n; i++)
291 connectivityList[i] = connectivityListTemp[i];
292 }
293 }
294
295 // print progress
296 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
297
298 return 1;
299}
300
301template <typename DT, typename IT>
303 IT *connectivityList,
304 const size_t &icosphereIndex,
305 const size_t &nVerticesPerIcosphere,
306 const size_t &nTrianglesPerIcosphere,
307 const DT *centers) const {
308 size_t vertexCoordOffset = icosphereIndex * nVerticesPerIcosphere * 3;
309 size_t connectivityListOffset = icosphereIndex * nTrianglesPerIcosphere * 3;
310 size_t temp = icosphereIndex * 3;
311 const DT &centerX = centers[temp++];
312 const DT &centerY = centers[temp++];
313 const DT &centerZ = centers[temp];
314
315 // vertex coords
316 for(size_t i = 0, limit = nVerticesPerIcosphere * 3; i < limit;) {
317 vertexCoords[vertexCoordOffset++] = vertexCoords[i++] + centerX;
318 vertexCoords[vertexCoordOffset++] = vertexCoords[i++] + centerY;
319 vertexCoords[vertexCoordOffset++] = vertexCoords[i++] + centerZ;
320 }
321
322 // connectivity list
323 size_t vertexIdOffset = icosphereIndex * nVerticesPerIcosphere;
324 for(size_t i = 0, limit = nTrianglesPerIcosphere * 3; i < limit;) {
325 connectivityList[connectivityListOffset++]
326 = connectivityList[i++] + vertexIdOffset;
327 connectivityList[connectivityListOffset++]
328 = connectivityList[i++] + vertexIdOffset;
329 connectivityList[connectivityListOffset++]
330 = connectivityList[i++] + vertexIdOffset;
331 }
332
333 return 1;
334}
335
336template <typename DT, typename IT>
338 // Output
339 DT *vertexCoords,
340 IT *connectivityList,
341
342 // Input
343 const size_t &nSpheres,
344 const size_t &nSubdivisions,
345 const DT &radius,
346 const DT *centers,
347
348 // Optional Output
349 DT *normals) const {
350
351 if(nSpheres < 1) {
352 this->printWrn("Number of input points smaller than 1.");
353 return 1;
354 }
355
356 // compute number of vertices and triangles for one ico sphere
357 size_t nVerticesPerIcosphere, nTrianglesPerIcosphere;
358 if(!this->computeNumberOfVerticesAndTriangles(
359 nVerticesPerIcosphere, nTrianglesPerIcosphere, nSubdivisions))
360 return 0;
361
362 // compute ico sphere around origin
363 if(!this->computeIcosphere(vertexCoords, connectivityList, nSubdivisions))
364 return 0;
365
366 // store normals if requested
367 if(normals != nullptr) {
368 ttk::Timer t;
369 this->printMsg("Computing Normals", 0, 0, this->threadNumber_,
371
372#ifdef TTK_ENABLE_OPENMP
373#pragma omp parallel for num_threads(threadNumber_)
374#endif
375 for(size_t i = 0; i < nSpheres; i++) {
376 size_t n = nVerticesPerIcosphere * 3;
377 size_t offset = i * n;
378 for(size_t j = 0; j < n; j++)
379 normals[offset++] = vertexCoords[j];
380 }
381 this->printMsg(
382 "Computing Normals", 1, t.getElapsedTime(), this->threadNumber_);
383 }
384
385 // Translating Icospheres
386 ttk::Timer timer;
387 const std::string transMsg = "Moving " + std::to_string(nSpheres)
388 + " Icospheres (R: " + std::to_string(radius)
389 + ")";
390 this->printMsg(
391 transMsg, 0, 0, this->threadNumber_, ttk::debug::LineMode::REPLACE);
392
393 // apply radius
394 if(radius != 1.0)
395 for(size_t i = 0, j = nVerticesPerIcosphere * 3; i < j; i++) {
396 vertexCoords[i] *= radius;
397 }
398
399#ifdef TTK_ENABLE_OPENMP
400#pragma omp parallel for num_threads(threadNumber_)
401#endif
402 for(size_t i = 1; i < nSpheres; i++) {
403 this->translateIcosphere(vertexCoords, connectivityList, i,
404 nVerticesPerIcosphere, nTrianglesPerIcosphere,
405 centers);
406 }
407
408 // translate first icosphere
409 this->translateIcosphere(vertexCoords, connectivityList, 0,
410 nVerticesPerIcosphere, nTrianglesPerIcosphere,
411 centers);
412 // print status
413 this->printMsg(transMsg, 1, timer.getElapsedTime(), this->threadNumber_);
414
415 return 1;
416}
int threadNumber_
Definition: BaseClass.h:95
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
int computeIcosphere(DT *vertexCoords, IT *connectivityList, const size_t &nSubdivisions) const
Definition: Icosphere.h:175
~Icosphere() override=default
int computeIcospheres(DT *vertexCoords, IT *connectivityList, const size_t &nSpheres, const size_t &nSubdivisions, const DT &radius, const DT *center, DT *normals=nullptr) const
Definition: Icosphere.h:337
int computeNumberOfVerticesAndTriangles(size_t &nVertices, size_t &nTriangles, const size_t nSubdivisions) const
Definition: Icosphere.h:33
int translateIcosphere(DT *vertexCoords, IT *connectivityList, const size_t &icosphereIndex, const size_t &nVerticesPerIcosphere, const size_t &nTrianglesPerIcosphere, const DT *centers) const
Definition: Icosphere.h:302
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)