Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/properties.cpp
20860 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 <limits>
16
17
#if MANIFOLD_PAR == 1
18
#include <tbb/combinable.h>
19
#endif
20
21
#include "impl.h"
22
#include "parallel.h"
23
#include "tri_dist.h"
24
25
namespace {
26
using namespace manifold;
27
28
struct CurvatureAngles {
29
VecView<double> meanCurvature;
30
VecView<double> gaussianCurvature;
31
VecView<double> area;
32
VecView<double> degree;
33
VecView<const Halfedge> halfedge;
34
VecView<const vec3> vertPos;
35
VecView<const vec3> triNormal;
36
37
void operator()(size_t tri) {
38
vec3 edge[3];
39
vec3 edgeLength(0.0);
40
for (int i : {0, 1, 2}) {
41
const int startVert = halfedge[3 * tri + i].startVert;
42
const int endVert = halfedge[3 * tri + i].endVert;
43
edge[i] = vertPos[endVert] - vertPos[startVert];
44
edgeLength[i] = la::length(edge[i]);
45
edge[i] /= edgeLength[i];
46
const int neighborTri = halfedge[3 * tri + i].pairedHalfedge / 3;
47
const double dihedral =
48
0.25 * edgeLength[i] *
49
std::asin(la::dot(la::cross(triNormal[tri], triNormal[neighborTri]),
50
edge[i]));
51
AtomicAdd(meanCurvature[startVert], dihedral);
52
AtomicAdd(meanCurvature[endVert], dihedral);
53
AtomicAdd(degree[startVert], 1.0);
54
}
55
56
vec3 phi;
57
phi[0] = std::acos(-la::dot(edge[2], edge[0]));
58
phi[1] = std::acos(-la::dot(edge[0], edge[1]));
59
phi[2] = kPi - phi[0] - phi[1];
60
const double area3 = edgeLength[0] * edgeLength[1] *
61
la::length(la::cross(edge[0], edge[1])) / 6;
62
63
for (int i : {0, 1, 2}) {
64
const int vert = halfedge[3 * tri + i].startVert;
65
AtomicAdd(gaussianCurvature[vert], -phi[i]);
66
AtomicAdd(area[vert], area3);
67
}
68
}
69
};
70
71
struct CheckHalfedges {
72
VecView<const Halfedge> halfedges;
73
74
bool operator()(size_t edge) const {
75
const Halfedge halfedge = halfedges[edge];
76
if (halfedge.startVert == -1 && halfedge.endVert == -1 &&
77
halfedge.pairedHalfedge == -1)
78
return true;
79
if (halfedges[NextHalfedge(edge)].startVert == -1 ||
80
halfedges[NextHalfedge(NextHalfedge(edge))].startVert == -1) {
81
return false;
82
}
83
if (halfedge.pairedHalfedge == -1) return false;
84
85
const Halfedge paired = halfedges[halfedge.pairedHalfedge];
86
bool good = true;
87
good &= paired.pairedHalfedge == static_cast<int>(edge);
88
good &= halfedge.startVert != halfedge.endVert;
89
good &= halfedge.startVert == paired.endVert;
90
good &= halfedge.endVert == paired.startVert;
91
return good;
92
}
93
};
94
95
struct CheckCCW {
96
VecView<const Halfedge> halfedges;
97
VecView<const vec3> vertPos;
98
VecView<const vec3> triNormal;
99
const double tol;
100
101
bool operator()(size_t face) const {
102
if (halfedges[3 * face].pairedHalfedge < 0) return true;
103
104
const mat2x3 projection = GetAxisAlignedProjection(triNormal[face]);
105
vec2 v[3];
106
for (int i : {0, 1, 2})
107
v[i] = projection * vertPos[halfedges[3 * face + i].startVert];
108
109
const int ccw = CCW(v[0], v[1], v[2], std::abs(tol));
110
return tol > 0 ? ccw >= 0 : ccw == 0;
111
}
112
};
113
} // namespace
114
115
namespace manifold {
116
117
/**
118
* Returns true if this manifold is in fact an oriented even manifold and all of
119
* the data structures are consistent.
120
*/
121
bool Manifold::Impl::IsManifold() const {
122
if (halfedge_.size() == 0) return true;
123
if (halfedge_.size() % 3 != 0) return false;
124
return all_of(countAt(0_uz), countAt(halfedge_.size()),
125
CheckHalfedges({halfedge_}));
126
}
127
128
/**
129
* Returns true if this manifold is in fact an oriented 2-manifold and all of
130
* the data structures are consistent.
131
*/
132
bool Manifold::Impl::Is2Manifold() const {
133
if (halfedge_.size() == 0) return true;
134
if (!IsManifold()) return false;
135
136
Vec<Halfedge> halfedge(halfedge_);
137
stable_sort(halfedge.begin(), halfedge.end());
138
139
return all_of(
140
countAt(0_uz), countAt(2 * NumEdge() - 1), [&halfedge](size_t edge) {
141
const Halfedge h = halfedge[edge];
142
if (h.startVert == -1 && h.endVert == -1 && h.pairedHalfedge == -1)
143
return true;
144
return h.startVert != halfedge[edge + 1].startVert ||
145
h.endVert != halfedge[edge + 1].endVert;
146
});
147
}
148
149
#ifdef MANIFOLD_DEBUG
150
std::mutex dump_lock;
151
#endif
152
153
/**
154
* Returns true if this manifold is self-intersecting.
155
* Note that this is not checking for epsilon-validity.
156
*/
157
bool Manifold::Impl::IsSelfIntersecting() const {
158
const double ep = 2 * epsilon_;
159
const double epsilonSq = ep * ep;
160
Vec<Box> faceBox;
161
Vec<uint32_t> faceMorton;
162
GetFaceBoxMorton(faceBox, faceMorton);
163
164
std::atomic<bool> intersecting(false);
165
166
auto f = [&](int tri0, int tri1) {
167
std::array<vec3, 3> triVerts0, triVerts1;
168
for (int i : {0, 1, 2}) {
169
triVerts0[i] = vertPos_[halfedge_[3 * tri0 + i].startVert];
170
triVerts1[i] = vertPos_[halfedge_[3 * tri1 + i].startVert];
171
}
172
// if triangles tri0 and tri1 share a vertex, return true to skip the
173
// check. we relax the sharing criteria a bit to allow for at most
174
// distance epsilon squared
175
for (int i : {0, 1, 2})
176
for (int j : {0, 1, 2})
177
if (distance2(triVerts0[i], triVerts1[j]) <= epsilonSq) return;
178
179
if (DistanceTriangleTriangleSquared(triVerts0, triVerts1) == 0.0) {
180
// try to move the triangles around the normal of the other face
181
std::array<vec3, 3> tmp0, tmp1;
182
for (int i : {0, 1, 2}) tmp0[i] = triVerts0[i] + ep * faceNormal_[tri1];
183
if (DistanceTriangleTriangleSquared(tmp0, triVerts1) > 0.0) return;
184
for (int i : {0, 1, 2}) tmp0[i] = triVerts0[i] - ep * faceNormal_[tri1];
185
if (DistanceTriangleTriangleSquared(tmp0, triVerts1) > 0.0) return;
186
for (int i : {0, 1, 2}) tmp1[i] = triVerts1[i] + ep * faceNormal_[tri0];
187
if (DistanceTriangleTriangleSquared(triVerts0, tmp1) > 0.0) return;
188
for (int i : {0, 1, 2}) tmp1[i] = triVerts1[i] - ep * faceNormal_[tri0];
189
if (DistanceTriangleTriangleSquared(triVerts0, tmp1) > 0.0) return;
190
191
#ifdef MANIFOLD_DEBUG
192
if (ManifoldParams().verbose > 0) {
193
dump_lock.lock();
194
std::cout << "intersecting:" << std::endl;
195
for (int i : {0, 1, 2}) std::cout << triVerts0[i] << " ";
196
std::cout << std::endl;
197
for (int i : {0, 1, 2}) std::cout << triVerts1[i] << " ";
198
std::cout << std::endl;
199
dump_lock.unlock();
200
}
201
#endif
202
intersecting.store(true);
203
}
204
};
205
206
auto recorder = MakeSimpleRecorder(f);
207
collider_.Collisions<true>(faceBox.cview(), recorder);
208
209
return intersecting.load();
210
}
211
212
/**
213
* Returns true if all triangles are CCW relative to their triNormals_.
214
*/
215
bool Manifold::Impl::MatchesTriNormals() const {
216
if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true;
217
return all_of(countAt(0_uz), countAt(NumTri()),
218
CheckCCW({halfedge_, vertPos_, faceNormal_, 2 * epsilon_}));
219
}
220
221
/**
222
* Returns the number of triangles that are colinear within epsilon_.
223
*/
224
int Manifold::Impl::NumDegenerateTris() const {
225
if (halfedge_.size() == 0 || faceNormal_.size() != NumTri()) return true;
226
return count_if(
227
countAt(0_uz), countAt(NumTri()),
228
CheckCCW({halfedge_, vertPos_, faceNormal_, -1 * epsilon_ / 2}));
229
}
230
231
double Manifold::Impl::GetProperty(Property prop) const {
232
ZoneScoped;
233
if (IsEmpty()) return 0;
234
235
auto Volume = [this](size_t tri) {
236
const vec3 v = vertPos_[halfedge_[3 * tri].startVert];
237
vec3 crossP = la::cross(vertPos_[halfedge_[3 * tri + 1].startVert] - v,
238
vertPos_[halfedge_[3 * tri + 2].startVert] - v);
239
return la::dot(crossP, v) / 6.0;
240
};
241
242
auto Area = [this](size_t tri) {
243
const vec3 v = vertPos_[halfedge_[3 * tri].startVert];
244
return la::length(
245
la::cross(vertPos_[halfedge_[3 * tri + 1].startVert] - v,
246
vertPos_[halfedge_[3 * tri + 2].startVert] - v)) /
247
2.0;
248
};
249
250
// Kahan summation
251
double value = 0;
252
double valueCompensation = 0;
253
for (size_t i = 0; i < NumTri(); ++i) {
254
const double value1 = prop == Property::SurfaceArea ? Area(i) : Volume(i);
255
const double t = value + value1;
256
valueCompensation += (value - t) + value1;
257
value = t;
258
}
259
value += valueCompensation;
260
return value;
261
}
262
263
void Manifold::Impl::CalculateCurvature(int gaussianIdx, int meanIdx) {
264
ZoneScoped;
265
if (IsEmpty()) return;
266
if (gaussianIdx < 0 && meanIdx < 0) return;
267
Vec<double> vertMeanCurvature(NumVert(), 0);
268
Vec<double> vertGaussianCurvature(NumVert(), kTwoPi);
269
Vec<double> vertArea(NumVert(), 0);
270
Vec<double> degree(NumVert(), 0);
271
auto policy = autoPolicy(NumTri(), 1e4);
272
for_each(policy, countAt(0_uz), countAt(NumTri()),
273
CurvatureAngles({vertMeanCurvature, vertGaussianCurvature, vertArea,
274
degree, halfedge_, vertPos_, faceNormal_}));
275
for_each_n(policy, countAt(0), NumVert(),
276
[&vertMeanCurvature, &vertGaussianCurvature, &vertArea,
277
&degree](const int vert) {
278
const double factor = degree[vert] / (6 * vertArea[vert]);
279
vertMeanCurvature[vert] *= factor;
280
vertGaussianCurvature[vert] *= factor;
281
});
282
283
const int oldNumProp = NumProp();
284
const int numProp = std::max(oldNumProp, std::max(gaussianIdx, meanIdx) + 1);
285
const Vec<double> oldProperties = properties_;
286
properties_ = Vec<double>(numProp * NumPropVert(), 0);
287
numProp_ = numProp;
288
289
Vec<uint8_t> counters(NumPropVert(), 0);
290
for_each_n(policy, countAt(0_uz), NumTri(), [&](const size_t tri) {
291
for (const int i : {0, 1, 2}) {
292
const Halfedge& edge = halfedge_[3 * tri + i];
293
const int vert = edge.startVert;
294
const int propVert = edge.propVert;
295
296
auto old = std::atomic_exchange(
297
reinterpret_cast<std::atomic<uint8_t>*>(&counters[propVert]),
298
static_cast<uint8_t>(1));
299
if (old == 1) continue;
300
301
for (int p = 0; p < oldNumProp; ++p) {
302
properties_[numProp * propVert + p] =
303
oldProperties[oldNumProp * propVert + p];
304
}
305
306
if (gaussianIdx >= 0) {
307
properties_[numProp * propVert + gaussianIdx] =
308
vertGaussianCurvature[vert];
309
}
310
if (meanIdx >= 0) {
311
properties_[numProp * propVert + meanIdx] = vertMeanCurvature[vert];
312
}
313
}
314
});
315
}
316
317
/**
318
* Calculates the bounding box of the entire manifold, which is stored
319
* internally to short-cut Boolean operations. Ignores NaNs.
320
*/
321
void Manifold::Impl::CalculateBBox() {
322
bBox_.min =
323
reduce(vertPos_.begin(), vertPos_.end(),
324
vec3(std::numeric_limits<double>::infinity()), [](auto a, auto b) {
325
if (std::isnan(a.x)) return b;
326
if (std::isnan(b.x)) return a;
327
return la::min(a, b);
328
});
329
bBox_.max = reduce(vertPos_.begin(), vertPos_.end(),
330
vec3(-std::numeric_limits<double>::infinity()),
331
[](auto a, auto b) {
332
if (std::isnan(a.x)) return b;
333
if (std::isnan(b.x)) return a;
334
return la::max(a, b);
335
});
336
}
337
338
/**
339
* Determines if all verts are finite. Checking just the bounding box dimensions
340
* is insufficient as it ignores NaNs.
341
*/
342
bool Manifold::Impl::IsFinite() const {
343
return transform_reduce(
344
vertPos_.begin(), vertPos_.end(), true,
345
[](bool a, bool b) { return a && b; },
346
[](auto v) { return la::all(la::isfinite(v)); });
347
}
348
349
/**
350
* Checks that the input triVerts array has all indices inside bounds of the
351
* vertPos_ array.
352
*/
353
bool Manifold::Impl::IsIndexInBounds(VecView<const ivec3> triVerts) const {
354
ivec2 minmax = transform_reduce(
355
triVerts.begin(), triVerts.end(),
356
ivec2(std::numeric_limits<int>::max(), std::numeric_limits<int>::min()),
357
[](auto a, auto b) {
358
a[0] = std::min(a[0], b[0]);
359
a[1] = std::max(a[1], b[1]);
360
return a;
361
},
362
[](auto tri) {
363
return ivec2(std::min(tri[0], std::min(tri[1], tri[2])),
364
std::max(tri[0], std::max(tri[1], tri[2])));
365
});
366
367
return minmax[0] >= 0 && minmax[1] < static_cast<int>(NumVert());
368
}
369
370
struct MinDistanceRecorder {
371
using Local = double;
372
const Manifold::Impl &self, &other;
373
#if MANIFOLD_PAR == 1
374
tbb::combinable<double> store = tbb::combinable<double>(
375
[]() { return std::numeric_limits<double>::infinity(); });
376
Local& local() { return store.local(); }
377
double get() {
378
double result = std::numeric_limits<double>::infinity();
379
store.combine_each([&](double& val) { result = std::min(result, val); });
380
return result;
381
}
382
#else
383
double result = std::numeric_limits<double>::infinity();
384
Local& local() { return result; }
385
double get() { return result; }
386
#endif
387
388
void record(int triOther, int tri, double& minDistance) {
389
std::array<vec3, 3> p;
390
std::array<vec3, 3> q;
391
392
for (const int j : {0, 1, 2}) {
393
p[j] = self.vertPos_[self.halfedge_[3 * tri + j].startVert];
394
q[j] = other.vertPos_[other.halfedge_[3 * triOther + j].startVert];
395
}
396
minDistance = std::min(minDistance, DistanceTriangleTriangleSquared(p, q));
397
}
398
};
399
400
/*
401
* Returns the minimum gap between two manifolds. Returns a double between
402
* 0 and searchLength.
403
*/
404
double Manifold::Impl::MinGap(const Manifold::Impl& other,
405
double searchLength) const {
406
ZoneScoped;
407
Vec<Box> faceBoxOther;
408
Vec<uint32_t> faceMortonOther;
409
410
other.GetFaceBoxMorton(faceBoxOther, faceMortonOther);
411
412
transform(faceBoxOther.begin(), faceBoxOther.end(), faceBoxOther.begin(),
413
[searchLength](const Box& box) {
414
return Box(box.min - vec3(searchLength),
415
box.max + vec3(searchLength));
416
});
417
418
MinDistanceRecorder recorder{*this, other};
419
collider_.Collisions<false, Box, MinDistanceRecorder>(faceBoxOther.cview(),
420
recorder, false);
421
double minDistanceSquared =
422
std::min(recorder.get(), searchLength * searchLength);
423
return sqrt(minDistanceSquared);
424
};
425
426
} // namespace manifold
427
428