Lagrange
Loading...
Searching...
No Matches
generate_rounded_plane.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/Edge.h>
18#include <lagrange/Mesh.h>
19#include <lagrange/common.h>
20#include <lagrange/compute_vertex_normal.h>
21#include <lagrange/create_mesh.h>
22#include <lagrange/legacy/inline.h>
23#include <lagrange/mesh_cleanup/remove_degenerate_triangles.h>
24#include <lagrange/mesh_cleanup/remove_duplicate_vertices.h>
25#include <lagrange/primitive/generation_utils.h>
26#include <lagrange/utils/safe_cast.h>
27
28
29namespace lagrange {
30namespace primitive {
31LAGRANGE_LEGACY_INLINE
32namespace legacy {
33
34namespace Plane {
35
36template <typename MeshType>
37typename MeshType::VertexArray generate_vertices(
38 typename MeshType::Scalar width,
39 typename MeshType::Scalar height,
40 typename MeshType::Scalar radius,
41 const Eigen::Matrix<ScalarOf<MeshType>, 3, 1>& center);
42
43template <typename VertexArray, typename FacetArray>
44SubdividedMeshData<VertexArray, FacetArray> subdivide_corners(
45 const VertexArray& vertices,
46 const FacetArray& corner_tris,
47 typename FacetArray::Scalar segments);
48
49template <typename MeshType>
50typename MeshType::FacetArray generate_facets();
51
52template <typename MeshType>
53typename MeshType::FacetArray generate_corner_triangles();
54
55template <typename MeshType>
56typename MeshType::FacetArray generate_quads();
57
58template <typename MeshType>
59typename MeshType::AttributeArray generate_uvs(
60 typename MeshType::Scalar width,
61 typename MeshType::Scalar height,
62 typename MeshType::Scalar radius);
63
64template <typename MeshType>
65void generate_normals(MeshType& mesh);
66} // namespace Plane
67
69{
70 using Scalar = float;
71 using Index = uint32_t;
72
76 Scalar width = 1;
77 Scalar height = 1;
78 Scalar radius = 0;
79 Index num_segments = 1;
80 Eigen::Matrix<Scalar, 3, 1> center{0, 0, 0};
81
85 bool output_normals = true;
86
90
94 Scalar dist_threshold = static_cast<Scalar>(1e-6);
96
104 {
105 width = std::max(width, static_cast<Scalar>(0.0f));
106 height = std::max(height, static_cast<Scalar>(0.0f));
107 radius = std::min(
108 std::max(radius, static_cast<Scalar>(0.0f)),
109 (std::min(width, height)) / static_cast<Scalar>(2.0f));
110 num_segments = std::max(num_segments, static_cast<Index>(0));
111 }
112};
113
114template <typename MeshType>
115std::unique_ptr<MeshType> generate_rounded_plane(RoundedPlaneConfig config)
116{
117 using VertexArray = typename MeshType::VertexArray;
118 using FacetArray = typename MeshType::FacetArray;
119 using Scalar = typename MeshType::Scalar;
120 using Index = typename MeshType::Index;
121 using VertexType = typename MeshType::VertexType;
122
123 config.project_to_valid_range();
124
125 /*
126 * Handle Empty Mesh
127 */
128 if (config.width < config.dist_threshold || config.height < config.dist_threshold) {
129 auto mesh = create_empty_mesh<VertexArray, FacetArray>();
130 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
131 return mesh;
132 }
133
134 auto vertices = Plane::generate_vertices<MeshType>(
135 config.width,
136 config.height,
137 2 * config.radius,
138 config.center.template cast<Scalar>());
139 auto facets = Plane::generate_facets<MeshType>();
140 auto corner_tris = Plane::generate_corner_triangles<MeshType>();
141 auto quads = Plane::generate_quads<MeshType>();
142 auto uvs = Plane::generate_uvs<MeshType>(config.width, config.height, config.radius);
143 FacetArray concat_tris;
144
145 if (config.radius < config.dist_threshold) {
146 auto mesh = lagrange::create_mesh(vertices, facets);
147
148 // Set default uvs
149 normalize_to_unit_box(uvs);
150 mesh->initialize_uv(uvs, facets);
151 mesh = remove_duplicate_vertices(*mesh, "", false);
152
153 // Set normals
154 if (config.output_normals) {
155 Plane::generate_normals(*mesh);
156 }
157
158 // Set uniform semantic label
159 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
160
161 return mesh;
162
163 } else if (config.num_segments <= 1) {
164 concat_tris.resize(facets.rows() + corner_tris.rows() + quads.rows(), 3);
165 concat_tris << facets, corner_tris, quads;
166
167 auto mesh = lagrange::create_mesh(vertices, concat_tris);
168
169 // Set default uvs
170 normalize_to_unit_box(uvs);
171 mesh->initialize_uv(uvs, concat_tris);
172
173 if (config.width > config.dist_threshold && config.height > config.dist_threshold) {
174 mesh = remove_degenerate_triangles(*mesh);
175 }
176
177 // Set normals
178 if (config.output_normals) {
179 Plane::generate_normals(*mesh);
180 }
181
182 // Set uniform semantic label
183 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
184
185 return mesh;
186 }
187
188 auto vertex_data =
189 Plane::subdivide_corners(vertices, corner_tris, safe_cast<Index>(config.num_segments));
190
191 auto uv_data =
192 Plane::subdivide_corners(uvs, corner_tris, safe_cast<Index>(config.num_segments));
193
194 auto output_vertices = vertex_data.vertices;
195 auto output_uvs = uv_data.vertices;
196
197 // Project 4 Corner Points and UVs to Circle
198 Eigen::Matrix<Index, Eigen::Dynamic, 1> centers = corner_tris.col(0);
199 assert(centers.rows() == 4);
200 for (auto i : range(centers.rows())) {
201 VertexType center = vertices.row(centers[i]);
202 Eigen::Matrix<Scalar, Eigen::Dynamic, 2> uv_center = uvs.row(centers[i]);
203 auto vertex_indices = vertex_data.segment_indices[i];
204 auto uv_indices = uv_data.segment_indices[i];
205 Index size = safe_cast<Index>(vertex_indices.size());
206 for (Index col : range(size)) {
207 // Project Vertices
208 VertexType point = output_vertices.row(vertex_indices[col]);
209 output_vertices.row(vertex_indices[col]) =
210 project_to_sphere(center, point, config.radius);
211
212 // Project UVs
213 Eigen::Matrix<Scalar, Eigen::Dynamic, 2> uv_point = output_uvs.row(uv_indices[col]);
214 output_uvs.row(uv_indices[col]) = project_to_sphere(uv_center, uv_point, config.radius);
215 }
216 }
217
218 concat_tris.resize(facets.rows() + quads.rows() + vertex_data.triangles.rows(), 3);
219 concat_tris << facets, quads, vertex_data.triangles;
220 auto mesh = lagrange::create_mesh(output_vertices, concat_tris);
221
222 // Set uvs
223 normalize_to_unit_box(output_uvs);
224 mesh->initialize_uv(output_uvs, concat_tris);
225
226 // Clean mesh
227 if (config.width > config.dist_threshold && config.height > config.dist_threshold) {
228 mesh = remove_degenerate_triangles(*mesh);
229 }
230
231 // Set normals
232 if (config.output_normals) {
233 Plane::generate_normals(*mesh);
234 }
235
236 // Set uniform semantic label
237 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
238 return mesh;
239}
240
241template <typename MeshType>
242std::unique_ptr<MeshType> generate_rounded_plane(
243 typename MeshType::Scalar width,
244 typename MeshType::Scalar height,
245 typename MeshType::Scalar radius,
246 typename MeshType::Index num_segments)
247{
248 using Scalar = typename RoundedPlaneConfig::Scalar;
249 using Index = typename RoundedPlaneConfig::Index;
250
251 RoundedPlaneConfig config;
252 config.width = safe_cast<Scalar>(width);
253 config.height = safe_cast<Scalar>(height);
254 config.radius = safe_cast<Scalar>(radius);
255 config.num_segments = safe_cast<Index>(num_segments);
256
257 return generate_rounded_plane<MeshType>(std::move(config));
258}
259
260namespace Plane {
261
262template <typename MeshType>
263typename MeshType::VertexArray generate_vertices(
264 typename MeshType::Scalar width,
265 typename MeshType::Scalar height,
266 typename MeshType::Scalar radius,
267 const Eigen::Matrix<ScalarOf<MeshType>, 3, 1>& center)
268{
269 typename MeshType::VertexArray vertices(12, 3);
270 // clang-format off
271 vertices <<
272 // Plane Vertices
273 // Top
274 -width + radius, 0, height - radius,
275 width - radius, 0, height - radius,
276
277 // Bottom
278 width - radius, 0, -height + radius,
279 -width + radius, 0, -height + radius,
280
281 // Top Bevelled Vertices
282 -width + radius, 0, height,
283 width - radius, 0, height,
284 width, 0, height - radius,
285 -width, 0, height - radius,
286
287 // Bottom Bevelled Vertices
288 -width, 0, -height + radius,
289 width, 0, -height + radius,
290 width - radius, 0, -height,
291 -width + radius, 0, -height;
292 // clang-format on
293
294 return (vertices / 2).rowwise() + center.transpose();
295}
296
297template <typename MeshType>
298typename MeshType::FacetArray generate_facets()
299{
300 typename MeshType::FacetArray facets(2, 3);
301 facets << 0, 2, 3, 0, 1, 2;
302 return facets;
303}
304
305template <typename MeshType>
306typename MeshType::FacetArray generate_corner_triangles()
307{
308 typename MeshType::FacetArray triangles(4, 3);
309 triangles <<
310 // Top Triangles
311 0,
312 7, 4, 1, 5, 6,
313
314 // Bottom Triangles
315 2, 9, 10, 3, 11, 8;
316
317 return triangles;
318}
319
320template <typename MeshType>
321typename MeshType::FacetArray generate_quads()
322{
323 typename MeshType::FacetArray quads(8, 3);
324 quads << 0, 4, 5, 0, 5, 1, 0, 3, 8, 0, 8, 7, 1, 6, 9, 1, 9, 2, 3, 2, 10, 3, 10, 11;
325 return quads;
326}
327
328template <typename MeshType>
329typename MeshType::AttributeArray generate_uvs(
330 const typename MeshType::Scalar width,
331 const typename MeshType::Scalar height,
332 const typename MeshType::Scalar radius)
333{
334 typename MeshType::AttributeArray uvs(12, 2);
335 const auto h = height - 2 * radius;
336 const auto w = width - 2 * radius;
337 const auto r = radius;
338 // UVs per vertex
339
340 // Top
341 uvs.row(0) << r, r + h;
342 uvs.row(1) << r + w, r + h;
343
344 // Bottom
345 uvs.row(2) << r + w, r;
346 uvs.row(3) << r, r;
347
348 // Top Bevelled
349 uvs.row(4) << r, h + 2 * r;
350 uvs.row(5) << r + w, h + 2 * r;
351 uvs.row(6) << 2 * r + w, r + h;
352 uvs.row(7) << 0, r + h;
353
354 // Bottom Bevelled
355 uvs.row(8) << 0, r;
356 uvs.row(9) << 2 * r + w, r;
357 uvs.row(10) << r + w, 0;
358 uvs.row(11) << r, 0;
359
360 uvs.col(1) *= -1;
361 return uvs;
362}
363
364template <typename VertexArray, typename FacetArray>
365SubdividedMeshData<VertexArray, FacetArray> subdivide_corners(
366 const VertexArray& vertices,
367 const FacetArray& corner_tris,
368 typename FacetArray::Scalar num_segments)
369{
370 using Index = typename FacetArray::Scalar;
371 using IndexList = std::vector<Index>;
372
373 assert(corner_tris.rows() == 4);
374 SubdividedMeshData<VertexArray, FacetArray> subdiv_data;
375 std::vector<IndexList> index_list;
376 FacetArray subdivided_tris(num_segments * 4, 3);
377 VertexArray output_vertices = vertices;
378 IndexList seg_indices;
379
380 Index sub_idx = 0;
381 for (Index i = 0; i < 4; i++) {
382 auto triangle = corner_tris.row(i);
383 std::tie(output_vertices, seg_indices) =
384 divide_line_into_segments(output_vertices, triangle[1], triangle[2], num_segments);
385
386 for (auto j : range(num_segments)) {
387 subdivided_tris.row(sub_idx++) << triangle[0], seg_indices[j], seg_indices[j + 1];
388 }
389 index_list.push_back(seg_indices);
390 }
391
392 subdiv_data.vertices = output_vertices;
393 subdiv_data.triangles = subdivided_tris;
394 subdiv_data.segment_indices = index_list;
395 return subdiv_data;
396}
397
398template <typename MeshType>
399void generate_normals(MeshType& mesh)
400{
401 typename MeshType::AttributeArray normals(1, 3);
402 normals << 0, 1, 0;
403 typename MeshType::IndexArray indices(mesh.get_num_facets(), 3);
404 indices.setZero();
405 mesh.add_indexed_attribute("normal");
406 mesh.set_indexed_attribute("normal", std::move(normals), std::move(indices));
407}
408
409} // namespace Plane
410
411} // namespace legacy
412} // namespace primitive
413} // namespace lagrange
Index get_num_facets() const
Retrieves the number of facets.
Definition SurfaceMesh.h:687
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
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.
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_plane(RoundedPlaneOptions settings)
Generate a rounded plane mesh.
Definition generate_rounded_plane.cpp:126
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
void remove_duplicate_vertices(SurfaceMesh< Scalar, Index > &mesh, const RemoveDuplicateVerticesOptions &options={})
Removes duplicate vertices from a mesh.
Definition remove_duplicate_vertices.cpp:33
Definition generate_rounded_plane.h:69
void project_to_valid_range()
Project config setting into valid range.
Definition generate_rounded_plane.h:103
Scalar dist_threshold
Two vertices are considered coinciding iff the distance between them is smaller than dist_threshold.
Definition generate_rounded_plane.h:94