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