Lagrange
Loading...
Searching...
No Matches
generation_utils.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 <vector>
15
16#include <lagrange/Mesh.h>
17#include <lagrange/common.h>
18#include <lagrange/create_mesh.h>
19#include <lagrange/internal/constants.h>
20#include <lagrange/legacy/inline.h>
21#include <lagrange/mesh_cleanup/remove_duplicate_vertices.h>
22#include <lagrange/utils/assert.h>
23
24namespace lagrange {
25LAGRANGE_LEGACY_INLINE
26namespace legacy {
27// TODO: This whole file should be in the primitive namespace.
28
29enum class PrimitiveSemanticLabel : std::uint8_t { SIDE = 0, TOP = 1, BOTTOM = 2, UNKNOWN };
30
31template <typename Scalar>
32Scalar compute_sweep_angle(const Scalar begin_angle, Scalar end_angle)
33{
34 while (end_angle < begin_angle) {
35 end_angle += (Scalar)(2 * lagrange::internal::pi);
36 }
37 return end_angle - begin_angle;
38}
39
40template <typename VertexArray, typename FacetArray>
42{
43 // Output vertices after subdivision
44 VertexArray vertices;
45 // Output faces after subdivision
46 FacetArray triangles;
47 // Ordered list of segment indices per corner
48 std::vector<std::vector<typename FacetArray::Scalar>> segment_indices;
49};
50
51template <typename VertexArray, typename Index>
52struct GeometricProfile
53{
54 GeometricProfile(VertexArray _samples, Index _num_samples)
55 : samples(_samples)
56 , num_samples(_num_samples)
57 {}
58
59 // Vertices for this profile
60 VertexArray samples;
61 // Number of vertices for a single vertical repeat
62 Index num_samples;
63
64 // Return number of spans for this profile
65 Index spans() const { return num_samples > 0 ? num_samples - 1 : 0; }
66};
67
72template <typename VertexType, typename Scalar>
73VertexType project_to_sphere(const VertexType& center, const VertexType& point, const Scalar radius)
74{
75 const VertexType PC = point - center;
76 auto length = PC.norm();
77 // Point coincides with the sphere center. Don't project.
78 if (length == 0.0) {
79 return center;
80 }
81 auto scale = radius / length;
82 return scale * PC + center;
83}
84
89template <typename VertexType, typename Scalar, typename Index>
90VertexType project_to_sphere(
91 const VertexType& center,
92 const Scalar radius,
93 const Index row,
94 const Index col,
95 const Index size)
96{
97 Scalar theta = row < size ? (col * lagrange::internal::pi) / (2 * (size - row)) : 0.0;
98 Scalar phi = ((size - row) * lagrange::internal::pi) / (2 * size);
99
100 auto dx = std::copysign(radius * std::cos(theta) * std::sin(phi), center.x());
101 auto dy = std::copysign(radius * std::sin(theta) * std::sin(phi), center.y());
102 auto dz = std::copysign(radius * std::cos(phi), center.z());
103 return center + VertexType(dx, dy, dz);
104}
105
109template <typename VertexArray>
110void normalize_to_unit_box(VertexArray& input_vertices)
111{
112 const auto xmax = input_vertices.col(0).maxCoeff();
113 const auto xmin = input_vertices.col(0).minCoeff();
114 const auto ymax = input_vertices.col(1).maxCoeff();
115 const auto ymin = input_vertices.col(1).minCoeff();
116 const auto max_diff = std::max(
117 {xmax - xmin, ymax - ymin, std::numeric_limits<typename VertexArray::Scalar>::epsilon()});
118
119 for (auto idx : range(input_vertices.rows())) {
120 auto vertex = input_vertices.row(idx);
121 auto x = (vertex[0] - xmin) / max_diff;
122 auto y = (vertex[1] - ymin) / max_diff;
123 input_vertices.row(idx) << x, y;
124 }
125}
126
131template <typename VertexArray, typename Index>
132std::tuple<VertexArray, std::vector<Index>> divide_line_into_segments(
133 const VertexArray& vertices,
134 const Index v1,
135 const Index v2,
136 const Index num_segments)
137{
138 using Scalar = typename VertexArray::Scalar;
139 using IndexList = std::vector<Index>;
140
141 const Index num_vertices = safe_cast<Index>(vertices.rows());
142 IndexList output_indices(num_segments + 1);
143 VertexArray output_vertices(num_vertices + num_segments - 1, vertices.cols());
144
145 output_vertices.topRows(num_vertices) = vertices;
146 output_indices[0] = v1;
147
148 for (auto i : range(num_segments - 1)) {
149 const Scalar ratio = safe_cast<Scalar>(i + 1) / num_segments;
150 output_vertices.row(num_vertices + i)
151 << vertices.row(v2) * ratio + vertices.row(v1) * (1.0 - ratio);
152 output_indices[i + 1] = num_vertices + i;
153 }
154
155 output_indices[num_segments] = v2;
156 return std::make_tuple(output_vertices, output_indices);
157}
158
163
164template <typename Scalar, typename Point>
165std::function<Point(Scalar)> partial_torus_generator(
166 Scalar major_radius,
167 Scalar minor_radius,
168 Eigen::Matrix<Scalar, 3, 1> center,
169 Scalar start_slice_angle,
170 Scalar slice_angle)
171{
172 return [=](Scalar t) -> Point {
173 Point vert;
174 Scalar theta = t * slice_angle + start_slice_angle;
175 vert << major_radius + minor_radius * std::cos(theta) + center.x(),
176 minor_radius * std::sin(theta) + center.y(), center.z();
177 return vert;
178 };
179}
180
181
182/*
183Generate samples from a single vertex generator
184*/
185template <typename MeshType, typename Point>
187 std::function<Point(typename MeshType::Scalar)> generate_vertex,
188 typename MeshType::Index spans,
189 bool reverse_profile = false)
190{
191 using VertexArray = typename MeshType::VertexArray;
192 using Scalar = typename MeshType::Scalar;
193 using Index = typename MeshType::Index;
194 VertexArray samples(spans + 1, 3);
195 for (auto sample_idx : range(spans + 1)) {
196 Scalar t = (Scalar)sample_idx / (Scalar)spans;
197 if (reverse_profile) {
198 samples.row(sample_idx) = generate_vertex(1 - t);
199 } else {
200 samples.row(sample_idx) = generate_vertex(t);
201 }
202 }
203 return GeometricProfile<VertexArray, Index>(samples, spans + 1);
204}
205
206/*
207Combine different geometric profiles to generate one geometric profile
208*/
209template <typename VertexArray, typename Index>
210const GeometricProfile<VertexArray, Index> combine_geometric_profiles(
211 std::vector<GeometricProfile<VertexArray, Index>>& profiles)
212{
213 Index total_samples = 0, rows = 0, start_idx = 0;
214 la_runtime_assert(profiles.size() > 0, "No geometric profiles found, 0 samples generated.");
215
216 for (const auto& profile : profiles) total_samples += profile.num_samples;
217
218 VertexArray samples(total_samples, 3);
219 // Concatenate profiles
220 for (auto i : range(profiles.size())) {
221 auto profile_sample = profiles[i].samples;
222 assert(profile_sample.cols() == 3);
223
224 // Stitch profiles at merge points
225 if (rows && profile_sample.row(0).isApprox(samples.row(rows - 1))) {
226 ++start_idx;
227 }
228 Index rows_to_copy = (Index)(profile_sample.rows() - start_idx);
229 samples.block(rows, 0, rows_to_copy, 3) = profile_sample.bottomRows(rows_to_copy);
230 rows += rows_to_copy;
231 start_idx = 0;
232 }
233
234 return GeometricProfile<VertexArray, Index>(samples.topRows(rows), rows);
235}
236
237/*
238 Rotate a geometric profile
239*/
240template <typename VertexArray, typename Index, typename Scalar>
241const GeometricProfile<VertexArray, Index> rotate_geometric_profile(
243 Scalar theta)
244{
245 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
246
247 Index num_samples = profile.num_samples;
248 VertexArray samples(num_samples, 3);
249
250 auto rotation = Eigen::AngleAxis<Scalar>(theta, Vector3S::UnitY());
251
252 for (auto i : range(num_samples)) {
253 auto sample = profile.samples.row(i);
254 samples.row(i) = rotation * Vector3S(sample[0], sample[1], sample[2]);
255 }
256
257 return GeometricProfile<VertexArray, Index>(samples, num_samples);
258}
259
263template <typename MeshType>
264std::unique_ptr<MeshType> fan_triangulate_profile(
266 Eigen::Matrix<typename MeshType::Scalar, 3, 1> center =
267 Eigen::Matrix<typename MeshType::Scalar, 3, 1>(0.0, 0.0, 0.0),
268 bool flip_normals = false)
269{
270 using VertexArray = typename MeshType::VertexArray;
271 using FacetArray = typename MeshType::FacetArray;
272 using Index = typename MeshType::Index;
273
274 la_runtime_assert(profile.num_samples >= 2);
275
276 Index vertex_count = profile.num_samples + 1; // Add origin as center vertex
277 Index triangle_count = profile.spans();
278 VertexArray vertices(vertex_count, 3);
279
280 vertices.row(0) = center;
281 vertices.block(1, 0, profile.num_samples, 3) = profile.samples;
282
283 FacetArray facets(triangle_count, 3);
284 for (Index triangle_id = 0; triangle_id < triangle_count; triangle_id++) {
285 Index v0 = 0;
286 Index v1 = 1 + triangle_id; // connect new ring verts with center
287 Index v2 = 2 + triangle_id;
288
289 if (flip_normals) {
290 facets.row(triangle_id) << v0, v1, v2;
291 } else {
292 facets.row(triangle_id) << v0, v2, v1;
293 }
294 }
295
296 return lagrange::create_mesh(vertices, facets);
297}
298
302template <typename MeshType>
303std::unique_ptr<MeshType> connect_geometric_profiles_with_facets(
305 profiles)
306{
307 using VertexArray = typename MeshType::VertexArray;
308 using FacetArray = typename MeshType::FacetArray;
309 using Index = typename MeshType::Index;
310
311 Index num_profiles = safe_cast<Index>(profiles.size());
312 la_runtime_assert(num_profiles > 1, "Need more than 1 profile to connect.");
313 Index num_samples = profiles[0].num_samples;
314 Index spans = profiles[0].spans();
315 Index vertex_count = num_profiles * num_samples;
316 Index triangle_count = (num_profiles - 1) * spans * 2;
317 ;
318 VertexArray vertices(vertex_count, 3);
319 Index rows = 0;
320
321 for (auto i : range(num_profiles)) {
322 la_runtime_assert(profiles[i].num_samples == num_samples);
323 vertices.block(rows, 0, num_samples, 3) = profiles[i].samples;
324 rows += num_samples;
325 }
326
327 la_runtime_assert(rows == vertex_count);
328
329 auto get_index = [&spans](Index x, Index y) -> Index { return x * (spans + 1) + y; };
330
331 // Connect vertices, making triangle strips
332 FacetArray facets(triangle_count, 3);
333 Index triangle_id = 0;
334
335 for (auto p : range(num_profiles - 1)) {
336 for (auto span : range(spans)) {
337 auto q0 = get_index(p, span);
338 auto q1 = get_index(p + 1, span);
339 auto q2 = get_index(p + 1, span + 1);
340 auto q3 = get_index(p, span + 1);
341
342 assert(q0 <= vertex_count - 1);
343 assert(q1 <= vertex_count - 1);
344 assert(q2 <= vertex_count - 1);
345 assert(q3 <= vertex_count - 1);
346
347 // Triangles might be degenerate if points are too close; check?
348 facets.row(triangle_id) << q0, q1, q2;
349 facets.row(triangle_id + 1) << q0, q2, q3;
350 triangle_id += 2;
351 }
352 }
353 la_runtime_assert(triangle_id == triangle_count);
354 return lagrange::create_mesh(vertices, facets);
355}
356
362template <typename MeshType>
363std::unique_ptr<MeshType> sweep(
365 typename MeshType::Index sections,
366 typename MeshType::Scalar radius_top,
367 typename MeshType::Scalar radius_bottom,
368 typename MeshType::Scalar bevel_top = 0.0,
369 typename MeshType::Scalar bevel_bottom = 0.0,
370 typename MeshType::Scalar top_slice = 0.0,
371 typename MeshType::Scalar base_slice = 0.0,
372 typename MeshType::Scalar start_angle = 0.0,
373 typename MeshType::Scalar sweep_angle = 2 * lagrange::internal::pi)
374{
375 using AttributeArray = typename MeshType::AttributeArray;
376 using VertexArray = typename MeshType::VertexArray;
377 using FacetArray = typename MeshType::FacetArray;
378 using Scalar = typename MeshType::Scalar;
379 using Index = typename MeshType::Index;
380 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
381
382 const Index section_count = sections + 1; // @TODO: Causes boundary loop, else UV gap
383 const Index spans = profile.spans();
384 const Index sample_count = profile.num_samples;
385 const Index vertex_count = section_count * sample_count;
386 const Index triangle_count = sections * spans * 2;
387 VertexArray samples = profile.samples;
388
389 // Generate vertices for each section
390 VertexArray vertices(vertex_count, 3);
391 AttributeArray uvs(vertex_count, 2);
392 Index vertex_index = 0;
393
394 // L(t) is the arclength function
395 // s.t L(0.0) = 0 and
396 // L(sample_count) = L_b(bottom curve) + L_mid(straight profile) + L_t(top curve)
397 std::vector<Scalar> L(sample_count);
398 auto L_b = bevel_bottom * base_slice;
399 auto L_t = bevel_top * top_slice;
400
401 // Initialize arc lengths with default parameters
402 L[0] = 0.0;
403 for (auto t : range<Index>(1, sample_count)) {
404 Scalar segment_length = (samples.row(t) - samples.row(t - 1)).norm();
405 L[t] = L[t - 1] + segment_length;
406 }
407
408 // UV calculations when radii are different
409 //[todo]: high tolerance until normalization can maintain aspect ratio
410 constexpr Scalar uv_tol = safe_cast<Scalar>(1.5);
411
412 auto height = samples.col(1).maxCoeff() - samples.col(1).minCoeff();
413 auto angle = std::atan2(height, fabs(radius_bottom - radius_top));
414 auto phi = radius_bottom > radius_top ? angle : (0.5 * lagrange::internal::pi - angle);
415 auto psi = radius_bottom > radius_top ? 0.5 * lagrange::internal::pi - phi : phi;
416 auto tan_half_angle = std::tan(0.5 * phi);
417
418 // R is the length from apex of cone of radius r (radius top or bottom)
419 // to the end of the curve.
420 Scalar R(0.0);
421 if (radius_bottom > radius_top) {
422 /* Profile to sweep:
423 ¯¯৲Lt
424 \
425 \
426 _____)Lb
427 */
428 auto cot_half_angle = tan_half_angle > 0 ? 1 / tan_half_angle : 0.0;
429 auto H = height * radius_bottom / (radius_bottom - radius_top);
430 auto hyp = sqrt(pow(radius_bottom, 2) + pow(H, 2));
431 R = (Scalar)(hyp - bevel_bottom * cot_half_angle + L_b);
432 } else if (radius_bottom < radius_top) {
433 /* Profile to sweep:
434 ¯¯৲Lt
435 /
436 /
437 _____⎠Lb
438 */
439 auto H = height * radius_top / (radius_top - radius_bottom);
440 auto hyp = sqrt(pow(radius_top, 2) + pow(H, 2));
441 R = (Scalar)(hyp - bevel_top * tan_half_angle + L_t);
442 }
443
444 for (auto section : range(section_count)) {
445 Scalar theta = (Scalar)section / (Scalar)sections * sweep_angle + start_angle;
446 auto rotation = Eigen::AngleAxis<Scalar>(theta, Vector3S::UnitY());
447 for (auto t : range(sample_count)) {
448 // Transform the source vertex, to sweep it to new position/orientation
449 auto sample = samples.row(t);
450 vertices.row(vertex_index) = rotation * Vector3S(sample[0], sample[1], sample[2]);
451
452 // Set uvs per vertex
453 if (fabs(radius_bottom - radius_top) < uv_tol) {
454 /* Profile to sweep:
455 ¯¯¯⎞Lt
456 |
457 |
458 ___⎠Lb
459 */
460 auto ratio = (sweep_angle * radius_bottom) / sections;
461 // Map 1:1 with the uv grid
462 uvs(vertex_index, 0) = ratio * section;
463 uvs(vertex_index, 1) = L[t];
464 } else {
465 Scalar angle2 = (Scalar)(theta * std::sin(psi)); // Polar angle after unwrapping
466 auto multiplier =
467 radius_bottom > radius_top ? R - L[t] : L[t] + (R - L[sample_count - 1]);
468 uvs(vertex_index, 0) = multiplier * std::cos(angle2);
469 uvs(vertex_index, 1) = multiplier * std::sin(angle2);
470 }
471 vertex_index++;
472 }
473 }
474
475 auto get_index = [&spans](Index x, Index y) -> Index { return x * (spans + 1) + y; };
476
477 // Connect vertices, making triangle strips
478 FacetArray facets(triangle_count, 3);
479 Index triangle_id = 0;
480
481 for (auto section : range(sections)) {
482 // For full sweep, the next section for the penultimate vertex is the 0th section.
483 Index next_section = (section + 1) % section_count;
484 for (auto span : range(spans)) {
485 auto q0 = get_index(section, span);
486 auto q1 = get_index(next_section, span);
487 auto q2 = get_index(next_section, span + 1);
488 auto q3 = get_index(section, span + 1);
489
490 assert(q0 < vertex_index);
491 assert(q1 < vertex_index);
492 assert(q2 < vertex_index);
493 assert(q3 < vertex_index);
494
495 facets.row(triangle_id) << q0, q1, q2;
496 facets.row(triangle_id + 1) << q0, q2, q3;
497 triangle_id += 2;
498 }
499 }
500 auto mesh = lagrange::create_mesh(vertices, facets);
501 const auto xmin = uvs.col(0).minCoeff();
502 const auto ymin = uvs.col(1).minCoeff();
503
504 // Recenter uv coordinates to begin from (0,0)
505 if (xmin < 0.0) {
506 uvs.col(0).array() += -1.0f * xmin;
507 }
508 if (ymin < 0.0) {
509 uvs.col(1).array() += -1.0f * ymin;
510 }
511 mesh->initialize_uv(uvs, facets);
512
513 return mesh;
514}
515
520template <typename MeshType>
521std::unique_ptr<MeshType> generate_disk(
522 typename MeshType::Scalar radius,
523 typename MeshType::Index sections,
524 typename MeshType::Scalar start_angle = 0.0,
525 typename MeshType::Scalar sweep_angle = 2 * lagrange::internal::pi,
526 Eigen::Matrix<typename MeshType::Scalar, 3, 1> center =
527 Eigen::Matrix<typename MeshType::Scalar, 3, 1>(0.0, 0.0, 0.0),
528 bool flip_normals = true)
529{
530 using VertexArray = typename MeshType::VertexArray;
531 using FacetArray = typename MeshType::FacetArray;
532 using Scalar = typename MeshType::Scalar;
533 using Index = typename MeshType::Index;
534 using UVArray = typename MeshType::UVArray;
535 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
536 using Affine3S = Eigen::Transform<Scalar, 3, Eigen::Affine>;
537 using Translation3S = Eigen::Translation<Scalar, 3>;
538
539 const Index section_count = sweep_angle == 2 * lagrange::internal::pi ? sections : sections + 1;
540 Index vertex_count = section_count + 1; // Add origin as center vertex
541 Index triangle_count = sections;
542
543 auto transform = Affine3S(Translation3S(center) * Eigen::Scaling(radius));
544 Vector3S origin = transform * Vector3S::Zero();
545 VertexArray vertices(vertex_count, 3);
546 UVArray uvs(vertex_count, 2);
547 Index vertex_index = 0;
548
549 for (auto section : range(section_count)) {
550 Scalar theta = (Scalar)section / (Scalar)sections * sweep_angle + start_angle;
551 la_runtime_assert(vertex_index < vertex_count, "Index out of range");
552 auto transformed_vertex = transform * Vector3S(std::cos(theta), 0, -std::sin(theta));
553 vertices.row(vertex_index) = transformed_vertex;
554
555 // Set uvs per vertex
556 uvs(vertex_index, 0) = transformed_vertex[0];
557 uvs(vertex_index, 1) = transformed_vertex[2];
558
559 vertex_index++;
560 }
561
562 // create cap center point
563 Index center_vertex_index = vertex_index;
564 la_runtime_assert(center_vertex_index < vertex_count);
565 vertices.row(center_vertex_index) = origin;
566
567 // Add uv for origin
568 uvs(center_vertex_index, 0) = origin[0];
569 uvs(center_vertex_index, 1) = origin[2];
570
571 la_runtime_assert(++vertex_index == vertex_count);
572
573 FacetArray facets(triangle_count, 3);
574 Index triangle_id = 0;
575 for (auto section : range(sections)) {
576 Index next_section = (section + 1) % section_count;
577 la_runtime_assert(triangle_id < triangle_count);
578
579 Index v0 = center_vertex_index;
580 Index v1 = section; // connect new ring verts with center
581 Index v2 = next_section;
582
583 assert(v0 < vertex_index);
584 assert(v1 < vertex_index);
585 assert(v2 < vertex_index);
586
587 if (flip_normals) {
588 facets.row(triangle_id++) << v0, v2, v1;
589 } else {
590 facets.row(triangle_id++) << v0, v1, v2;
591 }
592 }
593 la_runtime_assert(triangle_id == triangle_count);
594
595 auto mesh = lagrange::create_mesh(vertices, facets);
596
597 // Ensure UV has positive orientation.
598 if (!flip_normals) {
599 uvs.col(0) *= -1;
600 }
601
602 // Recenter uv coordinates to begin from (0,0)
603 uvs.col(0).array() += radius;
604 uvs.col(1).array() += radius;
605
606 mesh->initialize_uv(uvs, facets);
607 return mesh;
608}
609
610// ==========================================
611// Semantic labels
612// ==========================================
613
614template <typename MeshType>
615void set_uniform_semantic_label(MeshType& mesh, const PrimitiveSemanticLabel label)
616{
617 static_assert(MeshTrait<MeshType>::is_mesh(), "this is not a mesh");
618 using AttributeArray = typename MeshType::AttributeArray;
619 using Scalar = typename MeshType::Scalar;
620 AttributeArray semantic_label =
621 AttributeArray::Constant(mesh.get_num_facets(), 1, safe_cast_enum<Scalar>(label));
622 if (!mesh.has_facet_attribute("semantic_label")) {
623 mesh.add_facet_attribute("semantic_label");
624 }
625 mesh.import_facet_attribute("semantic_label", semantic_label);
626}
627
628// ==========================================
629// Spherical Projection Per Corner UVs
630// ==========================================
631
632template <typename MeshType>
633typename MeshType::UVArray compute_spherical_uv_mapping(
634 const MeshType& mesh,
635 const Eigen::Matrix<typename MeshType::Scalar, 3, 1>& center)
636{
637 using Index = typename MeshType::Index;
638 using UVArray = typename MeshType::UVArray;
639 using Scalar = typename MeshType::Scalar;
640
641 const Index num_vertices = mesh.get_num_vertices();
642 const auto& vertices = mesh.get_vertices();
643
644 UVArray uvs_per_vertex(num_vertices, 2);
645
646 for (auto i : range(num_vertices)) {
647 const auto vertex = (vertices.row(i) - center.transpose()).eval();
648 const Scalar theta = std::atan2(vertex[0], vertex[2]);
649 const Scalar phi = lagrange::internal::pi - std::acos(vertex[1] / vertex.norm());
650 uvs_per_vertex.row(i) << (theta + lagrange::internal::pi) / (2 * lagrange::internal::pi),
651 phi / lagrange::internal::pi;
652 }
653
654 const Index num_facets = mesh.get_num_facets();
655 const auto& facets = mesh.get_facets();
656 const Index vertex_per_facet = mesh.get_vertex_per_facet();
657 UVArray uvs(num_facets * vertex_per_facet, uvs_per_vertex.cols());
658
659 // Assign UVs per face
660 for (Index i = 0; i < num_facets; i++) {
661 for (Index j = 0; j < vertex_per_facet; j++) {
662 uvs.row(i * vertex_per_facet + j) = uvs_per_vertex.row(facets(i, j));
663 }
664 }
665
666 // Correct Seams
667 constexpr Scalar tol = 1e-6;
668 constexpr Scalar UV_THRESH = 1.0 / 3.0 + tol;
669
670 for (Index i = 0; i < num_facets; i++) {
671 Index face_idx = i * vertex_per_facet;
672 Scalar min_u = 2;
673 Scalar max_u = -2;
674 std::vector<bool> vertices_on_border(vertex_per_facet, false);
675 Scalar sum_mid_u = 0;
676 Index count = 0;
677 bool left_side = true;
678 for (auto j : range(vertex_per_facet)) {
679 const Scalar u = uvs(face_idx + j, 0);
680 vertices_on_border[j] = (u < tol) || (u > 1.0 - tol);
681 if (!vertices_on_border[j]) {
682 sum_mid_u += u;
683 count++;
684 }
685 if (u < min_u) min_u = u;
686 if (u > max_u) max_u = u;
687 }
688 left_side = (sum_mid_u / count) < 0.5;
689
690 if (max_u > min_u + UV_THRESH) {
691 for (auto j : range(vertex_per_facet)) {
692 if (vertices_on_border[j]) {
693 auto& u = uvs(face_idx + j, 0);
694 if (left_side && u > 1.0 - tol) u = 0.0;
695 if (!left_side && u < tol) u = 1.0;
696 }
697 }
698 }
699 }
700
701 // Correct Poles: v = 0 or v = 1
702 if (vertex_per_facet == 3) {
703 for (Index i = 0; i < num_facets; i++) {
704 for (Index j = 0; j < vertex_per_facet; j++) {
705 const Index face_idx = i * vertex_per_facet;
706 const Scalar v = uvs(face_idx + j, 1);
707 if (v < tol || v > 1.0 - tol) {
708 Index prev = j < 1 ? j + vertex_per_facet - 1 : j - 1;
709 Index next = (j + 1) % vertex_per_facet;
710
711 // Average of residual vertices
712 uvs(face_idx + j, 0) =
713 0.5 * (uvs(face_idx + prev, 0) + uvs(face_idx + next, 0));
714 }
715 }
716 }
717 } else if (vertex_per_facet == 4) {
718 for (Index i = 0; i < num_facets; i++) {
719 for (Index j = 0; j < vertex_per_facet; j++) {
720 const Index face_idx = i * vertex_per_facet;
721 const Scalar v = uvs(face_idx + j, 1);
722 if (v < tol || v > 1.0 - tol) {
723 Index prev = j < 1 ? j + vertex_per_facet - 1 : j - 1;
724 Index next = (j + 1) % vertex_per_facet;
725 Index opposite = (j + 2) % vertex_per_facet;
726
727 // Theta_pole = t - (theta_opposite - t),
728 // where t = 0.5 * (theta_prev + theta_next)
729 const Scalar t = 0.5 * (uvs(face_idx + prev, 0) + uvs(face_idx + next, 0));
730 uvs(face_idx + j, 0) = t - (uvs(face_idx + opposite, 0) - t);
731 continue;
732 }
733 }
734 }
735 } else {
736 throw std::runtime_error("Not supported");
737 }
738 return uvs;
739}
740
741} // namespace legacy
742} // namespace lagrange
Index get_num_vertices() const
Retrieves the number of vertices.
Definition SurfaceMesh.h:680
Index get_num_facets() const
Retrieves the number of facets.
Definition SurfaceMesh.h:687
Index get_vertex_per_facet() const
Retrieves the number of vertex per facet in a regular mesh.
Definition SurfaceMesh.cpp:2336
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
#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
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition span.h:27
constexpr T safe_cast_enum(const U u)
Casting an enum to scalar and vice versa.
Definition safe_cast.h:163
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
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
Definition generation_utils.h:53
Definition generation_utils.h:42