Lagrange
select_facets_in_frustum.h
1/*
2 * Copyright 2020 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 <array>
15#include <atomic>
16
17// clang-format off
18#include <lagrange/utils/warnoff.h>
19#include <tbb/blocked_range.h>
20#include <tbb/enumerable_thread_specific.h>
21#include <tbb/parallel_for.h>
22#include <lagrange/utils/warnon.h>
23// clang-format on
24
25#include <lagrange/legacy/inline.h>
26#include <lagrange/Mesh.h>
27#include <lagrange/MeshTrait.h>
28#include <lagrange/common.h>
29#include <lagrange/utils/tbb.h>
30
31namespace lagrange {
32LAGRANGE_LEGACY_INLINE
33namespace legacy {
34
50template <typename MeshType, typename Point3D>
52 MeshType& mesh,
53 const Eigen::PlainObjectBase<Point3D>& n0,
54 const Eigen::PlainObjectBase<Point3D>& p0,
55 const Eigen::PlainObjectBase<Point3D>& n1,
56 const Eigen::PlainObjectBase<Point3D>& p1,
57 const Eigen::PlainObjectBase<Point3D>& n2,
58 const Eigen::PlainObjectBase<Point3D>& p2,
59 const Eigen::PlainObjectBase<Point3D>& n3,
60 const Eigen::PlainObjectBase<Point3D>& p3,
61 bool greedy = false)
62{
63 static_assert(MeshTrait<MeshType>::is_mesh(), "Input is not a mesh.");
64 using AttributeArray = typename MeshType::AttributeArray;
65 using Scalar = typename MeshType::Scalar;
66 using Index = typename MeshType::Index;
67
68 const auto num_facets = mesh.get_num_facets();
69 const auto& vertices = mesh.get_vertices();
70 const auto& facets = mesh.get_facets();
71
72 // Per-thread buffers to store intermediate results.
73 struct LocalBuffers
74 {
75 Point3D v0, v1, v2; // Triangle vertices.
76 Point3D q0, q1, q2, q3; // Intermediate tet vertices.
77 std::array<double, 2> r0, r1, r2; // Triangle projections.
78 };
79 tbb::enumerable_thread_specific<LocalBuffers> temp_vars; // we can safely remove this and just declare these variables in the lambdas (as long as there's no memory allocation on the heap, this is not necessary)
80
81
82 auto edge_overlap_with_negative_octant = [](const Point3D& q0, const Point3D& q1) -> bool {
83 Scalar t_min = 0, t_max = 1;
84 const Point3D e = q0 - q1;
85 if (e[0] > 0) {
86 t_max = std::min(t_max, -q1[0] / e[0]);
87 } else if (e[0] < 0) {
88 t_min = std::max(t_min, -q1[0] / e[0]);
89 } else {
90 if (q1[0] >= 0) return false;
91 }
92
93 if (e[1] > 0) {
94 t_max = std::min(t_max, -q1[1] / e[1]);
95 } else if (e[1] < 0) {
96 t_min = std::max(t_min, -q1[1] / e[1]);
97 } else {
98 if (q1[1] >= 0) return false;
99 }
100
101 if (e[2] > 0) {
102 t_max = std::min(t_max, -q1[2] / e[2]);
103 } else if (e[2] < 0) {
104 t_min = std::max(t_min, -q1[2] / e[2]);
105 } else {
106 if (q1[2] >= 0) return false;
107 }
108
109 return t_max >= t_min;
110 };
111
112 auto compute_plane =
113 [](const Point3D& q0, const Point3D& q1, const Point3D& q2) -> std::pair<Point3D, Scalar> {
114 const Point3D n = {
115 q0[2] * (q2[1] - q1[1]) + q1[2] * (q0[1] - q2[1]) + q2[2] * (q1[1] - q0[1]),
116 q0[0] * (q2[2] - q1[2]) + q1[0] * (q0[2] - q2[2]) + q2[0] * (q1[2] - q0[2]),
117 q0[1] * (q2[0] - q1[0]) + q1[1] * (q0[0] - q2[0]) + q2[1] * (q1[0] - q0[0])};
118 const Scalar c = (n.dot(q0) + n.dot(q1) + n.dot(q2)) / 3;
119 return {n, c};
120 };
121
122 // Compute the orientation of 2D traingle (v0, v1, O), where O = (0, 0).
123 auto orient2D_inexact = [](const std::array<double, 2>& v0,
124 const std::array<double, 2>& v1) -> int {
125 const auto r = v0[0] * v1[1] - v0[1] * v1[0];
126 if (r > 0) return 1;
127 if (r < 0) return -1;
128 return 0;
129 };
130
131 auto triangle_intersects_negative_axis = [&](const Point3D& q0,
132 const Point3D& q1,
133 const Point3D& q2,
134 const Point3D& n,
135 const Scalar c,
136 const int axis) -> bool {
137 auto& r0 = temp_vars.local().r0;
138 auto& r1 = temp_vars.local().r1;
139 auto& r2 = temp_vars.local().r2;
140
141 r0[0] = q0[(axis + 1) % 3];
142 r0[1] = q0[(axis + 2) % 3];
143 r1[0] = q1[(axis + 1) % 3];
144 r1[1] = q1[(axis + 2) % 3];
145 r2[0] = q2[(axis + 1) % 3];
146 r2[1] = q2[(axis + 2) % 3];
147
148 auto o01 = orient2D_inexact(r0, r1);
149 auto o12 = orient2D_inexact(r1, r2);
150 auto o20 = orient2D_inexact(r2, r0);
151 if (o01 == o12 && o01 == o20) {
152 if (o01 == 0) {
153 // Triangle projection is degenerate.
154 // Note that the case where axis is coplanar with the triangle
155 // is treated as no intersection (which is debatale).
156 return false;
157 } else {
158 // Triangle projection contains the origin.
159 // Check axis intercept.
160 return (c < 0 && n[axis] > 0) || (c > 0 && n[axis] < 0);
161 }
162 } else {
163 // Note that we treat the case where the axis intersect triangle at
164 // its boundary as no intersection.
165 return false;
166 }
167 };
168
169 auto triangle_intersects_negative_axes =
170 [&](const Point3D& q0, const Point3D& q1, const Point3D& q2) -> bool {
171 const auto r = compute_plane(q0, q1, q2);
172 if (triangle_intersects_negative_axis(q0, q1, q2, r.first, r.second, 0)) return true;
173 if (triangle_intersects_negative_axis(q0, q1, q2, r.first, r.second, 1)) return true;
174 if (triangle_intersects_negative_axis(q0, q1, q2, r.first, r.second, 2)) return true;
175 return false;
176 };
177
178 auto tet_overlap_with_negative_octant =
179 [&](const Point3D& q0, const Point3D& q1, const Point3D& q2, const Point3D& q3) -> bool {
180 // Check 1: Check if any tet vertices is in negative octant.
181 if ((q0.array() < 0).all()) return true;
182 if ((q1.array() < 0).all()) return true;
183 if ((q2.array() < 0).all()) return true;
184 if ((q3.array() < 0).all()) return true;
185
186 // Check 2: Check if any tet edges cross the negative octant.
187 if (edge_overlap_with_negative_octant(q0, q1)) return true;
188 if (edge_overlap_with_negative_octant(q0, q2)) return true;
189 if (edge_overlap_with_negative_octant(q0, q3)) return true;
190 if (edge_overlap_with_negative_octant(q1, q2)) return true;
191 if (edge_overlap_with_negative_octant(q1, q3)) return true;
192 if (edge_overlap_with_negative_octant(q2, q3)) return true;
193
194 // Check 3: Check if -X, -Y or -Z axis intersect the tet.
195 if (triangle_intersects_negative_axes(q0, q1, q2)) return true;
196 if (triangle_intersects_negative_axes(q1, q2, q3)) return true;
197 if (triangle_intersects_negative_axes(q2, q3, q0)) return true;
198 if (triangle_intersects_negative_axes(q3, q0, q1)) return true;
199
200 // All check failed iff tet does not intersect the negative octant.
201 return false;
202 };
203
204 AttributeArray attr;
205 if (!greedy) {
206 attr.resize(num_facets, 1);
207 attr.setZero();
208 }
209 std::atomic_bool r{false};
210
211 tbb::parallel_for(
212 tbb::blocked_range<Index>(0, num_facets),
213 [&](const tbb::blocked_range<Index>& tbb_range) {
214 for (auto fi = tbb_range.begin(); fi != tbb_range.end(); fi++) {
215 if (tbb_utils::is_cancelled()) break;
216
217 // Triangle (v0, v1, v2) intersect the cone defined by 4 planes
218 // iff the tetrahedron (q0, q1, q2, q3) does not intersect the negative octant.
219 // This can be proved by Farkas' lemma.
220
221 auto& v0 = temp_vars.local().v0;
222 auto& v1 = temp_vars.local().v1;
223 auto& v2 = temp_vars.local().v2;
224
225 auto& q0 = temp_vars.local().q0;
226 auto& q1 = temp_vars.local().q1;
227 auto& q2 = temp_vars.local().q2;
228 auto& q3 = temp_vars.local().q3;
229
230 v0 = vertices.row(facets(fi, 0));
231 v1 = vertices.row(facets(fi, 1));
232 v2 = vertices.row(facets(fi, 2));
233
234 q0 = {(v0 - p0).dot(n0), (v1 - p0).dot(n0), (v2 - p0).dot(n0)};
235 q1 = {(v0 - p1).dot(n1), (v1 - p1).dot(n1), (v2 - p1).dot(n1)};
236 q2 = {(v0 - p2).dot(n2), (v1 - p2).dot(n2), (v2 - p2).dot(n2)};
237 q3 = {(v0 - p3).dot(n3), (v1 - p3).dot(n3), (v2 - p3).dot(n3)};
238
239 const auto ri = !tet_overlap_with_negative_octant(q0, q1, q2, q3);
240
241 if (ri) r = true;
242
243 if (!greedy) {
244 attr(fi, 0) = ri;
245 }
246
247 if (greedy && ri) {
248 tbb_utils::cancel_group_execution();
249 break;
250 }
251 }
252 });
253
254 if (!greedy) {
255
256 mesh.add_facet_attribute("is_selected");
257 mesh.import_facet_attribute("is_selected", attr);
258 }
259 return r.load();
260}
261
262} // namespace legacy
263} // namespace lagrange
Definition: Mesh.h:48
bool select_facets_in_frustum(SurfaceMesh< Scalar, Index > &mesh, const Frustum< Scalar > &frustum, const FrustumSelectionOptions &options={})
Select all facets that intersect the cone/frustrum bounded by 4 planes defined by (n_i,...
Definition: select_facets_in_frustum.cpp:31
Main namespace for Lagrange.
Definition: AABBIGL.h:30