16#include <lagrange/Mesh.h>
17#include <lagrange/attributes/attribute_utils.h>
18#include <lagrange/bvh/zip_boundary.h>
19#include <lagrange/combine_mesh_list.h>
20#include <lagrange/compute_normal.h>
21#include <lagrange/internal/constants.h>
22#include <lagrange/legacy/inline.h>
23#include <lagrange/mesh_cleanup/remove_degenerate_triangles.h>
24#include <lagrange/packing/compute_rectangle_packing.h>
25#include <lagrange/primitive/generation_utils.h>
26#include <lagrange/utils/safe_cast.h>
36 using Index = uint32_t;
41 Scalar major_radius = 5;
42 Scalar minor_radius = 1;
43 Index ring_segments = 50;
44 Index pipe_segments = 50;
45 Eigen::Matrix<Scalar, 3, 1> center{0, 0, 0};
46 Scalar start_sweep_angle = 0;
47 Scalar end_sweep_angle =
static_cast<Scalar
>(2 * lagrange::internal::pi);
48 bool with_caps =
true;
53 bool output_normals =
true;
73 Scalar
epsilon =
static_cast<Scalar
>(1e-6);
84 minor_radius = std::max(minor_radius,
static_cast<Scalar
>(0.0));
85 major_radius = std::max(major_radius,
static_cast<Scalar
>(minor_radius));
86 ring_segments = std::max(ring_segments,
static_cast<Index
>(3));
87 pipe_segments = std::max(pipe_segments,
static_cast<Index
>(3));
91template <
typename MeshType>
94 using AttributeArray =
typename MeshType::AttributeArray;
95 using VertexArray =
typename MeshType::VertexArray;
96 using FacetArray =
typename MeshType::FacetArray;
97 using UVArray =
typename MeshType::UVArray;
98 using Scalar =
typename MeshType::Scalar;
99 using Index =
typename MeshType::Index;
100 using Point = Eigen::Matrix<Scalar, 1, 3>;
101 using Generator_function = std::function<Point(
Scalar)>;
103 config.project_to_valid_range();
108 if (config.major_radius < config.dist_threshold) {
109 auto mesh = create_empty_mesh<VertexArray, FacetArray>();
110 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::SIDE);
114 std::vector<std::shared_ptr<MeshType>> meshes;
116 Scalar torus_start_angle = lagrange::internal::pi;
117 Scalar torus_slice = 2 * lagrange::internal::pi;
118 Scalar sweep_angle = compute_sweep_angle(config.start_sweep_angle, config.end_sweep_angle);
120 sweep_angle = std::min<Scalar>(2 * lagrange::internal::pi, sweep_angle);
126 Generator_function torus_generator = lagrange::partial_torus_generator<Scalar, Point>(
132 auto torus_profile = generate_profile<MeshType, Point>(torus_generator, config.pipe_segments);
133 auto torus_mesh = lagrange::sweep<MeshType>(
142 config.start_sweep_angle,
145 meshes.push_back(std::move(torus_mesh));
149 if (config.with_caps && config.major_radius > config.dist_threshold &&
150 sweep_angle < 2 * lagrange::internal::pi - config.epsilon) {
151 using Vector3S = Eigen::Matrix<Scalar, 3, 1>;
153 auto rotated_profile = lagrange::rotate_geometric_profile(
155 static_cast<Scalar
>(config.start_sweep_angle));
156 auto end_profile = lagrange::rotate_geometric_profile(rotated_profile, sweep_angle);
157 auto rotation_start = Eigen::AngleAxis<Scalar>(config.start_sweep_angle, Vector3S::UnitY());
158 auto rotation_subtended = Eigen::AngleAxis<Scalar>(sweep_angle, Vector3S::UnitY());
160 auto start_sample = torus_profile.samples.row(0);
161 auto cross_section_center =
162 Vector3S(start_sample[0] + config.minor_radius, start_sample[1], start_sample[2]);
163 auto center_start = rotation_start * cross_section_center;
164 auto center_end = rotation_subtended * center_start;
166 auto cross_section_start =
167 lagrange::fan_triangulate_profile<MeshType>(rotated_profile, center_start,
true);
168 auto cross_section_end =
169 lagrange::fan_triangulate_profile<MeshType>(end_profile, center_end);
172 AttributeArray uvs(cross_section_start->get_num_vertices(), 2);
173 auto uv_samples = torus_profile.samples.leftCols(2);
174 uvs.row(0) << cross_section_center[0], cross_section_center[1];
175 uvs.block(1, 0, torus_profile.num_samples, 2) = uv_samples;
177 const auto xmin = uvs.col(0).minCoeff();
178 const auto ymin = uvs.col(1).minCoeff();
182 uvs.col(0).array() += -1.0 * xmin;
185 uvs.col(1).array() += -1.0 * ymin;
188 cross_section_start->initialize_uv(uvs, cross_section_start->get_facets());
189 cross_section_end->initialize_uv(uvs, cross_section_end->get_facets());
191 meshes.push_back(std::move(cross_section_start));
192 meshes.push_back(std::move(cross_section_end));
196 auto mesh = lagrange::combine_mesh_list(meshes,
true);
197 const auto& vertices = mesh->get_vertices();
198 const auto bbox_diag = (vertices.colwise().maxCoeff() - vertices.colwise().minCoeff()).norm();
200 mesh = lagrange::bvh::zip_boundary(
203 static_cast<Scalar>(1e-6 * bbox_diag),
204 static_cast<Scalar>(config.dist_threshold)));
207 if (config.output_normals) {
213 if (mesh->is_uv_initialized()) {
214 const auto uv_mesh = mesh->get_uv_mesh();
215 UVArray uvs = uv_mesh->get_vertices();
216 normalize_to_unit_box(uvs);
217 mesh->initialize_uv(uvs, uv_mesh->get_facets());
221 mesh = remove_degenerate_triangles(*mesh);
224 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
226 packing::compute_rectangle_packing(*mesh);
230template <
typename MeshType>
231std::unique_ptr<MeshType> generate_torus(
232 typename MeshType::Scalar major_radius,
233 typename MeshType::Scalar minor_radius,
234 typename MeshType::Index ring_segments = 50,
235 typename MeshType::Index pipe_segments = 50,
236 Eigen::Matrix<typename MeshType::Scalar, 3, 1> center =
237 Eigen::Matrix<typename MeshType::Scalar, 3, 1>(0.0, 0.0, 0.0),
238 typename MeshType::Scalar start_sweep_angle = 0.0,
239 typename MeshType::Scalar end_sweep_angle = 2 * lagrange::internal::pi)
241 using Scalar =
typename TorusConfig::Scalar;
242 using Index =
typename TorusConfig::Index;
253 return generate_torus<MeshType>(std::move(config));
@ 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
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
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_torus(TorusOptions setting)
Generate a torus mesh.
Definition generate_torus.cpp:42
Main namespace for Lagrange.
Definition generate_torus.h:34
void project_to_valid_range()
Project config setting into valid range.
Definition generate_torus.h:82
Scalar dist_threshold
Two vertices are considered coinciding iff the distance between them is smaller than dist_threshold.
Definition generate_torus.h:62
Scalar angle_threshold
An edge is considered sharp if its dihedral angle is larger than angle_threshold.
Definition generate_torus.h:68
Scalar epsilon
Numerical tolerence used for comparing Scalar values.
Definition generate_torus.h:73