1.3.64
 
Loading...
Searching...
No Matches
VisualizerGeometry.cpp
Go to the documentation of this file.
1
16// Freetype Libraries (rendering fonts)
17extern "C" {
18#include <ft2build.h>
19#include FT_FREETYPE_H
20}
21
22#include "Visualizer.h"
23
24using namespace helios;
25
27 geometry_handler.clearAllGeometry();
28
29 contextUUIDs_build.clear();
30 colorPrimitives_UUIDs.clear();
31 colorPrimitives_objIDs.clear();
32 contextUUIDs_build.clear();
33 depth_buffer_data.clear();
34 colorbar_min = 0;
35 colorbar_max = 0;
36}
37
38size_t Visualizer::addRectangleByCenter(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color, CoordinateSystem coordFlag) {
39 return addRectangleByCenter(center, size, rotation, make_RGBAcolor(color.r, color.g, color.b, 1), coordFlag);
40}
41
42size_t Visualizer::addRectangleByCenter(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBAcolor &color, CoordinateSystem coordFlag) {
43 std::vector<vec3> vertices;
44 vertices.resize(4);
45
46 vec3 v0 = make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
47 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
48 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
49 vertices.at(0) = center + v0;
50
51 vec3 v1 = make_vec3(+0.5f * size.x, -0.5f * size.y, 0.f);
52 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
53 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
54 vertices.at(1) = center + v1;
55
56 vec3 v2 = make_vec3(+0.5f * size.x, +0.5f * size.y, 0.f);
57 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
58 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
59 vertices.at(2) = center + v2;
60
61 vec3 v3 = make_vec3(-0.5f * size.x, +0.5f * size.y, 0.f);
62 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
63 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
64 vertices.at(3) = center + v3;
65
66 return addRectangleByVertices(vertices, color, coordFlag);
67}
68
69size_t Visualizer::addRectangleByCenter(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const char *texture_file, CoordinateSystem coordFlag) {
70 std::vector<vec3> vertices;
71 vertices.resize(4);
72
73 vec3 v0 = make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
74 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
75 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
76 vertices.at(0) = center + v0;
77
78 vec3 v1 = make_vec3(+0.5f * size.x, -0.5f * size.y, 0.f);
79 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
80 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
81 vertices.at(1) = center + v1;
82
83 vec3 v2 = make_vec3(+0.5f * size.x, +0.5f * size.y, 0.f);
84 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
85 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
86 vertices.at(2) = center + v2;
87
88 vec3 v3 = make_vec3(-0.5f * size.x, +0.5f * size.y, 0.f);
89 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
90 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
91 vertices.at(3) = center + v3;
92
93 return addRectangleByVertices(vertices, texture_file, coordFlag);
94}
95
96size_t Visualizer::addRectangleByCenter(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color, const char *texture_file, CoordinateSystem coordFlag) {
97 std::vector<vec3> vertices;
98 vertices.resize(4);
99
100 vec3 v0 = make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
101 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
102 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
103 vertices.at(0) = center + v0;
104
105 vec3 v1 = make_vec3(+0.5f * size.x, -0.5f * size.y, 0.f);
106 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
107 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
108 vertices.at(1) = center + v1;
109
110 vec3 v2 = make_vec3(+0.5f * size.x, +0.5f * size.y, 0.f);
111 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
112 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
113 vertices.at(2) = center + v2;
114
115 vec3 v3 = make_vec3(-0.5f * size.x, +0.5f * size.y, 0.f);
116 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
117 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
118 vertices.at(3) = center + v3;
119
120 return addRectangleByVertices(vertices, color, texture_file, coordFlag);
121}
122
123size_t Visualizer::addRectangleByCenter(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color, const Glyph *glyph, CoordinateSystem coordFlag) {
124 std::vector<vec3> vertices;
125 vertices.resize(4);
126
127 vec3 v0 = make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
128 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
129 v0 = rotatePointAboutLine(v0, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
130 vertices.at(0) = center + v0;
131
132 vec3 v1 = make_vec3(+0.5f * size.x, -0.5f * size.y, 0.f);
133 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
134 v1 = rotatePointAboutLine(v1, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
135 vertices.at(1) = center + v1;
136
137 vec3 v2 = make_vec3(+0.5f * size.x, +0.5f * size.y, 0.f);
138 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
139 v2 = rotatePointAboutLine(v2, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
140 vertices.at(2) = center + v2;
141
142 vec3 v3 = make_vec3(-0.5f * size.x, +0.5f * size.y, 0.f);
143 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(1, 0, 0), -rotation.elevation);
144 v3 = rotatePointAboutLine(v3, make_vec3(0, 0, 0), make_vec3(0, 0, 1), -rotation.azimuth);
145 vertices.at(3) = center + v3;
146
147 return addRectangleByVertices(vertices, color, glyph, coordFlag);
148}
149
150size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const RGBcolor &color, CoordinateSystem coordFlag) {
151 return addRectangleByVertices(vertices, make_RGBAcolor(color.r, color.g, color.b, 1), coordFlag);
152}
153
154size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const RGBAcolor &color, CoordinateSystem coordFlag) {
155 if (coordFlag == COORDINATES_WINDOW_NORMALIZED) { // No vertex transformation (i.e., identity matrix)
156
157 // Check that coordinates are inside drawable area
158 for (auto vertex: vertices) {
159 if (vertex.x < 0.f || vertex.x > 1.f) {
160 if (message_flag) {
161 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
162 }
163 } else if (vertex.y < 0.f || vertex.y > 1.f) {
164 if (message_flag) {
165 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
166 }
167 } else if (vertex.z < -1.f || vertex.z > 1.f) {
168 if (message_flag) {
169 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
170 }
171 }
172 }
173 }
174
175 size_t UUID = geometry_handler.sampleUUID();
176 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, color, {}, -1, false, false, coordFlag, true, false);
177 return UUID;
178}
179
180size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const char *texture_file, CoordinateSystem coordFlag) {
181 const std::vector<vec2> uvs{{0, 0}, {1, 0}, {1, 1}, {0, 1}};
182 return addRectangleByVertices(vertices, texture_file, uvs, coordFlag);
183}
184
185size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const char *texture_file, const std::vector<vec2> &uvs, CoordinateSystem coordFlag) {
186 if (coordFlag == COORDINATES_WINDOW_NORMALIZED) { // No vertex transformation (i.e., identity matrix)
187
188 // Check that coordinates are inside drawable area
189 for (auto vertex: vertices) {
190 if (vertex.x < 0.f || vertex.x > 1.f) {
191 if (message_flag) {
192 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
193 }
194 } else if (vertex.y < 0.f || vertex.y > 1.f) {
195 if (message_flag) {
196 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
197 }
198 } else if (vertex.z < -1.f || vertex.z > 1.f) {
199 if (message_flag) {
200 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
201 }
202 }
203 }
204 }
205
206 uint textureID = registerTextureImage(texture_file);
207
208 size_t UUID = geometry_handler.sampleUUID();
209 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, RGBA::black, uvs, textureID, false, false, coordFlag, true, false);
210 return UUID;
211}
212
213size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const RGBcolor &color, const char *texture_file, CoordinateSystem coordFlag) {
214 const std::vector<vec2> uvs{{0, 0}, {1, 0}, {1, 1}, {0, 1}};
215 return addRectangleByVertices(vertices, color, texture_file, uvs, coordFlag);
216}
217
218size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const helios::RGBcolor &color, const char *texture_file, const std::vector<vec2> &uvs, CoordinateSystem coordFlag) {
219 if (coordFlag == COORDINATES_WINDOW_NORMALIZED) { // No vertex transformation (i.e., identity matrix)
220
221 // Check that coordinates are inside drawable area
222 for (auto vertex: vertices) {
223 if (vertex.x < 0.f || vertex.x > 1.f) {
224 if (message_flag) {
225 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
226 }
227 } else if (vertex.y < 0.f || vertex.y > 1.f) {
228 if (message_flag) {
229 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
230 }
231 } else if (vertex.z < -1.f || vertex.z > 1.f) {
232 if (message_flag) {
233 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
234 }
235 }
236 }
237 }
238
239 uint textureID = registerTextureImage(texture_file);
240
241 size_t UUID = geometry_handler.sampleUUID();
242 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, make_RGBAcolor(color, 1.f), uvs, textureID, false, false, coordFlag, true, false);
243 return UUID;
244}
245
246size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const RGBcolor &color, const Glyph *glyph, CoordinateSystem coordFlag) {
247 return addRectangleByVertices(vertices, make_RGBAcolor(color, 1), glyph, coordFlag);
248}
249
250size_t Visualizer::addRectangleByVertices(const std::vector<vec3> &vertices, const RGBAcolor &color, const Glyph *glyph, CoordinateSystem coordFlag) {
251 if (coordFlag == COORDINATES_WINDOW_NORMALIZED) { // No vertex transformation (i.e., identity matrix)
252
253 // Check that coordinates are inside drawable area
254 for (auto vertex: vertices) {
255 if (vertex.x < 0.f || vertex.x > 1.f) {
256 if (message_flag) {
257 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
258 }
259 } else if (vertex.y < 0.f || vertex.y > 1.f) {
260 if (message_flag) {
261 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
262 }
263 } else if (vertex.z < -1.f || vertex.z > 1.f) {
264 if (message_flag) {
265 std::cerr << "WARNING (Visualizer::addRectangleByVertices): Rectangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
266 }
267 }
268 }
269 }
270
271 uint textureID = registerTextureGlyph(glyph);
272
273 const std::vector<vec2> uvs{{0, 0}, {1, 0}, {1, 1}, {0, 1}};
274
275 // Disable shadows for glyphs
276 CoordinateSystem coordFlag2 = coordFlag;
277 if (coordFlag == COORDINATES_CARTESIAN) {
278 coordFlag2 = scast<CoordinateSystem>(2);
279 }
280
281 size_t UUID = geometry_handler.sampleUUID();
282 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_RECTANGLE, vertices, color, uvs, textureID, true, true, coordFlag2, true, false);
283 return UUID;
284}
285
286size_t Visualizer::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const RGBcolor &color, CoordinateSystem coordFlag) {
287 return addTriangle(vertex0, vertex1, vertex2, make_RGBAcolor(color.r, color.g, color.b, 1), coordFlag);
288}
289
290size_t Visualizer::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const RGBAcolor &color, CoordinateSystem coordFlag) {
291 const std::vector<vec3> vertices{vertex0, vertex1, vertex2};
292
293 if (coordFlag == 0) { // No vertex transformation (i.e., identity matrix)
294
295 // Check that coordinates are inside drawable area
296 for (const auto &vertex: vertices) {
297 if (vertex.x < 0.f || vertex.x > 1.f) {
298 if (message_flag) {
299 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
300 }
301 } else if (vertex.y < 0.f || vertex.y > 1.f) {
302 if (message_flag) {
303 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
304 }
305 } else if (vertex.z < -1.f || vertex.z > 1.f) {
306 if (message_flag) {
307 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
308 }
309 }
310 }
311 }
312
313 size_t UUID = geometry_handler.sampleUUID();
314 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_TRIANGLE, vertices, color, {}, -1, false, false, coordFlag, true, false);
315 return UUID;
316}
317
318size_t Visualizer::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const char *texture_file, const helios::vec2 &uv0, const helios::vec2 &uv1, const helios::vec2 &uv2, CoordinateSystem coordFlag) {
319 const std::vector<vec3> vertices{vertex0, vertex1, vertex2};
320 const std::vector<vec2> uvs{uv0, uv1, uv2};
321
322 if (coordFlag == 0) { // No vertex transformation (i.e., identity matrix)
323
324 // Check that coordinates are inside drawable area
325 for (auto &vertex: vertices) {
326 if (vertex.x < 0.f || vertex.x > 1.f) {
327 if (message_flag) {
328 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `x' position ( " << vertex.x << " ) is outside of drawable area." << std::endl;
329 }
330 } else if (vertex.y < 0.f || vertex.y > 1.f) {
331 if (message_flag) {
332 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `y' position ( " << vertex.y << " ) is outside of drawable area." << std::endl;
333 }
334 } else if (vertex.z < -1.f || vertex.z > 1.f) {
335 if (message_flag) {
336 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `z' position ( " << vertex.z << " ) is outside of drawable area." << std::endl;
337 }
338 }
339 }
340 }
341
342 uint textureID = registerTextureImage(texture_file);
343
344 size_t UUID = geometry_handler.sampleUUID();
345 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_TRIANGLE, vertices, RGBA::black, uvs, textureID, false, false, coordFlag, true, false);
346 return UUID;
347}
348
349size_t Visualizer::addTriangle(const vec3 &vertex0, const vec3 &vertex1, const vec3 &vertex2, const char *texture_file, const helios::vec2 &uv0, const helios::vec2 &uv1, const helios::vec2 &uv2, const RGBAcolor &color, CoordinateSystem coordFlag) {
350 const std::vector<vec3> vertices{vertex0, vertex1, vertex2};
351 const std::vector<vec2> uvs{uv0, uv1, uv2};
352
353 if (coordFlag == 0) { // No vertex transformation (i.e., identity matrix)
354
355 // Check that coordinates are inside drawable area
356 for (const auto &tri_vertex: vertices) {
357 if (tri_vertex.x < 0.f || tri_vertex.x > 1.f) {
358 if (message_flag) {
359 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `x' position ( " << tri_vertex.x << " ) is outside of drawable area." << std::endl;
360 }
361 } else if (tri_vertex.y < 0.f || tri_vertex.y > 1.f) {
362 if (message_flag) {
363 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `y' position ( " << tri_vertex.y << " ) is outside of drawable area." << std::endl;
364 }
365 } else if (tri_vertex.z < -1.f || tri_vertex.z > 1.f) {
366 if (message_flag) {
367 std::cerr << "WARNING (Visualizer::addTriangle): Triangle `z' position ( " << tri_vertex.z << " ) is outside of drawable area." << std::endl;
368 }
369 }
370 }
371 }
372
373 uint textureID = registerTextureImage(texture_file);
374
375 size_t UUID = geometry_handler.sampleUUID();
376 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_TRIANGLE, vertices, color, uvs, textureID, true, false, coordFlag, true, false);
377 return UUID;
378}
379
380std::vector<size_t> Visualizer::addVoxelByCenter(const vec3 &center, const vec3 &size, const SphericalCoord &rotation, const RGBcolor &color, CoordinateSystem coordFlag) {
381 return addVoxelByCenter(center, size, rotation, make_RGBAcolor(color.r, color.g, color.b, 1), coordFlag);
382}
383
384std::vector<size_t> Visualizer::addVoxelByCenter(const vec3 &center, const vec3 &size, const SphericalCoord &rotation, const RGBAcolor &color, CoordinateSystem coordFlag) {
385 float eps = 1e-4; // Avoid z-fighting
386
387 float az = rotation.azimuth;
388
389 std::vector<size_t> UUIDs(6);
390
391 const vec3 c0 = center + rotatePoint(make_vec3(0, -0.5f * size.y, 0.f), 0, az) + eps;
392 UUIDs.at(0) = addRectangleByCenter(c0, make_vec2(size.x, size.z), make_SphericalCoord(-0.5 * PI_F, az), color, coordFlag);
393
394 const vec3 c1 = center + rotatePoint(make_vec3(0, 0.5f * size.y, 0.f), 0, az) + eps;
395 UUIDs.at(1) = addRectangleByCenter(c1, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, az), color, coordFlag);
396
397 const vec3 c2 = center + rotatePoint(make_vec3(0.5f * size.x, 0.f, 0.f), 0, az) + eps;
398 UUIDs.at(2) = addRectangleByCenter(c2, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F + az), color, coordFlag);
399
400 const vec3 c3 = center + rotatePoint(make_vec3(-0.5f * size.x, 0.f, 0.f), 0, az) + eps;
401 UUIDs.at(3) = addRectangleByCenter(c3, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F + az), color, coordFlag);
402
403 const vec3 c4 = center + make_vec3(0.f, 0.f, -0.5f * size.z) + eps;
404 UUIDs.at(4) = addRectangleByCenter(c4, make_vec2(size.x, size.y), make_SphericalCoord(PI_F, az), color, coordFlag);
405
406 const vec3 c5 = center + make_vec3(0.f, 0.f, 0.5f * size.z) + eps;
407 UUIDs.at(5) = addRectangleByCenter(c5, make_vec2(size.x, size.y), make_SphericalCoord(0, az), color, coordFlag);
408
409 return UUIDs;
410}
411
412size_t Visualizer::addLine(const vec3 &start, const vec3 &end, const RGBcolor &color, CoordinateSystem coordinate_system) {
413 return addLine(start, end, make_RGBAcolor(color, 1), coordinate_system);
414}
415
416size_t Visualizer::addLine(const vec3 &start, const vec3 &end, const RGBAcolor &color, CoordinateSystem coordFlag) {
417 const std::vector<vec3> vertices{start, end};
418
419 size_t UUID = geometry_handler.sampleUUID();
420 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_LINE, vertices, color, {}, -1, false, false, coordFlag, true, false);
421 return UUID;
422}
423
424size_t Visualizer::addLine(const vec3 &start, const vec3 &end, const RGBcolor &color, float line_width, CoordinateSystem coordinate_system) {
425 return addLine(start, end, make_RGBAcolor(color, 1), line_width, coordinate_system);
426}
427
428size_t Visualizer::addLine(const vec3 &start, const vec3 &end, const RGBAcolor &color, float line_width, CoordinateSystem coordFlag) {
429 // Validate line width
430 if (line_width <= 0.0f) {
431 helios_runtime_error("ERROR (Visualizer::addLine): Line width must be positive (got " + std::to_string(line_width) + "). Please specify a positive line width value.");
432 }
433
434 // Reasonable maximum line width to prevent rendering issues
435 // Lines are rendered using geometry shaders which expand line primitives into quads
436 const float MAX_LINE_WIDTH = 100.0f;
437 if (line_width > MAX_LINE_WIDTH) {
438 helios_runtime_error("ERROR (Visualizer::addLine): Line width " + std::to_string(line_width) + " exceeds maximum supported width (" + std::to_string(MAX_LINE_WIDTH) + "). Please specify a smaller line width value.");
439 }
440
441 const std::vector<vec3> vertices{start, end};
442
443 size_t UUID = geometry_handler.sampleUUID();
444 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_LINE, vertices, color, {}, -1, false, false, coordFlag, true, false, false, line_width);
445 return UUID;
446}
447
448size_t Visualizer::addPoint(const vec3 &position, const RGBcolor &color, float pointsize, CoordinateSystem coordinate_system) {
449 return addPoint(position, make_RGBAcolor(color, 1), pointsize, coordinate_system);
450}
451
452size_t Visualizer::addPoint(const vec3 &position, const RGBAcolor &color, float pointsize, CoordinateSystem coordinate_system) {
453 // Only perform OpenGL validation if we have a valid context (not in headless mode during initialization)
454 if (!headless && window != nullptr) {
455 // Use conservative OpenGL 3.3 Core Profile point size limits
456 // Most implementations support at least 1.0 to 64.0 for point sizes
457 const float MIN_POINT_SIZE = 1.0f;
458 const float MAX_POINT_SIZE = 64.0f;
459
460 if (pointsize < MIN_POINT_SIZE || pointsize > MAX_POINT_SIZE) {
461 std::cerr << "WARNING (Visualizer::addPoint): Point size ( " << pointsize << " ) is outside of supported range ( " << MIN_POINT_SIZE << ", " << MAX_POINT_SIZE << " ). Clamping value.." << std::endl;
462 if (pointsize < MIN_POINT_SIZE) {
463 pointsize = MIN_POINT_SIZE;
464 } else {
465 pointsize = MAX_POINT_SIZE;
466 }
467 }
468 }
469 this->point_width = pointsize;
470
471 size_t UUID = geometry_handler.sampleUUID();
472 geometry_handler.addGeometry(UUID, GeometryHandler::GEOMETRY_TYPE_POINT, {position}, color, {}, -1, false, false, coordinate_system, true, false, pointsize);
473 return UUID;
474}
475
476std::vector<size_t> Visualizer::addSphereByCenter(float radius, const vec3 &center, uint Ndivisions, const RGBcolor &color, CoordinateSystem coordinate_system) {
477 return addSphereByCenter(radius, center, Ndivisions, make_RGBAcolor(color.r, color.g, color.b, 1), coordinate_system);
478}
479
480std::vector<size_t> Visualizer::addSphereByCenter(float radius, const vec3 &center, uint Ndivisions, const RGBAcolor &color, CoordinateSystem coordinate_system) {
481 float dtheta = PI_F / scast<float>(Ndivisions);
482 float dphi = 2.f * PI_F / scast<float>(Ndivisions);
483
484 std::vector<size_t> UUIDs;
485 UUIDs.reserve(2 * Ndivisions + 2 * (Ndivisions - 2) * (Ndivisions - 1));
486
487 // bottom cap
488 for (int j = 0; j < Ndivisions; j++) {
489 float phi = scast<float>(j) * dphi;
490 float phi_plus = scast<float>(j + 1) * dphi;
491
492 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F, 0));
493 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, phi));
494 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, phi_plus));
495
496 UUIDs.push_back(addTriangle(v0, v1, v2, color, coordinate_system));
497 }
498
499 // top cap
500 for (int j = 0; j < Ndivisions; j++) {
501 float phi = scast<float>(j) * dphi;
502 float phi_plus = scast<float>(j + 1) * dphi;
503
504 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F, 0));
505 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, phi));
506 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, phi_plus));
507
508 UUIDs.push_back(addTriangle(v2, v1, v0, color, coordinate_system));
509 }
510
511 // middle
512 for (int j = 0; j < Ndivisions; j++) {
513 float phi = scast<float>(j) * dphi;
514 float phi_plus = scast<float>(j + 1) * dphi;
515 for (int i = 1; i < Ndivisions - 1; i++) {
516 float theta = -0.5f * PI_F + scast<float>(i) * dtheta;
517 float theta_plus = -0.5f * PI_F + scast<float>(i + 1) * dtheta;
518
519 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, theta, phi));
520 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, theta_plus, phi));
521 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, theta_plus, phi_plus));
522 vec3 v3 = center + sphere2cart(make_SphericalCoord(radius, theta, phi_plus));
523
524 UUIDs.push_back(addTriangle(v0, v1, v2, color, coordinate_system));
525 UUIDs.push_back(addTriangle(v0, v2, v3, color, coordinate_system));
526 }
527 }
528
529 return UUIDs;
530}
531
532void Visualizer::addSkyDomeByCenter(float radius, const vec3 &center, uint Ndivisions, const char *texture_file, int layer) {
533 // This deprecated overload with layer parameter simply ignores the layer argument
534 // and calls the implementation directly to avoid cascading deprecation warnings
535 std::cerr << "WARNING (Visualizer::addSkyDomeByCenter): This method is deprecated and will be removed in a future version. "
536 << "Please use Visualizer::setBackgroundSkyTexture() instead, which provides a more robust sky rendering solution "
537 << "that dynamically scales with camera movement and avoids the need to pre-specify a radius." << std::endl;
538
539 float thetaStart = -0.1f * PI_F;
540
541 float dtheta = (0.5f * PI_F - thetaStart) / float(Ndivisions - 1);
542 float dphi = 2.f * PI_F / float(Ndivisions - 1);
543
544 std::vector<size_t> UUIDs;
545 UUIDs.reserve(2u * Ndivisions * Ndivisions);
546
547 vec3 cart;
548
549 // top cap
550 for (int j = 0; j < scast<int>(Ndivisions - 1); j++) {
551 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F, 0));
552 vec3 v0 = center + radius * cart;
553 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F - dtheta, float(j + 1) * dphi));
554 vec3 v1 = center + radius * cart;
555 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F - dtheta, float(j) * dphi));
556 vec3 v2 = center + radius * cart;
557
558 vec3 n0 = v0 - center;
559 n0.normalize();
560 vec3 n1 = v1 - center;
561 n1.normalize();
562 vec3 n2 = v2 - center;
563 n2.normalize();
564
565 vec2 uv0 = make_vec2(1.f - atan2f(sinf((float(j) + 0.5f) * dphi), -cosf((float(j) + 0.5f) * dphi)) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
566 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
567 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
568
569 if (j == scast<int>(Ndivisions - 2)) {
570 uv2.x = 1;
571 }
572
573 UUIDs.push_back(addTriangle(v0, v1, v2, texture_file, uv0, uv1, uv2, scast<CoordinateSystem>(2)));
574 }
575
576 // middle
577 for (int j = 0; j < scast<int>(Ndivisions - 1); j++) {
578 for (int i = 0; i < scast<int>(Ndivisions - 1); i++) {
579 cart = sphere2cart(make_SphericalCoord(1.f, float(i) * dtheta, float(j) * dphi));
580 vec3 v0 = center + radius * cart;
581 cart = sphere2cart(make_SphericalCoord(1.f, float(i + 1) * dtheta, float(j) * dphi));
582 vec3 v1 = center + radius * cart;
583 cart = sphere2cart(make_SphericalCoord(1.f, float(i + 1) * dtheta, float(j + 1) * dphi));
584 vec3 v2 = center + radius * cart;
585 cart = sphere2cart(make_SphericalCoord(1.f, float(i) * dtheta, float(j + 1) * dphi));
586 vec3 v3 = center + radius * cart;
587
588 vec3 n0 = v0 - center;
589 n0.normalize();
590 vec3 n1 = v1 - center;
591 n1.normalize();
592 vec3 n2 = v2 - center;
593 n2.normalize();
594 vec3 n3 = v3 - center;
595 n3.normalize();
596
597 vec2 uv0 = make_vec2(1.f - atan2f(n0.x, -n0.y) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
598 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
599 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
600 vec2 uv3 = make_vec2(1.f - atan2f(n3.x, -n3.y) / (2.f * PI_F) - 0.5f, 1.f - n3.z * 0.5f - 0.5f);
601
602 if (j == scast<int>(Ndivisions - 2)) {
603 uv2.x = 1;
604 uv3.x = 1;
605 }
606
607 UUIDs.push_back(addTriangle(v0, v1, v2, texture_file, uv0, uv1, uv2, scast<CoordinateSystem>(2)));
608 UUIDs.push_back(addTriangle(v0, v2, v3, texture_file, uv0, uv2, uv3, scast<CoordinateSystem>(2)));
609 }
610 }
611}
612
613std::vector<size_t> Visualizer::addSkyDomeByCenter(float radius, const vec3 &center, uint Ndivisions, const char *texture_file) {
614 std::cerr << "WARNING (Visualizer::addSkyDomeByCenter): This method is deprecated and will be removed in a future version. "
615 << "Please use Visualizer::setBackgroundSkyTexture() instead, which provides a more robust sky rendering solution "
616 << "that dynamically scales with camera movement and avoids the need to pre-specify a radius." << std::endl;
617
618 float thetaStart = -0.1f * PI_F;
619
620 float dtheta = (0.5f * PI_F - thetaStart) / float(Ndivisions - 1);
621 float dphi = 2.f * PI_F / float(Ndivisions - 1);
622
623 std::vector<size_t> UUIDs;
624 UUIDs.reserve(2u * Ndivisions * Ndivisions);
625
626 vec3 cart;
627
628 // top cap
629 for (int j = 0; j < scast<int>(Ndivisions - 1); j++) {
630 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F, 0));
631 vec3 v0 = center + radius * cart;
632 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F - dtheta, float(j + 1) * dphi));
633 vec3 v1 = center + radius * cart;
634 cart = sphere2cart(make_SphericalCoord(1.f, 0.5f * PI_F - dtheta, float(j) * dphi));
635 vec3 v2 = center + radius * cart;
636
637 vec3 n0 = v0 - center;
638 n0.normalize();
639 vec3 n1 = v1 - center;
640 n1.normalize();
641 vec3 n2 = v2 - center;
642 n2.normalize();
643
644 vec2 uv0 = make_vec2(1.f - atan2f(sinf((float(j) + 0.5f) * dphi), -cosf((float(j) + 0.5f) * dphi)) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
645 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
646 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
647
648 if (j == scast<int>(Ndivisions - 2)) {
649 uv2.x = 1;
650 }
651
652 UUIDs.push_back(addTriangle(v0, v1, v2, texture_file, uv0, uv1, uv2, scast<CoordinateSystem>(2)));
653 }
654
655 // middle
656 for (int j = 0; j < scast<int>(Ndivisions - 1); j++) {
657 for (int i = 0; i < scast<int>(Ndivisions - 1); i++) {
658 cart = sphere2cart(make_SphericalCoord(1.f, float(i) * dtheta, float(j) * dphi));
659 vec3 v0 = center + radius * cart;
660 cart = sphere2cart(make_SphericalCoord(1.f, float(i + 1) * dtheta, float(j) * dphi));
661 vec3 v1 = center + radius * cart;
662 cart = sphere2cart(make_SphericalCoord(1.f, float(i + 1) * dtheta, float(j + 1) * dphi));
663 vec3 v2 = center + radius * cart;
664 cart = sphere2cart(make_SphericalCoord(1.f, float(i) * dtheta, float(j + 1) * dphi));
665 vec3 v3 = center + radius * cart;
666
667 vec3 n0 = v0 - center;
668 n0.normalize();
669 vec3 n1 = v1 - center;
670 n1.normalize();
671 vec3 n2 = v2 - center;
672 n2.normalize();
673 vec3 n3 = v3 - center;
674 n3.normalize();
675
676 vec2 uv0 = make_vec2(1.f - atan2f(n0.x, -n0.y) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
677 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
678 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
679 vec2 uv3 = make_vec2(1.f - atan2f(n3.x, -n3.y) / (2.f * PI_F) - 0.5f, 1.f - n3.z * 0.5f - 0.5f);
680
681 if (j == scast<int>(Ndivisions - 2)) {
682 uv2.x = 1;
683 uv3.x = 1;
684 }
685
686 UUIDs.push_back(addTriangle(v0, v1, v2, texture_file, uv0, uv1, uv2, scast<CoordinateSystem>(2)));
687 UUIDs.push_back(addTriangle(v0, v2, v3, texture_file, uv0, uv2, uv3, scast<CoordinateSystem>(2)));
688 }
689 }
690
691 return UUIDs;
692}
693
694std::vector<size_t> Visualizer::addTextboxByCenter(const char *textstring, const vec3 &center, const SphericalCoord &rotation, const RGBcolor &fontcolor, uint fontsize, const char *fontname, CoordinateSystem coordinate_system) {
695 FT_Library ft; // FreeType objects
696 FT_Face face;
697
698 // initialize the freetype library
699 if (FT_Init_FreeType(&ft) != 0) {
700 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Could not init freetype library");
701 }
702
703 std::vector<std::vector<unsigned char>> maskData; // This will hold the letter mask data
704
705 // Load the font
706 std::string font;
707 // std::snprintf(font,100,"plugins/visualizer/fonts/%s.ttf",fontname);
708 font = helios::resolvePluginAsset("visualizer", "fonts/" + (std::string) fontname + ".ttf").string();
709 auto error = FT_New_Face(ft, font.c_str(), 0, &face);
710 if (error != 0) {
711 switch (error) {
712 case FT_Err_Ok:; // do nothing
713 case FT_Err_Cannot_Open_Resource:
714 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Cannot open resource.");
715 case FT_Err_Unknown_File_Format:
716 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Unknown file format.");
717 case FT_Err_Invalid_File_Format:
718 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid file format.");
719 case FT_Err_Invalid_Version:
720 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid FreeType version.");
721 case FT_Err_Lower_Module_Version:
722 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Lower module version.");
723 case FT_Err_Invalid_Argument:
724 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid argument.");
725 case FT_Err_Unimplemented_Feature:
726 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Unimplemented feature.");
727 case FT_Err_Invalid_Table:
728 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid table.");
729 case FT_Err_Invalid_Offset:
730 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid offset.");
731 case FT_Err_Array_Too_Large:
732 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Array too large.");
733 case FT_Err_Missing_Module:
734 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Missing module.");
735 case FT_Err_Out_Of_Memory:
736 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Out of memory.");
737 case FT_Err_Invalid_Face_Handle:
738 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid face handle.");
739 case FT_Err_Invalid_Size_Handle:
740 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid size handle.");
741 case FT_Err_Invalid_Slot_Handle:
742 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid slot handle.");
743 case FT_Err_Invalid_CharMap_Handle:
744 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid charmap handle.");
745 case FT_Err_Invalid_Glyph_Index:
746 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid glyph index.");
747 case FT_Err_Invalid_Character_Code:
748 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid character code.");
749 case FT_Err_Invalid_Glyph_Format:
750 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid glyph format.");
751 case FT_Err_Cannot_Render_Glyph:
752 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Cannot render glyph.");
753 case FT_Err_Invalid_Outline:
754 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid outline.");
755 case FT_Err_Invalid_Composite:
756 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid composite glyph.");
757 case FT_Err_Too_Many_Hints:
758 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Too many hints.");
759 case FT_Err_Invalid_Pixel_Size:
760 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid pixel size.");
761 case FT_Err_Invalid_Library_Handle:
762 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid library handle.");
763 case FT_Err_Invalid_Stream_Handle:
764 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid stream handle.");
765 case FT_Err_Invalid_Frame_Operation:
766 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid frame operation.");
767 case FT_Err_Nested_Frame_Access:
768 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Nested frame access.");
769 case FT_Err_Invalid_Frame_Read:
770 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid frame read.");
771 case FT_Err_Raster_Uninitialized:
772 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Raster uninitialized.");
773 case FT_Err_Raster_Corrupted:
774 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Raster corrupted.");
775 case FT_Err_Raster_Overflow:
776 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Raster overflow.");
777 case FT_Err_Raster_Negative_Height:
778 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Raster negative height.");
779 case FT_Err_Too_Many_Caches:
780 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Too many caches.");
781 case FT_Err_Invalid_Opcode:
782 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Invalid opcode.");
783 case FT_Err_Too_Few_Arguments:
784 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Too few arguments.");
785 case FT_Err_Stack_Overflow:
786 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Stack overflow.");
787 case FT_Err_Stack_Underflow:
788 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Stack underflow.");
789 case FT_Err_Ignore:
790 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Ignore.");
791 case FT_Err_No_Unicode_Glyph_Name:
792 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): No Unicode glyph name.");
793 case FT_Err_Missing_Property:
794 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Missing property.");
795 default:
796 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Unknown FreeType error.");
797 }
798 }
799 if (error != 0) {
800 helios_runtime_error("ERROR (Visualizer::addTextboxByCenter): Could not open font '" + std::string(fontname) + "'");
801 }
802
803 // Load the font size
804 FT_Set_Pixel_Sizes(face, 0, fontsize);
805
806 // x- and y- size of a pixel in [0,1] normalized coordinates
807 float sx = 1.f / float(Wdisplay);
808 float sy = 1.f / float(Hdisplay);
809
810 FT_GlyphSlot gg = face->glyph; // FreeType glyph for font `fontname' and size `fontsize'
811
812 // first, find out how wide the text is going to be
813 // This is because we need to know the width beforehand if we want to center the text
814 float wtext = 0;
815 float htext = 0;
816 const char *textt = textstring;
817 for (const char *p = textt; *p; p++) { // looping over each letter in `textstring'
818 if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) // load the letter
819 continue;
820 float scale = 1;
821 if (strncmp(p, "_", 1) == 0) { // subscript
822 scale = 0.5;
823 continue;
824 } else if (strncmp(p, "^", 1) == 0) { // superscript
825 scale = 0.5;
826 continue;
827 }
828 wtext += gg->bitmap.width * sx * scale;
829 htext = std::max(gg->bitmap.rows * sy, htext);
830 }
831
832 // location of the center of our textbox
833 float xt = center.x - 0.5f * wtext;
834 float yt = center.y - 0.5f * htext;
835
836 if (message_flag) {
837 if (coordinate_system == COORDINATES_WINDOW_NORMALIZED) {
838 if (xt < 0 || xt > 1) {
839 if (message_flag) {
840 std::cerr << "WARNING (Visualizer::addTextboxByCenter): text x-coordinate is outside of window area" << std::endl;
841 }
842 }
843 if (yt < 0 || yt > 1) {
844 if (message_flag) {
845 std::cerr << "WARNING (Visualizer::addTextboxByCenter): text y-coordinate is outside of window area" << std::endl;
846 }
847 }
848 }
849 }
850
851 FT_GlyphSlot g = face->glyph; // Another FreeType glyph for font `fontname' and size `fontsize'
852
853 std::vector<size_t> UUIDs;
854 UUIDs.reserve(std::strlen(textstring));
855
856 const char *text = textstring;
857
858 float offset = 0; // baseline offset for subscript/superscript
859 float scale = 1; // scaling factor for subscript/superscript
860 for (const char *p = text; *p; p++) { // looping over each letter in `textstring'
861
862 if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) // load the letter
863 continue;
864
865 if (strncmp(p, "_", 1) == 0) { // subscript
866 offset = -0.3f * sy;
867 scale = 0.5f;
868 continue;
869 } else if (strncmp(p, "^", 1) == 0) { // superscript
870 offset = 0.3f * sy;
871 scale = 0.5f;
872 continue;
873 }
874
875 // Copy the letter's mask into 2D `maskData' structure
876 uint2 tsize(g->bitmap.width, g->bitmap.rows);
877 maskData.resize(tsize.y);
878 for (int j = 0; j < tsize.y; j++) {
879 maskData.at(j).resize(tsize.x);
880 for (int i = 0; i < tsize.x; i++) {
881 maskData.at(j).at(i) = g->bitmap.buffer[i + j * tsize.x];
882 }
883 }
884
885 // size of this letter (i.e., the size of the rectangle we're going to make
886 vec2 lettersize = make_vec2(g->bitmap.width * scale * sx, g->bitmap.rows * scale * sy);
887
888 // position of this letter (i.e., the center of the rectangle we're going to make
889 vec3 letterposition = make_vec3(xt + g->bitmap_left * sx + 0.5 * lettersize.x, yt + g->bitmap_top * (sy + offset) - 0.5 * lettersize.y, center.z);
890
891 // advance the x- and y- letter position
892 xt += (g->advance.x >> 6) * sx * scale;
893 yt += (g->advance.y >> 6) * sy * scale;
894
895 // reset the offset and scale
896 offset = 0;
897 scale = 1;
898
899 if (lettersize.x == 0 || lettersize.y == 0) { // if the size of the letter is 0, don't add a rectangle
900 continue;
901 }
902
903 Glyph glyph(tsize, maskData);
904
905 //\todo Currently, this adds a separate rectangle for each letter. Would be better to bake the whole string into a single rectangle/texture.
906 UUIDs.push_back(addRectangleByCenter(letterposition, lettersize, rotation, make_RGBcolor(fontcolor.r, fontcolor.g, fontcolor.b), &glyph, coordinate_system));
907 }
908
909 FT_Done_Face(face);
910 FT_Done_FreeType(ft);
911
912 return UUIDs;
913}
914
915void Visualizer::deleteGeometry(size_t geometry_id) {
916 if (geometry_handler.doesGeometryExist(geometry_id)) {
917 geometry_handler.deleteGeometry(geometry_id);
918 }
919}
920
921std::vector<helios::vec3> Visualizer::getGeometryVertices(size_t geometry_id) const {
922 return geometry_handler.getVertices(geometry_id);
923}
924
925void Visualizer::setGeometryVertices(size_t geometry_id, const std::vector<helios::vec3> &vertices) {
926 geometry_handler.setVertices(geometry_id, vertices);
927}
928
929std::vector<size_t> Visualizer::addColorbarByCenter(const char *title, const helios::vec2 &size, const helios::vec3 &center, const helios::RGBcolor &font_color, const Colormap &colormap) {
930 uint Ndivs = 50;
931
932 float cmin = clamp(colormap.getLowerLimit(), -1e7f, 1e7f);
933 float cmax = clamp(colormap.getUpperLimit(), -1e7f, 1e7f);
934
935 // Generate tick values - either user-specified or auto-generated
936 std::vector<float> tick_values;
937 if (!colorbar_ticks.empty()) {
938 tick_values = colorbar_ticks;
939 } else {
940 // Auto-generate nice tick values with adaptive count based on colorbar size
941 // Estimate: need ~0.04 normalized units per tick label vertically (based on font size)
942 // This accounts for font size, spacing, and readability
943 float estimated_tick_spacing = 0.04f * (colorbar_fontsize / 12.0f); // Scale with font size
944 int max_ticks = std::max(3, static_cast<int>(size.y / estimated_tick_spacing));
945 max_ticks = std::min(max_ticks, 8); // Cap at 8 ticks maximum
946
947 tick_values = generateNiceTicks(cmin, cmax, colorbar_integer_data, max_ticks);
948 }
949
950 uint Nticks = tick_values.size();
951
952 std::vector<size_t> UUIDs;
953 UUIDs.reserve(Ndivs + 2 * Nticks + 20);
954
955 float dx = size.x / float(Ndivs);
956
957 for (uint i = 0; i < Ndivs; i++) {
958 float x = center.x - 0.5f * size.x + (float(i) + 0.5f) * dx;
959
960 RGBcolor color = colormap.query(cmin + float(i) / float(Ndivs) * (cmax - cmin));
961
962 UUIDs.push_back(addRectangleByCenter(make_vec3(x, center.y, center.z), make_vec2(dx, 0.5f * size.y), make_SphericalCoord(0, 0), color, COORDINATES_WINDOW_NORMALIZED));
963 }
964
965 std::vector<vec3> border;
966 border.reserve(5);
967 border.push_back(make_vec3(center.x - 0.5f * size.x, center.y + 0.25f * size.y, center.z - 0.001f));
968 border.push_back(make_vec3(center.x + 0.5f * size.x, center.y + 0.25f * size.y, center.z - 0.001f));
969 border.push_back(make_vec3(center.x + 0.5f * size.x, center.y - 0.25f * size.y, center.z - 0.001f));
970 border.push_back(make_vec3(center.x - 0.5f * size.x, center.y - 0.25f * size.y, center.z - 0.001f));
971 border.push_back(make_vec3(center.x - 0.5f * size.x, center.y + 0.25f * size.y, center.z - 0.001f));
972
973 for (uint i = 0; i < border.size() - 1; i++) {
974 UUIDs.push_back(addLine(border.at(i), border.at(i + 1), font_color, COORDINATES_WINDOW_NORMALIZED));
975 }
976
977 // Calculate tick spacing for formatting
978 double tick_spacing = 1.0;
979 if (Nticks > 1) {
980 tick_spacing = std::fabs(tick_values[1] - tick_values[0]);
981 }
982
983 std::vector<vec3> ticks;
984 ticks.resize(2);
985 for (uint i = 0; i < Nticks; i++) {
986 float value = tick_values.at(i);
987 float x = center.x - 0.5f * size.x + (value - cmin) / (cmax - cmin) * size.x;
988
989 // Format tick label using new formatting function
990 std::string label = formatTickLabel(value, tick_spacing, colorbar_integer_data);
991
992 // tick labels
993 std::vector<size_t> UUIDs_text = addTextboxByCenter(label.c_str(), make_vec3(x, center.y - 0.4f * size.y, center.z), make_SphericalCoord(0, 0), font_color, colorbar_fontsize, "OpenSans-Regular", COORDINATES_WINDOW_NORMALIZED);
994 UUIDs.insert(UUIDs.end(), UUIDs_text.begin(), UUIDs_text.end());
995
996 if (i > 0 && i < Nticks - 1) {
997 ticks[0] = make_vec3(x, center.y - 0.25f * size.y, center.z - 0.001f);
998 ticks[1] = make_vec3(x, center.y - 0.25f * size.y + 0.05f * size.y, center.z - 0.001f);
999 addLine(ticks[0], ticks[1], make_RGBcolor(0.25, 0.25, 0.25), COORDINATES_WINDOW_NORMALIZED);
1000 ticks[0] = make_vec3(x, center.y + 0.25f * size.y, center.z - 0.001f);
1001 ticks[1] = make_vec3(x, center.y + 0.25f * size.y - 0.05f * size.y, center.z - 0.001f);
1002 UUIDs.push_back(addLine(ticks[0], ticks[1], make_RGBcolor(0.25, 0.25, 0.25), COORDINATES_WINDOW_NORMALIZED));
1003 }
1004 }
1005
1006 // title
1007 std::vector<size_t> UUIDs_text = addTextboxByCenter(title, make_vec3(center.x, center.y + 0.4f * size.y, center.z), make_SphericalCoord(0, 0), font_color, colorbar_fontsize, "CantoraOne-Regular", COORDINATES_WINDOW_NORMALIZED);
1008 UUIDs.insert(UUIDs.end(), UUIDs_text.begin(), UUIDs_text.end());
1009
1010 return UUIDs;
1011}
1012
1014 addCoordinateAxes(helios::make_vec3(0, 0, 0), helios::make_vec3(1, 1, 1), "positive");
1015}
1016
1017void Visualizer::addCoordinateAxes(const helios::vec3 &origin, const helios::vec3 &length, const std::string &sign) {
1018 float mult;
1019 if (sign == "both") {
1020 mult = 1.0;
1021 } else {
1022 mult = 0.0;
1023 }
1024
1025 float Lmag = length.magnitude();
1026
1027 std::vector<size_t> UUIDs, UUIDs_text;
1028 UUIDs.reserve(12);
1029
1030 // x axis
1031 UUIDs.push_back(addLine(make_vec3(mult * -1.0f * length.x + origin.x, origin.y, origin.z), make_vec3(length.x + origin.x, origin.y, origin.z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1032
1033 if (length.x > 0) {
1034 UUIDs_text = addTextboxByCenter("+ X", helios::make_vec3(1.2f * length.x + origin.x, origin.y, origin.z), nullrotation, helios::RGB::black, uint(200 * Lmag), "OpenSans-Regular", Visualizer::COORDINATES_CARTESIAN);
1035 UUIDs.insert(UUIDs.end(), UUIDs_text.begin(), UUIDs_text.end());
1036 }
1037
1038 // y axis
1039 UUIDs.push_back(addLine(make_vec3(origin.x, mult * -1.0f * length.y + origin.y, origin.z), make_vec3(origin.x, length.y + origin.y, origin.z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1040
1041 if (length.y > 0) {
1042 UUIDs_text = addTextboxByCenter("+ Y", helios::make_vec3(origin.x, 1.1f * length.y + origin.y, origin.z), nullrotation, RGB::black, uint(200 * Lmag), "OpenSans-Regular", Visualizer::COORDINATES_CARTESIAN);
1043 UUIDs.insert(UUIDs.end(), UUIDs_text.begin(), UUIDs_text.end());
1044 }
1045
1046 // z axis
1047 UUIDs.push_back(addLine(make_vec3(origin.x, origin.y, mult * -1.f * length.z + origin.z), make_vec3(origin.x, origin.y, length.z + origin.z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1048
1049 if (length.z > 0) {
1050 UUIDs_text = addTextboxByCenter("+ Z", helios::make_vec3(origin.x, origin.y, length.z + origin.z), nullrotation, RGB::black, uint(200 * Lmag), "OpenSans-Regular", Visualizer::COORDINATES_CARTESIAN);
1051 UUIDs.insert(UUIDs.end(), UUIDs_text.begin(), UUIDs_text.end());
1052 }
1053
1054 this->coordinate_axes_IDs = UUIDs;
1055}
1056
1058 if (!coordinate_axes_IDs.empty()) {
1059 geometry_handler.deleteGeometry(coordinate_axes_IDs);
1060 }
1061}
1062
1063void Visualizer::addGridWireFrame(const helios::vec3 &center, const helios::vec3 &size, const helios::int3 &subdiv) {
1064 const helios::vec3 boxmin = make_vec3(center.x - 0.5f * size.x, center.y - 0.5f * size.y, center.z - 0.5f * size.z);
1065 const helios::vec3 boxmax = make_vec3(center.x + 0.5f * size.x, center.y + 0.5f * size.y, center.z + 0.5f * size.z);
1066
1067 float spacing_x = size.x / scast<float>(subdiv.x);
1068 float spacing_y = size.y / scast<float>(subdiv.y);
1069 float spacing_z = size.z / scast<float>(subdiv.z);
1070
1071 std::vector<size_t> UUIDs;
1072 UUIDs.reserve(subdiv.x * subdiv.y + subdiv.y * subdiv.z + subdiv.x * subdiv.z);
1073
1074 for (int i = 0; i <= subdiv.x; i++) {
1075 for (int j = 0; j <= subdiv.y; j++) {
1076 UUIDs.push_back(addLine(make_vec3(boxmin.x + i * spacing_x, boxmin.y + j * spacing_y, boxmin.z), make_vec3(boxmin.x + i * spacing_x, boxmin.y + j * spacing_y, boxmax.z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1077 }
1078 }
1079
1080 for (int i = 0; i <= subdiv.z; i++) {
1081 for (int j = 0; j <= subdiv.y; j++) {
1082 UUIDs.push_back(addLine(make_vec3(boxmin.x, boxmin.y + j * spacing_y, boxmin.z + i * spacing_z), make_vec3(boxmax.x, boxmin.y + j * spacing_y, boxmin.z + i * spacing_z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1083 }
1084 }
1085
1086 for (int i = 0; i <= subdiv.x; i++) {
1087 for (int j = 0; j <= subdiv.z; j++) {
1088 UUIDs.push_back(addLine(make_vec3(boxmin.x + i * spacing_x, boxmin.y, boxmin.z + j * spacing_z), make_vec3(boxmin.x + i * spacing_x, boxmax.y, boxmin.z + j * spacing_z), RGB::black, Visualizer::COORDINATES_CARTESIAN));
1089 }
1090 }
1091
1092 if (primitiveColorsNeedUpdate) {
1094 }
1095}
1096
1097void Visualizer::updateNavigationGizmo() {
1098 // If disabled, clean up and return
1099 if (!navigation_gizmo_enabled) {
1100 if (!navigation_gizmo_IDs.empty()) {
1101 geometry_handler.deleteGeometry(navigation_gizmo_IDs);
1102 navigation_gizmo_IDs.clear();
1103 }
1104 return;
1105 }
1106
1107 // Delete existing gizmo geometry
1108 if (!navigation_gizmo_IDs.empty()) {
1109 geometry_handler.deleteGeometry(navigation_gizmo_IDs);
1110 navigation_gizmo_IDs.clear();
1111 }
1112
1113 // Gizmo parameters
1114 const float axis_length = 0.03f; // Length of each axis line
1115 const float bubble_size = 0.025f; // Size of letter bubbles
1116 const float line_width = 3.f;
1117
1118 // Calculate aspect ratio to maintain proper gizmo proportions in non-square windows
1119 float aspect_ratio = static_cast<float>(Wdisplay) / static_cast<float>(Hdisplay);
1120
1121 // Gizmo center position - keep fixed regardless of aspect ratio
1122 // Use -0.9999 to ensure visibility even when scene geometry is extremely close to camera
1123 const vec3 gizmo_center = make_vec3(0.9f, 0.1f, -0.9999f); // Lower-right corner, as close as possible to near plane
1124
1125 // Compute camera view matrix directly from current camera position
1126 // This avoids relying on potentially uninitialized cameraViewMatrix
1127 glm::mat4 view_matrix = glm::lookAt(glm_vec3(camera_eye_location), glm_vec3(camera_lookat_center), glm::vec3(0, 0, 1));
1128
1129 // Extract camera right, up, and forward vectors from the view matrix
1130 // The view matrix transforms world coordinates to camera coordinates
1131 // We need the inverse to transform camera axes to world axes
1132 glm::mat3 rotation = glm::transpose(glm::mat3(view_matrix));
1133
1134 vec3 camera_right = make_vec3(rotation[0][0], rotation[0][1], rotation[0][2]);
1135 vec3 camera_up = make_vec3(rotation[1][0], rotation[1][1], rotation[1][2]);
1136
1137 // Define world axes
1138 vec3 world_x = make_vec3(1, 0, 0);
1139 vec3 world_y = make_vec3(0, 1, 0);
1140 vec3 world_z = make_vec3(0, 0, 1);
1141
1142 // Project world axes onto camera's screen plane
1143 // For each world axis, we want to know how it appears on the 2D screen
1144 auto projectAxisToScreen = [&](const vec3 &world_axis) -> vec3 {
1145 // Project world axis onto camera's right and up vectors to get 2D screen coordinates
1146 float x_component = world_axis.x * camera_right.x + world_axis.y * camera_right.y + world_axis.z * camera_right.z;
1147 float y_component = world_axis.x * camera_up.x + world_axis.y * camera_up.y + world_axis.z * camera_up.z;
1148
1149 // Calculate foreshortening based on uncorrected projection
1150 vec3 screen_dir_uncorrected = make_vec3(x_component, y_component, 0);
1151 float mag = screen_dir_uncorrected.magnitude();
1152
1153 if (mag > 1e-6f) {
1154 // Apply foreshortening: the projected magnitude represents how much of the axis
1155 // is visible from the current viewing angle (1.0 = fully visible, 0.0 = end-on)
1156 float foreshortening_factor = mag;
1157
1158 // Apply a minimum visibility threshold to prevent axes from becoming too small
1159 // This ensures axes remain somewhat visible even at extreme viewing angles
1160 const float min_visibility = 0.05f;
1161 foreshortening_factor = std::max(foreshortening_factor, min_visibility);
1162
1163 // Normalize direction
1164 vec3 normalized_dir = screen_dir_uncorrected / mag;
1165
1166 // Apply aspect ratio correction to maintain proper proportions
1167 // Key insight: keep Y-dimension constant (maintains height), adjust X-dimension
1168 if (aspect_ratio >= 1.0f) {
1169 // Wide window: compress X to compensate for wider pixels
1170 normalized_dir.x /= aspect_ratio;
1171 } else {
1172 // Tall window: expand Y to compensate for taller pixels
1173 // This maintains constant visual height while preventing skew
1174 normalized_dir.y *= aspect_ratio;
1175 }
1176
1177 // Renormalize after aspect correction to maintain consistent visual size
1178 // Without this, the gizmo would shrink in wide windows and grow in tall windows
1179 float corrected_mag = normalized_dir.magnitude();
1180 if (corrected_mag > 1e-6f) {
1181 normalized_dir = normalized_dir / corrected_mag;
1182 }
1183
1184 vec3 screen_dir = normalized_dir * (axis_length * foreshortening_factor);
1185 return screen_dir;
1186 } else {
1187 // If axis is perpendicular to screen (magnitude too small), make it invisible
1188 return make_vec3(0, 0, 0);
1189 }
1190 };
1191
1192 vec3 x_screen_dir = projectAxisToScreen(world_x);
1193 vec3 y_screen_dir = projectAxisToScreen(world_y);
1194 vec3 z_screen_dir = projectAxisToScreen(world_z);
1195
1196 // Calculate end points for each axis
1197 vec3 x_end = gizmo_center + x_screen_dir;
1198 vec3 y_end = gizmo_center + y_screen_dir;
1199 vec3 z_end = gizmo_center + z_screen_dir;
1200
1201 // Add axis lines with appropriate colors
1202 RGBcolor x_color = make_RGBcolor(0.8, 0.26, 0.23); // Red
1203 RGBcolor y_color = make_RGBcolor(0.37, 0.59, 0.29); // Green
1204 RGBcolor z_color = make_RGBcolor(0.24, 0.39, 0.83); // Blue
1205
1206 navigation_gizmo_IDs.push_back(addLine(gizmo_center, x_end, x_color, line_width, COORDINATES_WINDOW_NORMALIZED));
1207 navigation_gizmo_IDs.push_back(addLine(gizmo_center, y_end, y_color, line_width, COORDINATES_WINDOW_NORMALIZED));
1208 navigation_gizmo_IDs.push_back(addLine(gizmo_center, z_end, z_color, line_width, COORDINATES_WINDOW_NORMALIZED));
1209
1210 // Calculate bubble positions - extend beyond axis endpoints and offset in z to render in front
1211 // Extension prevents axis lines from showing through transparent parts of the letter textures
1212 const float z_offset = 0.001f; // Z offset to ensure bubbles render in front of lines
1213
1214 // Extension needs to account for aspect ratio to match the corrected bubble size
1215 // Use the average of the corrected bubble dimensions for a consistent extension
1216 float bubble_extension_base = bubble_size * 0.4f;
1217
1218 auto extendBubblePosition = [&](const vec3 &end_pos, const vec3 &direction) -> vec3 {
1219 float dir_mag = direction.magnitude();
1220 vec3 extended_pos = end_pos;
1221 if (dir_mag > 1e-6f) {
1222 vec3 unit_dir = direction / dir_mag;
1223
1224 // Calculate aspect-corrected extension distance based on direction
1225 // This ensures bubbles stay aligned with axis lines at all aspect ratios
1226 float extension_x = bubble_extension_base;
1227 float extension_y = bubble_extension_base;
1228
1229 if (aspect_ratio >= 1.0f) {
1230 // Wide window: X is compressed, so use smaller X extension
1231 extension_x /= aspect_ratio;
1232 } else {
1233 // Tall window: Y is expanded, so use larger Y extension
1234 extension_y /= aspect_ratio;
1235 }
1236
1237 // Use the component-wise corrected extension
1238 vec3 corrected_extension = make_vec3(unit_dir.x * extension_x, unit_dir.y * extension_y, 0.0f);
1239
1240 extended_pos = end_pos + corrected_extension;
1241 }
1242 extended_pos.z += z_offset; // Move slightly toward camera for proper rendering order
1243 return extended_pos;
1244 };
1245
1246 vec3 x_bubble_pos = extendBubblePosition(x_end, x_screen_dir);
1247 vec3 y_bubble_pos = extendBubblePosition(y_end, y_screen_dir);
1248 vec3 z_bubble_pos = extendBubblePosition(z_end, z_screen_dir);
1249
1250 // Add textured bubbles at the extended positions, rendering in front of axis lines
1251 // Apply aspect ratio correction to maintain circular shape in non-square windows
1252 // Keep height constant, adjust width based on aspect ratio
1253 // Apply 25% size increase for hovered bubble
1254 const float hover_size_multiplier = 1.25f;
1255
1256 auto calculateBubbleSize = [this, bubble_size, aspect_ratio, hover_size_multiplier](int bubble_index) -> vec2 {
1257 float effective_bubble_size = bubble_size;
1258 if (bubble_index == hovered_gizmo_bubble) {
1259 effective_bubble_size *= hover_size_multiplier;
1260 }
1261
1262 if (aspect_ratio >= 1.0f) {
1263 // Wide window: reduce bubble width to compensate
1264 return make_vec2(effective_bubble_size / aspect_ratio, effective_bubble_size);
1265 } else {
1266 // Tall window: increase bubble height to maintain proportions
1267 return make_vec2(effective_bubble_size, effective_bubble_size / aspect_ratio);
1268 }
1269 };
1270
1271 SphericalCoord no_rotation = make_SphericalCoord(0, 0);
1272
1273 size_t x_bubble_id = addRectangleByCenter(x_bubble_pos, calculateBubbleSize(0), no_rotation, "plugins/visualizer/textures/nav_gizmo_x.png", COORDINATES_WINDOW_NORMALIZED);
1274 size_t y_bubble_id = addRectangleByCenter(y_bubble_pos, calculateBubbleSize(1), no_rotation, "plugins/visualizer/textures/nav_gizmo_y.png", COORDINATES_WINDOW_NORMALIZED);
1275 size_t z_bubble_id = addRectangleByCenter(z_bubble_pos, calculateBubbleSize(2), no_rotation, "plugins/visualizer/textures/nav_gizmo_z.png", COORDINATES_WINDOW_NORMALIZED);
1276
1277 navigation_gizmo_IDs.push_back(x_bubble_id);
1278 navigation_gizmo_IDs.push_back(y_bubble_id);
1279 navigation_gizmo_IDs.push_back(z_bubble_id);
1280}