Path: blob/master/thirdparty/manifold/src/manifold.cpp
9903 views
// Copyright 2021 The Manifold Authors.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#include <algorithm>15#include <map>16#include <numeric>1718#include "boolean3.h"19#include "csg_tree.h"20#include "impl.h"21#include "parallel.h"22#include "shared.h"2324namespace {25using namespace manifold;2627ExecutionParams manifoldParams;2829Manifold Halfspace(Box bBox, vec3 normal, double originOffset) {30normal = la::normalize(normal);31Manifold cutter = Manifold::Cube(vec3(2.0), true).Translate({1.0, 0.0, 0.0});32double size = la::length(bBox.Center() - normal * originOffset) +330.5 * la::length(bBox.Size());34cutter = cutter.Scale(vec3(size)).Translate({originOffset, 0.0, 0.0});35double yDeg = degrees(-std::asin(normal.z));36double zDeg = degrees(std::atan2(normal.y, normal.x));37return cutter.Rotate(0.0, yDeg, zDeg);38}3940template <typename Precision, typename I>41MeshGLP<Precision, I> GetMeshGLImpl(const manifold::Manifold::Impl& impl,42int normalIdx) {43ZoneScoped;44const int numProp = impl.NumProp();45const int numVert = impl.NumPropVert();46const int numTri = impl.NumTri();4748const bool isOriginal = impl.meshRelation_.originalID >= 0;49const bool updateNormals = !isOriginal && normalIdx >= 0;5051MeshGLP<Precision, I> out;52out.numProp = 3 + numProp;53out.tolerance = impl.tolerance_;54if (std::is_same<Precision, float>::value)55out.tolerance =56std::max(out.tolerance,57static_cast<Precision>(std::numeric_limits<float>::epsilon() *58impl.bBox_.Scale()));59out.triVerts.resize(3 * numTri);6061const int numHalfedge = impl.halfedgeTangent_.size();62out.halfedgeTangent.resize(4 * numHalfedge);63for (int i = 0; i < numHalfedge; ++i) {64const vec4 t = impl.halfedgeTangent_[i];65out.halfedgeTangent[4 * i] = t.x;66out.halfedgeTangent[4 * i + 1] = t.y;67out.halfedgeTangent[4 * i + 2] = t.z;68out.halfedgeTangent[4 * i + 3] = t.w;69}70// Sort the triangles into runs71out.faceID.resize(numTri);72std::vector<int> triNew2Old(numTri);73std::iota(triNew2Old.begin(), triNew2Old.end(), 0);74VecView<const TriRef> triRef = impl.meshRelation_.triRef;75// Don't sort originals - keep them in order76if (!isOriginal) {77std::stable_sort(triNew2Old.begin(), triNew2Old.end(),78[triRef](int a, int b) {79return triRef[a].originalID == triRef[b].originalID80? triRef[a].meshID < triRef[b].meshID81: triRef[a].originalID < triRef[b].originalID;82});83}8485std::vector<mat3> runNormalTransform;86auto addRun = [updateNormals, isOriginal](87MeshGLP<Precision, I>& out,88std::vector<mat3>& runNormalTransform, int tri,89const manifold::Manifold::Impl::Relation& rel) {90out.runIndex.push_back(3 * tri);91out.runOriginalID.push_back(rel.originalID);92if (updateNormals) {93runNormalTransform.push_back(NormalTransform(rel.transform) *94(rel.backSide ? -1.0 : 1.0));95}96if (!isOriginal) {97for (const int col : {0, 1, 2, 3}) {98for (const int row : {0, 1, 2}) {99out.runTransform.push_back(rel.transform[col][row]);100}101}102}103};104105auto meshIDtransform = impl.meshRelation_.meshIDtransform;106int lastID = -1;107for (int tri = 0; tri < numTri; ++tri) {108const int oldTri = triNew2Old[tri];109const auto ref = triRef[oldTri];110const int meshID = ref.meshID;111112out.faceID[tri] = ref.faceID >= 0 ? ref.faceID : ref.coplanarID;113for (const int i : {0, 1, 2})114out.triVerts[3 * tri + i] = impl.halfedge_[3 * oldTri + i].startVert;115116if (meshID != lastID) {117manifold::Manifold::Impl::Relation rel;118auto it = meshIDtransform.find(meshID);119if (it != meshIDtransform.end()) rel = it->second;120addRun(out, runNormalTransform, tri, rel);121meshIDtransform.erase(meshID);122lastID = meshID;123}124}125// Add runs for originals that did not contribute any faces to the output126for (const auto& pair : meshIDtransform) {127addRun(out, runNormalTransform, numTri, pair.second);128}129out.runIndex.push_back(3 * numTri);130131// Early return for no props132if (numProp == 0) {133out.vertProperties.resize(3 * numVert);134for (int i = 0; i < numVert; ++i) {135const vec3 v = impl.vertPos_[i];136out.vertProperties[3 * i] = v.x;137out.vertProperties[3 * i + 1] = v.y;138out.vertProperties[3 * i + 2] = v.z;139}140return out;141}142// Duplicate verts with different props143std::vector<int> vert2idx(impl.NumVert(), -1);144std::vector<std::vector<ivec2>> vertPropPair(impl.NumVert());145out.vertProperties.reserve(numVert * static_cast<size_t>(out.numProp));146147for (size_t run = 0; run < out.runOriginalID.size(); ++run) {148for (size_t tri = out.runIndex[run] / 3; tri < out.runIndex[run + 1] / 3;149++tri) {150for (const int i : {0, 1, 2}) {151const int prop = impl.halfedge_[3 * triNew2Old[tri] + i].propVert;152const int vert = out.triVerts[3 * tri + i];153154auto& bin = vertPropPair[vert];155bool bFound = false;156for (const auto& b : bin) {157if (b.x == prop) {158bFound = true;159out.triVerts[3 * tri + i] = b.y;160break;161}162}163if (bFound) continue;164const int idx = out.vertProperties.size() / out.numProp;165out.triVerts[3 * tri + i] = idx;166bin.push_back({prop, idx});167168for (int p : {0, 1, 2}) {169out.vertProperties.push_back(impl.vertPos_[vert][p]);170}171for (int p = 0; p < numProp; ++p) {172out.vertProperties.push_back(impl.properties_[prop * numProp + p]);173}174175if (updateNormals) {176vec3 normal;177const int start = out.vertProperties.size() - out.numProp;178for (int i : {0, 1, 2}) {179normal[i] = out.vertProperties[start + 3 + normalIdx + i];180}181normal = la::normalize(runNormalTransform[run] * normal);182for (int i : {0, 1, 2}) {183out.vertProperties[start + 3 + normalIdx + i] = normal[i];184}185}186187if (vert2idx[vert] == -1) {188vert2idx[vert] = idx;189} else {190out.mergeFromVert.push_back(idx);191out.mergeToVert.push_back(vert2idx[vert]);192}193}194}195}196return out;197}198} // namespace199200namespace manifold {201202/**203* Construct an empty Manifold.204*205*/206Manifold::Manifold() : pNode_{std::make_shared<CsgLeafNode>()} {}207Manifold::~Manifold() = default;208Manifold::Manifold(Manifold&&) noexcept = default;209Manifold& Manifold::operator=(Manifold&&) noexcept = default;210211Manifold::Manifold(const Manifold& other) : pNode_(other.pNode_) {}212213Manifold::Manifold(std::shared_ptr<CsgNode> pNode) : pNode_(pNode) {}214215Manifold::Manifold(std::shared_ptr<Impl> pImpl_)216: pNode_(std::make_shared<CsgLeafNode>(pImpl_)) {}217218Manifold Manifold::Invalid() {219auto pImpl_ = std::make_shared<Impl>();220pImpl_->status_ = Error::InvalidConstruction;221return Manifold(pImpl_);222}223224Manifold& Manifold::operator=(const Manifold& other) {225if (this != &other) {226pNode_ = other.pNode_;227}228return *this;229}230231CsgLeafNode& Manifold::GetCsgLeafNode() const {232if (pNode_->GetNodeType() != CsgNodeType::Leaf) {233pNode_ = pNode_->ToLeafNode();234}235return *std::static_pointer_cast<CsgLeafNode>(pNode_);236}237238/**239* Convert a MeshGL into a Manifold, retaining its properties and merging only240* the positions according to the merge vectors. Will return an empty Manifold241* and set an Error Status if the result is not an oriented 2-manifold. Will242* collapse degenerate triangles and unnecessary vertices.243*244* All fields are read, making this structure suitable for a lossless round-trip245* of data from GetMeshGL. For multi-material input, use ReserveIDs to set a246* unique originalID for each material, and sort the materials into triangle247* runs.248*249* @param meshGL The input MeshGL.250*/251Manifold::Manifold(const MeshGL& meshGL)252: pNode_(std::make_shared<CsgLeafNode>(std::make_shared<Impl>(meshGL))) {}253254/**255* Convert a MeshGL into a Manifold, retaining its properties and merging only256* the positions according to the merge vectors. Will return an empty Manifold257* and set an Error Status if the result is not an oriented 2-manifold. Will258* collapse degenerate triangles and unnecessary vertices.259*260* All fields are read, making this structure suitable for a lossless round-trip261* of data from GetMeshGL. For multi-material input, use ReserveIDs to set a262* unique originalID for each material, and sort the materials into triangle263* runs.264*265* @param meshGL64 The input MeshGL64.266*/267Manifold::Manifold(const MeshGL64& meshGL64)268: pNode_(std::make_shared<CsgLeafNode>(std::make_shared<Impl>(meshGL64))) {}269270/**271* The most complete output of this library, returning a MeshGL that is designed272* to easily push into a renderer, including all interleaved vertex properties273* that may have been input. It also includes relations to all the input meshes274* that form a part of this result and the transforms applied to each.275*276* @param normalIdx If the original MeshGL inputs that formed this manifold had277* properties corresponding to normal vectors, you can specify the first of the278* three consecutive property channels forming the (x, y, z) normals, which will279* cause this output MeshGL to automatically update these normals according to280* the applied transforms and front/back side. normalIdx + 3 must be <=281* numProp, and all original MeshGLs must use the same channels for their282* normals.283*/284MeshGL Manifold::GetMeshGL(int normalIdx) const {285const Impl& impl = *GetCsgLeafNode().GetImpl();286return GetMeshGLImpl<float, uint32_t>(impl, normalIdx);287}288289/**290* The most complete output of this library, returning a MeshGL that is designed291* to easily push into a renderer, including all interleaved vertex properties292* that may have been input. It also includes relations to all the input meshes293* that form a part of this result and the transforms applied to each.294*295* @param normalIdx If the original MeshGL inputs that formed this manifold had296* properties corresponding to normal vectors, you can specify the first of the297* three consecutive property channels forming the (x, y, z) normals, which will298* cause this output MeshGL to automatically update these normals according to299* the applied transforms and front/back side. normalIdx + 3 must be <=300* numProp, and all original MeshGLs must use the same channels for their301* normals.302*/303MeshGL64 Manifold::GetMeshGL64(int normalIdx) const {304const Impl& impl = *GetCsgLeafNode().GetImpl();305return GetMeshGLImpl<double, uint64_t>(impl, normalIdx);306}307308/**309* Does the Manifold have any triangles?310*/311bool Manifold::IsEmpty() const { return GetCsgLeafNode().GetImpl()->IsEmpty(); }312/**313* Returns the reason for an input Mesh producing an empty Manifold. This Status314* will carry on through operations like NaN propogation, ensuring an errored315* mesh doesn't get mysteriously lost. Empty meshes may still show316* NoError, for instance the intersection of non-overlapping meshes.317*/318Manifold::Error Manifold::Status() const {319return GetCsgLeafNode().GetImpl()->status_;320}321/**322* The number of vertices in the Manifold.323*/324size_t Manifold::NumVert() const {325return GetCsgLeafNode().GetImpl()->NumVert();326}327/**328* The number of edges in the Manifold.329*/330size_t Manifold::NumEdge() const {331return GetCsgLeafNode().GetImpl()->NumEdge();332}333/**334* The number of triangles in the Manifold.335*/336size_t Manifold::NumTri() const { return GetCsgLeafNode().GetImpl()->NumTri(); }337/**338* The number of properties per vertex in the Manifold.339*/340size_t Manifold::NumProp() const {341return GetCsgLeafNode().GetImpl()->NumProp();342}343/**344* The number of property vertices in the Manifold. This will always be >=345* NumVert, as some physical vertices may be duplicated to account for different346* properties on different neighboring triangles.347*/348size_t Manifold::NumPropVert() const {349return GetCsgLeafNode().GetImpl()->NumPropVert();350}351352/**353* Returns the axis-aligned bounding box of all the Manifold's vertices.354*/355Box Manifold::BoundingBox() const { return GetCsgLeafNode().GetImpl()->bBox_; }356357/**358* Returns the epsilon value of this Manifold's vertices, which tracks the359* approximate rounding error over all the transforms and operations that have360* led to this state. This is the value of ε defining361* [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).362*/363double Manifold::GetEpsilon() const {364return GetCsgLeafNode().GetImpl()->epsilon_;365}366367/**368* Returns the tolerance value of this Manifold. Triangles that are coplanar369* within tolerance tend to be merged and edges shorter than tolerance tend to370* be collapsed.371*/372double Manifold::GetTolerance() const {373return GetCsgLeafNode().GetImpl()->tolerance_;374}375376/**377* Return a copy of the manifold with the set tolerance value.378* This performs mesh simplification when the tolerance value is increased.379*/380Manifold Manifold::SetTolerance(double tolerance) const {381auto impl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());382if (tolerance > impl->tolerance_) {383impl->tolerance_ = tolerance;384impl->MarkCoplanar();385impl->SimplifyTopology();386impl->Finish();387} else {388// for reducing tolerance, we need to make sure it is still at least389// equal to epsilon.390impl->tolerance_ = std::max(impl->epsilon_, tolerance);391}392return Manifold(impl);393}394395/**396* Return a copy of the manifold simplified to the given tolerance, but with its397* actual tolerance value unchanged. If the tolerance is not given or is less398* than the current tolerance, the current tolerance is used for simplification.399* The result will contain a subset of the original verts and all surfaces will400* have moved by less than tolerance.401*/402Manifold Manifold::Simplify(double tolerance) const {403auto impl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());404const double oldTolerance = impl->tolerance_;405if (tolerance == 0) tolerance = oldTolerance;406if (tolerance > oldTolerance) {407impl->tolerance_ = tolerance;408impl->MarkCoplanar();409}410impl->SimplifyTopology();411impl->Finish();412impl->tolerance_ = oldTolerance;413return Manifold(impl);414}415416/**417* The genus is a topological property of the manifold, representing the number418* of "handles". A sphere is 0, torus 1, etc. It is only meaningful for a single419* mesh, so it is best to call Decompose() first.420*/421int Manifold::Genus() const {422int chi = NumVert() - NumEdge() + NumTri();423return 1 - chi / 2;424}425426/**427* Returns the surface area of the manifold.428*/429double Manifold::SurfaceArea() const {430return GetCsgLeafNode().GetImpl()->GetProperty(Impl::Property::SurfaceArea);431}432433/**434* Returns the volume of the manifold.435*/436double Manifold::Volume() const {437return GetCsgLeafNode().GetImpl()->GetProperty(Impl::Property::Volume);438}439440/**441* If this mesh is an original, this returns its meshID that can be referenced442* by product manifolds' MeshRelation. If this manifold is a product, this443* returns -1.444*/445int Manifold::OriginalID() const {446return GetCsgLeafNode().GetImpl()->meshRelation_.originalID;447}448449/**450* This removes all relations (originalID, faceID, transform) to ancestor meshes451* and this new Manifold is marked an original. It also recreates faces452* - these don't get joined at boundaries where originalID changes, so the453* reset may allow triangles of flat faces to be further collapsed with454* Simplify().455*/456Manifold Manifold::AsOriginal() const {457auto oldImpl = GetCsgLeafNode().GetImpl();458if (oldImpl->status_ != Error::NoError) {459auto newImpl = std::make_shared<Impl>();460newImpl->status_ = oldImpl->status_;461return Manifold(std::make_shared<CsgLeafNode>(newImpl));462}463auto newImpl = std::make_shared<Impl>(*oldImpl);464newImpl->InitializeOriginal();465newImpl->MarkCoplanar();466newImpl->InitializeOriginal(true);467return Manifold(std::make_shared<CsgLeafNode>(newImpl));468}469470/**471* Returns the first of n sequential new unique mesh IDs for marking sets of472* triangles that can be looked up after further operations. Assign to473* MeshGL.runOriginalID vector.474*/475uint32_t Manifold::ReserveIDs(uint32_t n) {476return Manifold::Impl::ReserveIDs(n);477}478479/**480* The triangle normal vectors are saved over the course of operations rather481* than recalculated to avoid rounding error. This checks that triangles still482* match their normal vectors within Precision().483*/484bool Manifold::MatchesTriNormals() const {485return GetCsgLeafNode().GetImpl()->MatchesTriNormals();486}487488/**489* The number of triangles that are colinear within Precision(). This library490* attempts to remove all of these, but it cannot always remove all of them491* without changing the mesh by too much.492*/493size_t Manifold::NumDegenerateTris() const {494return GetCsgLeafNode().GetImpl()->NumDegenerateTris();495}496497/**498* Move this Manifold in space. This operation can be chained. Transforms are499* combined and applied lazily.500*501* @param v The vector to add to every vertex.502*/503Manifold Manifold::Translate(vec3 v) const {504return Manifold(pNode_->Translate(v));505}506507/**508* Scale this Manifold in space. This operation can be chained. Transforms are509* combined and applied lazily.510*511* @param v The vector to multiply every vertex by per component.512*/513Manifold Manifold::Scale(vec3 v) const { return Manifold(pNode_->Scale(v)); }514515/**516* Applies an Euler angle rotation to the manifold, first about the X axis, then517* Y, then Z, in degrees. We use degrees so that we can minimize rounding error,518* and eliminate it completely for any multiples of 90 degrees. Additionally,519* more efficient code paths are used to update the manifold when the transforms520* only rotate by multiples of 90 degrees. This operation can be chained.521* Transforms are combined and applied lazily.522*523* @param xDegrees First rotation, degrees about the X-axis.524* @param yDegrees Second rotation, degrees about the Y-axis.525* @param zDegrees Third rotation, degrees about the Z-axis.526*/527Manifold Manifold::Rotate(double xDegrees, double yDegrees,528double zDegrees) const {529return Manifold(pNode_->Rotate(xDegrees, yDegrees, zDegrees));530}531532/**533* Transform this Manifold in space. The first three columns form a 3x3 matrix534* transform and the last is a translation vector. This operation can be535* chained. Transforms are combined and applied lazily.536*537* @param m The affine transform matrix to apply to all the vertices.538*/539Manifold Manifold::Transform(const mat3x4& m) const {540return Manifold(pNode_->Transform(m));541}542543/**544* Mirror this Manifold over the plane described by the unit form of the given545* normal vector. If the length of the normal is zero, an empty Manifold is546* returned. This operation can be chained. Transforms are combined and applied547* lazily.548*549* @param normal The normal vector of the plane to be mirrored over550*/551Manifold Manifold::Mirror(vec3 normal) const {552if (la::length(normal) == 0.) {553return Manifold();554}555auto n = la::normalize(normal);556auto m = mat3x4(mat3(la::identity) - 2.0 * la::outerprod(n, n), vec3());557return Manifold(pNode_->Transform(m));558}559560/**561* This function does not change the topology, but allows the vertices to be562* moved according to any arbitrary input function. It is easy to create a563* function that warps a geometrically valid object into one which overlaps, but564* that is not checked here, so it is up to the user to choose their function565* with discretion.566*567* @param warpFunc A function that modifies a given vertex position.568*/569Manifold Manifold::Warp(std::function<void(vec3&)> warpFunc) const {570auto oldImpl = GetCsgLeafNode().GetImpl();571if (oldImpl->status_ != Error::NoError) {572auto pImpl = std::make_shared<Impl>();573pImpl->status_ = oldImpl->status_;574return Manifold(std::make_shared<CsgLeafNode>(pImpl));575}576auto pImpl = std::make_shared<Impl>(*oldImpl);577pImpl->Warp(warpFunc);578return Manifold(std::make_shared<CsgLeafNode>(pImpl));579}580581/**582* Same as Manifold::Warp but calls warpFunc with with583* a VecView which is roughly equivalent to std::span584* pointing to all vec3 elements to be modified in-place585*586* @param warpFunc A function that modifies multiple vertex positions.587*/588Manifold Manifold::WarpBatch(589std::function<void(VecView<vec3>)> warpFunc) const {590auto oldImpl = GetCsgLeafNode().GetImpl();591if (oldImpl->status_ != Error::NoError) {592auto pImpl = std::make_shared<Impl>();593pImpl->status_ = oldImpl->status_;594return Manifold(std::make_shared<CsgLeafNode>(pImpl));595}596auto pImpl = std::make_shared<Impl>(*oldImpl);597pImpl->WarpBatch(warpFunc);598return Manifold(std::make_shared<CsgLeafNode>(pImpl));599}600601/**602* Create a new copy of this manifold with updated vertex properties by603* supplying a function that takes the existing position and properties as604* input. You may specify any number of output properties, allowing creation and605* removal of channels. Note: undefined behavior will result if you read past606* the number of input properties or write past the number of output properties.607*608* If propFunc is a nullptr, this function will just set the channel to zeroes.609*610* @param numProp The new number of properties per vertex.611* @param propFunc A function that modifies the properties of a given vertex.612*/613Manifold Manifold::SetProperties(614int numProp,615std::function<void(double* newProp, vec3 position, const double* oldProp)>616propFunc) const {617auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());618const int oldNumProp = NumProp();619const Vec<double> oldProperties = pImpl->properties_;620621if (numProp == 0) {622pImpl->properties_.clear();623} else {624pImpl->properties_ = Vec<double>(numProp * NumPropVert(), 0);625for_each_n(626propFunc == nullptr ? ExecutionPolicy::Par : ExecutionPolicy::Seq,627countAt(0), NumTri(), [&](int tri) {628for (int i : {0, 1, 2}) {629const Halfedge& edge = pImpl->halfedge_[3 * tri + i];630const int vert = edge.startVert;631const int propVert = edge.propVert;632if (propFunc == nullptr) {633for (int p = 0; p < numProp; ++p) {634pImpl->properties_[numProp * propVert + p] = 0;635}636} else {637propFunc(&pImpl->properties_[numProp * propVert],638pImpl->vertPos_[vert],639oldProperties.data() + oldNumProp * propVert);640}641}642});643}644645pImpl->numProp_ = numProp;646return Manifold(std::make_shared<CsgLeafNode>(pImpl));647}648649/**650* Curvature is the inverse of the radius of curvature, and signed such that651* positive is convex and negative is concave. There are two orthogonal652* principal curvatures at any point on a manifold, with one maximum and the653* other minimum. Gaussian curvature is their product, while mean654* curvature is their sum. This approximates them for every vertex and assigns655* them as vertex properties on the given channels.656*657* @param gaussianIdx The property channel index in which to store the Gaussian658* curvature. An index < 0 will be ignored (stores nothing). The property set659* will be automatically expanded to include the channel index specified.660*661* @param meanIdx The property channel index in which to store the mean662* curvature. An index < 0 will be ignored (stores nothing). The property set663* will be automatically expanded to include the channel index specified.664*/665Manifold Manifold::CalculateCurvature(int gaussianIdx, int meanIdx) const {666auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());667pImpl->CalculateCurvature(gaussianIdx, meanIdx);668return Manifold(std::make_shared<CsgLeafNode>(pImpl));669}670671/**672* Fills in vertex properties for normal vectors, calculated from the mesh673* geometry. Flat faces composed of three or more triangles will remain flat.674*675* @param normalIdx The property channel in which to store the X676* values of the normals. The X, Y, and Z channels will be sequential. The677* property set will be automatically expanded such that NumProp will be at678* least normalIdx + 3.679*680* @param minSharpAngle Any edges with angles greater than this value will681* remain sharp, getting different normal vector properties on each side of the682* edge. By default, no edges are sharp and all normals are shared. With a value683* of zero, the model is faceted and all normals match their triangle normals,684* but in this case it would be better not to calculate normals at all.685*/686Manifold Manifold::CalculateNormals(int normalIdx, double minSharpAngle) const {687auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());688pImpl->SetNormals(normalIdx, minSharpAngle);689return Manifold(std::make_shared<CsgLeafNode>(pImpl));690}691692/**693* Smooths out the Manifold by filling in the halfedgeTangent vectors. The694* geometry will remain unchanged until Refine or RefineToLength is called to695* interpolate the surface. This version uses the supplied vertex normal696* properties to define the tangent vectors. Faces of two coplanar triangles697* will be marked as quads, while faces with three or more will be flat.698*699* @param normalIdx The first property channel of the normals. NumProp must be700* at least normalIdx + 3. Any vertex where multiple normals exist and don't701* agree will result in a sharp edge.702*/703Manifold Manifold::SmoothByNormals(int normalIdx) const {704auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());705if (!IsEmpty()) {706pImpl->CreateTangents(normalIdx);707}708return Manifold(std::make_shared<CsgLeafNode>(pImpl));709}710711/**712* Smooths out the Manifold by filling in the halfedgeTangent vectors. The713* geometry will remain unchanged until Refine or RefineToLength is called to714* interpolate the surface. This version uses the geometry of the triangles and715* pseudo-normals to define the tangent vectors. Faces of two coplanar triangles716* will be marked as quads.717*718* @param minSharpAngle degrees, default 60. Any edges with angles greater than719* this value will remain sharp. The rest will be smoothed to G1 continuity,720* with the caveat that flat faces of three or more triangles will always remain721* flat. With a value of zero, the model is faceted, but in this case there is722* no point in smoothing.723*724* @param minSmoothness range: 0 - 1, default 0. The smoothness applied to sharp725* angles. The default gives a hard edge, while values > 0 will give a small726* fillet on these sharp edges. A value of 1 is equivalent to a minSharpAngle of727* 180 - all edges will be smooth.728*/729Manifold Manifold::SmoothOut(double minSharpAngle, double minSmoothness) const {730auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());731if (!IsEmpty()) {732if (minSmoothness == 0) {733const int numProp = pImpl->numProp_;734Vec<double> properties = pImpl->properties_;735Vec<Halfedge> halfedge = pImpl->halfedge_;736pImpl->SetNormals(0, minSharpAngle);737pImpl->CreateTangents(0);738// Reset the properties to the original values, removing temporary normals739pImpl->numProp_ = numProp;740pImpl->properties_.swap(properties);741pImpl->halfedge_.swap(halfedge);742} else {743pImpl->CreateTangents(pImpl->SharpenEdges(minSharpAngle, minSmoothness));744}745}746return Manifold(std::make_shared<CsgLeafNode>(pImpl));747}748749/**750* Increase the density of the mesh by splitting every edge into n pieces. For751* instance, with n = 2, each triangle will be split into 4 triangles. Quads752* will ignore their interior triangle bisector. These will all be coplanar (and753* will not be immediately collapsed) unless the Mesh/Manifold has754* halfedgeTangents specified (e.g. from the Smooth() constructor), in which755* case the new vertices will be moved to the interpolated surface according to756* their barycentric coordinates.757*758* @param n The number of pieces to split every edge into. Must be > 1.759*/760Manifold Manifold::Refine(int n) const {761auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());762if (n > 1) {763pImpl->Refine([n](vec3, vec4, vec4) { return n - 1; });764}765return Manifold(std::make_shared<CsgLeafNode>(pImpl));766}767768/**769* Increase the density of the mesh by splitting each edge into pieces of770* roughly the input length. Interior verts are added to keep the rest of the771* triangulation edges also of roughly the same length. If halfedgeTangents are772* present (e.g. from the Smooth() constructor), the new vertices will be moved773* to the interpolated surface according to their barycentric coordinates. Quads774* will ignore their interior triangle bisector.775*776* @param length The length that edges will be broken down to.777*/778Manifold Manifold::RefineToLength(double length) const {779length = std::abs(length);780auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());781pImpl->Refine([length](vec3 edge, vec4, vec4) {782return static_cast<int>(la::length(edge) / length);783});784return Manifold(std::make_shared<CsgLeafNode>(pImpl));785}786787/**788* Increase the density of the mesh by splitting each edge into pieces such that789* any point on the resulting triangles is roughly within tolerance of the790* smoothly curved surface defined by the tangent vectors. This means tightly791* curving regions will be divided more finely than smoother regions. If792* halfedgeTangents are not present, the result will simply be a copy of the793* original. Quads will ignore their interior triangle bisector.794*795* @param tolerance The desired maximum distance between the faceted mesh796* produced and the exact smoothly curving surface. All vertices are exactly on797* the surface, within rounding error.798*/799Manifold Manifold::RefineToTolerance(double tolerance) const {800tolerance = std::abs(tolerance);801auto pImpl = std::make_shared<Impl>(*GetCsgLeafNode().GetImpl());802if (!pImpl->halfedgeTangent_.empty()) {803pImpl->Refine(804[tolerance](vec3 edge, vec4 tangentStart, vec4 tangentEnd) {805const vec3 edgeNorm = la::normalize(edge);806// Weight heuristic807const vec3 tStart = vec3(tangentStart);808const vec3 tEnd = vec3(tangentEnd);809// Perpendicular to edge810const vec3 start = tStart - edgeNorm * la::dot(edgeNorm, tStart);811const vec3 end = tEnd - edgeNorm * la::dot(edgeNorm, tEnd);812// Circular arc result plus heuristic term for non-circular curves813const double d = 0.5 * (la::length(start) + la::length(end)) +814la::length(start - end);815return static_cast<int>(std::sqrt(3 * d / (4 * tolerance)));816},817true);818}819return Manifold(std::make_shared<CsgLeafNode>(pImpl));820}821822/**823* The central operation of this library: the Boolean combines two manifolds824* into another by calculating their intersections and removing the unused825* portions.826* [ε-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid)827* inputs will produce ε-valid output. ε-invalid input may fail828* triangulation.829*830* These operations are optimized to produce nearly-instant results if either831* input is empty or their bounding boxes do not overlap.832*833* @param second The other Manifold.834* @param op The type of operation to perform.835*/836Manifold Manifold::Boolean(const Manifold& second, OpType op) const {837return Manifold(pNode_->Boolean(second.pNode_, op));838}839840/**841* Perform the given boolean operation on a list of Manifolds. In case of842* Subtract, all Manifolds in the tail are differenced from the head.843*/844Manifold Manifold::BatchBoolean(const std::vector<Manifold>& manifolds,845OpType op) {846if (manifolds.size() == 0)847return Manifold();848else if (manifolds.size() == 1)849return manifolds[0];850std::vector<std::shared_ptr<CsgNode>> children;851children.reserve(manifolds.size());852for (const auto& m : manifolds) children.push_back(m.pNode_);853return Manifold(std::make_shared<CsgOpNode>(children, op));854}855856/**857* Shorthand for Boolean Union.858*/859Manifold Manifold::operator+(const Manifold& Q) const {860return Boolean(Q, OpType::Add);861}862863/**864* Shorthand for Boolean Union assignment.865*/866Manifold& Manifold::operator+=(const Manifold& Q) {867*this = *this + Q;868return *this;869}870871/**872* Shorthand for Boolean Difference.873*/874Manifold Manifold::operator-(const Manifold& Q) const {875return Boolean(Q, OpType::Subtract);876}877878/**879* Shorthand for Boolean Difference assignment.880*/881Manifold& Manifold::operator-=(const Manifold& Q) {882*this = *this - Q;883return *this;884}885886/**887* Shorthand for Boolean Intersection.888*/889Manifold Manifold::operator^(const Manifold& Q) const {890return Boolean(Q, OpType::Intersect);891}892893/**894* Shorthand for Boolean Intersection assignment.895*/896Manifold& Manifold::operator^=(const Manifold& Q) {897*this = *this ^ Q;898return *this;899}900901/**902* Split cuts this manifold in two using the cutter manifold. The first result903* is the intersection, second is the difference. This is more efficient than904* doing them separately.905*906* @param cutter907*/908std::pair<Manifold, Manifold> Manifold::Split(const Manifold& cutter) const {909auto impl1 = GetCsgLeafNode().GetImpl();910auto impl2 = cutter.GetCsgLeafNode().GetImpl();911912Boolean3 boolean(*impl1, *impl2, OpType::Subtract);913auto result1 = std::make_shared<CsgLeafNode>(914std::make_unique<Impl>(boolean.Result(OpType::Intersect)));915auto result2 = std::make_shared<CsgLeafNode>(916std::make_unique<Impl>(boolean.Result(OpType::Subtract)));917return std::make_pair(Manifold(result1), Manifold(result2));918}919920/**921* Convenient version of Split() for a half-space.922*923* @param normal This vector is normal to the cutting plane and its length does924* not matter. The first result is in the direction of this vector, the second925* result is on the opposite side.926* @param originOffset The distance of the plane from the origin in the927* direction of the normal vector.928*/929std::pair<Manifold, Manifold> Manifold::SplitByPlane(930vec3 normal, double originOffset) const {931return Split(Halfspace(BoundingBox(), normal, originOffset));932}933934/**935* Identical to SplitByPlane(), but calculating and returning only the first936* result.937*938* @param normal This vector is normal to the cutting plane and its length does939* not matter. The result is in the direction of this vector from the plane.940* @param originOffset The distance of the plane from the origin in the941* direction of the normal vector.942*/943Manifold Manifold::TrimByPlane(vec3 normal, double originOffset) const {944return *this ^ Halfspace(BoundingBox(), normal, originOffset);945}946947/**948* Returns the cross section of this object parallel to the X-Y plane at the949* specified Z height, defaulting to zero. Using a height equal to the bottom of950* the bounding box will return the bottom faces, while using a height equal to951* the top of the bounding box will return empty.952*/953Polygons Manifold::Slice(double height) const {954return GetCsgLeafNode().GetImpl()->Slice(height);955}956957/**958* Returns polygons representing the projected outline of this object959* onto the X-Y plane. These polygons will often self-intersect, so it is960* recommended to run them through the positive fill rule of CrossSection to get961* a sensible result before using them.962*/963Polygons Manifold::Project() const {964return GetCsgLeafNode().GetImpl()->Project();965}966967ExecutionParams& ManifoldParams() { return manifoldParams; }968969/**970* Compute the convex hull of a set of points. If the given points are fewer971* than 4, or they are all coplanar, an empty Manifold will be returned.972*973* @param pts A vector of 3-dimensional points over which to compute a convex974* hull.975*/976Manifold Manifold::Hull(const std::vector<vec3>& pts) {977std::shared_ptr<Impl> impl = std::make_shared<Impl>();978impl->Hull(Vec<vec3>(pts));979return Manifold(std::make_shared<CsgLeafNode>(impl));980}981982/**983* Compute the convex hull of this manifold.984*/985Manifold Manifold::Hull() const {986std::shared_ptr<Impl> impl = std::make_shared<Impl>();987impl->Hull(GetCsgLeafNode().GetImpl()->vertPos_);988return Manifold(std::make_shared<CsgLeafNode>(impl));989}990991/**992* Compute the convex hull enveloping a set of manifolds.993*994* @param manifolds A vector of manifolds over which to compute a convex hull.995*/996Manifold Manifold::Hull(const std::vector<Manifold>& manifolds) {997return Compose(manifolds).Hull();998}9991000/**1001* Returns the minimum gap between two manifolds. Returns a double between1002* 0 and searchLength.1003*1004* @param other The other manifold to compute the minimum gap to.1005* @param searchLength The maximum distance to search for a minimum gap.1006*/1007double Manifold::MinGap(const Manifold& other, double searchLength) const {1008auto intersect = *this ^ other;1009if (!intersect.IsEmpty()) return 0.0;10101011return GetCsgLeafNode().GetImpl()->MinGap(*other.GetCsgLeafNode().GetImpl(),1012searchLength);1013}1014} // namespace manifold101510161017