|
23 | 23 | #include "generated/gen_geometry_utils.h"
|
24 | 24 |
|
25 | 25 | #include "api/geometry_object.h"
|
| 26 | +#include <vtkPlatonicSolidSource.h> |
| 27 | +#include <vtkPolyData.h> |
| 28 | +#include <vtkSmartPointer.h> |
26 | 29 |
|
27 | 30 | using namespace std;
|
28 | 31 |
|
@@ -72,6 +75,224 @@ std::shared_ptr<GeometryObject> create_box(const std::string& name, const double
|
72 | 75 | return res;
|
73 | 76 | }
|
74 | 77 |
|
| 78 | + |
| 79 | + |
| 80 | +// used code from the link below due to absence of any reasonable library |
| 81 | +// https://github.com/caosdoar/spheres/blob/master/src/spheres.cpp |
| 82 | +// published under MIT license |
| 83 | + |
| 84 | +struct Edge |
| 85 | +{ |
| 86 | + uint32_t v0; |
| 87 | + uint32_t v1; |
| 88 | + |
| 89 | + Edge(uint32_t v0, uint32_t v1) |
| 90 | + : v0(v0 < v1 ? v0 : v1) |
| 91 | + , v1(v0 < v1 ? v1 : v0) |
| 92 | + { |
| 93 | + } |
| 94 | + |
| 95 | + bool operator <(const Edge &rhs) const |
| 96 | + { |
| 97 | + return v0 < rhs.v0 || (v0 == rhs.v0 && v1 < rhs.v1); |
| 98 | + } |
| 99 | +}; |
| 100 | + |
| 101 | + |
| 102 | +static Vec3 normalize(const Vec3 &a) |
| 103 | +{ |
| 104 | + const double lrcp = 1.0 / std::sqrt(dot(a, a)); |
| 105 | + return Vec3(a.x * lrcp, a.y * lrcp, a.z * lrcp); |
| 106 | +} |
| 107 | + |
| 108 | + |
| 109 | +static void init_icosphere( |
| 110 | + vector<Vec3>& vertex_list, |
| 111 | + vector<vector<int>>& wall_list) { |
| 112 | + |
| 113 | + vertex_list.clear(); |
| 114 | + wall_list.clear(); |
| 115 | + |
| 116 | + const double t = (1.0 + std::sqrt(5.0)) / 2.0; |
| 117 | + |
| 118 | + // Vertices |
| 119 | + vertex_list.push_back(normalize(Vec3(-1.0, t, 0.0))); |
| 120 | + vertex_list.push_back(normalize(Vec3( 1.0, t, 0.0))); |
| 121 | + vertex_list.push_back(normalize(Vec3(-1.0, -t, 0.0))); |
| 122 | + vertex_list.push_back(normalize(Vec3( 1.0, -t, 0.0))); |
| 123 | + vertex_list.push_back(normalize(Vec3(0.0, -1.0, t))); |
| 124 | + vertex_list.push_back(normalize(Vec3(0.0, 1.0, t))); |
| 125 | + vertex_list.push_back(normalize(Vec3(0.0, -1.0, -t))); |
| 126 | + vertex_list.push_back(normalize(Vec3(0.0, 1.0, -t))); |
| 127 | + vertex_list.push_back(normalize(Vec3( t, 0.0, -1.0))); |
| 128 | + vertex_list.push_back(normalize(Vec3( t, 0.0, 1.0))); |
| 129 | + vertex_list.push_back(normalize(Vec3(-t, 0.0, -1.0))); |
| 130 | + vertex_list.push_back(normalize(Vec3(-t, 0.0, 1.0))); |
| 131 | + |
| 132 | + // Faces |
| 133 | + wall_list.push_back(vector<int>{0, 11, 5}); |
| 134 | + wall_list.push_back(vector<int>{0, 5, 1}); |
| 135 | + wall_list.push_back(vector<int>{0, 1, 7}); |
| 136 | + wall_list.push_back(vector<int>{0, 7, 10}); |
| 137 | + wall_list.push_back(vector<int>{0, 10, 11}); |
| 138 | + wall_list.push_back(vector<int>{1, 5, 9}); |
| 139 | + wall_list.push_back(vector<int>{5, 11, 4}); |
| 140 | + wall_list.push_back(vector<int>{11, 10, 2}); |
| 141 | + wall_list.push_back(vector<int>{10, 7, 6}); |
| 142 | + wall_list.push_back(vector<int>{7, 1, 8}); |
| 143 | + wall_list.push_back(vector<int>{3, 9, 4}); |
| 144 | + wall_list.push_back(vector<int>{3, 4, 2}); |
| 145 | + wall_list.push_back(vector<int>{3, 2, 6}); |
| 146 | + wall_list.push_back(vector<int>{3, 6, 8}); |
| 147 | + wall_list.push_back(vector<int>{3, 8, 9}); |
| 148 | + wall_list.push_back(vector<int>{4, 9, 5}); |
| 149 | + wall_list.push_back(vector<int>{2, 4, 11}); |
| 150 | + wall_list.push_back(vector<int>{6, 2, 10}); |
| 151 | + wall_list.push_back(vector<int>{8, 6, 7}); |
| 152 | + wall_list.push_back(vector<int>{9, 8, 1}); |
| 153 | +} |
| 154 | + |
| 155 | + |
| 156 | +int subdivide_icosphere_edge( |
| 157 | + int f0, int f1, |
| 158 | + const Vec3& v0, const Vec3& v1, |
| 159 | + vector<Vec3>& vertex_list_out, |
| 160 | + std::map<Edge, uint32_t> &io_divisions |
| 161 | + ) |
| 162 | +{ |
| 163 | + // required to make the mesh watertight |
| 164 | + const Edge edge(f0, f1); |
| 165 | + auto it = io_divisions.find(edge); |
| 166 | + if (it != io_divisions.end()) { |
| 167 | + return it->second; |
| 168 | + } |
| 169 | + |
| 170 | + const Vec3 v = normalize(Vec3(0.5) * (v0 + v1)); |
| 171 | + const uint32_t f = vertex_list_out.size(); |
| 172 | + vertex_list_out.push_back(v); |
| 173 | + io_divisions[edge] = f; |
| 174 | + return f; |
| 175 | +} |
| 176 | + |
| 177 | + |
| 178 | +static void subdivide_icosphere_mesh( |
| 179 | + const vector<Vec3>& vertex_list_in, |
| 180 | + const vector<vector<int>>& wall_list_in, |
| 181 | + vector<Vec3>& vertex_list_out, |
| 182 | + vector<vector<int>>& wall_list_out) { |
| 183 | + |
| 184 | + vertex_list_out = vertex_list_in; |
| 185 | + |
| 186 | + std::map<Edge, uint32_t> divisions; // Edge -> new vertex |
| 187 | + |
| 188 | + for (size_t i = 0; i < wall_list_in.size(); ++i) |
| 189 | + { |
| 190 | + const int f0 = wall_list_in[i][0]; |
| 191 | + const int f1 = wall_list_in[i][1]; |
| 192 | + const int f2 = wall_list_in[i][2]; |
| 193 | + |
| 194 | + const Vec3& v0 = vertex_list_in[f0]; |
| 195 | + const Vec3& v1 = vertex_list_in[f1]; |
| 196 | + const Vec3& v2 = vertex_list_in[f2]; |
| 197 | + |
| 198 | + const int f3 = subdivide_icosphere_edge(f0, f1, v0, v1, vertex_list_out, divisions); |
| 199 | + const int f4 = subdivide_icosphere_edge(f1, f2, v1, v2, vertex_list_out, divisions); |
| 200 | + const int f5 = subdivide_icosphere_edge(f2, f0, v2, v0, vertex_list_out, divisions); |
| 201 | + |
| 202 | + wall_list_out.push_back(vector<int>{f0, f3, f5}); |
| 203 | + wall_list_out.push_back(vector<int>{f3, f1, f4}); |
| 204 | + wall_list_out.push_back(vector<int>{f4, f2, f5}); |
| 205 | + wall_list_out.push_back(vector<int>{f3, f4, f5}); |
| 206 | + } |
| 207 | +} |
| 208 | + |
| 209 | + |
| 210 | +std::shared_ptr<GeometryObject> create_icosphere( |
| 211 | + const std::string& name, const double radius, const int subdivisions) { |
| 212 | + |
| 213 | + if (subdivisions < 1) { |
| 214 | + throw ValueError(S("Value of parameter ") + NAME_SUBDIVISIONS + " must be greater or equal to 1."); |
| 215 | + } |
| 216 | + |
| 217 | + if (subdivisions > 8) { |
| 218 | + throw ValueError(S("Value of parameter ") + NAME_SUBDIVISIONS + " must be less or equal to 8."); |
| 219 | + } |
| 220 | + |
| 221 | + vector<Vec3> vertex_list; |
| 222 | + vector<vector<int>> wall_list; |
| 223 | + |
| 224 | + init_icosphere(vertex_list, wall_list); |
| 225 | + |
| 226 | + for (int i = 0; i < subdivisions - 1; i++) { |
| 227 | + vector<Vec3> vertex_list_new; |
| 228 | + vector<vector<int>> wall_list_new; |
| 229 | + subdivide_icosphere_mesh(vertex_list, wall_list, vertex_list_new, wall_list_new); |
| 230 | + |
| 231 | + vertex_list.swap(vertex_list_new); |
| 232 | + wall_list.swap(wall_list_new); |
| 233 | + } |
| 234 | + |
| 235 | + // convert vertices to the required format and scale |
| 236 | + vector<vector<double>> double_vertices; |
| 237 | + for (const Vec3& v: vertex_list) { |
| 238 | + double_vertices.push_back(vector<double>{v.x * radius, v.y * radius, v.z * radius}); |
| 239 | + } |
| 240 | + |
| 241 | + auto res = make_shared<GeometryObject>( |
| 242 | + name, |
| 243 | + double_vertices, |
| 244 | + wall_list |
| 245 | + ); |
| 246 | + |
| 247 | + return res; |
| 248 | +} |
| 249 | + |
| 250 | +#if 0 |
| 251 | +// keeping this code as an example on how to transform vtkPolyData |
| 252 | +std::shared_ptr<GeometryObject> create_sphere( |
| 253 | + const std::string& name, const double radius, const int resolution) { |
| 254 | + |
| 255 | + vtkNew<vtkSphereSource> sphere; |
| 256 | + sphere->SetCenter(0.0, 0.0, 0.0); |
| 257 | + sphere->SetRadius(radius); |
| 258 | + sphere->SetPhiResolution(resolution); |
| 259 | + sphere->SetThetaResolution(resolution); |
| 260 | + sphere->Update(); |
| 261 | + |
| 262 | + vtkSmartPointer<vtkPolyData> polydata = sphere->GetOutput(); |
| 263 | + |
| 264 | + vtkPoints* points = polydata->GetPoints(); |
| 265 | + |
| 266 | + vector<vector<double>> vertex_list; |
| 267 | + int num_vertices = points->GetNumberOfPoints(); |
| 268 | + for (int i = 0; i < num_vertices; i++) { |
| 269 | + double pt[3]; |
| 270 | + points->GetPoint(i, pt); |
| 271 | + vertex_list.push_back(vector<double>{pt[0], pt[1], pt[2]}); |
| 272 | + } |
| 273 | + |
| 274 | + vector<vector<int>> wall_list; |
| 275 | + int num_walls = polydata->GetNumberOfCells(); |
| 276 | + for (int i = 0; i < num_walls; i++) { |
| 277 | + vtkCell* cell = polydata->GetCell(i); |
| 278 | + |
| 279 | + vector<int> wall; |
| 280 | + for (int k = 0; k < 3; k++) { |
| 281 | + wall.push_back(cell->GetPointId(k)); |
| 282 | + } |
| 283 | + wall_list.push_back(wall); |
| 284 | + } |
| 285 | + |
| 286 | + auto res = make_shared<GeometryObject>( |
| 287 | + name, |
| 288 | + vertex_list, |
| 289 | + wall_list |
| 290 | + ); |
| 291 | + |
| 292 | + return res; |
| 293 | +} |
| 294 | +#endif |
| 295 | + |
75 | 296 | } // namespace geometry_utils
|
76 | 297 |
|
77 | 298 | } // namespace API
|
|
0 commit comments