Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/boolean_result.cpp
9903 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 <algorithm>
16
#include <array>
17
#include <map>
18
19
#include "boolean3.h"
20
#include "parallel.h"
21
#include "utils.h"
22
23
#if (MANIFOLD_PAR == 1) && __has_include(<tbb/concurrent_map.h>)
24
#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1
25
#include <tbb/concurrent_map.h>
26
#include <tbb/parallel_for.h>
27
28
template <typename K, typename V>
29
using concurrent_map = tbb::concurrent_map<K, V>;
30
#else
31
template <typename K, typename V>
32
// not really concurrent when tbb is disabled
33
using concurrent_map = std::map<K, V>;
34
#endif
35
36
using namespace manifold;
37
38
template <>
39
struct std::hash<std::pair<int, int>> {
40
size_t operator()(const std::pair<int, int>& p) const {
41
return std::hash<int>()(p.first) ^ std::hash<int>()(p.second);
42
}
43
};
44
45
namespace {
46
47
constexpr int kParallelThreshold = 128;
48
49
struct AbsSum {
50
int operator()(int a, int b) const { return abs(a) + abs(b); }
51
};
52
53
struct DuplicateVerts {
54
VecView<vec3> vertPosR;
55
VecView<const int> inclusion;
56
VecView<const int> vertR;
57
VecView<const vec3> vertPosP;
58
59
void operator()(const int vert) {
60
const int n = std::abs(inclusion[vert]);
61
for (int i = 0; i < n; ++i) {
62
vertPosR[vertR[vert] + i] = vertPosP[vert];
63
}
64
}
65
};
66
67
template <bool atomic>
68
struct CountVerts {
69
VecView<Halfedge> halfedges;
70
VecView<int> count;
71
VecView<const int> inclusion;
72
73
void operator()(size_t i) {
74
if (atomic)
75
AtomicAdd(count[i / 3], std::abs(inclusion[halfedges[i].startVert]));
76
else
77
count[i / 3] += std::abs(inclusion[halfedges[i].startVert]);
78
}
79
};
80
81
template <const bool inverted, const bool atomic>
82
struct CountNewVerts {
83
VecView<int> countP;
84
VecView<int> countQ;
85
VecView<const int> i12;
86
const Vec<std::array<int, 2>>& pq;
87
VecView<const Halfedge> halfedges;
88
89
void operator()(const int idx) {
90
int edgeP = pq[idx][inverted ? 1 : 0];
91
int faceQ = pq[idx][inverted ? 0 : 1];
92
int inclusion = std::abs(i12[idx]);
93
94
if (atomic) {
95
AtomicAdd(countQ[faceQ], inclusion);
96
const Halfedge half = halfedges[edgeP];
97
AtomicAdd(countP[edgeP / 3], inclusion);
98
AtomicAdd(countP[half.pairedHalfedge / 3], inclusion);
99
} else {
100
countQ[faceQ] += inclusion;
101
const Halfedge half = halfedges[edgeP];
102
countP[edgeP / 3] += inclusion;
103
countP[half.pairedHalfedge / 3] += inclusion;
104
}
105
}
106
};
107
108
std::tuple<Vec<int>, Vec<int>> SizeOutput(
109
Manifold::Impl& outR, const Manifold::Impl& inP, const Manifold::Impl& inQ,
110
const Vec<int>& i03, const Vec<int>& i30, const Vec<int>& i12,
111
const Vec<int>& i21, const Vec<std::array<int, 2>>& p1q2,
112
const Vec<std::array<int, 2>>& p2q1, bool invertQ) {
113
ZoneScoped;
114
Vec<int> sidesPerFacePQ(inP.NumTri() + inQ.NumTri(), 0);
115
// note: numFaceR <= facePQ2R.size() = sidesPerFacePQ.size() + 1
116
117
auto sidesPerFaceP = sidesPerFacePQ.view(0, inP.NumTri());
118
auto sidesPerFaceQ = sidesPerFacePQ.view(inP.NumTri(), inQ.NumTri());
119
120
if (inP.halfedge_.size() >= 1e5) {
121
for_each(ExecutionPolicy::Par, countAt(0_uz), countAt(inP.halfedge_.size()),
122
CountVerts<true>({inP.halfedge_, sidesPerFaceP, i03}));
123
for_each(ExecutionPolicy::Par, countAt(0_uz), countAt(inQ.halfedge_.size()),
124
CountVerts<true>({inQ.halfedge_, sidesPerFaceQ, i30}));
125
} else {
126
for_each(ExecutionPolicy::Seq, countAt(0_uz), countAt(inP.halfedge_.size()),
127
CountVerts<false>({inP.halfedge_, sidesPerFaceP, i03}));
128
for_each(ExecutionPolicy::Seq, countAt(0_uz), countAt(inQ.halfedge_.size()),
129
CountVerts<false>({inQ.halfedge_, sidesPerFaceQ, i30}));
130
}
131
132
if (i12.size() >= 1e5) {
133
for_each_n(ExecutionPolicy::Par, countAt(0), i12.size(),
134
CountNewVerts<false, true>(
135
{sidesPerFaceP, sidesPerFaceQ, i12, p1q2, inP.halfedge_}));
136
for_each_n(ExecutionPolicy::Par, countAt(0), i21.size(),
137
CountNewVerts<true, true>(
138
{sidesPerFaceQ, sidesPerFaceP, i21, p2q1, inQ.halfedge_}));
139
} else {
140
for_each_n(ExecutionPolicy::Seq, countAt(0), i12.size(),
141
CountNewVerts<false, false>(
142
{sidesPerFaceP, sidesPerFaceQ, i12, p1q2, inP.halfedge_}));
143
for_each_n(ExecutionPolicy::Seq, countAt(0), i21.size(),
144
CountNewVerts<true, false>(
145
{sidesPerFaceQ, sidesPerFaceP, i21, p2q1, inQ.halfedge_}));
146
}
147
148
Vec<int> facePQ2R(inP.NumTri() + inQ.NumTri() + 1, 0);
149
auto keepFace = TransformIterator(sidesPerFacePQ.begin(),
150
[](int x) { return x > 0 ? 1 : 0; });
151
152
inclusive_scan(keepFace, keepFace + sidesPerFacePQ.size(),
153
facePQ2R.begin() + 1);
154
int numFaceR = facePQ2R.back();
155
facePQ2R.resize(inP.NumTri() + inQ.NumTri());
156
157
outR.faceNormal_.resize_nofill(numFaceR);
158
159
Vec<size_t> tmpBuffer(outR.faceNormal_.size());
160
auto faceIds = TransformIterator(countAt(0_uz), [&sidesPerFacePQ](size_t i) {
161
if (sidesPerFacePQ[i] > 0) return i;
162
return std::numeric_limits<size_t>::max();
163
});
164
165
auto next =
166
copy_if(faceIds, faceIds + inP.faceNormal_.size(), tmpBuffer.begin(),
167
[](size_t v) { return v != std::numeric_limits<size_t>::max(); });
168
169
gather(tmpBuffer.begin(), next, inP.faceNormal_.begin(),
170
outR.faceNormal_.begin());
171
172
auto faceIdsQ =
173
TransformIterator(countAt(0_uz), [&sidesPerFacePQ, &inP](size_t i) {
174
if (sidesPerFacePQ[i + inP.faceNormal_.size()] > 0) return i;
175
return std::numeric_limits<size_t>::max();
176
});
177
auto end =
178
copy_if(faceIdsQ, faceIdsQ + inQ.faceNormal_.size(), next,
179
[](size_t v) { return v != std::numeric_limits<size_t>::max(); });
180
181
if (invertQ) {
182
gather(next, end,
183
TransformIterator(inQ.faceNormal_.begin(), Negate<vec3>()),
184
outR.faceNormal_.begin() + std::distance(tmpBuffer.begin(), next));
185
} else {
186
gather(next, end, inQ.faceNormal_.begin(),
187
outR.faceNormal_.begin() + std::distance(tmpBuffer.begin(), next));
188
}
189
190
auto newEnd = remove(sidesPerFacePQ.begin(), sidesPerFacePQ.end(), 0);
191
Vec<int> faceEdge(newEnd - sidesPerFacePQ.begin() + 1, 0);
192
inclusive_scan(sidesPerFacePQ.begin(), newEnd, faceEdge.begin() + 1);
193
outR.halfedge_.resize(faceEdge.back());
194
195
return std::make_tuple(faceEdge, facePQ2R);
196
}
197
198
struct EdgePos {
199
double edgePos;
200
int vert;
201
int collisionId;
202
bool isStart;
203
};
204
205
// thread sanitizer doesn't really know how to check when there are too many
206
// mutex
207
#if defined(__has_feature)
208
#if __has_feature(thread_sanitizer)
209
__attribute__((no_sanitize("thread")))
210
#endif
211
#endif
212
void AddNewEdgeVerts(
213
// we need concurrent_map because we will be adding things concurrently
214
concurrent_map<int, std::vector<EdgePos>> &edgesP,
215
concurrent_map<std::pair<int, int>, std::vector<EdgePos>> &edgesNew,
216
const Vec<std::array<int, 2>> &p1q2, const Vec<int> &i12, const Vec<int> &v12R,
217
const Vec<Halfedge> &halfedgeP, bool forward, size_t offset) {
218
ZoneScoped;
219
// For each edge of P that intersects a face of Q (p1q2), add this vertex to
220
// P's corresponding edge vector and to the two new edges, which are
221
// intersections between the face of Q and the two faces of P attached to the
222
// edge. The direction and duplicity are given by i12, while v12R remaps to
223
// the output vert index. When forward is false, all is reversed.
224
auto process = [&](std::function<void(size_t)> lock,
225
std::function<void(size_t)> unlock, size_t i) {
226
const int edgeP = p1q2[i][forward ? 0 : 1];
227
const int faceQ = p1q2[i][forward ? 1 : 0];
228
const int vert = v12R[i];
229
const int inclusion = i12[i];
230
231
Halfedge halfedge = halfedgeP[edgeP];
232
std::pair<int, int> keyRight = {halfedge.pairedHalfedge / 3, faceQ};
233
if (!forward) std::swap(keyRight.first, keyRight.second);
234
235
std::pair<int, int> keyLeft = {edgeP / 3, faceQ};
236
if (!forward) std::swap(keyLeft.first, keyLeft.second);
237
238
bool direction = inclusion < 0;
239
std::hash<std::pair<int, int>> pairHasher;
240
std::array<std::tuple<bool, size_t, std::vector<EdgePos>*>, 3> edges = {
241
std::make_tuple(direction, std::hash<int>{}(edgeP), &edgesP[edgeP]),
242
std::make_tuple(direction ^ !forward, // revert if not forward
243
pairHasher(keyRight), &edgesNew[keyRight]),
244
std::make_tuple(direction ^ forward, // revert if forward
245
pairHasher(keyLeft), &edgesNew[keyLeft])};
246
for (const auto& tuple : edges) {
247
lock(std::get<1>(tuple));
248
for (int j = 0; j < std::abs(inclusion); ++j)
249
std::get<2>(tuple)->push_back(
250
{0.0, vert + j, static_cast<int>(i + offset), std::get<0>(tuple)});
251
unlock(std::get<1>(tuple));
252
direction = !direction;
253
}
254
};
255
#if (MANIFOLD_PAR == 1) && __has_include(<tbb/concurrent_map.h>)
256
// parallelize operations, requires concurrent_map so we can only enable this
257
// with tbb
258
if (p1q2.size() > kParallelThreshold) {
259
// ideally we should have 1 mutex per key, but kParallelThreshold is enough
260
// to avoid contention for most of the cases
261
std::array<std::mutex, kParallelThreshold> mutexes;
262
static tbb::affinity_partitioner ap;
263
auto processFun = std::bind(
264
process, [&](size_t hash) { mutexes[hash % mutexes.size()].lock(); },
265
[&](size_t hash) { mutexes[hash % mutexes.size()].unlock(); },
266
std::placeholders::_1);
267
tbb::parallel_for(
268
tbb::blocked_range<size_t>(0_uz, p1q2.size(), 32),
269
[&](const tbb::blocked_range<size_t>& range) {
270
for (size_t i = range.begin(); i != range.end(); i++) processFun(i);
271
},
272
ap);
273
return;
274
}
275
#endif
276
auto processFun =
277
std::bind(process, [](size_t) {}, [](size_t) {}, std::placeholders::_1);
278
for (size_t i = 0; i < p1q2.size(); ++i) processFun(i);
279
}
280
281
std::vector<Halfedge> PairUp(std::vector<EdgePos>& edgePos) {
282
// Pair start vertices with end vertices to form edges. The choice of pairing
283
// is arbitrary for the manifoldness guarantee, but must be ordered to be
284
// geometrically valid. If the order does not go start-end-start-end... then
285
// the input and output are not geometrically valid and this algorithm becomes
286
// a heuristic.
287
DEBUG_ASSERT(edgePos.size() % 2 == 0, topologyErr,
288
"Non-manifold edge! Not an even number of points.");
289
size_t nEdges = edgePos.size() / 2;
290
auto middle = std::partition(edgePos.begin(), edgePos.end(),
291
[](EdgePos x) { return x.isStart; });
292
DEBUG_ASSERT(static_cast<size_t>(middle - edgePos.begin()) == nEdges,
293
topologyErr, "Non-manifold edge!");
294
auto cmp = [](EdgePos a, EdgePos b) {
295
return a.edgePos < b.edgePos ||
296
// we also sort by collisionId to make things deterministic
297
(a.edgePos == b.edgePos && a.collisionId < b.collisionId);
298
};
299
std::stable_sort(edgePos.begin(), middle, cmp);
300
std::stable_sort(middle, edgePos.end(), cmp);
301
std::vector<Halfedge> edges;
302
for (size_t i = 0; i < nEdges; ++i)
303
edges.push_back({edgePos[i].vert, edgePos[i + nEdges].vert, -1});
304
return edges;
305
}
306
307
void AppendPartialEdges(Manifold::Impl& outR, Vec<char>& wholeHalfedgeP,
308
Vec<int>& facePtrR,
309
concurrent_map<int, std::vector<EdgePos>>& edgesP,
310
Vec<TriRef>& halfedgeRef, const Manifold::Impl& inP,
311
const Vec<int>& i03, const Vec<int>& vP2R,
312
const Vec<int>::IterC faceP2R, bool forward) {
313
ZoneScoped;
314
// Each edge in the map is partially retained; for each of these, look up
315
// their original verts and include them based on their winding number (i03),
316
// while remapping them to the output using vP2R. Use the verts position
317
// projected along the edge vector to pair them up, then distribute these
318
// edges to their faces.
319
Vec<Halfedge>& halfedgeR = outR.halfedge_;
320
const Vec<vec3>& vertPosP = inP.vertPos_;
321
const Vec<Halfedge>& halfedgeP = inP.halfedge_;
322
323
for (auto& value : edgesP) {
324
const int edgeP = value.first;
325
std::vector<EdgePos>& edgePosP = value.second;
326
327
const Halfedge& halfedge = halfedgeP[edgeP];
328
wholeHalfedgeP[edgeP] = false;
329
wholeHalfedgeP[halfedge.pairedHalfedge] = false;
330
331
const int vStart = halfedge.startVert;
332
const int vEnd = halfedge.endVert;
333
const vec3 edgeVec = vertPosP[vEnd] - vertPosP[vStart];
334
// Fill in the edge positions of the old points.
335
for (EdgePos& edge : edgePosP) {
336
edge.edgePos = la::dot(outR.vertPos_[edge.vert], edgeVec);
337
}
338
339
int inclusion = i03[vStart];
340
EdgePos edgePos = {la::dot(outR.vertPos_[vP2R[vStart]], edgeVec),
341
vP2R[vStart], std::numeric_limits<int>::max(),
342
inclusion > 0};
343
for (int j = 0; j < std::abs(inclusion); ++j) {
344
edgePosP.push_back(edgePos);
345
++edgePos.vert;
346
}
347
348
inclusion = i03[vEnd];
349
edgePos = {la::dot(outR.vertPos_[vP2R[vEnd]], edgeVec), vP2R[vEnd],
350
std::numeric_limits<int>::max(), inclusion < 0};
351
for (int j = 0; j < std::abs(inclusion); ++j) {
352
edgePosP.push_back(edgePos);
353
++edgePos.vert;
354
}
355
356
// sort edges into start/end pairs along length
357
std::vector<Halfedge> edges = PairUp(edgePosP);
358
359
// add halfedges to result
360
const int faceLeftP = edgeP / 3;
361
const int faceLeft = faceP2R[faceLeftP];
362
const int faceRightP = halfedge.pairedHalfedge / 3;
363
const int faceRight = faceP2R[faceRightP];
364
// Negative inclusion means the halfedges are reversed, which means our
365
// reference is now to the endVert instead of the startVert, which is one
366
// position advanced CCW. This is only valid if this is a retained vert; it
367
// will be ignored later if the vert is new.
368
const TriRef forwardRef = {forward ? 0 : 1, -1, faceLeftP, -1};
369
const TriRef backwardRef = {forward ? 0 : 1, -1, faceRightP, -1};
370
371
for (Halfedge e : edges) {
372
const int forwardEdge = facePtrR[faceLeft]++;
373
const int backwardEdge = facePtrR[faceRight]++;
374
375
e.pairedHalfedge = backwardEdge;
376
halfedgeR[forwardEdge] = e;
377
halfedgeRef[forwardEdge] = forwardRef;
378
379
std::swap(e.startVert, e.endVert);
380
e.pairedHalfedge = forwardEdge;
381
halfedgeR[backwardEdge] = e;
382
halfedgeRef[backwardEdge] = backwardRef;
383
}
384
}
385
}
386
387
void AppendNewEdges(
388
Manifold::Impl& outR, Vec<int>& facePtrR,
389
concurrent_map<std::pair<int, int>, std::vector<EdgePos>>& edgesNew,
390
Vec<TriRef>& halfedgeRef, const Vec<int>& facePQ2R, const int numFaceP) {
391
ZoneScoped;
392
// Pair up each edge's verts and distribute to faces based on indices in key.
393
Vec<Halfedge>& halfedgeR = outR.halfedge_;
394
Vec<vec3>& vertPosR = outR.vertPos_;
395
396
for (auto& value : edgesNew) {
397
const int faceP = value.first.first;
398
const int faceQ = value.first.second;
399
std::vector<EdgePos>& edgePos = value.second;
400
401
Box bbox;
402
for (auto edge : edgePos) {
403
bbox.Union(vertPosR[edge.vert]);
404
}
405
const vec3 size = bbox.Size();
406
// Order the points along their longest dimension.
407
const int i = (size.x > size.y && size.x > size.z) ? 0
408
: size.y > size.z ? 1
409
: 2;
410
for (auto& edge : edgePos) {
411
edge.edgePos = vertPosR[edge.vert][i];
412
}
413
414
// sort edges into start/end pairs along length.
415
std::vector<Halfedge> edges = PairUp(edgePos);
416
417
// add halfedges to result
418
const int faceLeft = facePQ2R[faceP];
419
const int faceRight = facePQ2R[numFaceP + faceQ];
420
const TriRef forwardRef = {0, -1, faceP, -1};
421
const TriRef backwardRef = {1, -1, faceQ, -1};
422
for (Halfedge e : edges) {
423
const int forwardEdge = facePtrR[faceLeft]++;
424
const int backwardEdge = facePtrR[faceRight]++;
425
426
e.pairedHalfedge = backwardEdge;
427
halfedgeR[forwardEdge] = e;
428
halfedgeRef[forwardEdge] = forwardRef;
429
430
std::swap(e.startVert, e.endVert);
431
e.pairedHalfedge = forwardEdge;
432
halfedgeR[backwardEdge] = e;
433
halfedgeRef[backwardEdge] = backwardRef;
434
}
435
}
436
}
437
438
struct DuplicateHalfedges {
439
VecView<Halfedge> halfedgesR;
440
VecView<TriRef> halfedgeRef;
441
VecView<int> facePtr;
442
VecView<const char> wholeHalfedgeP;
443
VecView<const Halfedge> halfedgesP;
444
VecView<const int> i03;
445
VecView<const int> vP2R;
446
VecView<const int> faceP2R;
447
const bool forward;
448
449
void operator()(const int idx) {
450
if (!wholeHalfedgeP[idx]) return;
451
Halfedge halfedge = halfedgesP[idx];
452
if (!halfedge.IsForward()) return;
453
454
const int inclusion = i03[halfedge.startVert];
455
if (inclusion == 0) return;
456
if (inclusion < 0) { // reverse
457
std::swap(halfedge.startVert, halfedge.endVert);
458
}
459
halfedge.startVert = vP2R[halfedge.startVert];
460
halfedge.endVert = vP2R[halfedge.endVert];
461
const int faceLeftP = idx / 3;
462
const int newFace = faceP2R[faceLeftP];
463
const int faceRightP = halfedge.pairedHalfedge / 3;
464
const int faceRight = faceP2R[faceRightP];
465
// Negative inclusion means the halfedges are reversed, which means our
466
// reference is now to the endVert instead of the startVert, which is one
467
// position advanced CCW.
468
const TriRef forwardRef = {forward ? 0 : 1, -1, faceLeftP, -1};
469
const TriRef backwardRef = {forward ? 0 : 1, -1, faceRightP, -1};
470
471
for (int i = 0; i < std::abs(inclusion); ++i) {
472
int forwardEdge = AtomicAdd(facePtr[newFace], 1);
473
int backwardEdge = AtomicAdd(facePtr[faceRight], 1);
474
halfedge.pairedHalfedge = backwardEdge;
475
476
halfedgesR[forwardEdge] = halfedge;
477
halfedgesR[backwardEdge] = {halfedge.endVert, halfedge.startVert,
478
forwardEdge};
479
halfedgeRef[forwardEdge] = forwardRef;
480
halfedgeRef[backwardEdge] = backwardRef;
481
482
++halfedge.startVert;
483
++halfedge.endVert;
484
}
485
}
486
};
487
488
void AppendWholeEdges(Manifold::Impl& outR, Vec<int>& facePtrR,
489
Vec<TriRef>& halfedgeRef, const Manifold::Impl& inP,
490
const Vec<char> wholeHalfedgeP, const Vec<int>& i03,
491
const Vec<int>& vP2R, VecView<const int> faceP2R,
492
bool forward) {
493
ZoneScoped;
494
for_each_n(
495
autoPolicy(inP.halfedge_.size()), countAt(0), inP.halfedge_.size(),
496
DuplicateHalfedges({outR.halfedge_, halfedgeRef, facePtrR, wholeHalfedgeP,
497
inP.halfedge_, i03, vP2R, faceP2R, forward}));
498
}
499
500
struct MapTriRef {
501
VecView<const TriRef> triRefP;
502
VecView<const TriRef> triRefQ;
503
const int offsetQ;
504
505
void operator()(TriRef& triRef) {
506
const int tri = triRef.faceID;
507
const bool PQ = triRef.meshID == 0;
508
triRef = PQ ? triRefP[tri] : triRefQ[tri];
509
if (!PQ) triRef.meshID += offsetQ;
510
}
511
};
512
513
void UpdateReference(Manifold::Impl& outR, const Manifold::Impl& inP,
514
const Manifold::Impl& inQ, bool invertQ) {
515
const int offsetQ = Manifold::Impl::meshIDCounter_;
516
for_each_n(
517
autoPolicy(outR.NumTri(), 1e5), outR.meshRelation_.triRef.begin(),
518
outR.NumTri(),
519
MapTriRef({inP.meshRelation_.triRef, inQ.meshRelation_.triRef, offsetQ}));
520
521
for (const auto& pair : inP.meshRelation_.meshIDtransform) {
522
outR.meshRelation_.meshIDtransform[pair.first] = pair.second;
523
}
524
for (const auto& pair : inQ.meshRelation_.meshIDtransform) {
525
outR.meshRelation_.meshIDtransform[pair.first + offsetQ] = pair.second;
526
outR.meshRelation_.meshIDtransform[pair.first + offsetQ].backSide ^=
527
invertQ;
528
}
529
}
530
531
struct Barycentric {
532
VecView<vec3> uvw;
533
VecView<const TriRef> ref;
534
VecView<const vec3> vertPosP;
535
VecView<const vec3> vertPosQ;
536
VecView<const vec3> vertPosR;
537
VecView<const Halfedge> halfedgeP;
538
VecView<const Halfedge> halfedgeQ;
539
VecView<const Halfedge> halfedgeR;
540
const double epsilon;
541
542
void operator()(const int tri) {
543
const TriRef refPQ = ref[tri];
544
if (halfedgeR[3 * tri].startVert < 0) return;
545
546
const int triPQ = refPQ.faceID;
547
const bool PQ = refPQ.meshID == 0;
548
const auto& vertPos = PQ ? vertPosP : vertPosQ;
549
const auto& halfedge = PQ ? halfedgeP : halfedgeQ;
550
551
mat3 triPos;
552
for (const int j : {0, 1, 2})
553
triPos[j] = vertPos[halfedge[3 * triPQ + j].startVert];
554
555
for (const int i : {0, 1, 2}) {
556
const int vert = halfedgeR[3 * tri + i].startVert;
557
uvw[3 * tri + i] = GetBarycentric(vertPosR[vert], triPos, epsilon);
558
}
559
}
560
};
561
562
void CreateProperties(Manifold::Impl& outR, const Manifold::Impl& inP,
563
const Manifold::Impl& inQ) {
564
ZoneScoped;
565
const int numPropP = inP.NumProp();
566
const int numPropQ = inQ.NumProp();
567
const int numProp = std::max(numPropP, numPropQ);
568
outR.numProp_ = numProp;
569
if (numProp == 0) return;
570
571
const int numTri = outR.NumTri();
572
Vec<vec3> bary(outR.halfedge_.size());
573
for_each_n(autoPolicy(numTri, 1e4), countAt(0), numTri,
574
Barycentric({bary, outR.meshRelation_.triRef, inP.vertPos_,
575
inQ.vertPos_, outR.vertPos_, inP.halfedge_,
576
inQ.halfedge_, outR.halfedge_, outR.epsilon_}));
577
578
using Entry = std::pair<ivec3, int>;
579
int idMissProp = outR.NumVert();
580
std::vector<std::vector<Entry>> propIdx(outR.NumVert() + 1);
581
std::vector<int> propMissIdx[2];
582
propMissIdx[0].resize(inQ.NumPropVert(), -1);
583
propMissIdx[1].resize(inP.NumPropVert(), -1);
584
585
outR.properties_.reserve(outR.NumVert() * numProp);
586
int idx = 0;
587
588
for (int tri = 0; tri < numTri; ++tri) {
589
// Skip collapsed triangles
590
if (outR.halfedge_[3 * tri].startVert < 0) continue;
591
592
const TriRef ref = outR.meshRelation_.triRef[tri];
593
const bool PQ = ref.meshID == 0;
594
const int oldNumProp = PQ ? numPropP : numPropQ;
595
const auto& properties = PQ ? inP.properties_ : inQ.properties_;
596
const auto& halfedge = PQ ? inP.halfedge_ : inQ.halfedge_;
597
598
for (const int i : {0, 1, 2}) {
599
const int vert = outR.halfedge_[3 * tri + i].startVert;
600
const vec3& uvw = bary[3 * tri + i];
601
602
ivec4 key(PQ, idMissProp, -1, -1);
603
if (oldNumProp > 0) {
604
int edge = -2;
605
for (const int j : {0, 1, 2}) {
606
if (uvw[j] == 1) {
607
// On a retained vert, the propVert must also match
608
key[2] = halfedge[3 * ref.faceID + j].propVert;
609
edge = -1;
610
break;
611
}
612
if (uvw[j] == 0) edge = j;
613
}
614
if (edge >= 0) {
615
// On an edge, both propVerts must match
616
const int p0 = halfedge[3 * ref.faceID + Next3(edge)].propVert;
617
const int p1 = halfedge[3 * ref.faceID + Prev3(edge)].propVert;
618
key[1] = vert;
619
key[2] = std::min(p0, p1);
620
key[3] = std::max(p0, p1);
621
} else if (edge == -2) {
622
key[1] = vert;
623
}
624
}
625
626
if (key.y == idMissProp && key.z >= 0) {
627
// only key.x/key.z matters
628
auto& entry = propMissIdx[key.x][key.z];
629
if (entry >= 0) {
630
outR.halfedge_[3 * tri + i].propVert = entry;
631
continue;
632
}
633
entry = idx;
634
} else {
635
auto& bin = propIdx[key.y];
636
bool bFound = false;
637
for (const auto& b : bin) {
638
if (b.first == ivec3(key.x, key.z, key.w)) {
639
bFound = true;
640
outR.halfedge_[3 * tri + i].propVert = b.second;
641
break;
642
}
643
}
644
if (bFound) continue;
645
bin.push_back(std::make_pair(ivec3(key.x, key.z, key.w), idx));
646
}
647
648
outR.halfedge_[3 * tri + i].propVert = idx++;
649
for (int p = 0; p < numProp; ++p) {
650
if (p < oldNumProp) {
651
vec3 oldProps;
652
for (const int j : {0, 1, 2})
653
oldProps[j] =
654
properties[oldNumProp * halfedge[3 * ref.faceID + j].propVert +
655
p];
656
outR.properties_.push_back(la::dot(uvw, oldProps));
657
} else {
658
outR.properties_.push_back(0);
659
}
660
}
661
}
662
}
663
}
664
665
void ReorderHalfedges(VecView<Halfedge>& halfedges) {
666
ZoneScoped;
667
// halfedges in the same face are added in non-deterministic order, so we have
668
// to reorder them for determinism
669
670
// step 1: reorder within the same face, such that the halfedge with the
671
// smallest starting vertex is placed first
672
for_each(autoPolicy(halfedges.size() / 3), countAt(0),
673
countAt(halfedges.size() / 3), [&halfedges](size_t tri) {
674
std::array<Halfedge, 3> face = {halfedges[tri * 3],
675
halfedges[tri * 3 + 1],
676
halfedges[tri * 3 + 2]};
677
int index = 0;
678
for (int i : {1, 2})
679
if (face[i].startVert < face[index].startVert) index = i;
680
for (int i : {0, 1, 2})
681
halfedges[tri * 3 + i] = face[(index + i) % 3];
682
});
683
// step 2: fix paired halfedge
684
for_each(autoPolicy(halfedges.size() / 3), countAt(0),
685
countAt(halfedges.size() / 3), [&halfedges](size_t tri) {
686
for (int i : {0, 1, 2}) {
687
Halfedge& curr = halfedges[tri * 3 + i];
688
int oppositeFace = curr.pairedHalfedge / 3;
689
int index = -1;
690
for (int j : {0, 1, 2})
691
if (curr.startVert == halfedges[oppositeFace * 3 + j].endVert)
692
index = j;
693
curr.pairedHalfedge = oppositeFace * 3 + index;
694
}
695
});
696
}
697
698
} // namespace
699
700
namespace manifold {
701
702
Manifold::Impl Boolean3::Result(OpType op) const {
703
#ifdef MANIFOLD_DEBUG
704
Timer assemble;
705
assemble.Start();
706
#endif
707
708
DEBUG_ASSERT((expandP_ > 0) == (op == OpType::Add), logicErr,
709
"Result op type not compatible with constructor op type.");
710
const int c1 = op == OpType::Intersect ? 0 : 1;
711
const int c2 = op == OpType::Add ? 1 : 0;
712
const int c3 = op == OpType::Intersect ? 1 : -1;
713
714
if (inP_.status_ != Manifold::Error::NoError) {
715
auto impl = Manifold::Impl();
716
impl.status_ = inP_.status_;
717
return impl;
718
}
719
if (inQ_.status_ != Manifold::Error::NoError) {
720
auto impl = Manifold::Impl();
721
impl.status_ = inQ_.status_;
722
return impl;
723
}
724
725
if (inP_.IsEmpty()) {
726
if (!inQ_.IsEmpty() && op == OpType::Add) {
727
return inQ_;
728
}
729
return Manifold::Impl();
730
} else if (inQ_.IsEmpty()) {
731
if (op == OpType::Intersect) {
732
return Manifold::Impl();
733
}
734
return inP_;
735
}
736
737
if (!valid) {
738
auto impl = Manifold::Impl();
739
impl.status_ = Manifold::Error::ResultTooLarge;
740
return impl;
741
}
742
743
const bool invertQ = op == OpType::Subtract;
744
745
// Convert winding numbers to inclusion values based on operation type.
746
Vec<int> i12(x12_.size());
747
Vec<int> i21(x21_.size());
748
Vec<int> i03(w03_.size());
749
Vec<int> i30(w30_.size());
750
751
transform(x12_.begin(), x12_.end(), i12.begin(),
752
[c3](int v) { return c3 * v; });
753
transform(x21_.begin(), x21_.end(), i21.begin(),
754
[c3](int v) { return c3 * v; });
755
transform(w03_.begin(), w03_.end(), i03.begin(),
756
[c1, c3](int v) { return c1 + c3 * v; });
757
transform(w30_.begin(), w30_.end(), i30.begin(),
758
[c2, c3](int v) { return c2 + c3 * v; });
759
760
Vec<int> vP2R(inP_.NumVert());
761
exclusive_scan(i03.begin(), i03.end(), vP2R.begin(), 0, AbsSum());
762
int numVertR = AbsSum()(vP2R.back(), i03.back());
763
const int nPv = numVertR;
764
765
Vec<int> vQ2R(inQ_.NumVert());
766
exclusive_scan(i30.begin(), i30.end(), vQ2R.begin(), numVertR, AbsSum());
767
numVertR = AbsSum()(vQ2R.back(), i30.back());
768
const int nQv = numVertR - nPv;
769
770
Vec<int> v12R(v12_.size());
771
if (v12_.size() > 0) {
772
exclusive_scan(i12.begin(), i12.end(), v12R.begin(), numVertR, AbsSum());
773
numVertR = AbsSum()(v12R.back(), i12.back());
774
}
775
const int n12 = numVertR - nPv - nQv;
776
777
Vec<int> v21R(v21_.size());
778
if (v21_.size() > 0) {
779
exclusive_scan(i21.begin(), i21.end(), v21R.begin(), numVertR, AbsSum());
780
numVertR = AbsSum()(v21R.back(), i21.back());
781
}
782
const int n21 = numVertR - nPv - nQv - n12;
783
784
// Create the output Manifold
785
Manifold::Impl outR;
786
787
if (numVertR == 0) return outR;
788
789
outR.epsilon_ = std::max(inP_.epsilon_, inQ_.epsilon_);
790
outR.tolerance_ = std::max(inP_.tolerance_, inQ_.tolerance_);
791
792
outR.vertPos_.resize_nofill(numVertR);
793
// Add vertices, duplicating for inclusion numbers not in [-1, 1].
794
// Retained vertices from P and Q:
795
for_each_n(autoPolicy(inP_.NumVert(), 1e4), countAt(0), inP_.NumVert(),
796
DuplicateVerts({outR.vertPos_, i03, vP2R, inP_.vertPos_}));
797
for_each_n(autoPolicy(inQ_.NumVert(), 1e4), countAt(0), inQ_.NumVert(),
798
DuplicateVerts({outR.vertPos_, i30, vQ2R, inQ_.vertPos_}));
799
// New vertices created from intersections:
800
for_each_n(autoPolicy(i12.size(), 1e4), countAt(0), i12.size(),
801
DuplicateVerts({outR.vertPos_, i12, v12R, v12_}));
802
for_each_n(autoPolicy(i21.size(), 1e4), countAt(0), i21.size(),
803
DuplicateVerts({outR.vertPos_, i21, v21R, v21_}));
804
805
PRINT(nPv << " verts from inP");
806
PRINT(nQv << " verts from inQ");
807
PRINT(n12 << " new verts from edgesP -> facesQ");
808
PRINT(n21 << " new verts from facesP -> edgesQ");
809
810
// Build up new polygonal faces from triangle intersections. At this point the
811
// calculation switches from parallel to serial.
812
813
// Level 3
814
815
// This key is the forward halfedge index of P or Q. Only includes intersected
816
// edges.
817
concurrent_map<int, std::vector<EdgePos>> edgesP, edgesQ;
818
// This key is the face index of <P, Q>
819
concurrent_map<std::pair<int, int>, std::vector<EdgePos>> edgesNew;
820
821
AddNewEdgeVerts(edgesP, edgesNew, p1q2_, i12, v12R, inP_.halfedge_, true, 0);
822
AddNewEdgeVerts(edgesQ, edgesNew, p2q1_, i21, v21R, inQ_.halfedge_, false,
823
p1q2_.size());
824
825
v12R.clear();
826
v21R.clear();
827
828
// Level 4
829
Vec<int> faceEdge;
830
Vec<int> facePQ2R;
831
std::tie(faceEdge, facePQ2R) =
832
SizeOutput(outR, inP_, inQ_, i03, i30, i12, i21, p1q2_, p2q1_, invertQ);
833
834
i12.clear();
835
i21.clear();
836
837
// This gets incremented for each halfedge that's added to a face so that the
838
// next one knows where to slot in.
839
Vec<int> facePtrR = faceEdge;
840
// Intersected halfedges are marked false.
841
Vec<char> wholeHalfedgeP(inP_.halfedge_.size(), true);
842
Vec<char> wholeHalfedgeQ(inQ_.halfedge_.size(), true);
843
// The halfedgeRef contains the data that will become triRef once the faces
844
// are triangulated.
845
Vec<TriRef> halfedgeRef(2 * outR.NumEdge());
846
847
AppendPartialEdges(outR, wholeHalfedgeP, facePtrR, edgesP, halfedgeRef, inP_,
848
i03, vP2R, facePQ2R.begin(), true);
849
AppendPartialEdges(outR, wholeHalfedgeQ, facePtrR, edgesQ, halfedgeRef, inQ_,
850
i30, vQ2R, facePQ2R.begin() + inP_.NumTri(), false);
851
852
edgesP.clear();
853
edgesQ.clear();
854
855
AppendNewEdges(outR, facePtrR, edgesNew, halfedgeRef, facePQ2R,
856
inP_.NumTri());
857
858
edgesNew.clear();
859
860
AppendWholeEdges(outR, facePtrR, halfedgeRef, inP_, wholeHalfedgeP, i03, vP2R,
861
facePQ2R.cview(0, inP_.NumTri()), true);
862
AppendWholeEdges(outR, facePtrR, halfedgeRef, inQ_, wholeHalfedgeQ, i30, vQ2R,
863
facePQ2R.cview(inP_.NumTri(), inQ_.NumTri()), false);
864
865
wholeHalfedgeP.clear();
866
wholeHalfedgeQ.clear();
867
facePtrR.clear();
868
facePQ2R.clear();
869
i03.clear();
870
i30.clear();
871
vP2R.clear();
872
vQ2R.clear();
873
874
#ifdef MANIFOLD_DEBUG
875
assemble.Stop();
876
Timer triangulate;
877
triangulate.Start();
878
#endif
879
880
// Level 6
881
882
if (ManifoldParams().intermediateChecks)
883
DEBUG_ASSERT(outR.IsManifold(), logicErr, "polygon mesh is not manifold!");
884
885
outR.Face2Tri(faceEdge, halfedgeRef);
886
halfedgeRef.clear();
887
faceEdge.clear();
888
889
ReorderHalfedges(outR.halfedge_);
890
891
#ifdef MANIFOLD_DEBUG
892
triangulate.Stop();
893
Timer simplify;
894
simplify.Start();
895
#endif
896
897
if (ManifoldParams().intermediateChecks)
898
DEBUG_ASSERT(outR.IsManifold(), logicErr,
899
"triangulated mesh is not manifold!");
900
901
CreateProperties(outR, inP_, inQ_);
902
903
UpdateReference(outR, inP_, inQ_, invertQ);
904
905
outR.SimplifyTopology(nPv + nQv);
906
outR.RemoveUnreferencedVerts();
907
908
if (ManifoldParams().intermediateChecks)
909
DEBUG_ASSERT(outR.Is2Manifold(), logicErr,
910
"simplified mesh is not 2-manifold!");
911
912
#ifdef MANIFOLD_DEBUG
913
simplify.Stop();
914
Timer sort;
915
sort.Start();
916
#endif
917
918
outR.Finish();
919
outR.IncrementMeshIDs();
920
921
#ifdef MANIFOLD_DEBUG
922
sort.Stop();
923
if (ManifoldParams().verbose) {
924
assemble.Print("Assembly");
925
triangulate.Print("Triangulation");
926
simplify.Print("Simplification");
927
sort.Print("Sorting");
928
std::cout << outR.NumVert() << " verts and " << outR.NumTri() << " tris"
929
<< std::endl;
930
}
931
#endif
932
933
return outR;
934
}
935
936
} // namespace manifold
937
938