GCC Code Coverage Report


Directory: ./
File: lib/exploragram/hexdom/intersect_tools.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 0 168 0.0%
Functions: 0 24 0.0%
Branches: 0 396 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <exploragram/hexdom/intersect_tools.h>
41 #include <exploragram/hexdom/mesh_utils.h>
42 #define FPG_UNCERTAIN_VALUE 0
43 #include <geogram/numerics/predicates/orient3d.h>
44 #include <geogram/mesh/triangle_intersection.h>
45 #include <geogram/mesh/mesh_io.h>
46 namespace {
47 using namespace GEO;
48
49 struct IndexPointCmp {
50 IndexPointCmp(vector<vec3>& p_data, index_t p_dim)
51 : dim(p_dim), data(p_data) {}
52 bool operator()(const index_t A, const index_t B) {
53 return data[A][dim] > data[B][dim];
54 }
55 index_t dim;
56 vector<vec3>& data;
57 };
58 }
59
60 namespace GEO {
61
62
63 const index_t quad_rand_split[2][3] = { { 0, 1, 2 },{ 0, 2, 3 } }; // random triangulation
64 const index_t quad_split[4][3] = { { 0, 1, 2 },{ 0, 2, 3 },{ 1, 2, 3 },{ 1, 3, 0 } }; // convex hull
65 const index_t diamon_split[12][3] = { // surface and inside
66 { 4, 5, 0 },{ 4, 5, 1 },{ 4, 5, 2 },{ 4, 5, 3 },
67 { 0, 1, 4 },{ 1, 2, 4 },{ 2, 3, 4 },{ 3, 0, 4 },
68 { 0, 1, 5 },{ 1, 2, 5 },{ 2, 3, 5 },{ 3, 0, 5 } // orientation doesn't matter
69 };
70
71 const index_t diamon_quad_split[16][3] = { // surface and inside
72 { 4, 5, 0 },{ 4, 5, 1 },{ 4, 5, 2 },{ 4, 5, 3 },
73 { 0, 1, 4 },{ 1, 2, 4 },{ 2, 3, 4 },{ 3, 0, 4 },
74 { 0, 1, 5 },{ 1, 2, 5 },{ 2, 3, 5 },{ 3, 0, 5 }, // orientation doesn't matter
75 { 0, 1, 2 },{ 0, 2, 3 },{ 1, 2, 3 },{ 1, 3, 0 }
76 };
77
78
79
80
81 bool BBox::intersect(const BBox& b) const {
82 FOR(d, 3) {
83 if (min[d] > b.max[d] || max[d] < b.min[d]) {
84 return false;
85 }
86 }
87 return true;
88 }
89
90 bool BBox::contains(const vec3& v) const {
91 FOR(d, 3) {
92 if (min[d] > v[d] || max[d] < v[d]) {
93 return false;
94 }
95 }
96 return true;
97 }
98
99 bool BBox::is_null() const {
100 FOR(d, 3) {
101 if (max[d] - min[d] < 0) {
102 return true;
103 }
104 }
105 return false;
106 }
107
108 void BBox::add(const BBox& b) {
109 if (b.is_null()) return;
110 add(b.min);
111 add(b.max);
112 }
113
114 void BBox::add(const vec3& P) {
115 FOR(d, 3) {
116 min[d] = std::min(min[d], P[d]);
117 max[d] = std::max(max[d], P[d]);
118 }
119 }
120
121 vec3 BBox::bary() const {
122 return 0.5*(min + max);
123 }
124
125
126 /**********************************************************/
127
128 inline unsigned int mylog2( unsigned int x ) {
129 unsigned int ans = 0 ;
130 while( x>>=1 ) ans++;
131 return ans ;
132 }
133
134
135 void HBoxes::init(vector<BBox>& inboxes) {
136 vector<vec3> G(inboxes.size());
137 tree_pos_to_org.resize(inboxes.size());
138 FOR(p, G.size()) G[p] = inboxes[p].bary();
139 FOR(p, G.size()) tree_pos_to_org[p] = p;
140 sort(G, 0, tree_pos_to_org.size());
141
142 offset = index_t(pow(2.0, 1.0 + mylog2(G.size()))) - 1;
143 tree.resize(offset + G.size());
144 FOR(i, G.size()) tree[offset + i] = inboxes[tree_pos_to_org[i]];
145 for (int i = int(offset) - 1; i >= 0; i--) {
146 for (int son = 2 * i + 1; son < 2 * i + 3; son++)
147 if (son < int(tree.size())) tree[i].add(tree[son]);
148 }
149
150 STAT_nb_visits = 0;
151 STAT_nb_leafs = 0;
152 STAT_nb_requests = 0;
153 }
154
155 void HBoxes::sort(vector<vec3> &G, index_t org, index_t dest) {
156
157 // find the best dim to cut
158 index_t dim = 2;
159 BBox b;
160 for (index_t i = org; i < dest; i++) b.add(G[tree_pos_to_org[i]]);
161 FOR(d, 2) if (b.max[d] - b.min[d] > b.max[dim] - b.min[dim]) dim = d;
162 // sort
163 IndexPointCmp cmp(G, dim);
164 std::sort(tree_pos_to_org.begin() + int(org), tree_pos_to_org.begin() + int(dest), cmp);
165 if (dest - org <= 2) return;
166 index_t m = org + index_t(pow(2.0, int(mylog2(dest - org - 1))));
167 sort(G, org, m);
168 sort(G, m, dest);
169 }
170
171 void HBoxes::intersect(BBox& b, vector<index_t>& primitives, index_t node) {
172 if (node == 0) STAT_nb_requests++;
173 geo_assert(node < tree.size());
174 STAT_nb_visits++;
175 if (!tree[node].intersect(b)) return;
176 if (node >= offset) {
177 STAT_nb_leafs++;
178 primitives.push_back(tree_pos_to_org[node - offset]);
179 } else {
180 for (index_t son = 2 * node + 1; son < 2 * node + 3; son++)
181 if (son < tree.size())
182 intersect(b, primitives, son);
183 }
184 }
185
186 /**********************************************************/
187
188 void DynamicHBoxes::init(vector<BBox>& inboxes) {
189 hbox.init(inboxes);
190 moved.clear();
191 movedbbox.clear();
192 }
193
194 void DynamicHBoxes::intersect(BBox& b, vector<index_t>& primitives) {
195 hbox.intersect(b, primitives);
196 FOR(i, moved.size()) {
197 if (movedbbox[i].intersect(b))
198 primitives.push_back(moved[i]);
199 }
200 }
201
202 void DynamicHBoxes::update_bbox(index_t id, BBox b) {
203 geo_assert(id<hbox.tree_pos_to_org.size());
204 FOR(i, moved.size()) {
205 if (moved[i] == id) {
206 movedbbox[i] = b;
207 return;
208 }
209 }
210 moved.push_back(id);
211 movedbbox.push_back(b);
212 }
213
214
215
216
217 static double tetra_volume(vec3 A, vec3 B, vec3 C, vec3 D) {
218 return dot(cross(B - A, C - A), D - A);
219 }
220
221 double tetra_volume_sign(vec3 A, vec3 B, vec3 C, vec3 D) {
222 double res = tetra_volume(A, B, C, D);
223 if (std::abs(res) > 1e-15) return res;
224 return dot(normalize(cross(normalize(B - A), normalize(C - A))), normalize(D - A));
225 }
226
227 bool same_sign(double a, double b) { return (a > 0) == (b > 0); }
228
229
230
231 vector<BBox> facets_bbox(Mesh* m) {
232 vector<BBox> inboxes(m->facets.nb());
233 FOR(f, m->facets.nb()) {
234 index_t nbv = m->facets.nb_vertices(f);
235 FOR(fv, nbv) inboxes[f].add(X(m)[m->facets.vertex(f, fv)]);
236 }
237 return inboxes;
238 }
239
240
241
242
243 FacetIntersect::FacetIntersect(Mesh* p_m) { m = p_m;
244 inboxes = facets_bbox(m);
245 hb.init(inboxes);
246 }
247
248 static void save_conflict(std::string name, vec3 A0, vec3 B0, vec3 C0, vec3 A1, vec3 B1, vec3 C1) {
249 Mesh conflict;
250 conflict.vertices.create_vertices(6);
251 conflict.facets.create_triangles(2);
252 X(&conflict)[0] = A0; X(&conflict)[1] = B0; X(&conflict)[2] = C0;
253 X(&conflict)[3] = A1; X(&conflict)[4] = B1; X(&conflict)[5] = C1;
254 FOR(f, 2) FOR(lv, 3) conflict.facets.set_vertex(f, lv, 3 * f + lv);
255 mesh_save(conflict, "C:/DATA/debug/" + name + "conflict.geogram");
256 }
257
258 static bool polyintersect_both_triangulation(vector<vec3>& P, vector<vec3>& Q) {
259 bool conflict = false;
260 FOR(trP, 4) {
261 FOR(trQ, 4) {
262 if (trP > 0 && P.size() == 3) continue;
263 if (trQ > 0 && Q.size() == 3) continue;
264 TriangleIsects trash;
265 conflict = conflict || triangles_intersections(
266 P[quad_split[trP][0]], P[quad_split[trP][1]], P[quad_split[trP][2]],
267 Q[quad_split[trQ][0]], Q[quad_split[trQ][1]], Q[quad_split[trQ][2]],
268 trash
269 );
270 FOR(i, trash.size()) {
271 //if (trash[i].first > 2 || trash[i].second > 2) conflict = true;
272 }
273 // static int nb_intersects = 0;
274 if (conflict) {
275 // nb_intersects++;
276 // save_conflict("gna"+ String::to_string(nb_intersects), P[quad_split[trP][0]], P[quad_split[trP][1]], P[quad_split[trP][2]],
277 // Q[quad_split[trQ][0]], Q[quad_split[trQ][1]], Q[quad_split[trQ][2]]);
278 return true;
279 }
280 }
281 }
282 return false;
283 }
284 bool polyintersect(vector<vec3>& P, vector<vec3>& Q) {
285 geo_assert(P.size() == 3 || P.size() == 4);
286 geo_assert(Q.size() == 3 || Q.size() == 4);
287
288 // check for same facet
289 if (P.size() == Q.size()) FOR(off, P.size()) {
290 bool is_same = true;
291 FOR(v, P.size()) {
292 if ((P[v] - Q[(v + off) % P.size()]).length2() != 0) {
293 is_same = false;
294 break;
295 }
296 }
297 if (is_same) return false;
298 }
299 return polyintersect_both_triangulation(P, Q);
300 }
301
302 vector<index_t> FacetIntersect::get_intersections(vector<vec3>& P) {
303 vector<index_t> res;
304 vector<index_t> primitives;
305 BBox request_bbox;
306 FOR(v, P.size()) request_bbox.add(P[v]);
307 request_bbox.dilate(1e-15);
308 hb.intersect(request_bbox, primitives);
309 FOR(i, primitives.size()) {
310 index_t opp_f = primitives[i];
311 vector<vec3> Q;
312 FOR(fv, m->facets.nb_vertices(opp_f))
313 Q.push_back(X(m)[m->facets.vertex(opp_f, fv)]);
314 if (polyintersect(P, Q) || polyintersect(Q,P))
315 res.push_back(opp_f);
316 }
317 return res;
318 }
319 vector<index_t> FacetIntersect::get_intersections(index_t& f) {
320 vector<vec3> verts(m->facets.nb_vertices(f));
321 FOR(v, m->facets.nb_vertices(f)) verts[v] = X(m)[m->facets.vertex(f,v)];
322 return get_intersections(verts);
323 }
324
325
326 vector<index_t> get_intersecting_faces(Mesh* m) {
327 vector<index_t> res;
328 FacetIntersect finter(m);
329 FOR(f, m->facets.nb()) {
330 // if ((f%50) ==0)plop(double(f) / double(m->facets.nb()));
331 vector<index_t> opp = finter.get_intersections(f);
332 FOR(i, opp.size()) {
333 res.push_back(f);
334 res.push_back(opp[i]);
335 }
336 }
337 return res;
338 }
339
340
341
342
343 void check_no_intersecting_faces(Mesh* m, bool allow_duplicated ) {
344 vector<index_t> intersect = get_intersecting_faces(m);
345 if (allow_duplicated) {
346 index_t offpair = 0;
347 while (offpair < intersect.size()) {
348 index_t f0 = intersect[offpair];
349 index_t f1 = intersect[offpair + 1];
350 if (f0 != f1 && (facet_bary(m, f0) - facet_bary(m, f1)).length2() < 1e-15) {
351 FOR(d, 2) std::swap(intersect[offpair + d], intersect[intersect.size() - 2 + d]);
352 FOR(d, 2) intersect.pop_back();
353 }
354 else offpair += 2;
355 }
356 }
357 if (intersect.empty()) return;
358 Attribute<int> intersection(m->facets.attributes(), "intersection");
359 FOR(f, m->facets.nb()) intersection[f] = 0;
360 FOR(i, intersect.size()) intersection[intersect[i]] = 1;
361 intersection[intersect[0]] = 2;
362 intersection[intersect[1]] = 3;
363 FOR(i, 2) plop(m->facets.nb_vertices(intersect[i]));
364 FOR(i, 2) FOR(lv, m->facets.nb_vertices(intersect[i])) plop(m->facets.vertex(intersect[i], lv));
365 FOR(i, 2) FOR(lv, m->facets.nb_vertices(intersect[i])) plop(X(m)[m->facets.vertex(intersect[i], lv)]);
366 mesh_save(*m, "C:/DATA/debug/intersectingsurface.geogram");
367
368 save_conflict("first",
369 X(m)[m->facets.vertex(intersect[0], 0)],
370 X(m)[m->facets.vertex(intersect[0], 1)],
371 X(m)[m->facets.vertex(intersect[0], 2)],
372 X(m)[m->facets.vertex(intersect[1], 0)],
373 X(m)[m->facets.vertex(intersect[1], 1)],
374 X(m)[m->facets.vertex(intersect[1], 2)]
375 );
376 Mesh conflict;
377 conflict.vertices.create_vertices(6);
378 conflict.facets.create_triangles(2);
379 FOR(f, 2) FOR(lv, 3) X(&conflict)[3*f+lv] = X(m)[m->facets.vertex(intersect[f], lv)];
380 FOR(f, 2) FOR(lv, 3) conflict.facets.set_vertex(f, lv, 3 * f + lv);
381 mesh_save(conflict, "C:/DATA/debug/conflict.geogram");
382 geo_assert_not_reached;
383 }
384
385
386 }
387