Lagrange
Loading...
Searching...
No Matches
generate_rounded_cube.h
1/*
2 * Copyright 2019 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <algorithm>
15#include <vector>
16
17#include <lagrange/Mesh.h>
18#include <lagrange/attributes/attribute_utils.h>
19#include <lagrange/bvh/zip_boundary.h>
20#include <lagrange/combine_mesh_list.h>
21#include <lagrange/compute_edge_lengths.h>
22#include <lagrange/compute_normal.h>
23#include <lagrange/create_mesh.h>
24#include <lagrange/internal/constants.h>
25#include <lagrange/legacy/inline.h>
26#include <lagrange/primitive/generation_utils.h>
27#include <lagrange/utils/safe_cast.h>
28
29
30namespace lagrange {
31namespace primitive {
32LAGRANGE_LEGACY_INLINE
33namespace legacy {
34namespace Cube {
35
36template <typename MeshType>
37std::unique_ptr<MeshType> generate_rounded_corner(
38 const typename MeshType::Scalar radius,
39 const typename MeshType::Index num_segments,
40 const typename Eigen::Transform<typename MeshType::Scalar, 3, Eigen::AffineCompact>&
41 transformation,
42 const typename Eigen::Transform<typename MeshType::Scalar, 2, Eigen::AffineCompact>&
43 uv_transformation)
44{
45 using Scalar = typename MeshType::Scalar;
46 using Index = typename MeshType::Index;
47 using VertexArray = typename MeshType::VertexArray;
48 using FacetArray = typename MeshType::FacetArray;
49 using UVArray = typename MeshType::UVArray;
50 using UVIndices = typename MeshType::UVIndices;
51
52 const Index num_vertices = (num_segments + 2) * (num_segments + 1) / 2;
53 const Index num_facets = num_segments * num_segments;
54 VertexArray vertices(num_vertices, 3);
55 FacetArray facets(num_facets, 3);
56 UVArray uvs(num_vertices, 2);
57 UVIndices uv_indices(num_facets, 3);
58
59 Index count = 0;
60 for (auto i : range(num_segments + 1)) {
61 for (auto j : range(num_segments + 1 - i)) {
62 const Scalar theta = (Scalar)((i == num_segments) ? lagrange::internal::pi / 4
63 : (j * lagrange::internal::pi) /
64 (2 * (num_segments - i)));
65 const Scalar phi =
66 (Scalar)(lagrange::internal::pi / 2 -
67 (num_segments - i) * lagrange::internal::pi / (2 * num_segments));
68
69 vertices.row(count) << radius * sin(theta) * cos(phi), radius * sin(phi),
70 radius * cos(theta) * cos(phi);
71 uvs.row(count) << radius * theta, radius * phi;
72 count++;
73 }
74 }
75 assert(count == num_vertices);
76 vertices.transpose() = transformation * vertices.transpose();
77 uvs.template leftCols<2>().transpose() =
78 uv_transformation * uvs.template leftCols<2>().transpose();
79
80 count = 0;
81 Index prev_base = 0;
82 for (auto i : range(num_segments)) {
83 Index next_base = prev_base + num_segments - i + 1;
84 for (auto j : range(num_segments - i)) {
85 facets.row(count) << prev_base + j, prev_base + j + 1, next_base + j;
86 count++;
87 if (j + 1 < num_segments - i) {
88 facets.row(count) << next_base + j, prev_base + j + 1, next_base + j + 1;
89 count++;
90 }
91 }
92 prev_base = next_base;
93 }
94 assert(count == num_facets);
95 uv_indices = facets; // Same index buffer.
96
97 auto mesh = create_mesh(std::move(vertices), std::move(facets));
98 mesh->initialize_uv(uvs, uv_indices);
99 return mesh;
100}
101
102template <typename MeshType>
103void generate_rounded_corners(
104 const typename MeshType::Scalar width,
105 const typename MeshType::Scalar height,
106 const typename MeshType::Scalar depth,
107 const typename MeshType::Scalar radius,
108 const typename MeshType::Index num_segments,
109 std::vector<std::unique_ptr<MeshType>>& parts)
110{
111 using Scalar = typename MeshType::Scalar;
112 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
113 using Vector2S = Eigen::Matrix<Scalar, 2, 1>;
114
115 const Scalar w = width - 2 * radius;
116 const Scalar h = height - 2 * radius;
117 const Scalar d = depth - 2 * radius;
118 const Scalar r = radius;
119 const Scalar t = (Scalar)(radius * lagrange::internal::pi / 2);
120 const Scalar s = std::max(2 * d + 2 * w + 4 * t, 2 * d + 2 * t + h);
121
122 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> transformation;
123 Eigen::Transform<Scalar, 2, Eigen::AffineCompact> uv_transformation;
124
125 // +X +Y +Z corner
126 transformation.setIdentity();
127 transformation.translate(Vector3S(w / 2, h / 2, d / 2));
128 uv_transformation.setIdentity();
129 uv_transformation.scale(1.0f / s);
130 uv_transformation.translate(Vector2S(d + t + w, d + t + h));
131 parts.push_back(
132 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
133 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
134
135 // +X +Y -Z corner
136 transformation.setIdentity();
137 transformation.translate(Vector3S(w / 2, h / 2, -d / 2));
138 transformation.rotate(
139 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitY()));
140 uv_transformation.setIdentity();
141 uv_transformation.scale(1.0f / s);
142 uv_transformation.translate(Vector2S(2 * d + 2 * t + w, d + t + h));
143 parts.push_back(
144 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
145 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
146
147 // -X +Y -Z corner
148 transformation.setIdentity();
149 transformation.translate(Vector3S(-w / 2, h / 2, -d / 2));
150 transformation.rotate(
151 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitY()));
152 uv_transformation.setIdentity();
153 uv_transformation.scale(1.0f / s);
154 uv_transformation.translate(Vector2S(2 * d + 3 * t + 2 * w, d + t + h));
155 parts.push_back(
156 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
157 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
158
159 // -X +Y +Z corner
160 transformation.setIdentity();
161 transformation.translate(Vector3S(-w / 2, h / 2, d / 2));
162 transformation.rotate(
163 Eigen::AngleAxis<Scalar>((Scalar)(1.5 * lagrange::internal::pi), Vector3S::UnitY()));
164 uv_transformation.setIdentity();
165 uv_transformation.scale(1.0f / s);
166 uv_transformation.translate(Vector2S(d, d + t + h));
167 parts.push_back(
168 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
169 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
170
171 // -X -Y +Z corner
172 transformation.setIdentity();
173 transformation.translate(Vector3S(-w / 2, -h / 2, d / 2));
174 transformation.rotate(
175 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitZ()));
176 uv_transformation.setIdentity();
177 uv_transformation.scale(1.0f / s);
178 uv_transformation.translate(Vector2S(d + t, d + t));
179 uv_transformation.rotate(lagrange::internal::pi);
180 parts.push_back(
181 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
182 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
183
184 // -X -Y -Z corner
185 transformation.setIdentity();
186 transformation.translate(Vector3S(-w / 2, -h / 2, -d / 2));
187 transformation.rotate(
188 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitZ()));
189 transformation.rotate(
190 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitY()));
191 uv_transformation.setIdentity();
192 uv_transformation.scale(1.0f / s);
193 uv_transformation.translate(Vector2S(2 * d + 4 * t + 2 * w, d + t));
194 uv_transformation.rotate(lagrange::internal::pi);
195 parts.push_back(
196 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
197 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
198
199 // X -Y -Z corner
200 transformation.setIdentity();
201 transformation.translate(Vector3S(w / 2, -h / 2, -d / 2));
202 transformation.rotate(
203 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitZ()));
204 transformation.rotate(
205 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitY()));
206 uv_transformation.setIdentity();
207 uv_transformation.scale(1.0f / s);
208 uv_transformation.translate(Vector2S(2 * d + 3 * t + w, d + t));
209 uv_transformation.rotate(lagrange::internal::pi);
210 parts.push_back(
211 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
212 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
213
214 // X -Y Z corner
215 transformation.setIdentity();
216 transformation.translate(Vector3S(w / 2, -h / 2, d / 2));
217 transformation.rotate(
218 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitZ()));
219 transformation.rotate(
220 Eigen::AngleAxis<Scalar>((Scalar)(1.5 * lagrange::internal::pi), Vector3S::UnitY()));
221 uv_transformation.setIdentity();
222 uv_transformation.scale(1.0f / s);
223 uv_transformation.translate(Vector2S(d + 2 * t + w, d + t));
224 uv_transformation.rotate(lagrange::internal::pi);
225 parts.push_back(
226 generate_rounded_corner<MeshType>(r, num_segments, transformation, uv_transformation));
227 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
228}
229
230template <typename MeshType>
231std::unique_ptr<MeshType> generate_rounded_edge(
232 const typename MeshType::Scalar radius,
233 const typename MeshType::Scalar l,
234 const typename MeshType::Index num_round_segments,
235 const typename MeshType::Index num_straight_segments,
236 const typename Eigen::Transform<typename MeshType::Scalar, 3, Eigen::AffineCompact>&
237 transformation,
238 const typename Eigen::Transform<typename MeshType::Scalar, 2, Eigen::AffineCompact>&
239 uv_transformation)
240{
241 using Scalar = typename MeshType::Scalar;
242 using Index = typename MeshType::Index;
243 using VertexArray = typename MeshType::VertexArray;
244 using FacetArray = typename MeshType::FacetArray;
245 using UVArray = typename MeshType::UVArray;
246 using UVIndices = typename MeshType::UVIndices;
247
248 Index num_vertices = (num_round_segments + 1) * (num_straight_segments + 1);
249 VertexArray vertices(num_vertices, 3);
250 UVArray uvs(num_vertices, 2);
251 for (auto i : range(num_round_segments + 1)) {
252 const Scalar theta = (Scalar)((i * lagrange::internal::pi) / (2 * num_round_segments));
253 for (auto j : range(num_straight_segments + 1)) {
254 auto idx = i * (num_straight_segments + 1) + j;
255 auto lj = l * j / (Scalar)(num_straight_segments);
256 vertices.row(idx) << radius * sin(theta), lj, radius * cos(theta);
257 uvs.row(idx) << radius * theta, lj;
258 }
259 }
260 vertices.transpose() = transformation * vertices.transpose();
261 uvs.template leftCols<2>().transpose() =
262 uv_transformation * uvs.template leftCols<2>().transpose();
263
264 Index num_facets = 2 * num_round_segments * num_straight_segments;
265 FacetArray facets(num_facets, 3);
266 for (auto i : range(num_round_segments)) {
267 for (auto j : range(num_straight_segments)) {
268 auto v0 = i * (num_straight_segments + 1) + j;
269 auto v1 = (i + 1) * (num_straight_segments + 1) + j;
270 auto v2 = (i + 1) * (num_straight_segments + 1) + j + 1;
271 auto v3 = i * (num_straight_segments + 1) + j + 1;
272
273 auto idx = i * num_straight_segments + j;
274 facets.row(idx * 2) << v0, v1, v2;
275 facets.row(idx * 2 + 1) << v0, v2, v3;
276 }
277 }
278 UVIndices uv_indices = facets;
279
280 auto mesh = create_mesh(std::move(vertices), std::move(facets));
281 mesh->initialize_uv(uvs, uv_indices);
282 return mesh;
283}
284
285template <typename MeshType>
286void generate_rounded_edges(
287 const typename MeshType::Scalar width,
288 const typename MeshType::Scalar height,
289 const typename MeshType::Scalar depth,
290 const typename MeshType::Scalar radius,
291 const typename MeshType::Index num_round_segments,
292 const typename MeshType::Index num_width_segments,
293 const typename MeshType::Index num_height_segments,
294 const typename MeshType::Index num_depth_segments,
295 const typename MeshType::Scalar tolerance,
296 std::vector<std::unique_ptr<MeshType>>& parts)
297{
298 using Scalar = typename MeshType::Scalar;
299 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
300 using Vector2S = Eigen::Matrix<Scalar, 2, 1>;
301
302 const Scalar w = width - 2 * radius;
303 const Scalar h = height - 2 * radius;
304 const Scalar d = depth - 2 * radius;
305 const Scalar r = radius;
306 const Scalar t = (Scalar)(radius * lagrange::internal::pi / 2);
307 const Scalar s = std::max(2 * d + 2 * w + 4 * t, 2 * d + 2 * t + h);
308
309 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> transformation;
310 Eigen::Transform<Scalar, 2, Eigen::AffineCompact> uv_transformation;
311
312 if (h > tolerance) {
313 // +X +Z edge
314 transformation.setIdentity();
315 transformation.translate(Vector3S(w / 2, -h / 2, d / 2));
316 uv_transformation.setIdentity();
317 uv_transformation.scale(1.0f / s);
318 uv_transformation.translate(Vector2S(d + t + w, d + t));
319 parts.push_back(
320 generate_rounded_edge<MeshType>(
321 r,
322 h,
323 num_round_segments,
324 num_height_segments,
325 transformation,
326 uv_transformation));
327 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
328
329 // +X -Z edge
330 transformation.setIdentity();
331 transformation.translate(Vector3S(w / 2, -h / 2, -d / 2));
332 transformation.rotate(
333 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitY()));
334 uv_transformation.setIdentity();
335 uv_transformation.scale(1.0f / s);
336 uv_transformation.translate(Vector2S(2 * d + 2 * t + w, d + t));
337 parts.push_back(
338 generate_rounded_edge<MeshType>(
339 r,
340 h,
341 num_round_segments,
342 num_height_segments,
343 transformation,
344 uv_transformation));
345 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
346
347 // -X -Z edge
348 transformation.setIdentity();
349 transformation.translate(Vector3S(-w / 2, -h / 2, -d / 2));
350 transformation.rotate(
351 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitY()));
352 uv_transformation.setIdentity();
353 uv_transformation.scale(1.0f / s);
354 uv_transformation.translate(Vector2S(2 * d + 3 * t + 2 * w, d + t));
355 parts.push_back(
356 generate_rounded_edge<MeshType>(
357 r,
358 h,
359 num_round_segments,
360 num_height_segments,
361 transformation,
362 uv_transformation));
363 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
364
365 // -X +Z edge
366 transformation.setIdentity();
367 transformation.translate(Vector3S(-w / 2, -h / 2, d / 2));
368 transformation.rotate(
369 Eigen::AngleAxis<Scalar>((Scalar)(1.5 * lagrange::internal::pi), Vector3S::UnitY()));
370 uv_transformation.setIdentity();
371 uv_transformation.scale(1.0f / s);
372 uv_transformation.translate(Vector2S(d, d + t));
373 parts.push_back(
374 generate_rounded_edge<MeshType>(
375 r,
376 h,
377 num_round_segments,
378 num_height_segments,
379 transformation,
380 uv_transformation));
381 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
382 }
383
384 if (w > tolerance) {
385 // +Y +Z edge
386 transformation.setIdentity();
387 transformation.translate(Vector3S(w / 2, h / 2, d / 2));
388 transformation.rotate(
389 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
390 uv_transformation.setIdentity();
391 uv_transformation.scale(1.0f / s);
392 uv_transformation.translate(Vector2S(d + t + w, d + t + h));
393 uv_transformation.rotate(0.5f * lagrange::internal::pi);
394 parts.push_back(
395 generate_rounded_edge<MeshType>(
396 r,
397 w,
398 num_round_segments,
399 num_width_segments,
400 transformation,
401 uv_transformation));
402 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
403
404 // +Y -Z edge
405 transformation.setIdentity();
406 transformation.translate(Vector3S(w / 2, h / 2, -d / 2));
407 transformation.rotate(
408 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitX()));
409 transformation.rotate(
410 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
411 uv_transformation.setIdentity();
412 uv_transformation.scale(1.0f / s);
413 uv_transformation.translate(Vector2S(2 * d + 3 * t + w, d + 2 * t + h));
414 uv_transformation.rotate((Scalar)(-0.5 * lagrange::internal::pi));
415 parts.push_back(
416 generate_rounded_edge<MeshType>(
417 r,
418 w,
419 num_round_segments,
420 num_width_segments,
421 transformation,
422 uv_transformation));
423 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
424
425 // -Y -Z edge
426 transformation.setIdentity();
427 transformation.translate(Vector3S(w / 2, -h / 2, -d / 2));
428 transformation.rotate(
429 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitX()));
430 transformation.rotate(
431 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
432 uv_transformation.setIdentity();
433 uv_transformation.scale(1.0f / s);
434 uv_transformation.translate(Vector2S(2 * d + 3 * t + w, d + t));
435 uv_transformation.scale(-1);
436 uv_transformation.rotate((Scalar)(0.5f * lagrange::internal::pi));
437 parts.push_back(
438 generate_rounded_edge<MeshType>(
439 r,
440 w,
441 num_round_segments,
442 num_width_segments,
443 transformation,
444 uv_transformation));
445 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
446
447 // -Y +Z edge
448 transformation.setIdentity();
449 transformation.translate(Vector3S(w / 2, -h / 2, d / 2));
450 transformation.rotate(
451 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitX()));
452 transformation.rotate(
453 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
454 uv_transformation.setIdentity();
455 uv_transformation.scale(1.0f / s);
456 uv_transformation.translate(Vector2S(d + t + w, d));
457 uv_transformation.rotate((Scalar)(0.5f * lagrange::internal::pi));
458 parts.push_back(
459 generate_rounded_edge<MeshType>(
460 r,
461 w,
462 num_round_segments,
463 num_width_segments,
464 transformation,
465 uv_transformation));
466 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
467 }
468
469 if (d > tolerance) {
470 // +X +Y edge
471 transformation.setIdentity();
472 transformation.translate(Vector3S(w / 2, h / 2, d / 2));
473 transformation.rotate(
474 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitX()));
475 uv_transformation.setIdentity();
476 uv_transformation.scale(1.0f / s);
477 uv_transformation.translate(Vector2S(d + 2 * t + w, d + 2 * t + h));
478 uv_transformation.rotate((Scalar)(-0.5f * lagrange::internal::pi));
479 parts.push_back(
480 generate_rounded_edge<MeshType>(
481 r,
482 d,
483 num_round_segments,
484 num_depth_segments,
485 transformation,
486 uv_transformation));
487 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
488
489 // -X +Y edge
490 transformation.setIdentity();
491 transformation.translate(Vector3S(-w / 2, h / 2, d / 2));
492 transformation.rotate(
493 Eigen::AngleAxis<Scalar>((Scalar)(0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
494 transformation.rotate(
495 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitX()));
496 uv_transformation.setIdentity();
497 uv_transformation.scale(1.0f / s);
498 uv_transformation.translate(Vector2S(d, d + t + h));
499 uv_transformation.rotate((Scalar)(0.5 * lagrange::internal::pi));
500 parts.push_back(
501 generate_rounded_edge<MeshType>(
502 r,
503 d,
504 num_round_segments,
505 num_depth_segments,
506 transformation,
507 uv_transformation));
508 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
509
510 // -X -Y edge
511 transformation.setIdentity();
512 transformation.translate(Vector3S(-w / 2, -h / 2, d / 2));
513 transformation.rotate(
514 Eigen::AngleAxis<Scalar>((Scalar)lagrange::internal::pi, Vector3S::UnitZ()));
515 transformation.rotate(
516 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitX()));
517 uv_transformation.setIdentity();
518 uv_transformation.scale(1.0f / s);
519 uv_transformation.translate(Vector2S(d, d));
520 uv_transformation.rotate((Scalar)(0.5 * lagrange::internal::pi));
521 parts.push_back(
522 generate_rounded_edge<MeshType>(
523 r,
524 d,
525 num_round_segments,
526 num_depth_segments,
527 transformation,
528 uv_transformation));
529 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
530
531 // +X -Y edge
532 transformation.setIdentity();
533 transformation.translate(Vector3S(w / 2, -h / 2, d / 2));
534 transformation.rotate(
535 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitZ()));
536 transformation.rotate(
537 Eigen::AngleAxis<Scalar>((Scalar)(-0.5 * lagrange::internal::pi), Vector3S::UnitX()));
538 uv_transformation.setIdentity();
539 uv_transformation.scale(1.0f / s);
540 uv_transformation.translate(Vector2S(d + 2 * t + w, d + t));
541 uv_transformation.rotate((Scalar)(-0.5 * lagrange::internal::pi));
542 parts.push_back(
543 generate_rounded_edge<MeshType>(
544 r,
545 d,
546 num_round_segments,
547 num_depth_segments,
548 transformation,
549 uv_transformation));
550 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
551 }
552}
553
554template <typename MeshType>
555std::unique_ptr<MeshType> generate_flat_quad(
556 const typename MeshType::Scalar l0,
557 const typename MeshType::Scalar l1,
558 const typename MeshType::Index num_segments_0,
559 const typename MeshType::Index num_segments_1,
560 const typename Eigen::Transform<typename MeshType::Scalar, 3, Eigen::AffineCompact>&
561 transformation,
562 const typename Eigen::Transform<typename MeshType::Scalar, 2, Eigen::AffineCompact>&
563 uv_transformation)
564{
565 using Scalar = typename MeshType::Scalar;
566 using Index = typename MeshType::Index;
567 using VertexArray = typename MeshType::VertexArray;
568 using FacetArray = typename MeshType::FacetArray;
569 using UVArray = typename MeshType::UVArray;
570 using UVIndices = typename MeshType::UVIndices;
571
572 const Index num_vertices = (num_segments_0 + 1) * (num_segments_1 + 1);
573 VertexArray vertices(num_vertices, 3);
574 for (Index i = 0; i <= num_segments_0; i++) {
575 for (Index j = 0; j <= num_segments_1; j++) {
576 vertices.row(i * (num_segments_1 + 1) + j) << l0 * i / Scalar(num_segments_0),
577 l1 * j / Scalar(num_segments_1), 0;
578 }
579 }
580
581 UVArray uvs = vertices.template leftCols<2>();
582
583 vertices.transpose() = transformation * vertices.transpose();
584 uvs.template leftCols<2>().transpose() =
585 uv_transformation * uvs.template leftCols<2>().transpose();
586
587 const Index num_facets = num_segments_0 * num_segments_1 * 2;
588 FacetArray facets(num_facets, 3);
589 for (Index i = 0; i < num_segments_0; i++) {
590 for (Index j = 0; j < num_segments_1; j++) {
591 const Index k = i * num_segments_1 + j;
592 const Index v0 = i * (num_segments_1 + 1) + j;
593 const Index v1 = (i + 1) * (num_segments_1 + 1) + j;
594 const Index v2 = (i + 1) * (num_segments_1 + 1) + j + 1;
595 const Index v3 = i * (num_segments_1 + 1) + j + 1;
596 facets.row(k * 2) << v0, v1, v2;
597 facets.row(k * 2 + 1) << v0, v2, v3;
598 }
599 }
600 UVIndices uv_indices = facets;
601
602 auto mesh = create_mesh(std::move(vertices), std::move(facets));
603 mesh->initialize_uv(uvs, uv_indices);
604 return mesh;
605}
606
607template <typename MeshType>
608void generate_flat_quads(
609 const typename MeshType::Scalar width,
610 const typename MeshType::Scalar height,
611 const typename MeshType::Scalar depth,
612 const typename MeshType::Scalar radius,
613 const typename MeshType::Index num_width_segments,
614 const typename MeshType::Index num_height_segments,
615 const typename MeshType::Index num_depth_segments,
616 const typename MeshType::Scalar tolerance,
617 std::vector<std::unique_ptr<MeshType>>& parts)
618{
619 using Scalar = typename MeshType::Scalar;
620 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
621 using Vector2S = Eigen::Matrix<Scalar, 2, 1>;
622
623 const Scalar w = width - 2 * radius;
624 const Scalar h = height - 2 * radius;
625 const Scalar d = depth - 2 * radius;
626 const Scalar r = radius;
627 const Scalar t = (Scalar)(radius * lagrange::internal::pi / 2);
628 const Scalar s = std::max(2 * d + 2 * w + 4 * t, 2 * d + 2 * t + h);
629
630 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> transformation;
631 Eigen::Transform<Scalar, 2, Eigen::AffineCompact> uv_transformation;
632
633 Eigen::Matrix<Scalar, 3, 3> rot_y_180, rot_y_90, rot_x_90;
634 rot_y_180 << -1, 0, 0, 0, 1, 0, 0, 0, -1;
635 rot_y_90 << 0, 0, 1, 0, 1, 0, -1, 0, 0;
636 rot_x_90 << 1, 0, 0, 0, 0, -1, 0, 1, 0;
637
638 if (w > tolerance && h > tolerance) {
639 // +Z quad
640 transformation.setIdentity();
641 transformation.translate(Vector3S(-w / 2, -h / 2, d / 2 + r));
642 uv_transformation.setIdentity();
643 uv_transformation.scale(1.0f / s);
644 uv_transformation.translate(Vector2S(d + t, d + t));
645 parts.push_back(
646 generate_flat_quad<MeshType>(
647 w,
648 h,
649 num_width_segments,
650 num_height_segments,
651 transformation,
652 uv_transformation));
653 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
654
655 // -Z quad
656 transformation.setIdentity();
657 transformation.translate(Vector3S(w / 2, -h / 2, -d / 2 - r));
658 transformation.rotate(rot_y_180);
659 uv_transformation.setIdentity();
660 uv_transformation.scale(1.0f / s);
661 uv_transformation.translate(Vector2S(2 * d + 3 * t + w, d + t));
662 parts.push_back(
663 generate_flat_quad<MeshType>(
664 w,
665 h,
666 num_width_segments,
667 num_height_segments,
668 transformation,
669 uv_transformation));
670 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
671 }
672
673 if (d > tolerance && h > tolerance) {
674 // +X quad
675 transformation.setIdentity();
676 transformation.translate(Vector3S(w / 2 + r, -h / 2, d / 2));
677 transformation.rotate(rot_y_90);
678 uv_transformation.setIdentity();
679 uv_transformation.scale(1.0f / s);
680 uv_transformation.translate(Vector2S(d + 2 * t + w, d + t));
681 parts.push_back(
682 generate_flat_quad<MeshType>(
683 d,
684 h,
685 num_depth_segments,
686 num_height_segments,
687 transformation,
688 uv_transformation));
689 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
690
691 // -X quad
692 transformation.setIdentity();
693 transformation.translate(Vector3S(-w / 2 - r, -h / 2, -d / 2));
694 transformation.rotate(rot_y_90.transpose());
695 uv_transformation.setIdentity();
696 uv_transformation.scale(1.0f / s);
697 uv_transformation.translate(Vector2S(0, d + t));
698 parts.push_back(
699 generate_flat_quad<MeshType>(
700 d,
701 h,
702 num_depth_segments,
703 num_height_segments,
704 transformation,
705 uv_transformation));
706 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::SIDE);
707 }
708
709 if (w > tolerance && d > tolerance) {
710 // +Y quad
711 transformation.setIdentity();
712 transformation.translate(Vector3S(-w / 2, h / 2 + r, d / 2));
713 transformation.rotate(rot_x_90.transpose());
714 uv_transformation.setIdentity();
715 uv_transformation.scale(1.0f / s);
716 uv_transformation.translate(Vector2S(d + t, d + 2 * t + h));
717 parts.push_back(
718 generate_flat_quad<MeshType>(
719 w,
720 d,
721 num_width_segments,
722 num_depth_segments,
723 transformation,
724 uv_transformation));
725 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::TOP);
726
727 // -Y quad
728 transformation.setIdentity();
729 transformation.translate(Vector3S(-w / 2, -h / 2 - r, -d / 2));
730 transformation.rotate(rot_x_90);
731 uv_transformation.setIdentity();
732 uv_transformation.scale(1.0f / s);
733 uv_transformation.translate(Vector2S(d + t, 0));
734 parts.push_back(
735 generate_flat_quad<MeshType>(
736 w,
737 d,
738 num_width_segments,
739 num_depth_segments,
740 transformation,
741 uv_transformation));
742 set_uniform_semantic_label(*parts.back(), PrimitiveSemanticLabel::BOTTOM);
743 }
744}
745} // End namespace Cube
746
751{
752 using Scalar = float;
753 using Index = uint32_t;
754
758
759 Scalar width = 1;
760 Scalar height = 1;
761 Scalar depth = 1;
762 Scalar radius = 0;
763 Index num_round_segments = 8;
764 Index num_width_segments = 1;
765 Index num_height_segments = 1;
766 Index num_depth_segments = 1;
767 Eigen::Matrix<Scalar, 3, 1> center{0, 0, 0};
768
772
773 bool output_normals = true;
774
778
782 Scalar angle_threshold = static_cast<Scalar>(11 * lagrange::internal::pi / 180);
783
788 Scalar dist_threshold = static_cast<Scalar>(1e-6);
790
798 {
799 width = std::max<Scalar>(width, 0);
800 height = std::max<Scalar>(height, 0);
801 depth = std::max<Scalar>(depth, 0);
802
803 num_round_segments = std::max<Index>(num_round_segments, 1);
804 num_width_segments = std::max<Index>(num_width_segments, 1);
805 num_height_segments = std::max<Index>(num_height_segments, 1);
806 num_depth_segments = std::max<Index>(num_depth_segments, 1);
807
808 Scalar max_acceptable_radius = (std::min(std::min(width, height), depth)) / 2;
809 radius = std::min(std::max(radius, Scalar(0)), max_acceptable_radius);
810
811 angle_threshold = std::max<Scalar>(angle_threshold, 0);
812 dist_threshold = std::max<Scalar>(dist_threshold, 0);
813 }
814};
815
824template <typename MeshType>
825std::unique_ptr<MeshType> generate_rounded_cube(RoundedCubeConfig config)
826{
827 using namespace Cube;
828 using Scalar = typename MeshType::Scalar;
829 using Index = typename MeshType::Index;
830
831 config.project_to_valid_range();
832
833 std::vector<std::unique_ptr<MeshType>> parts;
834 parts.reserve(26);
835 if (config.radius > config.dist_threshold && config.num_round_segments > 0) {
836 generate_rounded_corners(
837 config.width,
838 config.height,
839 config.depth,
840 config.radius,
841 safe_cast<Index>(config.num_round_segments),
842 parts);
843 generate_rounded_edges(
844 config.width,
845 config.height,
846 config.depth,
847 config.radius,
848 safe_cast<Index>(config.num_round_segments),
849 safe_cast<Index>(config.num_width_segments),
850 safe_cast<Index>(config.num_height_segments),
851 safe_cast<Index>(config.num_depth_segments),
852 config.dist_threshold,
853 parts);
854 }
855 generate_flat_quads(
856 config.width,
857 config.height,
858 config.depth,
859 config.radius,
860 safe_cast<Index>(config.num_width_segments),
861 safe_cast<Index>(config.num_height_segments),
862 safe_cast<Index>(config.num_depth_segments),
863 config.dist_threshold,
864 parts);
865
866 if (parts.size() == 0) {
867 return create_empty_mesh<typename MeshType::VertexArray, typename MeshType::FacetArray>();
868 }
869
870 auto cube_mesh = combine_mesh_list(parts, true);
871 {
872 compute_edge_lengths(*cube_mesh);
873 const auto& edge_lengths = cube_mesh->get_edge_attribute("length");
874 cube_mesh = bvh::zip_boundary(
875 *cube_mesh,
876 std::min(
877 static_cast<Scalar>(edge_lengths.array().mean() * 1e-2f),
878 static_cast<Scalar>(config.dist_threshold)));
879 }
880
881 // Apply post-generation transformations.
882 if (config.center.squaredNorm() > config.dist_threshold) {
883 typename MeshType::VertexArray vertices;
884 cube_mesh->export_vertices(vertices);
885 vertices.rowwise() += config.center.transpose().template cast<Scalar>();
886 cube_mesh->import_vertices(vertices);
887 }
888
889 // Compute corner normals
890 if (config.output_normals) {
891 compute_normal(*cube_mesh, config.angle_threshold);
892 la_runtime_assert(cube_mesh->has_indexed_attribute("normal"));
893 }
894
895 return cube_mesh;
896}
897
898template <typename MeshType>
899std::unique_ptr<MeshType> generate_rounded_cube(
900 typename MeshType::Scalar width,
901 typename MeshType::Scalar height,
902 typename MeshType::Scalar depth,
903 typename MeshType::Scalar radius,
904 typename MeshType::Index num_round_segments,
905 const Eigen::Matrix<typename MeshType::Scalar, 3, 1>& center = {0.0f, 0.0f, 0.0f},
906 typename MeshType::Index num_width_segments = 1,
907 typename MeshType::Index num_height_segments = 1,
908 typename MeshType::Index num_depth_segments = 1)
909{
910 using Scalar = typename RoundedCubeConfig::Scalar;
911 using Index = typename RoundedCubeConfig::Index;
912
913 RoundedCubeConfig config;
914 config.width = safe_cast<Scalar>(width);
915 config.height = safe_cast<Scalar>(height);
916 config.depth = safe_cast<Scalar>(depth);
917 config.radius = safe_cast<Scalar>(radius);
918 config.num_round_segments = safe_cast<Index>(num_round_segments);
919 config.center = center.template cast<Scalar>();
920 config.num_width_segments = safe_cast<Index>(num_width_segments);
921 config.num_height_segments = safe_cast<Index>(num_height_segments);
922 config.num_depth_segments = safe_cast<Index>(num_depth_segments);
923
924 return generate_rounded_cube<MeshType>(std::move(config));
925}
926
927} // namespace legacy
928} // namespace primitive
929} // namespace lagrange
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
AttributeId compute_normal(SurfaceMesh< Scalar, Index > &mesh, function_ref< bool(Index)> is_edge_smooth, span< const Index > cone_vertices={}, NormalOptions options={})
Compute smooth normals based on specified sharp edges and cone vertices.
Definition compute_normal.cpp:198
AttributeId compute_edge_lengths(SurfaceMesh< Scalar, Index > &mesh, const EdgeLengthOptions &options={})
Computes edge lengths attribute.
Definition compute_edge_lengths.cpp:28
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50
SurfaceMesh< Scalar, Index > generate_rounded_cube(RoundedCubeOptions setting)
Generate a rounded cube mesh.
Definition generate_rounded_cube.cpp:1070
Main namespace for Lagrange.
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition create_mesh.h:39
Configuration struct for rounded cube.
Definition generate_rounded_cube.h:751
void project_to_valid_range()
Project config setting into valid range.
Definition generate_rounded_cube.h:797
Scalar dist_threshold
Two vertices are considered coinciding iff the distance between them is smaller than dist_threshold.
Definition generate_rounded_cube.h:788
Scalar angle_threshold
An edge is sharp iff its dihedral angle is larger than angle_threshold.
Definition generate_rounded_cube.h:782