Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/impl.cpp
20879 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 "impl.h"
16
17
#include <algorithm>
18
#include <atomic>
19
#include <cstring>
20
#include <map>
21
#include <optional>
22
23
#include "csg_tree.h"
24
#include "disjoint_sets.h"
25
#include "hashtable.h"
26
#include "manifold/optional_assert.h"
27
#include "mesh_fixes.h"
28
#include "parallel.h"
29
#include "shared.h"
30
#include "svd.h"
31
32
namespace {
33
using namespace manifold;
34
35
/**
36
* Returns arc cosine of 𝑥.
37
*
38
* @return value in range [0,M_PI]
39
* @return NAN if 𝑥 ∈ {NAN,+INFINITY,-INFINITY}
40
* @return NAN if 𝑥 ∉ [-1,1]
41
*/
42
double sun_acos(double x) {
43
/*
44
* Origin of acos function: FreeBSD /usr/src/lib/msun/src/e_acos.c
45
* Changed the use of union to memcpy to avoid undefined behavior.
46
* ====================================================
47
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
48
*
49
* Developed at SunSoft, a Sun Microsystems, Inc. business.
50
* Permission to use, copy, modify, and distribute this
51
* software is freely granted, provided that this notice
52
* is preserved.
53
* ====================================================
54
*/
55
constexpr double pio2_hi =
56
1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
57
constexpr double pio2_lo =
58
6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
59
constexpr double pS0 =
60
1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
61
constexpr double pS1 =
62
-3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
63
constexpr double pS2 =
64
2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
65
constexpr double pS3 =
66
-4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
67
constexpr double pS4 =
68
7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
69
constexpr double pS5 =
70
3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
71
constexpr double qS1 =
72
-2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
73
constexpr double qS2 =
74
2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
75
constexpr double qS3 =
76
-6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
77
constexpr double qS4 =
78
7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
79
auto R = [=](double z) {
80
double p, q;
81
p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
82
q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
83
return p / q;
84
};
85
double z, w, s, c, df;
86
uint64_t xx;
87
uint32_t hx, lx, ix;
88
memcpy(&xx, &x, sizeof(xx));
89
hx = xx >> 32;
90
ix = hx & 0x7fffffff;
91
/* |x| >= 1 or nan */
92
if (ix >= 0x3ff00000) {
93
lx = xx;
94
if (((ix - 0x3ff00000) | lx) == 0) {
95
/* acos(1)=0, acos(-1)=pi */
96
if (hx >> 31) return 2 * pio2_hi + 0x1p-120f;
97
return 0;
98
}
99
return 0 / (x - x);
100
}
101
/* |x| < 0.5 */
102
if (ix < 0x3fe00000) {
103
if (ix <= 0x3c600000) /* |x| < 2**-57 */
104
return pio2_hi + 0x1p-120f;
105
return pio2_hi - (x - (pio2_lo - x * R(x * x)));
106
}
107
/* x < -0.5 */
108
if (hx >> 31) {
109
z = (1.0 + x) * 0.5;
110
s = sqrt(z);
111
w = R(z) * s - pio2_lo;
112
return 2 * (pio2_hi - (s + w));
113
}
114
/* x > 0.5 */
115
z = (1.0 - x) * 0.5;
116
s = sqrt(z);
117
memcpy(&xx, &s, sizeof(xx));
118
xx &= 0xffffffff00000000;
119
memcpy(&df, &xx, sizeof(xx));
120
c = (z - df * df) / (s + df);
121
w = R(z) * s + c;
122
return 2 * (df + w);
123
}
124
125
struct Transform4x3 {
126
const mat3x4 transform;
127
128
vec3 operator()(vec3 position) { return transform * vec4(position, 1.0); }
129
};
130
131
struct UpdateMeshID {
132
const HashTableD<uint32_t> meshIDold2new;
133
134
void operator()(TriRef& ref) { ref.meshID = meshIDold2new[ref.meshID]; }
135
};
136
137
int GetLabels(std::vector<int>& components,
138
const Vec<std::pair<int, int>>& edges, int numNodes) {
139
DisjointSets uf(numNodes);
140
for (auto edge : edges) {
141
if (edge.first == -1 || edge.second == -1) continue;
142
uf.unite(edge.first, edge.second);
143
}
144
145
return uf.connectedComponents(components);
146
}
147
} // namespace
148
149
namespace manifold {
150
151
#if (MANIFOLD_PAR == 1)
152
#if (TBB_VERSION_MAJOR < 2021)
153
tbb::task_arena gc_arena(1, 1);
154
#else
155
tbb::task_arena gc_arena(1, 1, tbb::task_arena::priority::low);
156
#endif
157
#endif
158
159
std::atomic<uint32_t> Manifold::Impl::meshIDCounter_(1);
160
161
uint32_t Manifold::Impl::ReserveIDs(uint32_t n) {
162
return Manifold::Impl::meshIDCounter_.fetch_add(n, std::memory_order_relaxed);
163
}
164
165
/**
166
* Create either a unit tetrahedron, cube or octahedron. The cube is in the
167
* first octant, while the others are symmetric about the origin.
168
*/
169
Manifold::Impl::Impl(Shape shape, const mat3x4 m) {
170
std::vector<vec3> vertPos;
171
std::vector<ivec3> triVerts;
172
switch (shape) {
173
case Shape::Tetrahedron:
174
vertPos = {{-1.0, -1.0, 1.0},
175
{-1.0, 1.0, -1.0},
176
{1.0, -1.0, -1.0},
177
{1.0, 1.0, 1.0}};
178
triVerts = {{2, 0, 1}, {0, 3, 1}, {2, 3, 0}, {3, 2, 1}};
179
break;
180
case Shape::Cube:
181
vertPos = {{0.0, 0.0, 0.0}, //
182
{0.0, 0.0, 1.0}, //
183
{0.0, 1.0, 0.0}, //
184
{0.0, 1.0, 1.0}, //
185
{1.0, 0.0, 0.0}, //
186
{1.0, 0.0, 1.0}, //
187
{1.0, 1.0, 0.0}, //
188
{1.0, 1.0, 1.0}};
189
triVerts = {{1, 0, 4}, {2, 4, 0}, //
190
{1, 3, 0}, {3, 1, 5}, //
191
{3, 2, 0}, {3, 7, 2}, //
192
{5, 4, 6}, {5, 1, 4}, //
193
{6, 4, 2}, {7, 6, 2}, //
194
{7, 3, 5}, {7, 5, 6}};
195
break;
196
case Shape::Octahedron:
197
vertPos = {{1.0, 0.0, 0.0}, //
198
{-1.0, 0.0, 0.0}, //
199
{0.0, 1.0, 0.0}, //
200
{0.0, -1.0, 0.0}, //
201
{0.0, 0.0, 1.0}, //
202
{0.0, 0.0, -1.0}};
203
triVerts = {{0, 2, 4}, {1, 5, 3}, //
204
{2, 1, 4}, {3, 5, 0}, //
205
{1, 3, 4}, {0, 5, 2}, //
206
{3, 0, 4}, {2, 5, 1}};
207
break;
208
}
209
vertPos_ = vertPos;
210
for (auto& v : vertPos_) v = m * vec4(v, 1.0);
211
CreateHalfedges(triVerts);
212
Finish();
213
InitializeOriginal();
214
MarkCoplanar();
215
}
216
217
void Manifold::Impl::RemoveUnreferencedVerts() {
218
ZoneScoped;
219
const int numVert = NumVert();
220
Vec<int> keep(numVert, 0);
221
auto policy = autoPolicy(numVert, 1e5);
222
for_each(policy, halfedge_.cbegin(), halfedge_.cend(), [&keep](Halfedge h) {
223
if (h.startVert >= 0) {
224
reinterpret_cast<std::atomic<int>*>(&keep[h.startVert])
225
->store(1, std::memory_order_relaxed);
226
}
227
});
228
229
for_each_n(policy, countAt(0), numVert, [&keep, this](int v) {
230
if (keep[v] == 0) {
231
vertPos_[v] = vec3(NAN);
232
}
233
});
234
}
235
236
void Manifold::Impl::InitializeOriginal(bool keepFaceID) {
237
const int meshID = ReserveIDs(1);
238
meshRelation_.originalID = meshID;
239
auto& triRef = meshRelation_.triRef;
240
triRef.resize_nofill(NumTri());
241
for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(),
242
[meshID, keepFaceID, &triRef](const int tri) {
243
triRef[tri] = {meshID, meshID, -1,
244
keepFaceID ? triRef[tri].coplanarID : tri};
245
});
246
meshRelation_.meshIDtransform.clear();
247
meshRelation_.meshIDtransform[meshID] = {meshID};
248
}
249
250
void Manifold::Impl::MarkCoplanar() {
251
ZoneScoped;
252
const int numTri = NumTri();
253
struct TriPriority {
254
double area2;
255
int tri;
256
};
257
Vec<TriPriority> triPriority(numTri);
258
for_each_n(autoPolicy(numTri), countAt(0), numTri,
259
[&triPriority, this](int tri) {
260
meshRelation_.triRef[tri].coplanarID = -1;
261
if (halfedge_[3 * tri].startVert < 0) {
262
triPriority[tri] = {0, tri};
263
return;
264
}
265
const vec3 v = vertPos_[halfedge_[3 * tri].startVert];
266
triPriority[tri] = {
267
length2(cross(vertPos_[halfedge_[3 * tri].endVert] - v,
268
vertPos_[halfedge_[3 * tri + 1].endVert] - v)),
269
tri};
270
});
271
272
stable_sort(triPriority.begin(), triPriority.end(),
273
[](auto a, auto b) { return a.area2 > b.area2; });
274
275
Vec<int> interiorHalfedges;
276
for (const auto tp : triPriority) {
277
if (meshRelation_.triRef[tp.tri].coplanarID >= 0) continue;
278
279
meshRelation_.triRef[tp.tri].coplanarID = tp.tri;
280
if (halfedge_[3 * tp.tri].startVert < 0) continue;
281
const vec3 base = vertPos_[halfedge_[3 * tp.tri].startVert];
282
const vec3 normal = faceNormal_[tp.tri];
283
interiorHalfedges.resize(3);
284
interiorHalfedges[0] = 3 * tp.tri;
285
interiorHalfedges[1] = 3 * tp.tri + 1;
286
interiorHalfedges[2] = 3 * tp.tri + 2;
287
while (!interiorHalfedges.empty()) {
288
const int h =
289
NextHalfedge(halfedge_[interiorHalfedges.back()].pairedHalfedge);
290
interiorHalfedges.pop_back();
291
if (meshRelation_.triRef[h / 3].coplanarID >= 0) continue;
292
293
const vec3 v = vertPos_[halfedge_[h].endVert];
294
if (std::abs(dot(v - base, normal)) < tolerance_) {
295
meshRelation_.triRef[h / 3].coplanarID = tp.tri;
296
297
if (interiorHalfedges.empty() ||
298
h != halfedge_[interiorHalfedges.back()].pairedHalfedge) {
299
interiorHalfedges.push_back(h);
300
} else {
301
interiorHalfedges.pop_back();
302
}
303
const int hNext = NextHalfedge(h);
304
interiorHalfedges.push_back(hNext);
305
}
306
}
307
}
308
}
309
310
/**
311
* Dereference duplicate property vertices if they are exactly floating-point
312
* equal. These unreferenced properties are then removed by CompactProps.
313
*/
314
void Manifold::Impl::DedupePropVerts() {
315
ZoneScoped;
316
const size_t numProp = NumProp();
317
if (numProp == 0) return;
318
319
Vec<std::pair<int, int>> vert2vert(halfedge_.size(), {-1, -1});
320
for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(),
321
[&vert2vert, numProp, this](const int edgeIdx) {
322
const Halfedge edge = halfedge_[edgeIdx];
323
if (edge.pairedHalfedge < 0) return;
324
const int edgeFace = edgeIdx / 3;
325
const int pairFace = edge.pairedHalfedge / 3;
326
327
if (meshRelation_.triRef[edgeFace].meshID !=
328
meshRelation_.triRef[pairFace].meshID)
329
return;
330
331
const int prop0 = halfedge_[edgeIdx].propVert;
332
const int prop1 =
333
halfedge_[NextHalfedge(edge.pairedHalfedge)].propVert;
334
bool propEqual = true;
335
for (size_t p = 0; p < numProp; ++p) {
336
if (properties_[numProp * prop0 + p] !=
337
properties_[numProp * prop1 + p]) {
338
propEqual = false;
339
break;
340
}
341
}
342
if (propEqual) {
343
vert2vert[edgeIdx] = std::make_pair(prop0, prop1);
344
}
345
});
346
347
std::vector<int> vertLabels;
348
const size_t numPropVert = NumPropVert();
349
const int numLabels = GetLabels(vertLabels, vert2vert, numPropVert);
350
351
std::vector<int> label2vert(numLabels);
352
for (size_t v = 0; v < numPropVert; ++v) label2vert[vertLabels[v]] = v;
353
for (Halfedge& edge : halfedge_)
354
edge.propVert = label2vert[vertLabels[edge.propVert]];
355
}
356
357
constexpr int kRemovedHalfedge = -2;
358
359
struct HalfedgePairData {
360
int largeVert;
361
int tri;
362
int edgeIndex;
363
364
bool operator<(const HalfedgePairData& other) const {
365
return largeVert < other.largeVert ||
366
(largeVert == other.largeVert && tri < other.tri);
367
}
368
};
369
370
template <bool useProp, typename F>
371
struct PrepHalfedges {
372
VecView<Halfedge> halfedges;
373
const VecView<ivec3> triProp;
374
const VecView<ivec3> triVert;
375
F& f;
376
377
void operator()(const int tri) {
378
const ivec3& props = triProp[tri];
379
for (const int i : {0, 1, 2}) {
380
const int j = Next3(i);
381
const int k = Next3(j);
382
const int e = 3 * tri + i;
383
const int v0 = useProp ? props[i] : triVert[tri][i];
384
const int v1 = useProp ? props[j] : triVert[tri][j];
385
DEBUG_ASSERT(v0 != v1, logicErr, "topological degeneracy");
386
halfedges[e] = {v0, v1, -1, props[i]};
387
f(e, v0, v1);
388
}
389
}
390
};
391
392
/**
393
* Create the halfedge_ data structure from a list of triangles. If the optional
394
* prop2vert array is missing, it's assumed these triangles are are pointing to
395
* both vert and propVert indices. If prop2vert is present, the triangles are
396
* assumed to be pointing to propVert indices only. The prop2vert array is used
397
* to map the propVert indices to vert indices.
398
*/
399
void Manifold::Impl::CreateHalfedges(const Vec<ivec3>& triProp,
400
const Vec<ivec3>& triVert) {
401
ZoneScoped;
402
const size_t numTri = triProp.size();
403
const int numHalfedge = 3 * numTri;
404
// drop the old value first to avoid copy
405
halfedge_.clear(true);
406
halfedge_.resize_nofill(numHalfedge);
407
auto policy = autoPolicy(numTri, 1e5);
408
409
int vertCount = static_cast<int>(vertPos_.size());
410
Vec<int> ids(numHalfedge);
411
{
412
ZoneScopedN("PrepHalfedges");
413
if (vertCount < (1 << 18)) {
414
// For small vertex count, it is faster to just do sorting
415
Vec<uint64_t> edge(numHalfedge);
416
auto setEdge = [&edge](int e, int v0, int v1) {
417
edge[e] = static_cast<uint64_t>(v0 < v1 ? 1 : 0) << 63 |
418
(static_cast<uint64_t>(std::min(v0, v1))) << 32 |
419
static_cast<uint64_t>(std::max(v0, v1));
420
};
421
if (triVert.empty()) {
422
for_each_n(policy, countAt(0), numTri,
423
PrepHalfedges<true, decltype(setEdge)>{halfedge_, triProp,
424
triVert, setEdge});
425
} else {
426
for_each_n(policy, countAt(0), numTri,
427
PrepHalfedges<false, decltype(setEdge)>{halfedge_, triProp,
428
triVert, setEdge});
429
}
430
sequence(ids.begin(), ids.end());
431
stable_sort(ids.begin(), ids.end(), [&edge](const int& a, const int& b) {
432
return edge[a] < edge[b];
433
});
434
} else {
435
// For larger vertex count, we separate the ids into slices for halfedges
436
// with the same smaller vertex.
437
// We first copy them there (as HalfedgePairData), and then do sorting
438
// locally for each slice.
439
// This helps with memory locality, and is faster for larger meshes.
440
Vec<HalfedgePairData> entries(numHalfedge);
441
Vec<int> offsets(vertCount * 2, 0);
442
auto setOffset = [&offsets, vertCount](int _e, int v0, int v1) {
443
const int offset = v0 > v1 ? 0 : vertCount;
444
AtomicAdd(offsets[std::min(v0, v1) + offset], 1);
445
};
446
if (triVert.empty()) {
447
for_each_n(policy, countAt(0), numTri,
448
PrepHalfedges<true, decltype(setOffset)>{
449
halfedge_, triProp, triVert, setOffset});
450
} else {
451
for_each_n(policy, countAt(0), numTri,
452
PrepHalfedges<false, decltype(setOffset)>{
453
halfedge_, triProp, triVert, setOffset});
454
}
455
exclusive_scan(offsets.begin(), offsets.end(), offsets.begin());
456
for_each_n(policy, countAt(0), numTri,
457
[this, &offsets, &entries, vertCount](const int tri) {
458
for (const int i : {0, 1, 2}) {
459
const int e = 3 * tri + i;
460
const int v0 = halfedge_[e].startVert;
461
const int v1 = halfedge_[e].endVert;
462
const int offset = v0 > v1 ? 0 : vertCount;
463
const int start = std::min(v0, v1);
464
const int index = AtomicAdd(offsets[start + offset], 1);
465
entries[index] = {std::max(v0, v1), tri, e};
466
}
467
});
468
for_each_n(policy, countAt(0), offsets.size(), [&](const int v) {
469
int start = v == 0 ? 0 : offsets[v - 1];
470
int end = offsets[v];
471
for (int i = start; i < end; ++i) ids[i] = i;
472
std::sort(ids.begin() + start, ids.begin() + end,
473
[&entries](int a, int b) { return entries[a] < entries[b]; });
474
for (int i = start; i < end; ++i) ids[i] = entries[ids[i]].edgeIndex;
475
});
476
}
477
}
478
479
// Mark opposed triangles for removal - this may strand unreferenced verts
480
// which are removed later by RemoveUnreferencedVerts() and Finish().
481
const int numEdge = numHalfedge / 2;
482
483
const auto body = [&](int i, int consecutiveStart, int segmentEnd) {
484
const int pair0 = ids[i];
485
Halfedge& h0 = halfedge_[pair0];
486
int k = consecutiveStart + numEdge;
487
while (1) {
488
const int pair1 = ids[k];
489
Halfedge& h1 = halfedge_[pair1];
490
if (h0.startVert != h1.endVert || h0.endVert != h1.startVert) break;
491
if (h1.pairedHalfedge != kRemovedHalfedge &&
492
halfedge_[NextHalfedge(pair0)].endVert ==
493
halfedge_[NextHalfedge(pair1)].endVert) {
494
h0.pairedHalfedge = h1.pairedHalfedge = kRemovedHalfedge;
495
// Reorder so that remaining edges pair up
496
if (k != i + numEdge) std::swap(ids[i + numEdge], ids[k]);
497
break;
498
}
499
++k;
500
if (k >= segmentEnd + numEdge) break;
501
}
502
if (i + 1 == segmentEnd) return consecutiveStart;
503
Halfedge& h1 = halfedge_[ids[i + 1]];
504
if (h1.startVert == h0.startVert && h1.endVert == h0.endVert)
505
return consecutiveStart;
506
return i + 1;
507
};
508
509
#if MANIFOLD_PAR == 1
510
Vec<std::pair<int, int>> ranges;
511
const int increment = std::min(
512
std::max(numEdge / tbb::this_task_arena::max_concurrency() / 2, 1024),
513
numEdge);
514
const auto duplicated = [&](int a, int b) {
515
const Halfedge& h0 = halfedge_[ids[a]];
516
const Halfedge& h1 = halfedge_[ids[b]];
517
return h0.startVert == h1.startVert && h0.endVert == h1.endVert;
518
};
519
int end = 0;
520
while (end < numEdge) {
521
const int start = end;
522
end = std::min(end + increment, numEdge);
523
// make sure duplicated halfedges are in the same partition
524
while (end < numEdge && duplicated(end - 1, end)) end++;
525
ranges.push_back(std::make_pair(start, end));
526
}
527
for_each(ExecutionPolicy::Par, ranges.begin(), ranges.end(),
528
[&](const std::pair<int, int>& range) {
529
const auto [start, end] = range;
530
int consecutiveStart = start;
531
for (int i = start; i < end; ++i)
532
consecutiveStart = body(i, consecutiveStart, end);
533
});
534
#else
535
int consecutiveStart = 0;
536
for (int i = 0; i < numEdge; ++i)
537
consecutiveStart = body(i, consecutiveStart, numEdge);
538
#endif
539
for_each_n(policy, countAt(0), numEdge, [this, &ids, numEdge](int i) {
540
const int pair0 = ids[i];
541
const int pair1 = ids[i + numEdge];
542
if (halfedge_[pair0].pairedHalfedge != kRemovedHalfedge) {
543
halfedge_[pair0].pairedHalfedge = pair1;
544
halfedge_[pair1].pairedHalfedge = pair0;
545
} else {
546
halfedge_[pair0] = halfedge_[pair1] = {-1, -1, -1};
547
}
548
});
549
}
550
551
/**
552
* Does a full recalculation of the face bounding boxes, including updating
553
* the collider, but does not resort the faces.
554
*/
555
void Manifold::Impl::Update() {
556
CalculateBBox();
557
Vec<Box> faceBox;
558
Vec<uint32_t> faceMorton;
559
GetFaceBoxMorton(faceBox, faceMorton);
560
collider_.UpdateBoxes(faceBox);
561
}
562
563
void Manifold::Impl::MakeEmpty(Error status) {
564
bBox_ = Box();
565
vertPos_.clear();
566
halfedge_.clear();
567
vertNormal_.clear();
568
faceNormal_.clear();
569
halfedgeTangent_.clear();
570
meshRelation_ = MeshRelationD();
571
status_ = status;
572
}
573
574
void Manifold::Impl::Warp(std::function<void(vec3&)> warpFunc) {
575
WarpBatch([&warpFunc](VecView<vec3> vecs) {
576
for_each(ExecutionPolicy::Seq, vecs.begin(), vecs.end(), warpFunc);
577
});
578
}
579
580
void Manifold::Impl::WarpBatch(std::function<void(VecView<vec3>)> warpFunc) {
581
warpFunc(vertPos_.view());
582
CalculateBBox();
583
if (!IsFinite()) {
584
MakeEmpty(Error::NonFiniteVertex);
585
return;
586
}
587
Update();
588
faceNormal_.clear(); // force recalculation of triNormal
589
SetEpsilon();
590
Finish();
591
MarkCoplanar();
592
meshRelation_.originalID = -1;
593
}
594
595
Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const {
596
ZoneScoped;
597
if (transform_ == mat3x4(la::identity)) return *this;
598
auto policy = autoPolicy(NumVert());
599
Impl result;
600
if (status_ != Manifold::Error::NoError) {
601
result.status_ = status_;
602
return result;
603
}
604
if (!all(la::isfinite(transform_))) {
605
result.MakeEmpty(Error::NonFiniteVertex);
606
return result;
607
}
608
result.collider_ = collider_;
609
result.meshRelation_ = meshRelation_;
610
result.epsilon_ = epsilon_;
611
result.tolerance_ = tolerance_;
612
result.numProp_ = numProp_;
613
result.properties_ = properties_;
614
result.bBox_ = bBox_;
615
result.halfedge_ = halfedge_;
616
result.halfedgeTangent_.resize(halfedgeTangent_.size());
617
618
result.meshRelation_.originalID = -1;
619
for (auto& m : result.meshRelation_.meshIDtransform) {
620
m.second.transform = transform_ * Mat4(m.second.transform);
621
}
622
623
result.vertPos_.resize(NumVert());
624
result.faceNormal_.resize(faceNormal_.size());
625
result.vertNormal_.resize(vertNormal_.size());
626
transform(vertPos_.begin(), vertPos_.end(), result.vertPos_.begin(),
627
Transform4x3({transform_}));
628
629
mat3 normalTransform = NormalTransform(transform_);
630
transform(faceNormal_.begin(), faceNormal_.end(), result.faceNormal_.begin(),
631
TransformNormals({normalTransform}));
632
transform(vertNormal_.begin(), vertNormal_.end(), result.vertNormal_.begin(),
633
TransformNormals({normalTransform}));
634
635
const bool invert = la::determinant(mat3(transform_)) < 0;
636
637
if (halfedgeTangent_.size() > 0) {
638
for_each_n(policy, countAt(0), halfedgeTangent_.size(),
639
TransformTangents({result.halfedgeTangent_, 0, mat3(transform_),
640
invert, halfedgeTangent_, halfedge_}));
641
}
642
643
if (invert) {
644
for_each_n(policy, countAt(0), result.NumTri(),
645
FlipTris({result.halfedge_}));
646
}
647
648
// This optimization does a cheap collider update if the transform is
649
// axis-aligned.
650
if (!result.collider_.Transform(transform_)) result.Update();
651
652
result.CalculateBBox();
653
// Scale epsilon by the norm of the 3x3 portion of the transform.
654
result.epsilon_ *= SpectralNorm(mat3(transform_));
655
// Maximum of inherited epsilon loss and translational epsilon loss.
656
result.SetEpsilon(result.epsilon_);
657
return result;
658
}
659
660
/**
661
* Sets epsilon based on the bounding box, and limits its minimum value
662
* by the optional input.
663
*/
664
void Manifold::Impl::SetEpsilon(double minEpsilon, bool useSingle) {
665
epsilon_ = MaxEpsilon(minEpsilon, bBox_);
666
double minTol = epsilon_;
667
if (useSingle)
668
minTol =
669
std::max(minTol, std::numeric_limits<float>::epsilon() * bBox_.Scale());
670
tolerance_ = std::max(tolerance_, minTol);
671
}
672
673
/**
674
* If face normals are already present, this function uses them to compute
675
* vertex normals (angle-weighted pseudo-normals); otherwise it also computes
676
* the face normals. Face normals are only calculated when needed because
677
* nearly degenerate faces will accrue rounding error, while the Boolean can
678
* retain their original normal, which is more accurate and can help with
679
* merging coplanar faces.
680
*
681
* If the face normals have been invalidated by an operation like Warp(),
682
* ensure you do faceNormal_.resize(0) before calling this function to force
683
* recalculation.
684
*/
685
void Manifold::Impl::CalculateNormals() {
686
ZoneScoped;
687
vertNormal_.resize(NumVert());
688
auto policy = autoPolicy(NumTri());
689
690
std::vector<std::atomic<int>> vertHalfedgeMap(NumVert());
691
for_each_n(policy, countAt(0), NumVert(), [&](const size_t vert) {
692
vertHalfedgeMap[vert] = std::numeric_limits<int>::max();
693
});
694
695
auto atomicMin = [&vertHalfedgeMap](int value, int vert) {
696
if (vert < 0) return;
697
int old = std::numeric_limits<int>::max();
698
while (!vertHalfedgeMap[vert].compare_exchange_strong(old, value))
699
if (old < value) break;
700
};
701
if (faceNormal_.size() != NumTri()) {
702
faceNormal_.resize(NumTri());
703
for_each_n(policy, countAt(0), NumTri(), [&](const int face) {
704
vec3& triNormal = faceNormal_[face];
705
if (halfedge_[3 * face].startVert < 0) {
706
triNormal = vec3(0, 0, 1);
707
return;
708
}
709
710
ivec3 triVerts;
711
for (int i : {0, 1, 2}) {
712
int v = halfedge_[3 * face + i].startVert;
713
triVerts[i] = v;
714
atomicMin(3 * face + i, v);
715
}
716
717
vec3 edge[3];
718
for (int i : {0, 1, 2}) {
719
const int j = (i + 1) % 3;
720
edge[i] = la::normalize(vertPos_[triVerts[j]] - vertPos_[triVerts[i]]);
721
}
722
triNormal = la::normalize(la::cross(edge[0], edge[1]));
723
if (std::isnan(triNormal.x)) triNormal = vec3(0, 0, 1);
724
});
725
} else {
726
for_each_n(policy, countAt(0), halfedge_.size(),
727
[&](const int i) { atomicMin(i, halfedge_[i].startVert); });
728
}
729
730
for_each_n(policy, countAt(0), NumVert(), [&](const size_t vert) {
731
int firstEdge = vertHalfedgeMap[vert].load();
732
// not referenced
733
if (firstEdge == std::numeric_limits<int>::max()) {
734
vertNormal_[vert] = vec3(0.0);
735
return;
736
}
737
vec3 normal = vec3(0.0);
738
ForVert(firstEdge, [&](int edge) {
739
ivec3 triVerts = {halfedge_[edge].startVert, halfedge_[edge].endVert,
740
halfedge_[NextHalfedge(edge)].endVert};
741
vec3 currEdge =
742
la::normalize(vertPos_[triVerts[1]] - vertPos_[triVerts[0]]);
743
vec3 prevEdge =
744
la::normalize(vertPos_[triVerts[0]] - vertPos_[triVerts[2]]);
745
746
// if it is not finite, this means that the triangle is degenerate, and we
747
// should just exclude it from the normal calculation...
748
if (!la::isfinite(currEdge[0]) || !la::isfinite(prevEdge[0])) return;
749
double dot = -la::dot(prevEdge, currEdge);
750
double phi = dot >= 1 ? 0 : (dot <= -1 ? kPi : sun_acos(dot));
751
normal += phi * faceNormal_[edge / 3];
752
});
753
vertNormal_[vert] = SafeNormalize(normal);
754
});
755
}
756
757
/**
758
* Remaps all the contained meshIDs to new unique values to represent new
759
* instances of these meshes.
760
*/
761
void Manifold::Impl::IncrementMeshIDs() {
762
HashTable<uint32_t> meshIDold2new(meshRelation_.meshIDtransform.size() * 2);
763
// Update keys of the transform map
764
std::map<int, Relation> oldTransforms;
765
std::swap(meshRelation_.meshIDtransform, oldTransforms);
766
const int numMeshIDs = oldTransforms.size();
767
int nextMeshID = ReserveIDs(numMeshIDs);
768
for (const auto& pair : oldTransforms) {
769
meshIDold2new.D().Insert(pair.first, nextMeshID);
770
meshRelation_.meshIDtransform[nextMeshID++] = pair.second;
771
}
772
773
const size_t numTri = NumTri();
774
for_each_n(autoPolicy(numTri, 1e5), meshRelation_.triRef.begin(), numTri,
775
UpdateMeshID({meshIDold2new.D()}));
776
}
777
778
#ifdef MANIFOLD_DEBUG
779
/**
780
* Debugging output using high precision OBJ files with specialized comments
781
*/
782
std::ostream& operator<<(std::ostream& stream, const Manifold::Impl& impl) {
783
stream << std::setprecision(19); // for double precision
784
stream << std::fixed; // for uniformity in output numbers
785
stream << "# ======= begin mesh ======" << std::endl;
786
stream << "# tolerance = " << impl.tolerance_ << std::endl;
787
stream << "# epsilon = " << impl.epsilon_ << std::endl;
788
// TODO: Mesh relation, vertex normal and face normal
789
for (const vec3& v : impl.vertPos_)
790
stream << "v " << v.x << " " << v.y << " " << v.z << std::endl;
791
std::vector<ivec3> triangles;
792
triangles.reserve(impl.halfedge_.size() / 3);
793
for (size_t i = 0; i < impl.halfedge_.size(); i += 3)
794
triangles.emplace_back(impl.halfedge_[i].startVert + 1,
795
impl.halfedge_[i + 1].startVert + 1,
796
impl.halfedge_[i + 2].startVert + 1);
797
sort(triangles.begin(), triangles.end());
798
for (const auto& tri : triangles)
799
stream << "f " << tri.x << " " << tri.y << " " << tri.z << std::endl;
800
stream << "# ======== end mesh =======" << std::endl;
801
return stream;
802
}
803
804
/**
805
* Import a mesh from a Wavefront OBJ file that was exported with Write. This
806
* function is the counterpart to Write and should be used with it. This
807
* function is not guaranteed to be able to import OBJ files not written by the
808
* Write function.
809
*/
810
Manifold Manifold::ReadOBJ(std::istream& stream) {
811
if (!stream.good()) return Invalid();
812
813
MeshGL64 mesh;
814
std::optional<double> epsilon;
815
stream >> std::setprecision(19);
816
while (true) {
817
char c = stream.get();
818
if (stream.eof()) break;
819
switch (c) {
820
case '#': {
821
char c = stream.get();
822
if (c == ' ') {
823
constexpr int SIZE = 10;
824
std::array<char, SIZE> tmp;
825
stream.get(tmp.data(), SIZE, '\n');
826
if (strncmp(tmp.data(), "tolerance", SIZE) == 0) {
827
// skip 3 letters
828
for (int _ : {0, 1, 2}) stream.get();
829
stream >> mesh.tolerance;
830
} else if (strncmp(tmp.data(), "epsilon =", SIZE) == 0) {
831
double tmp;
832
stream >> tmp;
833
epsilon = {tmp};
834
} else {
835
// add it back because it is not what we want
836
int end = 0;
837
while (end < SIZE && tmp[end] != 0) end++;
838
while (--end > -1) stream.putback(tmp[end]);
839
}
840
c = stream.get();
841
}
842
// just skip the remaining comment
843
while (c != '\n' && !stream.eof()) {
844
c = stream.get();
845
}
846
break;
847
}
848
case 'v':
849
for (int _ : {0, 1, 2}) {
850
double x;
851
stream >> x;
852
mesh.vertProperties.push_back(x);
853
}
854
break;
855
case 'f':
856
for (int _ : {0, 1, 2}) {
857
uint64_t x;
858
stream >> x;
859
mesh.triVerts.push_back(x - 1);
860
}
861
break;
862
case '\r':
863
case '\n':
864
break;
865
default:
866
DEBUG_ASSERT(false, userErr, "unexpected character in Manifold import");
867
}
868
}
869
auto m = std::make_shared<Manifold::Impl>(mesh);
870
if (epsilon) m->SetEpsilon(*epsilon);
871
return Manifold(m);
872
}
873
874
/**
875
* Export the mesh to a Wavefront OBJ file in a way that preserves the full
876
* 64-bit precision of the vertex positions, as well as storing metadata such as
877
* the tolerance and epsilon. Useful for debugging and testing. Files written
878
* by WriteOBJ should be read back in with ReadOBJ.
879
*/
880
bool Manifold::WriteOBJ(std::ostream& stream) const {
881
if (!stream.good()) return false;
882
stream << *this->GetCsgLeafNode().GetImpl();
883
return true;
884
}
885
#endif
886
887
} // namespace manifold
888
889