Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/manifold.cpp
20882 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 <map>
17
#include <numeric>
18
19
#include "boolean3.h"
20
#include "csg_tree.h"
21
#include "impl.h"
22
#include "parallel.h"
23
#include "shared.h"
24
25
namespace {
26
using namespace manifold;
27
28
ExecutionParams manifoldParams;
29
30
Manifold Halfspace(Box bBox, vec3 normal, double originOffset) {
31
normal = la::normalize(normal);
32
Manifold cutter = Manifold::Cube(vec3(2.0), true).Translate({1.0, 0.0, 0.0});
33
double size = la::length(bBox.Center() - normal * originOffset) +
34
0.5 * la::length(bBox.Size());
35
cutter = cutter.Scale(vec3(size)).Translate({originOffset, 0.0, 0.0});
36
double yDeg = degrees(-std::asin(normal.z));
37
double zDeg = degrees(std::atan2(normal.y, normal.x));
38
return cutter.Rotate(0.0, yDeg, zDeg);
39
}
40
41
template <typename Precision, typename I>
42
MeshGLP<Precision, I> GetMeshGLImpl(const manifold::Manifold::Impl& impl,
43
int normalIdx) {
44
ZoneScoped;
45
const int numProp = impl.NumProp();
46
const int numVert = impl.NumPropVert();
47
const int numTri = impl.NumTri();
48
49
const bool isOriginal = impl.meshRelation_.originalID >= 0;
50
const bool updateNormals = !isOriginal && normalIdx >= 0;
51
52
MeshGLP<Precision, I> out;
53
out.numProp = 3 + numProp;
54
out.tolerance = impl.tolerance_;
55
if (std::is_same<Precision, float>::value)
56
out.tolerance =
57
std::max(out.tolerance,
58
static_cast<Precision>(std::numeric_limits<float>::epsilon() *
59
impl.bBox_.Scale()));
60
out.triVerts.resize(3 * numTri);
61
62
const int numHalfedge = impl.halfedgeTangent_.size();
63
out.halfedgeTangent.resize(4 * numHalfedge);
64
for (int i = 0; i < numHalfedge; ++i) {
65
const vec4 t = impl.halfedgeTangent_[i];
66
out.halfedgeTangent[4 * i] = t.x;
67
out.halfedgeTangent[4 * i + 1] = t.y;
68
out.halfedgeTangent[4 * i + 2] = t.z;
69
out.halfedgeTangent[4 * i + 3] = t.w;
70
}
71
// Sort the triangles into runs
72
out.faceID.resize(numTri);
73
std::vector<int> triNew2Old(numTri);
74
std::iota(triNew2Old.begin(), triNew2Old.end(), 0);
75
VecView<const TriRef> triRef = impl.meshRelation_.triRef;
76
// Don't sort originals - keep them in order
77
if (!isOriginal) {
78
std::stable_sort(triNew2Old.begin(), triNew2Old.end(),
79
[triRef](int a, int b) {
80
return triRef[a].originalID == triRef[b].originalID
81
? triRef[a].meshID < triRef[b].meshID
82
: triRef[a].originalID < triRef[b].originalID;
83
});
84
}
85
86
std::vector<mat3> runNormalTransform;
87
auto addRun = [updateNormals, isOriginal](
88
MeshGLP<Precision, I>& out,
89
std::vector<mat3>& runNormalTransform, int tri,
90
const manifold::Manifold::Impl::Relation& rel) {
91
out.runIndex.push_back(3 * tri);
92
out.runOriginalID.push_back(rel.originalID);
93
if (updateNormals) {
94
runNormalTransform.push_back(NormalTransform(rel.transform) *
95
(rel.backSide ? -1.0 : 1.0));
96
}
97
if (!isOriginal) {
98
for (const int col : {0, 1, 2, 3}) {
99
for (const int row : {0, 1, 2}) {
100
out.runTransform.push_back(rel.transform[col][row]);
101
}
102
}
103
}
104
};
105
106
auto meshIDtransform = impl.meshRelation_.meshIDtransform;
107
int lastID = -1;
108
for (int tri = 0; tri < numTri; ++tri) {
109
const int oldTri = triNew2Old[tri];
110
const auto ref = triRef[oldTri];
111
const int meshID = ref.meshID;
112
113
out.faceID[tri] = ref.faceID >= 0 ? ref.faceID : ref.coplanarID;
114
for (const int i : {0, 1, 2})
115
out.triVerts[3 * tri + i] = impl.halfedge_[3 * oldTri + i].startVert;
116
117
if (meshID != lastID) {
118
manifold::Manifold::Impl::Relation rel;
119
auto it = meshIDtransform.find(meshID);
120
if (it != meshIDtransform.end()) rel = it->second;
121
addRun(out, runNormalTransform, tri, rel);
122
meshIDtransform.erase(meshID);
123
lastID = meshID;
124
}
125
}
126
// Add runs for originals that did not contribute any faces to the output
127
for (const auto& pair : meshIDtransform) {
128
addRun(out, runNormalTransform, numTri, pair.second);
129
}
130
out.runIndex.push_back(3 * numTri);
131
132
// Early return for no props
133
if (numProp == 0) {
134
out.vertProperties.resize(3 * numVert);
135
for (int i = 0; i < numVert; ++i) {
136
const vec3 v = impl.vertPos_[i];
137
out.vertProperties[3 * i] = v.x;
138
out.vertProperties[3 * i + 1] = v.y;
139
out.vertProperties[3 * i + 2] = v.z;
140
}
141
return out;
142
}
143
// Duplicate verts with different props
144
std::vector<int> vert2idx(impl.NumVert(), -1);
145
std::vector<std::vector<ivec2>> vertPropPair(impl.NumVert());
146
out.vertProperties.reserve(numVert * static_cast<size_t>(out.numProp));
147
148
for (size_t run = 0; run < out.runOriginalID.size(); ++run) {
149
for (size_t tri = out.runIndex[run] / 3; tri < out.runIndex[run + 1] / 3;
150
++tri) {
151
for (const int i : {0, 1, 2}) {
152
const int prop = impl.halfedge_[3 * triNew2Old[tri] + i].propVert;
153
const int vert = out.triVerts[3 * tri + i];
154
155
auto& bin = vertPropPair[vert];
156
bool bFound = false;
157
for (const auto& b : bin) {
158
if (b.x == prop) {
159
bFound = true;
160
out.triVerts[3 * tri + i] = b.y;
161
break;
162
}
163
}
164
if (bFound) continue;
165
const int idx = out.vertProperties.size() / out.numProp;
166
out.triVerts[3 * tri + i] = idx;
167
bin.push_back({prop, idx});
168
169
for (int p : {0, 1, 2}) {
170
out.vertProperties.push_back(impl.vertPos_[vert][p]);
171
}
172
for (int p = 0; p < numProp; ++p) {
173
out.vertProperties.push_back(impl.properties_[prop * numProp + p]);
174
}
175
176
if (updateNormals) {
177
vec3 normal;
178
const int start = out.vertProperties.size() - out.numProp;
179
for (int i : {0, 1, 2}) {
180
normal[i] = out.vertProperties[start + 3 + normalIdx + i];
181
}
182
normal = la::normalize(runNormalTransform[run] * normal);
183
for (int i : {0, 1, 2}) {
184
out.vertProperties[start + 3 + normalIdx + i] = normal[i];
185
}
186
}
187
188
if (vert2idx[vert] == -1) {
189
vert2idx[vert] = idx;
190
} else {
191
out.mergeFromVert.push_back(idx);
192
out.mergeToVert.push_back(vert2idx[vert]);
193
}
194
}
195
}
196
}
197
return out;
198
}
199
} // namespace
200
201
namespace manifold {
202
203
static int circularSegments_ = DEFAULT_SEGMENTS;
204
static double circularAngle_ = DEFAULT_ANGLE;
205
static double circularEdgeLength_ = DEFAULT_LENGTH;
206
207
/**
208
* Sets an angle constraint the default number of circular segments for the
209
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
210
* Manifold::Revolve() constructors. The number of segments will be rounded up
211
* to the nearest factor of four.
212
*
213
* @param angle The minimum angle in degrees between consecutive segments. The
214
* angle will increase if the the segments hit the minimum edge length.
215
* Default is 10 degrees.
216
*/
217
void Quality::SetMinCircularAngle(double angle) {
218
if (angle <= 0) return;
219
circularAngle_ = angle;
220
}
221
222
/**
223
* Sets a length constraint the default number of circular segments for the
224
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
225
* Manifold::Revolve() constructors. The number of segments will be rounded up
226
* to the nearest factor of four.
227
*
228
* @param length The minimum length of segments. The length will
229
* increase if the the segments hit the minimum angle. Default is 1.0.
230
*/
231
void Quality::SetMinCircularEdgeLength(double length) {
232
if (length <= 0) return;
233
circularEdgeLength_ = length;
234
}
235
236
/**
237
* Sets the default number of circular segments for the
238
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
239
* Manifold::Revolve() constructors. Overrides the edge length and angle
240
* constraints and sets the number of segments to exactly this value.
241
*
242
* @param number Number of circular segments. Default is 0, meaning no
243
* constraint is applied.
244
*/
245
void Quality::SetCircularSegments(int number) {
246
if (number < 3 && number != 0) return;
247
circularSegments_ = number;
248
}
249
250
/**
251
* Determine the result of the SetMinCircularAngle(),
252
* SetMinCircularEdgeLength(), and SetCircularSegments() defaults.
253
*
254
* @param radius For a given radius of circle, determine how many default
255
* segments there will be.
256
*/
257
int Quality::GetCircularSegments(double radius) {
258
if (circularSegments_ > 0) return circularSegments_;
259
int nSegA = 360.0 / circularAngle_;
260
int nSegL = 2.0 * radius * kPi / circularEdgeLength_;
261
int nSeg = fmin(nSegA, nSegL) + 3;
262
nSeg -= nSeg % 4;
263
return std::max(nSeg, 4);
264
}
265
266
/**
267
* Resets the circular construction parameters to their defaults if
268
* SetMinCircularAngle, SetMinCircularEdgeLength, or SetCircularSegments have
269
* been called.
270
*/
271
void Quality::ResetToDefaults() {
272
circularSegments_ = DEFAULT_SEGMENTS;
273
circularAngle_ = DEFAULT_ANGLE;
274
circularEdgeLength_ = DEFAULT_LENGTH;
275
}
276
277
/**
278
* Construct an empty Manifold.
279
*
280
*/
281
Manifold::Manifold() : pNode_{std::make_shared<CsgLeafNode>()} {}
282
Manifold::~Manifold() = default;
283
Manifold::Manifold(Manifold&&) noexcept = default;
284
Manifold& Manifold::operator=(Manifold&&) noexcept = default;
285
286
Manifold::Manifold(const Manifold& other) : pNode_(other.pNode_) {}
287
288
Manifold::Manifold(std::shared_ptr<CsgNode> pNode) : pNode_(pNode) {}
289
290
Manifold::Manifold(std::shared_ptr<Impl> pImpl_)
291
: pNode_(std::make_shared<CsgLeafNode>(pImpl_)) {}
292
293
Manifold Manifold::Invalid() {
294
auto pImpl_ = std::make_shared<Impl>();
295
pImpl_->status_ = Error::InvalidConstruction;
296
return Manifold(pImpl_);
297
}
298
299
Manifold& Manifold::operator=(const Manifold& other) {
300
if (this != &other) {
301
pNode_ = other.pNode_;
302
}
303
return *this;
304
}
305
306
CsgLeafNode& Manifold::GetCsgLeafNode() const {
307
if (pNode_->GetNodeType() != CsgNodeType::Leaf) {
308
pNode_ = pNode_->ToLeafNode();
309
}
310
return *std::static_pointer_cast<CsgLeafNode>(pNode_);
311
}
312
313
/**
314
* Convert a MeshGL into a Manifold, retaining its properties and merging only
315
* the positions according to the merge vectors. Will return an empty Manifold
316
* and set an Error Status if the result is not an oriented 2-manifold. Will
317
* collapse degenerate triangles and unnecessary vertices.
318
*
319
* All fields are read, making this structure suitable for a lossless round-trip
320
* of data from GetMeshGL. For multi-material input, use ReserveIDs to set a
321
* unique originalID for each material, and sort the materials into triangle
322
* runs.
323
*
324
* @param meshGL The input MeshGL.
325
*/
326
Manifold::Manifold(const MeshGL& meshGL)
327
: pNode_(std::make_shared<CsgLeafNode>(std::make_shared<Impl>(meshGL))) {}
328
329
/**
330
* Convert a MeshGL into a Manifold, retaining its properties and merging only
331
* the positions according to the merge vectors. Will return an empty Manifold
332
* and set an Error Status if the result is not an oriented 2-manifold. Will
333
* collapse degenerate triangles and unnecessary vertices.
334
*
335
* All fields are read, making this structure suitable for a lossless round-trip
336
* of data from GetMeshGL. For multi-material input, use ReserveIDs to set a
337
* unique originalID for each material, and sort the materials into triangle
338
* runs.
339
*
340
* @param meshGL64 The input MeshGL64.
341
*/
342
Manifold::Manifold(const MeshGL64& meshGL64)
343
: pNode_(std::make_shared<CsgLeafNode>(std::make_shared<Impl>(meshGL64))) {}
344
345
/**
346
* The most complete output of this library, returning a MeshGL that is designed
347
* to easily push into a renderer, including all interleaved vertex properties
348
* that may have been input. It also includes relations to all the input meshes
349
* that form a part of this result and the transforms applied to each.
350
*
351
* @param normalIdx If the original MeshGL inputs that formed this manifold had
352
* properties corresponding to normal vectors, you can specify the first of the
353
* three consecutive property channels forming the (x, y, z) normals, which will
354
* cause this output MeshGL to automatically update these normals according to
355
* the applied transforms and front/back side. normalIdx + 3 must be <=
356
* numProp, and all original MeshGLs must use the same channels for their
357
* normals.
358
*/
359
MeshGL Manifold::GetMeshGL(int normalIdx) const {
360
const Impl& impl = *GetCsgLeafNode().GetImpl();
361
return GetMeshGLImpl<float, uint32_t>(impl, normalIdx);
362
}
363
364
/**
365
* The most complete output of this library, returning a MeshGL that is designed
366
* to easily push into a renderer, including all interleaved vertex properties
367
* that may have been input. It also includes relations to all the input meshes
368
* that form a part of this result and the transforms applied to each.
369
*
370
* @param normalIdx If the original MeshGL inputs that formed this manifold had
371
* properties corresponding to normal vectors, you can specify the first of the
372
* three consecutive property channels forming the (x, y, z) normals, which will
373
* cause this output MeshGL to automatically update these normals according to
374
* the applied transforms and front/back side. normalIdx + 3 must be <=
375
* numProp, and all original MeshGLs must use the same channels for their
376
* normals.
377
*/
378
MeshGL64 Manifold::GetMeshGL64(int normalIdx) const {
379
const Impl& impl = *GetCsgLeafNode().GetImpl();
380
return GetMeshGLImpl<double, uint64_t>(impl, normalIdx);
381
}
382
383
/**
384
* Does the Manifold have any triangles?
385
*/
386
bool Manifold::IsEmpty() const { return GetCsgLeafNode().GetImpl()->IsEmpty(); }
387
/**
388
* Returns the reason for an input Mesh producing an empty Manifold. This Status
389
* will carry on through operations like NaN propogation, ensuring an errored
390
* mesh doesn't get mysteriously lost. Empty meshes may still show
391
* NoError, for instance the intersection of non-overlapping meshes.
392
*/
393
Manifold::Error Manifold::Status() const {
394
return GetCsgLeafNode().GetImpl()->status_;
395
}
396
/**
397
* The number of vertices in the Manifold.
398
*/
399
size_t Manifold::NumVert() const {
400
return GetCsgLeafNode().GetImpl()->NumVert();
401
}
402
/**
403
* The number of edges in the Manifold.
404
*/
405
size_t Manifold::NumEdge() const {
406
return GetCsgLeafNode().GetImpl()->NumEdge();
407
}
408
/**
409
* The number of triangles in the Manifold.
410
*/
411
size_t Manifold::NumTri() const { return GetCsgLeafNode().GetImpl()->NumTri(); }
412
/**
413
* The number of properties per vertex in the Manifold.
414
*/
415
size_t Manifold::NumProp() const {
416
return GetCsgLeafNode().GetImpl()->NumProp();
417
}
418
/**
419
* The number of property vertices in the Manifold. This will always be >=
420
* NumVert, as some physical vertices may be duplicated to account for different
421
* properties on different neighboring triangles.
422
*/
423
size_t Manifold::NumPropVert() const {
424
return GetCsgLeafNode().GetImpl()->NumPropVert();
425
}
426
427
/**
428
* Returns the axis-aligned bounding box of all the Manifold's vertices.
429
*/
430
Box Manifold::BoundingBox() const { return GetCsgLeafNode().GetImpl()->bBox_; }
431
432
/**
433
* Returns the epsilon value of this Manifold's vertices, which tracks the
434
* approximate rounding error over all the transforms and operations that have
435
* led to this state. This is the value of &epsilon; defining
436
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).
437
*/
438
double Manifold::GetEpsilon() const {
439
return GetCsgLeafNode().GetImpl()->epsilon_;
440
}
441
442
/**
443
* Returns the tolerance value of this Manifold. Triangles that are coplanar
444
* within tolerance tend to be merged and edges shorter than tolerance tend to
445
* be collapsed.
446
*/
447
double Manifold::GetTolerance() const {
448
return GetCsgLeafNode().GetImpl()->tolerance_;
449
}
450
451
/**
452
* Return a copy of the manifold with the set tolerance value.
453
* This performs mesh simplification when the tolerance value is increased.
454
*/
455
Manifold Manifold::SetTolerance(double tolerance) const {
456
auto impl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
457
if (tolerance > impl->tolerance_) {
458
impl->tolerance_ = tolerance;
459
impl->MarkCoplanar();
460
impl->SimplifyTopology();
461
impl->Finish();
462
} else {
463
// for reducing tolerance, we need to make sure it is still at least
464
// equal to epsilon.
465
impl->tolerance_ = std::max(impl->epsilon_, tolerance);
466
}
467
return Manifold(impl);
468
}
469
470
/**
471
* Return a copy of the manifold simplified to the given tolerance, but with its
472
* actual tolerance value unchanged. If the tolerance is not given or is less
473
* than the current tolerance, the current tolerance is used for simplification.
474
* The result will contain a subset of the original verts and all surfaces will
475
* have moved by less than tolerance.
476
*/
477
Manifold Manifold::Simplify(double tolerance) const {
478
auto impl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
479
const double oldTolerance = impl->tolerance_;
480
if (tolerance == 0) tolerance = oldTolerance;
481
if (tolerance > oldTolerance) {
482
impl->tolerance_ = tolerance;
483
impl->MarkCoplanar();
484
}
485
impl->SimplifyTopology();
486
impl->Finish();
487
impl->tolerance_ = oldTolerance;
488
return Manifold(impl);
489
}
490
491
/**
492
* The genus is a topological property of the manifold, representing the number
493
* of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single
494
* mesh, so it is best to call Decompose() first.
495
*/
496
int Manifold::Genus() const {
497
int chi = NumVert() - NumEdge() + NumTri();
498
return 1 - chi / 2;
499
}
500
501
/**
502
* Returns the surface area of the manifold.
503
*/
504
double Manifold::SurfaceArea() const {
505
return GetCsgLeafNode().GetImpl()->GetProperty(Impl::Property::SurfaceArea);
506
}
507
508
/**
509
* Returns the volume of the manifold.
510
*/
511
double Manifold::Volume() const {
512
return GetCsgLeafNode().GetImpl()->GetProperty(Impl::Property::Volume);
513
}
514
515
/**
516
* If this mesh is an original, this returns its meshID that can be referenced
517
* by product manifolds' MeshRelation. If this manifold is a product, this
518
* returns -1.
519
*/
520
int Manifold::OriginalID() const {
521
return GetCsgLeafNode().GetImpl()->meshRelation_.originalID;
522
}
523
524
/**
525
* This removes all relations (originalID, faceID, transform) to ancestor meshes
526
* and this new Manifold is marked an original. It also recreates faces
527
* - these don't get joined at boundaries where originalID changes, so the
528
* reset may allow triangles of flat faces to be further collapsed with
529
* Simplify().
530
*/
531
Manifold Manifold::AsOriginal() const {
532
auto oldImpl = GetCsgLeafNode().GetImpl();
533
if (oldImpl->status_ != Error::NoError) {
534
auto newImpl = std::make_shared<Impl>();
535
newImpl->status_ = oldImpl->status_;
536
return Manifold(std::make_shared<CsgLeafNode>(newImpl));
537
}
538
auto newImpl = std::make_shared<Impl>(*oldImpl);
539
newImpl->InitializeOriginal();
540
newImpl->MarkCoplanar();
541
newImpl->InitializeOriginal(true);
542
return Manifold(std::make_shared<CsgLeafNode>(newImpl));
543
}
544
545
/**
546
* Returns the first of n sequential new unique mesh IDs for marking sets of
547
* triangles that can be looked up after further operations. Assign to
548
* MeshGL.runOriginalID vector.
549
*/
550
uint32_t Manifold::ReserveIDs(uint32_t n) {
551
return Manifold::Impl::ReserveIDs(n);
552
}
553
554
/**
555
* The triangle normal vectors are saved over the course of operations rather
556
* than recalculated to avoid rounding error. This checks that triangles still
557
* match their normal vectors within Precision().
558
*/
559
bool Manifold::MatchesTriNormals() const {
560
return GetCsgLeafNode().GetImpl()->MatchesTriNormals();
561
}
562
563
/**
564
* The number of triangles that are colinear within Precision(). This library
565
* attempts to remove all of these, but it cannot always remove all of them
566
* without changing the mesh by too much.
567
*/
568
size_t Manifold::NumDegenerateTris() const {
569
return GetCsgLeafNode().GetImpl()->NumDegenerateTris();
570
}
571
572
/**
573
* Move this Manifold in space. This operation can be chained. Transforms are
574
* combined and applied lazily.
575
*
576
* @param v The vector to add to every vertex.
577
*/
578
Manifold Manifold::Translate(vec3 v) const {
579
return Manifold(pNode_->Translate(v));
580
}
581
582
/**
583
* Scale this Manifold in space. This operation can be chained. Transforms are
584
* combined and applied lazily.
585
*
586
* @param v The vector to multiply every vertex by per component.
587
*/
588
Manifold Manifold::Scale(vec3 v) const { return Manifold(pNode_->Scale(v)); }
589
590
/**
591
* Applies an Euler angle rotation to the manifold, first about the X axis, then
592
* Y, then Z, in degrees. We use degrees so that we can minimize rounding error,
593
* and eliminate it completely for any multiples of 90 degrees. Additionally,
594
* more efficient code paths are used to update the manifold when the transforms
595
* only rotate by multiples of 90 degrees. This operation can be chained.
596
* Transforms are combined and applied lazily.
597
*
598
* @param xDegrees First rotation, degrees about the X-axis.
599
* @param yDegrees Second rotation, degrees about the Y-axis.
600
* @param zDegrees Third rotation, degrees about the Z-axis.
601
*/
602
Manifold Manifold::Rotate(double xDegrees, double yDegrees,
603
double zDegrees) const {
604
return Manifold(pNode_->Rotate(xDegrees, yDegrees, zDegrees));
605
}
606
607
/**
608
* Transform this Manifold in space. The first three columns form a 3x3 matrix
609
* transform and the last is a translation vector. This operation can be
610
* chained. Transforms are combined and applied lazily.
611
*
612
* @param m The affine transform matrix to apply to all the vertices.
613
*/
614
Manifold Manifold::Transform(const mat3x4& m) const {
615
return Manifold(pNode_->Transform(m));
616
}
617
618
/**
619
* Mirror this Manifold over the plane described by the unit form of the given
620
* normal vector. If the length of the normal is zero, an empty Manifold is
621
* returned. This operation can be chained. Transforms are combined and applied
622
* lazily.
623
*
624
* @param normal The normal vector of the plane to be mirrored over
625
*/
626
Manifold Manifold::Mirror(vec3 normal) const {
627
if (la::length(normal) == 0.) {
628
return Manifold();
629
}
630
auto n = la::normalize(normal);
631
auto m = mat3x4(mat3(la::identity) - 2.0 * la::outerprod(n, n), vec3());
632
return Manifold(pNode_->Transform(m));
633
}
634
635
/**
636
* This function does not change the topology, but allows the vertices to be
637
* moved according to any arbitrary input function. It is easy to create a
638
* function that warps a geometrically valid object into one which overlaps, but
639
* that is not checked here, so it is up to the user to choose their function
640
* with discretion.
641
*
642
* @param warpFunc A function that modifies a given vertex position.
643
*/
644
Manifold Manifold::Warp(std::function<void(vec3&)> warpFunc) const {
645
auto oldImpl = GetCsgLeafNode().GetImpl();
646
if (oldImpl->status_ != Error::NoError) {
647
auto pImpl = std::make_shared<Impl>();
648
pImpl->status_ = oldImpl->status_;
649
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
650
}
651
auto pImpl = std::make_shared<Impl>(*oldImpl);
652
pImpl->Warp(warpFunc);
653
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
654
}
655
656
/**
657
* Same as Manifold::Warp but calls warpFunc with with
658
* a VecView which is roughly equivalent to std::span
659
* pointing to all vec3 elements to be modified in-place
660
*
661
* @param warpFunc A function that modifies multiple vertex positions.
662
*/
663
Manifold Manifold::WarpBatch(
664
std::function<void(VecView<vec3>)> warpFunc) const {
665
auto oldImpl = GetCsgLeafNode().GetImpl();
666
if (oldImpl->status_ != Error::NoError) {
667
auto pImpl = std::make_shared<Impl>();
668
pImpl->status_ = oldImpl->status_;
669
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
670
}
671
auto pImpl = std::make_shared<Impl>(*oldImpl);
672
pImpl->WarpBatch(warpFunc);
673
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
674
}
675
676
/**
677
* Create a new copy of this manifold with updated vertex properties by
678
* supplying a function that takes the existing position and properties as
679
* input. You may specify any number of output properties, allowing creation and
680
* removal of channels. Note: undefined behavior will result if you read past
681
* the number of input properties or write past the number of output properties.
682
*
683
* If propFunc is a nullptr, this function will just set the channel to zeroes.
684
*
685
* @param numProp The new number of properties per vertex.
686
* @param propFunc A function that modifies the properties of a given vertex.
687
*/
688
Manifold Manifold::SetProperties(
689
int numProp,
690
std::function<void(double* newProp, vec3 position, const double* oldProp)>
691
propFunc) const {
692
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
693
const int oldNumProp = NumProp();
694
const Vec<double> oldProperties = pImpl->properties_;
695
696
if (numProp == 0) {
697
pImpl->properties_.clear();
698
} else {
699
pImpl->properties_ = Vec<double>(numProp * NumPropVert(), 0);
700
for_each_n(
701
propFunc == nullptr ? ExecutionPolicy::Par : ExecutionPolicy::Seq,
702
countAt(0), NumTri(), [&](int tri) {
703
for (int i : {0, 1, 2}) {
704
const Halfedge& edge = pImpl->halfedge_[3 * tri + i];
705
const int vert = edge.startVert;
706
const int propVert = edge.propVert;
707
if (propFunc == nullptr) {
708
for (int p = 0; p < numProp; ++p) {
709
pImpl->properties_[numProp * propVert + p] = 0;
710
}
711
} else {
712
propFunc(&pImpl->properties_[numProp * propVert],
713
pImpl->vertPos_[vert],
714
oldProperties.data() + oldNumProp * propVert);
715
}
716
}
717
});
718
}
719
720
pImpl->numProp_ = numProp;
721
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
722
}
723
724
/**
725
* Curvature is the inverse of the radius of curvature, and signed such that
726
* positive is convex and negative is concave. There are two orthogonal
727
* principal curvatures at any point on a manifold, with one maximum and the
728
* other minimum. Gaussian curvature is their product, while mean
729
* curvature is their sum. This approximates them for every vertex and assigns
730
* them as vertex properties on the given channels.
731
*
732
* @param gaussianIdx The property channel index in which to store the Gaussian
733
* curvature. An index < 0 will be ignored (stores nothing). The property set
734
* will be automatically expanded to include the channel index specified.
735
*
736
* @param meanIdx The property channel index in which to store the mean
737
* curvature. An index < 0 will be ignored (stores nothing). The property set
738
* will be automatically expanded to include the channel index specified.
739
*/
740
Manifold Manifold::CalculateCurvature(int gaussianIdx, int meanIdx) const {
741
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
742
pImpl->CalculateCurvature(gaussianIdx, meanIdx);
743
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
744
}
745
746
/**
747
* Fills in vertex properties for normal vectors, calculated from the mesh
748
* geometry. Flat faces composed of three or more triangles will remain flat.
749
*
750
* @param normalIdx The property channel in which to store the X
751
* values of the normals. The X, Y, and Z channels will be sequential. The
752
* property set will be automatically expanded such that NumProp will be at
753
* least normalIdx + 3.
754
*
755
* @param minSharpAngle Any edges with angles greater than this value will
756
* remain sharp, getting different normal vector properties on each side of the
757
* edge. By default, no edges are sharp and all normals are shared. With a value
758
* of zero, the model is faceted and all normals match their triangle normals,
759
* but in this case it would be better not to calculate normals at all.
760
*/
761
Manifold Manifold::CalculateNormals(int normalIdx, double minSharpAngle) const {
762
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
763
pImpl->SetNormals(normalIdx, minSharpAngle);
764
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
765
}
766
767
/**
768
* Smooths out the Manifold by filling in the halfedgeTangent vectors. The
769
* geometry will remain unchanged until Refine or RefineToLength is called to
770
* interpolate the surface. This version uses the supplied vertex normal
771
* properties to define the tangent vectors. Faces of two coplanar triangles
772
* will be marked as quads, while faces with three or more will be flat.
773
*
774
* @param normalIdx The first property channel of the normals. NumProp must be
775
* at least normalIdx + 3. Any vertex where multiple normals exist and don't
776
* agree will result in a sharp edge.
777
*/
778
Manifold Manifold::SmoothByNormals(int normalIdx) const {
779
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
780
if (!IsEmpty()) {
781
pImpl->CreateTangents(normalIdx);
782
}
783
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
784
}
785
786
/**
787
* Smooths out the Manifold by filling in the halfedgeTangent vectors. The
788
* geometry will remain unchanged until Refine or RefineToLength is called to
789
* interpolate the surface. This version uses the geometry of the triangles and
790
* pseudo-normals to define the tangent vectors. Faces of two coplanar triangles
791
* will be marked as quads.
792
*
793
* @param minSharpAngle degrees, default 60. Any edges with angles greater than
794
* this value will remain sharp. The rest will be smoothed to G1 continuity,
795
* with the caveat that flat faces of three or more triangles will always remain
796
* flat. With a value of zero, the model is faceted, but in this case there is
797
* no point in smoothing.
798
*
799
* @param minSmoothness range: 0 - 1, default 0. The smoothness applied to sharp
800
* angles. The default gives a hard edge, while values > 0 will give a small
801
* fillet on these sharp edges. A value of 1 is equivalent to a minSharpAngle of
802
* 180 - all edges will be smooth.
803
*/
804
Manifold Manifold::SmoothOut(double minSharpAngle, double minSmoothness) const {
805
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
806
if (!IsEmpty()) {
807
if (minSmoothness == 0) {
808
const int numProp = pImpl->numProp_;
809
Vec<double> properties = pImpl->properties_;
810
Vec<Halfedge> halfedge = pImpl->halfedge_;
811
pImpl->SetNormals(0, minSharpAngle);
812
pImpl->CreateTangents(0);
813
// Reset the properties to the original values, removing temporary normals
814
pImpl->numProp_ = numProp;
815
pImpl->properties_.swap(properties);
816
pImpl->halfedge_.swap(halfedge);
817
} else {
818
pImpl->CreateTangents(pImpl->SharpenEdges(minSharpAngle, minSmoothness));
819
}
820
}
821
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
822
}
823
824
/**
825
* Increase the density of the mesh by splitting every edge into n pieces. For
826
* instance, with n = 2, each triangle will be split into 4 triangles. Quads
827
* will ignore their interior triangle bisector. These will all be coplanar (and
828
* will not be immediately collapsed) unless the Mesh/Manifold has
829
* halfedgeTangents specified (e.g. from the Smooth() constructor), in which
830
* case the new vertices will be moved to the interpolated surface according to
831
* their barycentric coordinates.
832
*
833
* @param n The number of pieces to split every edge into. Must be > 1.
834
*/
835
Manifold Manifold::Refine(int n) const {
836
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
837
if (n > 1) {
838
pImpl->Refine([n](vec3, vec4, vec4) { return n - 1; });
839
}
840
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
841
}
842
843
/**
844
* Increase the density of the mesh by splitting each edge into pieces of
845
* roughly the input length. Interior verts are added to keep the rest of the
846
* triangulation edges also of roughly the same length. If halfedgeTangents are
847
* present (e.g. from the Smooth() constructor), the new vertices will be moved
848
* to the interpolated surface according to their barycentric coordinates. Quads
849
* will ignore their interior triangle bisector.
850
*
851
* @param length The length that edges will be broken down to.
852
*/
853
Manifold Manifold::RefineToLength(double length) const {
854
length = std::abs(length);
855
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
856
pImpl->Refine([length](vec3 edge, vec4, vec4) {
857
return static_cast<int>(la::length(edge) / length);
858
});
859
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
860
}
861
862
/**
863
* Increase the density of the mesh by splitting each edge into pieces such that
864
* any point on the resulting triangles is roughly within tolerance of the
865
* smoothly curved surface defined by the tangent vectors. This means tightly
866
* curving regions will be divided more finely than smoother regions. If
867
* halfedgeTangents are not present, the result will simply be a copy of the
868
* original. Quads will ignore their interior triangle bisector.
869
*
870
* @param tolerance The desired maximum distance between the faceted mesh
871
* produced and the exact smoothly curving surface. All vertices are exactly on
872
* the surface, within rounding error.
873
*/
874
Manifold Manifold::RefineToTolerance(double tolerance) const {
875
tolerance = std::abs(tolerance);
876
auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());
877
if (!pImpl->halfedgeTangent_.empty()) {
878
pImpl->Refine(
879
[tolerance](vec3 edge, vec4 tangentStart, vec4 tangentEnd) {
880
const vec3 edgeNorm = la::normalize(edge);
881
// Weight heuristic
882
const vec3 tStart = vec3(tangentStart);
883
const vec3 tEnd = vec3(tangentEnd);
884
// Perpendicular to edge
885
const vec3 start = tStart - edgeNorm * la::dot(edgeNorm, tStart);
886
const vec3 end = tEnd - edgeNorm * la::dot(edgeNorm, tEnd);
887
// Circular arc result plus heuristic term for non-circular curves
888
const double d = 0.5 * (la::length(start) + la::length(end)) +
889
la::length(start - end);
890
return static_cast<int>(std::sqrt(3 * d / (4 * tolerance)));
891
},
892
true);
893
}
894
return Manifold(std::make_shared<CsgLeafNode>(pImpl));
895
}
896
897
/**
898
* The central operation of this library: the Boolean combines two manifolds
899
* into another by calculating their intersections and removing the unused
900
* portions.
901
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid)
902
* inputs will produce &epsilon;-valid output. &epsilon;-invalid input may fail
903
* triangulation.
904
*
905
* These operations are optimized to produce nearly-instant results if either
906
* input is empty or their bounding boxes do not overlap.
907
*
908
* @param second The other Manifold.
909
* @param op The type of operation to perform.
910
*/
911
Manifold Manifold::Boolean(const Manifold& second, OpType op) const {
912
return Manifold(pNode_->Boolean(second.pNode_, op));
913
}
914
915
/**
916
* Perform the given boolean operation on a list of Manifolds. In case of
917
* Subtract, all Manifolds in the tail are differenced from the head.
918
*/
919
Manifold Manifold::BatchBoolean(const std::vector<Manifold>& manifolds,
920
OpType op) {
921
if (manifolds.size() == 0)
922
return Manifold();
923
else if (manifolds.size() == 1)
924
return manifolds[0];
925
std::vector<std::shared_ptr<CsgNode>> children;
926
children.reserve(manifolds.size());
927
for (const auto& m : manifolds) children.push_back(m.pNode_);
928
return Manifold(std::make_shared<CsgOpNode>(children, op));
929
}
930
931
/**
932
* Shorthand for Boolean Union.
933
*/
934
Manifold Manifold::operator+(const Manifold& Q) const {
935
return Boolean(Q, OpType::Add);
936
}
937
938
/**
939
* Shorthand for Boolean Union assignment.
940
*/
941
Manifold& Manifold::operator+=(const Manifold& Q) {
942
*this = *this + Q;
943
return *this;
944
}
945
946
/**
947
* Shorthand for Boolean Difference.
948
*/
949
Manifold Manifold::operator-(const Manifold& Q) const {
950
return Boolean(Q, OpType::Subtract);
951
}
952
953
/**
954
* Shorthand for Boolean Difference assignment.
955
*/
956
Manifold& Manifold::operator-=(const Manifold& Q) {
957
*this = *this - Q;
958
return *this;
959
}
960
961
/**
962
* Shorthand for Boolean Intersection.
963
*/
964
Manifold Manifold::operator^(const Manifold& Q) const {
965
return Boolean(Q, OpType::Intersect);
966
}
967
968
/**
969
* Shorthand for Boolean Intersection assignment.
970
*/
971
Manifold& Manifold::operator^=(const Manifold& Q) {
972
*this = *this ^ Q;
973
return *this;
974
}
975
976
/**
977
* Split cuts this manifold in two using the cutter manifold. The first result
978
* is the intersection, second is the difference. This is more efficient than
979
* doing them separately.
980
*
981
* @param cutter
982
*/
983
std::pair<Manifold, Manifold> Manifold::Split(const Manifold& cutter) const {
984
auto impl1 = GetCsgLeafNode().GetImpl();
985
auto impl2 = cutter.GetCsgLeafNode().GetImpl();
986
987
Boolean3 boolean(*impl1, *impl2, OpType::Subtract);
988
auto result1 = std::make_shared<CsgLeafNode>(
989
std::make_unique<Impl>(boolean.Result(OpType::Intersect)));
990
auto result2 = std::make_shared<CsgLeafNode>(
991
std::make_unique<Impl>(boolean.Result(OpType::Subtract)));
992
return std::make_pair(Manifold(result1), Manifold(result2));
993
}
994
995
/**
996
* Convenient version of Split() for a half-space.
997
*
998
* @param normal This vector is normal to the cutting plane and its length does
999
* not matter. The first result is in the direction of this vector, the second
1000
* result is on the opposite side.
1001
* @param originOffset The distance of the plane from the origin in the
1002
* direction of the normal vector.
1003
*/
1004
std::pair<Manifold, Manifold> Manifold::SplitByPlane(
1005
vec3 normal, double originOffset) const {
1006
return Split(Halfspace(BoundingBox(), normal, originOffset));
1007
}
1008
1009
/**
1010
* Identical to SplitByPlane(), but calculating and returning only the first
1011
* result.
1012
*
1013
* @param normal This vector is normal to the cutting plane and its length does
1014
* not matter. The result is in the direction of this vector from the plane.
1015
* @param originOffset The distance of the plane from the origin in the
1016
* direction of the normal vector.
1017
*/
1018
Manifold Manifold::TrimByPlane(vec3 normal, double originOffset) const {
1019
return *this ^ Halfspace(BoundingBox(), normal, originOffset);
1020
}
1021
1022
/**
1023
* Returns the cross section of this object parallel to the X-Y plane at the
1024
* specified Z height, defaulting to zero. Using a height equal to the bottom of
1025
* the bounding box will return the bottom faces, while using a height equal to
1026
* the top of the bounding box will return empty.
1027
*/
1028
Polygons Manifold::Slice(double height) const {
1029
return GetCsgLeafNode().GetImpl()->Slice(height);
1030
}
1031
1032
/**
1033
* Returns polygons representing the projected outline of this object
1034
* onto the X-Y plane. These polygons will often self-intersect, so it is
1035
* recommended to run them through the positive fill rule of CrossSection to get
1036
* a sensible result before using them.
1037
*/
1038
Polygons Manifold::Project() const {
1039
return GetCsgLeafNode().GetImpl()->Project();
1040
}
1041
1042
ExecutionParams& ManifoldParams() { return manifoldParams; }
1043
1044
/**
1045
* Compute the convex hull of a set of points. If the given points are fewer
1046
* than 4, or they are all coplanar, an empty Manifold will be returned.
1047
*
1048
* @param pts A vector of 3-dimensional points over which to compute a convex
1049
* hull.
1050
*/
1051
Manifold Manifold::Hull(const std::vector<vec3>& pts) {
1052
std::shared_ptr<Impl> impl = std::make_shared<Impl>();
1053
impl->Hull(Vec<vec3>(pts));
1054
return Manifold(std::make_shared<CsgLeafNode>(impl));
1055
}
1056
1057
/**
1058
* Compute the convex hull of this manifold.
1059
*/
1060
Manifold Manifold::Hull() const {
1061
std::shared_ptr<Impl> impl = std::make_shared<Impl>();
1062
impl->Hull(GetCsgLeafNode().GetImpl()->vertPos_);
1063
return Manifold(std::make_shared<CsgLeafNode>(impl));
1064
}
1065
1066
/**
1067
* Compute the convex hull enveloping a set of manifolds.
1068
*
1069
* @param manifolds A vector of manifolds over which to compute a convex hull.
1070
*/
1071
Manifold Manifold::Hull(const std::vector<Manifold>& manifolds) {
1072
return Compose(manifolds).Hull();
1073
}
1074
1075
/**
1076
* Returns the minimum gap between two manifolds. Returns a double between
1077
* 0 and searchLength.
1078
*
1079
* @param other The other manifold to compute the minimum gap to.
1080
* @param searchLength The maximum distance to search for a minimum gap.
1081
*/
1082
double Manifold::MinGap(const Manifold& other, double searchLength) const {
1083
auto intersect = *this ^ other;
1084
if (!intersect.IsEmpty()) return 0.0;
1085
1086
return GetCsgLeafNode().GetImpl()->MinGap(*other.GetCsgLeafNode().GetImpl(),
1087
searchLength);
1088
}
1089
} // namespace manifold
1090
1091