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>
29enum class PrimitiveSemanticLabel : std::uint8_t { SIDE = 0, TOP = 1, BOTTOM = 2, UNKNOWN };
31template <
typename Scalar>
34 while (end_angle < begin_angle) {
35 end_angle += (
Scalar)(2 * lagrange::internal::pi);
37 return end_angle - begin_angle;
40template <
typename VertexArray,
typename FacetArray>
48 std::vector<std::vector<typename FacetArray::Scalar>> segment_indices;
51template <
typename VertexArray,
typename Index>
52struct GeometricProfile
54 GeometricProfile(VertexArray _samples, Index _num_samples)
56 , num_samples(_num_samples)
65 Index spans()
const {
return num_samples > 0 ? num_samples - 1 : 0; }
72template <
typename VertexType,
typename Scalar>
73VertexType project_to_sphere(
const VertexType& center,
const VertexType& point,
const Scalar radius)
75 const VertexType PC = point - center;
76 auto length = PC.norm();
81 auto scale = radius / length;
82 return scale * PC + center;
89template <
typename VertexType,
typename Scalar,
typename Index>
90VertexType project_to_sphere(
91 const VertexType& center,
97 Scalar theta = row < size ? (col * lagrange::internal::pi) / (2 * (size - row)) : 0.0;
98 Scalar phi = ((size - row) * lagrange::internal::pi) / (2 * size);
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);
109template <
typename VertexArray>
110void normalize_to_unit_box(VertexArray& input_vertices)
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()});
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;
131template <
typename VertexArray,
typename Index>
132std::tuple<VertexArray, std::vector<Index>> divide_line_into_segments(
133 const VertexArray& vertices,
136 const Index num_segments)
138 using Scalar =
typename VertexArray::Scalar;
139 using IndexList = std::vector<Index>;
142 IndexList output_indices(num_segments + 1);
143 VertexArray output_vertices(num_vertices + num_segments - 1, vertices.cols());
145 output_vertices.topRows(num_vertices) = vertices;
146 output_indices[0] = v1;
148 for (
auto i :
range(num_segments - 1)) {
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;
155 output_indices[num_segments] = v2;
156 return std::make_tuple(output_vertices, output_indices);
164template <
typename Scalar,
typename Po
int>
165std::function<Point(
Scalar)> partial_torus_generator(
168 Eigen::Matrix<Scalar, 3, 1> center,
172 return [=](
Scalar t) -> Point {
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();
185template <
typename MeshType,
typename Po
int>
187 std::function<Point(
typename MeshType::Scalar)> generate_vertex,
188 typename MeshType::Index spans,
189 bool reverse_profile =
false)
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)) {
197 if (reverse_profile) {
198 samples.row(sample_idx) = generate_vertex(1 - t);
200 samples.row(sample_idx) = generate_vertex(t);
209template <
typename VertexArray,
typename Index>
213 Index total_samples = 0, rows = 0, start_idx = 0;
214 la_runtime_assert(profiles.size() > 0,
"No geometric profiles found, 0 samples generated.");
216 for (
const auto& profile : profiles) total_samples += profile.num_samples;
218 VertexArray samples(total_samples, 3);
220 for (
auto i :
range(profiles.size())) {
221 auto profile_sample = profiles[i].samples;
222 assert(profile_sample.cols() == 3);
225 if (rows && profile_sample.row(0).isApprox(samples.row(rows - 1))) {
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;
240template <
typename VertexArray,
typename Index,
typename Scalar>
245 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
247 Index num_samples = profile.num_samples;
248 VertexArray samples(num_samples, 3);
250 auto rotation = Eigen::AngleAxis<Scalar>(theta, Vector3S::UnitY());
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]);
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)
270 using VertexArray =
typename MeshType::VertexArray;
271 using FacetArray =
typename MeshType::FacetArray;
272 using Index =
typename MeshType::Index;
276 Index vertex_count = profile.num_samples + 1;
277 Index triangle_count = profile.spans();
278 VertexArray vertices(vertex_count, 3);
280 vertices.row(0) = center;
281 vertices.block(1, 0, profile.num_samples, 3) = profile.samples;
283 FacetArray facets(triangle_count, 3);
284 for (Index triangle_id = 0; triangle_id < triangle_count; triangle_id++) {
286 Index v1 = 1 + triangle_id;
287 Index v2 = 2 + triangle_id;
290 facets.row(triangle_id) << v0, v1, v2;
292 facets.row(triangle_id) << v0, v2, v1;
302template <
typename MeshType>
303std::unique_ptr<MeshType> connect_geometric_profiles_with_facets(
307 using VertexArray =
typename MeshType::VertexArray;
308 using FacetArray =
typename MeshType::FacetArray;
309 using Index =
typename MeshType::Index;
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;
318 VertexArray vertices(vertex_count, 3);
321 for (
auto i :
range(num_profiles)) {
323 vertices.block(rows, 0, num_samples, 3) = profiles[i].samples;
329 auto get_index = [&spans](Index x, Index y) -> Index {
return x * (spans + 1) + y; };
332 FacetArray facets(triangle_count, 3);
333 Index triangle_id = 0;
335 for (
auto p :
range(num_profiles - 1)) {
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);
342 assert(q0 <= vertex_count - 1);
343 assert(q1 <= vertex_count - 1);
344 assert(q2 <= vertex_count - 1);
345 assert(q3 <= vertex_count - 1);
348 facets.row(triangle_id) << q0, q1, q2;
349 facets.row(triangle_id + 1) << q0, q2, q3;
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)
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>;
382 const Index section_count = sections + 1;
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;
390 VertexArray vertices(vertex_count, 3);
391 AttributeArray uvs(vertex_count, 2);
392 Index vertex_index = 0;
397 std::vector<Scalar> L(sample_count);
398 auto L_b = bevel_bottom * base_slice;
399 auto L_t = bevel_top * top_slice;
404 Scalar segment_length = (samples.row(t) - samples.row(t - 1)).norm();
405 L[t] = L[t - 1] + segment_length;
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);
421 if (radius_bottom > radius_top) {
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) {
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);
444 for (
auto section :
range(section_count)) {
446 auto rotation = Eigen::AngleAxis<Scalar>(theta, Vector3S::UnitY());
447 for (
auto t :
range(sample_count)) {
449 auto sample = samples.row(t);
450 vertices.row(vertex_index) = rotation * Vector3S(sample[0], sample[1], sample[2]);
453 if (fabs(radius_bottom - radius_top) < uv_tol) {
460 auto ratio = (sweep_angle * radius_bottom) / sections;
462 uvs(vertex_index, 0) = ratio * section;
463 uvs(vertex_index, 1) = L[t];
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);
475 auto get_index = [&spans](Index x, Index y) -> Index {
return x * (spans + 1) + y; };
478 FacetArray facets(triangle_count, 3);
479 Index triangle_id = 0;
481 for (
auto section :
range(sections)) {
483 Index next_section = (section + 1) % section_count;
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);
490 assert(q0 < vertex_index);
491 assert(q1 < vertex_index);
492 assert(q2 < vertex_index);
493 assert(q3 < vertex_index);
495 facets.row(triangle_id) << q0, q1, q2;
496 facets.row(triangle_id + 1) << q0, q2, q3;
501 const auto xmin = uvs.col(0).minCoeff();
502 const auto ymin = uvs.col(1).minCoeff();
506 uvs.col(0).array() += -1.0f * xmin;
509 uvs.col(1).array() += -1.0f * ymin;
511 mesh->initialize_uv(uvs, facets);
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)
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>;
539 const Index section_count = sweep_angle == 2 * lagrange::internal::pi ? sections : sections + 1;
540 Index vertex_count = section_count + 1;
541 Index triangle_count = sections;
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;
549 for (
auto section :
range(section_count)) {
552 auto transformed_vertex = transform * Vector3S(std::cos(theta), 0, -std::sin(theta));
553 vertices.row(vertex_index) = transformed_vertex;
556 uvs(vertex_index, 0) = transformed_vertex[0];
557 uvs(vertex_index, 1) = transformed_vertex[2];
563 Index center_vertex_index = vertex_index;
565 vertices.row(center_vertex_index) = origin;
568 uvs(center_vertex_index, 0) = origin[0];
569 uvs(center_vertex_index, 1) = origin[2];
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;
579 Index v0 = center_vertex_index;
581 Index v2 = next_section;
583 assert(v0 < vertex_index);
584 assert(v1 < vertex_index);
585 assert(v2 < vertex_index);
588 facets.row(triangle_id++) << v0, v2, v1;
590 facets.row(triangle_id++) << v0, v1, v2;
603 uvs.col(0).array() += radius;
604 uvs.col(1).array() += radius;
606 mesh->initialize_uv(uvs, facets);
614template <
typename MeshType>
615void set_uniform_semantic_label(MeshType& mesh,
const PrimitiveSemanticLabel label)
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 =
622 if (!mesh.has_facet_attribute(
"semantic_label")) {
623 mesh.add_facet_attribute(
"semantic_label");
625 mesh.import_facet_attribute(
"semantic_label", semantic_label);
632template <
typename MeshType>
633typename MeshType::UVArray compute_spherical_uv_mapping(
634 const MeshType& mesh,
635 const Eigen::Matrix<typename MeshType::Scalar, 3, 1>& center)
637 using Index =
typename MeshType::Index;
638 using UVArray =
typename MeshType::UVArray;
639 using Scalar =
typename MeshType::Scalar;
642 const auto& vertices = mesh.get_vertices();
644 UVArray uvs_per_vertex(num_vertices, 2);
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;
655 const auto& facets = mesh.get_facets();
657 UVArray uvs(num_facets * vertex_per_facet, uvs_per_vertex.cols());
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));
667 constexpr Scalar tol = 1e-6;
668 constexpr Scalar UV_THRESH = 1.0 / 3.0 + tol;
670 for (Index i = 0; i < num_facets; i++) {
671 Index face_idx = i * vertex_per_facet;
674 std::vector<bool> vertices_on_border(vertex_per_facet,
false);
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]) {
685 if (u < min_u) min_u = u;
686 if (u > max_u) max_u = u;
688 left_side = (sum_mid_u / count) < 0.5;
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;
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;
712 uvs(face_idx + j, 0) =
713 0.5 * (uvs(face_idx + prev, 0) + uvs(face_idx + next, 0));
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;
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);
736 throw std::runtime_error(
"Not supported");
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