Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/boolean3.cpp
9902 views
1
// Copyright 2021 The Manifold Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "boolean3.h"
16
17
#include <limits>
18
#include <unordered_set>
19
20
#include "disjoint_sets.h"
21
#include "parallel.h"
22
23
#if (MANIFOLD_PAR == 1)
24
#include <tbb/combinable.h>
25
#endif
26
27
using namespace manifold;
28
29
namespace {
30
31
// These two functions (Interpolate and Intersect) are the only places where
32
// floating-point operations take place in the whole Boolean function. These
33
// are carefully designed to minimize rounding error and to eliminate it at edge
34
// cases to ensure consistency.
35
36
vec2 Interpolate(vec3 pL, vec3 pR, double x) {
37
const double dxL = x - pL.x;
38
const double dxR = x - pR.x;
39
DEBUG_ASSERT(dxL * dxR <= 0, logicErr,
40
"Boolean manifold error: not in domain");
41
const bool useL = fabs(dxL) < fabs(dxR);
42
const vec3 dLR = pR - pL;
43
const double lambda = (useL ? dxL : dxR) / dLR.x;
44
if (!std::isfinite(lambda) || !std::isfinite(dLR.y) || !std::isfinite(dLR.z))
45
return vec2(pL.y, pL.z);
46
vec2 yz;
47
yz[0] = lambda * dLR.y + (useL ? pL.y : pR.y);
48
yz[1] = lambda * dLR.z + (useL ? pL.z : pR.z);
49
return yz;
50
}
51
52
vec4 Intersect(const vec3& pL, const vec3& pR, const vec3& qL, const vec3& qR) {
53
const double dyL = qL.y - pL.y;
54
const double dyR = qR.y - pR.y;
55
DEBUG_ASSERT(dyL * dyR <= 0, logicErr,
56
"Boolean manifold error: no intersection");
57
const bool useL = fabs(dyL) < fabs(dyR);
58
const double dx = pR.x - pL.x;
59
double lambda = (useL ? dyL : dyR) / (dyL - dyR);
60
if (!std::isfinite(lambda)) lambda = 0.0;
61
vec4 xyzz;
62
xyzz.x = lambda * dx + (useL ? pL.x : pR.x);
63
const double pDy = pR.y - pL.y;
64
const double qDy = qR.y - qL.y;
65
const bool useP = fabs(pDy) < fabs(qDy);
66
xyzz.y = lambda * (useP ? pDy : qDy) +
67
(useL ? (useP ? pL.y : qL.y) : (useP ? pR.y : qR.y));
68
xyzz.z = lambda * (pR.z - pL.z) + (useL ? pL.z : pR.z);
69
xyzz.w = lambda * (qR.z - qL.z) + (useL ? qL.z : qR.z);
70
return xyzz;
71
}
72
73
inline bool Shadows(double p, double q, double dir) {
74
return p == q ? dir < 0 : p < q;
75
}
76
77
inline std::pair<int, vec2> Shadow01(
78
const int p0, const int q1, VecView<const vec3> vertPosP,
79
VecView<const vec3> vertPosQ, VecView<const Halfedge> halfedgeQ,
80
const double expandP, VecView<const vec3> normal, const bool reverse) {
81
const int q1s = halfedgeQ[q1].startVert;
82
const int q1e = halfedgeQ[q1].endVert;
83
const double p0x = vertPosP[p0].x;
84
const double q1sx = vertPosQ[q1s].x;
85
const double q1ex = vertPosQ[q1e].x;
86
int s01 = reverse ? Shadows(q1sx, p0x, expandP * normal[q1s].x) -
87
Shadows(q1ex, p0x, expandP * normal[q1e].x)
88
: Shadows(p0x, q1ex, expandP * normal[p0].x) -
89
Shadows(p0x, q1sx, expandP * normal[p0].x);
90
vec2 yz01(NAN);
91
92
if (s01 != 0) {
93
yz01 = Interpolate(vertPosQ[q1s], vertPosQ[q1e], vertPosP[p0].x);
94
if (reverse) {
95
vec3 diff = vertPosQ[q1s] - vertPosP[p0];
96
const double start2 = la::dot(diff, diff);
97
diff = vertPosQ[q1e] - vertPosP[p0];
98
const double end2 = la::dot(diff, diff);
99
const double dir = start2 < end2 ? normal[q1s].y : normal[q1e].y;
100
if (!Shadows(yz01[0], vertPosP[p0].y, expandP * dir)) s01 = 0;
101
} else {
102
if (!Shadows(vertPosP[p0].y, yz01[0], expandP * normal[p0].y)) s01 = 0;
103
}
104
}
105
return std::make_pair(s01, yz01);
106
}
107
108
struct Kernel11 {
109
VecView<const vec3> vertPosP;
110
VecView<const vec3> vertPosQ;
111
VecView<const Halfedge> halfedgeP;
112
VecView<const Halfedge> halfedgeQ;
113
const double expandP;
114
VecView<const vec3> normalP;
115
116
std::pair<int, vec4> operator()(int p1, int q1) {
117
vec4 xyzz11 = vec4(NAN);
118
int s11 = 0;
119
120
// For pRL[k], qRL[k], k==0 is the left and k==1 is the right.
121
int k = 0;
122
vec3 pRL[2], qRL[2];
123
// Either the left or right must shadow, but not both. This ensures the
124
// intersection is between the left and right.
125
bool shadows = false;
126
s11 = 0;
127
128
const int p0[2] = {halfedgeP[p1].startVert, halfedgeP[p1].endVert};
129
for (int i : {0, 1}) {
130
const auto [s01, yz01] = Shadow01(p0[i], q1, vertPosP, vertPosQ,
131
halfedgeQ, expandP, normalP, false);
132
// If the value is NaN, then these do not overlap.
133
if (std::isfinite(yz01[0])) {
134
s11 += s01 * (i == 0 ? -1 : 1);
135
if (k < 2 && (k == 0 || (s01 != 0) != shadows)) {
136
shadows = s01 != 0;
137
pRL[k] = vertPosP[p0[i]];
138
qRL[k] = vec3(pRL[k].x, yz01.x, yz01.y);
139
++k;
140
}
141
}
142
}
143
144
const int q0[2] = {halfedgeQ[q1].startVert, halfedgeQ[q1].endVert};
145
for (int i : {0, 1}) {
146
const auto [s10, yz10] = Shadow01(q0[i], p1, vertPosQ, vertPosP,
147
halfedgeP, expandP, normalP, true);
148
// If the value is NaN, then these do not overlap.
149
if (std::isfinite(yz10[0])) {
150
s11 += s10 * (i == 0 ? -1 : 1);
151
if (k < 2 && (k == 0 || (s10 != 0) != shadows)) {
152
shadows = s10 != 0;
153
qRL[k] = vertPosQ[q0[i]];
154
pRL[k] = vec3(qRL[k].x, yz10.x, yz10.y);
155
++k;
156
}
157
}
158
}
159
160
if (s11 == 0) { // No intersection
161
xyzz11 = vec4(NAN);
162
} else {
163
DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: s11");
164
xyzz11 = Intersect(pRL[0], pRL[1], qRL[0], qRL[1]);
165
166
const int p1s = halfedgeP[p1].startVert;
167
const int p1e = halfedgeP[p1].endVert;
168
vec3 diff = vertPosP[p1s] - vec3(xyzz11);
169
const double start2 = la::dot(diff, diff);
170
diff = vertPosP[p1e] - vec3(xyzz11);
171
const double end2 = la::dot(diff, diff);
172
const double dir = start2 < end2 ? normalP[p1s].z : normalP[p1e].z;
173
174
if (!Shadows(xyzz11.z, xyzz11.w, expandP * dir)) s11 = 0;
175
}
176
177
return std::make_pair(s11, xyzz11);
178
}
179
};
180
181
struct Kernel02 {
182
VecView<const vec3> vertPosP;
183
VecView<const Halfedge> halfedgeQ;
184
VecView<const vec3> vertPosQ;
185
const double expandP;
186
VecView<const vec3> vertNormalP;
187
const bool forward;
188
189
std::pair<int, double> operator()(int p0, int q2) {
190
int s02 = 0;
191
double z02 = 0.0;
192
193
// For yzzLR[k], k==0 is the left and k==1 is the right.
194
int k = 0;
195
vec3 yzzRL[2];
196
// Either the left or right must shadow, but not both. This ensures the
197
// intersection is between the left and right.
198
bool shadows = false;
199
int closestVert = -1;
200
double minMetric = std::numeric_limits<double>::infinity();
201
s02 = 0;
202
203
const vec3 posP = vertPosP[p0];
204
for (const int i : {0, 1, 2}) {
205
const int q1 = 3 * q2 + i;
206
const Halfedge edge = halfedgeQ[q1];
207
const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge;
208
209
if (!forward) {
210
const int qVert = halfedgeQ[q1F].startVert;
211
const vec3 diff = posP - vertPosQ[qVert];
212
const double metric = la::dot(diff, diff);
213
if (metric < minMetric) {
214
minMetric = metric;
215
closestVert = qVert;
216
}
217
}
218
219
const auto syz01 = Shadow01(p0, q1F, vertPosP, vertPosQ, halfedgeQ,
220
expandP, vertNormalP, !forward);
221
const int s01 = syz01.first;
222
const vec2 yz01 = syz01.second;
223
// If the value is NaN, then these do not overlap.
224
if (std::isfinite(yz01[0])) {
225
s02 += s01 * (forward == edge.IsForward() ? -1 : 1);
226
if (k < 2 && (k == 0 || (s01 != 0) != shadows)) {
227
shadows = s01 != 0;
228
yzzRL[k++] = vec3(yz01[0], yz01[1], yz01[1]);
229
}
230
}
231
}
232
233
if (s02 == 0) { // No intersection
234
z02 = NAN;
235
} else {
236
DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: s02");
237
vec3 vertPos = vertPosP[p0];
238
z02 = Interpolate(yzzRL[0], yzzRL[1], vertPos.y)[1];
239
if (forward) {
240
if (!Shadows(vertPos.z, z02, expandP * vertNormalP[p0].z)) s02 = 0;
241
} else {
242
// DEBUG_ASSERT(closestVert != -1, topologyErr, "No closest vert");
243
if (!Shadows(z02, vertPos.z, expandP * vertNormalP[closestVert].z))
244
s02 = 0;
245
}
246
}
247
return std::make_pair(s02, z02);
248
}
249
};
250
251
struct Kernel12 {
252
VecView<const Halfedge> halfedgesP;
253
VecView<const Halfedge> halfedgesQ;
254
VecView<const vec3> vertPosP;
255
const bool forward;
256
Kernel02 k02;
257
Kernel11 k11;
258
259
std::pair<int, vec3> operator()(int p1, int q2) {
260
int x12 = 0;
261
vec3 v12 = vec3(NAN);
262
263
// For xzyLR-[k], k==0 is the left and k==1 is the right.
264
int k = 0;
265
vec3 xzyLR0[2];
266
vec3 xzyLR1[2];
267
// Either the left or right must shadow, but not both. This ensures the
268
// intersection is between the left and right.
269
bool shadows = false;
270
x12 = 0;
271
272
const Halfedge edge = halfedgesP[p1];
273
274
for (int vert : {edge.startVert, edge.endVert}) {
275
const auto [s, z] = k02(vert, q2);
276
if (std::isfinite(z)) {
277
x12 += s * ((vert == edge.startVert) == forward ? 1 : -1);
278
if (k < 2 && (k == 0 || (s != 0) != shadows)) {
279
shadows = s != 0;
280
xzyLR0[k] = vertPosP[vert];
281
std::swap(xzyLR0[k].y, xzyLR0[k].z);
282
xzyLR1[k] = xzyLR0[k];
283
xzyLR1[k][1] = z;
284
k++;
285
}
286
}
287
}
288
289
for (const int i : {0, 1, 2}) {
290
const int q1 = 3 * q2 + i;
291
const Halfedge edge = halfedgesQ[q1];
292
const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge;
293
const auto [s, xyzz] = forward ? k11(p1, q1F) : k11(q1F, p1);
294
if (std::isfinite(xyzz[0])) {
295
x12 -= s * (edge.IsForward() ? 1 : -1);
296
if (k < 2 && (k == 0 || (s != 0) != shadows)) {
297
shadows = s != 0;
298
xzyLR0[k][0] = xyzz.x;
299
xzyLR0[k][1] = xyzz.z;
300
xzyLR0[k][2] = xyzz.y;
301
xzyLR1[k] = xzyLR0[k];
302
xzyLR1[k][1] = xyzz.w;
303
if (!forward) std::swap(xzyLR0[k][1], xzyLR1[k][1]);
304
k++;
305
}
306
}
307
}
308
309
if (x12 == 0) { // No intersection
310
v12 = vec3(NAN);
311
} else {
312
DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: v12");
313
const vec4 xzyy = Intersect(xzyLR0[0], xzyLR0[1], xzyLR1[0], xzyLR1[1]);
314
v12.x = xzyy[0];
315
v12.y = xzyy[2];
316
v12.z = xzyy[1];
317
}
318
return std::make_pair(x12, v12);
319
}
320
};
321
322
struct Kernel12Tmp {
323
Vec<std::array<int, 2>> p1q2_;
324
Vec<int> x12_;
325
Vec<vec3> v12_;
326
};
327
328
struct Kernel12Recorder {
329
using Local = Kernel12Tmp;
330
Kernel12& k12;
331
bool forward;
332
333
#if MANIFOLD_PAR == 1
334
tbb::combinable<Kernel12Tmp> store;
335
Local& local() { return store.local(); }
336
#else
337
Kernel12Tmp localStore;
338
Local& local() { return localStore; }
339
#endif
340
341
void record(int queryIdx, int leafIdx, Local& tmp) {
342
const auto [x12, v12] = k12(queryIdx, leafIdx);
343
if (std::isfinite(v12[0])) {
344
if (forward)
345
tmp.p1q2_.push_back({queryIdx, leafIdx});
346
else
347
tmp.p1q2_.push_back({leafIdx, queryIdx});
348
tmp.x12_.push_back(x12);
349
tmp.v12_.push_back(v12);
350
}
351
}
352
353
Kernel12Tmp get() {
354
#if MANIFOLD_PAR == 1
355
Kernel12Tmp result;
356
std::vector<Kernel12Tmp> tmps;
357
store.combine_each(
358
[&](Kernel12Tmp& data) { tmps.emplace_back(std::move(data)); });
359
std::vector<size_t> sizes;
360
size_t total_size = 0;
361
for (const auto& tmp : tmps) {
362
sizes.push_back(total_size);
363
total_size += tmp.x12_.size();
364
}
365
result.p1q2_.resize(total_size);
366
result.x12_.resize(total_size);
367
result.v12_.resize(total_size);
368
for_each_n(ExecutionPolicy::Seq, countAt(0), tmps.size(), [&](size_t i) {
369
std::copy(tmps[i].p1q2_.begin(), tmps[i].p1q2_.end(),
370
result.p1q2_.begin() + sizes[i]);
371
std::copy(tmps[i].x12_.begin(), tmps[i].x12_.end(),
372
result.x12_.begin() + sizes[i]);
373
std::copy(tmps[i].v12_.begin(), tmps[i].v12_.end(),
374
result.v12_.begin() + sizes[i]);
375
});
376
return result;
377
#else
378
return localStore;
379
#endif
380
}
381
};
382
383
std::tuple<Vec<int>, Vec<vec3>> Intersect12(const Manifold::Impl& inP,
384
const Manifold::Impl& inQ,
385
Vec<std::array<int, 2>>& p1q2,
386
double expandP, bool forward) {
387
ZoneScoped;
388
// a: 1 (edge), b: 2 (face)
389
const Manifold::Impl& a = forward ? inP : inQ;
390
const Manifold::Impl& b = forward ? inQ : inP;
391
392
Kernel02 k02{a.vertPos_, b.halfedge_, b.vertPos_,
393
expandP, inP.vertNormal_, forward};
394
Kernel11 k11{inP.vertPos_, inQ.vertPos_, inP.halfedge_,
395
inQ.halfedge_, expandP, inP.vertNormal_};
396
397
Kernel12 k12{a.halfedge_, b.halfedge_, a.vertPos_, forward, k02, k11};
398
Kernel12Recorder recorder{k12, forward, {}};
399
auto f = [&a](int i) {
400
return a.halfedge_[i].IsForward()
401
? Box(a.vertPos_[a.halfedge_[i].startVert],
402
a.vertPos_[a.halfedge_[i].endVert])
403
: Box();
404
};
405
b.collider_.Collisions<false, decltype(f), Kernel12Recorder>(
406
f, a.halfedge_.size(), recorder);
407
408
Kernel12Tmp result = recorder.get();
409
p1q2 = std::move(result.p1q2_);
410
auto x12 = std::move(result.x12_);
411
auto v12 = std::move(result.v12_);
412
// sort p1q2 according to edges
413
Vec<size_t> i12(p1q2.size());
414
sequence(i12.begin(), i12.end());
415
416
int index = forward ? 0 : 1;
417
stable_sort(i12.begin(), i12.end(), [&](int a, int b) {
418
return p1q2[a][index] < p1q2[b][index] ||
419
(p1q2[a][index] == p1q2[b][index] &&
420
p1q2[a][1 - index] < p1q2[b][1 - index]);
421
});
422
Permute(p1q2, i12);
423
Permute(x12, i12);
424
Permute(v12, i12);
425
return std::make_tuple(x12, v12);
426
};
427
428
Vec<int> Winding03(const Manifold::Impl& inP, const Manifold::Impl& inQ,
429
const VecView<std::array<int, 2>> p1q2, double expandP,
430
bool forward) {
431
ZoneScoped;
432
const Manifold::Impl& a = forward ? inP : inQ;
433
const Manifold::Impl& b = forward ? inQ : inP;
434
Vec<int> brokenHalfedges;
435
int index = forward ? 0 : 1;
436
437
DisjointSets uA(a.vertPos_.size());
438
for_each(autoPolicy(a.halfedge_.size()), countAt(0),
439
countAt(a.halfedge_.size()), [&](int edge) {
440
const Halfedge& he = a.halfedge_[edge];
441
if (!he.IsForward()) return;
442
// check if the edge is broken
443
auto it = std::lower_bound(
444
p1q2.begin(), p1q2.end(), edge,
445
[index](const std::array<int, 2>& collisionPair, int e) {
446
return collisionPair[index] < e;
447
});
448
if (it == p1q2.end() || (*it)[index] != edge)
449
uA.unite(he.startVert, he.endVert);
450
});
451
452
// find components, the hope is the number of components should be small
453
std::unordered_set<int> components;
454
#if (MANIFOLD_PAR == 1)
455
if (a.vertPos_.size() > 1e5) {
456
tbb::combinable<std::unordered_set<int>> componentsShared;
457
for_each(autoPolicy(a.vertPos_.size()), countAt(0),
458
countAt(a.vertPos_.size()),
459
[&](int v) { componentsShared.local().insert(uA.find(v)); });
460
componentsShared.combine_each([&](const std::unordered_set<int>& data) {
461
components.insert(data.begin(), data.end());
462
});
463
} else
464
#endif
465
{
466
for (size_t v = 0; v < a.vertPos_.size(); v++)
467
components.insert(uA.find(v));
468
}
469
Vec<int> verts;
470
verts.reserve(components.size());
471
for (int c : components) verts.push_back(c);
472
473
Vec<int> w03(a.NumVert(), 0);
474
Kernel02 k02{a.vertPos_, b.halfedge_, b.vertPos_,
475
expandP, inP.vertNormal_, forward};
476
auto recorderf = [&](int i, int b) {
477
const auto [s02, z02] = k02(verts[i], b);
478
if (std::isfinite(z02)) w03[verts[i]] += s02 * (!forward ? -1 : 1);
479
};
480
auto recorder = MakeSimpleRecorder(recorderf);
481
auto f = [&](int i) { return a.vertPos_[verts[i]]; };
482
b.collider_.Collisions<false, decltype(f), decltype(recorder)>(
483
f, verts.size(), recorder);
484
// flood fill
485
for_each(autoPolicy(w03.size()), countAt(0), countAt(w03.size()),
486
[&](size_t i) {
487
size_t root = uA.find(i);
488
if (root == i) return;
489
w03[i] = w03[root];
490
});
491
return w03;
492
}
493
} // namespace
494
495
namespace manifold {
496
Boolean3::Boolean3(const Manifold::Impl& inP, const Manifold::Impl& inQ,
497
OpType op)
498
: inP_(inP), inQ_(inQ), expandP_(op == OpType::Add ? 1.0 : -1.0) {
499
// Symbolic perturbation:
500
// Union -> expand inP
501
// Difference, Intersection -> contract inP
502
constexpr size_t INT_MAX_SZ =
503
static_cast<size_t>(std::numeric_limits<int>::max());
504
505
if (inP.IsEmpty() || inQ.IsEmpty() || !inP.bBox_.DoesOverlap(inQ.bBox_)) {
506
PRINT("No overlap, early out");
507
w03_.resize(inP.NumVert(), 0);
508
w30_.resize(inQ.NumVert(), 0);
509
return;
510
}
511
512
#ifdef MANIFOLD_DEBUG
513
Timer intersections;
514
intersections.Start();
515
#endif
516
517
// Level 3
518
// Build up the intersection of the edges and triangles, keeping only those
519
// that intersect, and record the direction the edge is passing through the
520
// triangle.
521
std::tie(x12_, v12_) = Intersect12(inP, inQ, p1q2_, expandP_, true);
522
PRINT("x12 size = " << x12_.size());
523
524
std::tie(x21_, v21_) = Intersect12(inP, inQ, p2q1_, expandP_, false);
525
PRINT("x21 size = " << x21_.size());
526
527
if (x12_.size() > INT_MAX_SZ || x21_.size() > INT_MAX_SZ) {
528
valid = false;
529
return;
530
}
531
532
// Compute winding numbers of all vertices using flood fill
533
// Vertices on the same connected component have the same winding number
534
w03_ = Winding03(inP, inQ, p1q2_, expandP_, true);
535
w30_ = Winding03(inP, inQ, p2q1_, expandP_, false);
536
537
#ifdef MANIFOLD_DEBUG
538
intersections.Stop();
539
540
if (ManifoldParams().verbose) {
541
intersections.Print("Intersections");
542
}
543
#endif
544
}
545
} // namespace manifold
546
547