Lagrange
marching_triangles.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 <lagrange/Edge.h>
15#include <lagrange/MeshTrait.h>
16#include <lagrange/legacy/inline.h>
17#include <lagrange/utils/assert.h>
18#include <lagrange/utils/range.h>
19
20#include <Eigen/Core>
21
22#include <map>
23#include <memory>
24#include <string>
25#include <unordered_map>
26#include <vector>
27
28namespace lagrange {
29LAGRANGE_LEGACY_INLINE
30namespace legacy {
31
32template <typename MeshType_>
34{
35 using MeshType = MeshType_;
36 using Index = typename MeshType::Index;
37 using IndexList = typename MeshType::IndexList;
38 using Scalar = typename MeshType::Scalar;
39 using VertexArray = typename MeshType::VertexArray;
40 using EdgeArray = Eigen::Matrix<Index, Eigen::Dynamic, 2, Eigen::RowMajor>;
41
42 // The extracted vertices of the contour
43 VertexArray vertices;
44 // Note that the direction in the edges bears no particular meaning
45 EdgeArray edges;
46 // The edge parent that gives birth to a marching triangle vertex
47 IndexList vertices_parent_edge;
48 // The param ( 0 =< t =< 1 ) that gives birth to each vertex.
49 // NOTE: t is defined such that position of vertex is (1-t)*v0 + t*v1,
50 // i.e., t=0 means the vertex is on v0 and t=1 means that it is on v1.
51 std::vector<Scalar> vertices_parent_param;
52};
53
68template <typename MeshType, typename ValueFn>
69MarchingTrianglesOutput<MeshType> marching_triangles_general(
70 MeshType& mesh_ref,
71 ScalarOf<MeshType> isovalue,
72 const ValueFn& get_value)
73{
74 static_assert(MeshTrait<MeshType>::is_mesh(), "Input type is not Mesh");
75 using Index = typename MeshType::Index;
76 using Scalar = typename MeshType::Scalar;
77 using VertexType = typename MeshType::VertexType;
78 using MarchingTrianglesOutput = ::lagrange::MarchingTrianglesOutput<MeshType>;
79 using Edge = typename MeshType::Edge;
80
81 la_runtime_assert(mesh_ref.get_vertex_per_facet() == 3, "only works for triangle meshes");
82 mesh_ref.initialize_edge_data();
83
84 const auto& facets = mesh_ref.get_facets();
85 const auto& vertices = mesh_ref.get_vertices();
86
87 std::vector<Edge> extracted_edges;
88 std::vector<VertexType> extracted_vertices;
89 std::vector<Index> extracted_vertices_parent_edge;
90 std::vector<Scalar> extracted_vertices_parent_param;
91 std::vector<Index> parent_edge_to_extracted_vertex(mesh_ref.get_num_edges(), invalid<Index>());
92
93 //
94 // Find the point that attains a zero value on an edge
95 // (if it does not exists, creates it)
96 // Returns the index of this vertex
97 //
98 auto find_zero = [&](Index parent_edge_id, Index v0, Index v1, Scalar p0, Scalar p1) -> Index {
99 if (parent_edge_to_extracted_vertex[parent_edge_id] != invalid<Index>()) {
100 return parent_edge_to_extracted_vertex[parent_edge_id];
101 } else {
102 // Get the edge and see if the order of v0 v1 and the edge values are the same.
103 const auto parent_edge = mesh_ref.get_edge_vertices(parent_edge_id);
104 if ((v0 == parent_edge[1]) && (v1 == parent_edge[0])) {
105 std::swap(p0, p1);
106 std::swap(v0, v1);
107 }
108 assert((v1 == parent_edge[1]) && (v0 == parent_edge[0]));
109 // Now construct the vertex
110 const Scalar a = p1 / (p1 - p0);
111 const Scalar b = 1 - a;
112 assert(a >= 0);
113 assert(a <= 1);
114 assert(b >= 0);
115 assert(b <= 1);
116 la_runtime_assert(!std::isnan(a));
117 la_runtime_assert(!std::isnan(b));
118 const Index vertex_index = safe_cast<Index>(extracted_vertices.size());
119 const VertexType pos0 = vertices.row(v0);
120 const VertexType pos1 = vertices.row(v1);
121 // And push it into the data structures
122 extracted_vertices.emplace_back(a * pos0 + b * pos1);
123 extracted_vertices_parent_edge.emplace_back(parent_edge_id);
124 extracted_vertices_parent_param.emplace_back(b);
125 parent_edge_to_extracted_vertex[parent_edge_id] = vertex_index;
126 return vertex_index;
127 }
128 };
129
130 //
131 // Find the contour (if exists) in a triangle
132 //
133 auto contour_triangle = [&](const Index tri_id) {
134 const Index v0 = facets(tri_id, 0);
135 const Index v1 = facets(tri_id, 1);
136 const Index v2 = facets(tri_id, 2);
137 Scalar p0 = get_value(tri_id, 0) - isovalue;
138 Scalar p1 = get_value(tri_id, 1) - isovalue;
139 Scalar p2 = get_value(tri_id, 2) - isovalue;
140
141 const Index e01 = mesh_ref.get_edge(tri_id, 0);
142 const Index e12 = mesh_ref.get_edge(tri_id, 1);
143 const Index e20 = mesh_ref.get_edge(tri_id, 2);
144
145 // guard against topological degeneracies
146 if (p0 == 0) p0 = Scalar(1e-30);
147 if (p1 == 0) p1 = Scalar(1e-30);
148 if (p2 == 0) p2 = Scalar(1e-30);
149
150 if (p0 < 0) {
151 if (p1 < 0) {
152 if (p2 < 0) {
153 return; // no contour here
154 } else { /* p2>0 */
155 extracted_edges.push_back(
156 Edge(find_zero(e12, v1, v2, p1, p2), find_zero(e20, v0, v2, p0, p2)));
157 } // p2
158 } else { // p1>0
159 if (p2 < 0) {
160 extracted_edges.push_back(
161 Edge(find_zero(e01, v0, v1, p0, p1), find_zero(e12, v1, v2, p1, p2)));
162 } else { /* p2>0 */
163 extracted_edges.push_back(
164 Edge(find_zero(e01, v0, v1, p0, p1), find_zero(e20, v0, v2, p0, p2)));
165 } // p2
166 } // p1
167 } else { // p0>0
168 if (p1 < 0) {
169 if (p2 < 0) {
170 extracted_edges.push_back(
171 Edge(find_zero(e20, v0, v2, p0, p2), find_zero(e01, v0, v1, p0, p1)));
172 } else { /* p2>0 */
173 extracted_edges.push_back(
174 Edge(find_zero(e12, v1, v2, p1, p2), find_zero(e01, v0, v1, p0, p1)));
175 } // p2
176 } else { // p1>0
177 if (p2 < 0) {
178 extracted_edges.push_back(
179 Edge(find_zero(e20, v0, v2, p0, p2), find_zero(e12, v1, v2, p1, p2)));
180 } else { /* p2>0 */
181 return; // no contour here
182 } // p2
183 } // p1
184 } // p0
185 };
186
187
188 //
189 // Contour all triangles
190 //
191 for (auto tri : range(mesh_ref.get_num_facets())) {
192 contour_triangle(tri);
193 }
194
195 //
196 // Now convert to output
197 //
198 MarchingTrianglesOutput output;
199 // Output edges
200 output.edges.resize(extracted_edges.size(), 2);
201 for (auto i : range(extracted_edges.size())) {
202 output.edges.row(i) << extracted_edges[i][0], extracted_edges[i][1];
203 }
204 // output vertices
205 // This we can just swap!
206 output.vertices_parent_edge.swap(extracted_vertices_parent_edge);
207 output.vertices_parent_param.swap(extracted_vertices_parent_param);
208 // rest we have to copy
209 output.vertices.resize(extracted_vertices.size(), mesh_ref.get_dim());
210 for (auto i : range(extracted_vertices.size())) {
211 output.vertices.row(i) = extracted_vertices[i];
212 }
213 return output;
214}
215
216
217// Perform marching triangles to extract the isocontours of a field
218// defined by the linear interpolation of a vertex attribute.
219//
220// Adapted from https://www.cs.ubc.ca/~rbridson/download/common_2008_nov_12.tar.gz
221// (code released to the __public domain__ by Robert Bridson)
222//
223// Input:
224// mesh_ref, the mesh to run marching cubes on. Can be 2D or 3D,
225// but must be triangular.
226// isovalue, the isovalue to be extracted.
227// vertex_attribute_name, the name of the vertex attribute
228// attribute_col_index, which column of the vertex attribute should be used?
229//
230template <typename MeshType>
231MarchingTrianglesOutput<MeshType> marching_triangles(
232 MeshType& mesh_ref,
233 const typename MeshType::Scalar isovalue,
234 const std::string vertex_attribute_name,
235 const typename MeshType::Index attribute_col_index = 0)
236{
237 using Index = IndexOf<MeshType>;
239 mesh_ref.has_vertex_attribute(vertex_attribute_name),
240 "attribute does not exist in the mesh");
241 const auto& attribute = mesh_ref.get_vertex_attribute(vertex_attribute_name);
243 attribute_col_index < safe_cast<Index>(attribute.cols()),
244 "col_index is invalid");
245
246 const auto& facets = mesh_ref.get_facets();
247 return marching_triangles_general(mesh_ref, isovalue, [&](Index fi, Index ci) {
248 return attribute(facets(fi, ci), attribute_col_index);
249 });
250}
251
268template <typename MeshType>
269MarchingTrianglesOutput<MeshType> marching_triangles_indexed(
270 MeshType& mesh_ref,
271 const typename MeshType::Scalar isovalue,
272 const std::string indexed_attribute_name,
273 const typename MeshType::Index attribute_col_index = 0)
274{
275 using Index = IndexOf<MeshType>;
277 mesh_ref.has_indexed_attribute(indexed_attribute_name),
278 "attribute does not exist in the mesh");
279 const auto& attribute = mesh_ref.get_indexed_attribute(indexed_attribute_name);
281 attribute_col_index < safe_cast<Index>(std::get<0>(attribute).cols()),
282 "col_index is invalid");
283
284 return marching_triangles_general(mesh_ref, isovalue, [&](Index fi, Index ci) {
285 return std::get<0>(attribute)(std::get<1>(attribute)(fi, ci), attribute_col_index);
286 });
287}
288
289} // namespace legacy
290} // namespace lagrange
Definition: Edge.h:29
Definition: Mesh.h:48
Index get_edge(Index f, Index lv) const
Gets the edge index corresponding to (f, lv) – (f, lv+1).
Definition: Mesh.h:664
std::array< Index, 2 > get_edge_vertices(Index e) const
Retrieve edge endpoints.
Definition: Mesh.h:730
void initialize_edge_data()
Edge data initialization.
Definition: Mesh.h:633
Index get_num_edges() const
Gets the number of edges.
Definition: Mesh.h:650
@ Edge
Per-edge mesh attributes.
Definition: AttributeFwd.h:34
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
constexpr void swap(function_ref< R(Args...)> &lhs, function_ref< R(Args...)> &rhs) noexcept
Swaps the referred callables of lhs and rhs.
Definition: function_ref.h:114
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Main namespace for Lagrange.
Definition: AABBIGL.h:30
MeshTrait class provide compiler check for different mesh types.
Definition: MeshTrait.h:108
Definition: marching_triangles.h:34