feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -0,0 +1,650 @@
// Copyright 2021 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <limits>
#include <vector>
#ifdef MANIFOLD_DEBUG
#include <chrono>
#endif
#include "manifold/linalg.h"
namespace manifold {
/** @addtogroup Math
* @ingroup Core
* @brief Simple math operations.
* */
/** @addtogroup LinAlg
* @{
*/
namespace la = linalg;
using vec2 = la::vec<double, 2>;
using vec3 = la::vec<double, 3>;
using vec4 = la::vec<double, 4>;
using bvec4 = la::vec<bool, 4>;
using mat2 = la::mat<double, 2, 2>;
using mat3x2 = la::mat<double, 3, 2>;
using mat4x2 = la::mat<double, 4, 2>;
using mat2x3 = la::mat<double, 2, 3>;
using mat3 = la::mat<double, 3, 3>;
using mat4x3 = la::mat<double, 4, 3>;
using mat3x4 = la::mat<double, 3, 4>;
using mat4 = la::mat<double, 4, 4>;
using ivec2 = la::vec<int, 2>;
using ivec3 = la::vec<int, 3>;
using ivec4 = la::vec<int, 4>;
using quat = la::vec<double, 4>;
/** @} */
/** @addtogroup Scalar
* @ingroup Math
* @brief Simple scalar operations.
* @{
*/
constexpr double kPi = 3.14159265358979323846264338327950288;
constexpr double kTwoPi = 6.28318530717958647692528676655900576;
constexpr double kHalfPi = 1.57079632679489661923132169163975144;
/**
* Convert degrees to radians.
*
* @param a Angle in degrees.
*/
constexpr double radians(double a) { return a * kPi / 180; }
/**
* Convert radians to degrees.
*
* @param a Angle in radians.
*/
constexpr double degrees(double a) { return a * 180 / kPi; }
/**
* Performs smooth Hermite interpolation between 0 and 1 when edge0 < x < edge1.
*
* @param edge0 Specifies the value of the lower edge of the Hermite function.
* @param edge1 Specifies the value of the upper edge of the Hermite function.
* @param a Specifies the source value for interpolation.
*/
constexpr double smoothstep(double edge0, double edge1, double a) {
const double x = la::clamp((a - edge0) / (edge1 - edge0), 0, 1);
return x * x * (3 - 2 * x);
}
/**
* Sine function where multiples of 90 degrees come out exact.
*
* @param x Angle in degrees.
*/
inline double sind(double x) {
if (!la::isfinite(x)) return sin(x);
if (x < 0.0) return -sind(-x);
int quo;
x = remquo(fabs(x), 90.0, &quo);
switch (quo % 4) {
case 0:
return sin(radians(x));
case 1:
return cos(radians(x));
case 2:
return -sin(radians(x));
case 3:
return -cos(radians(x));
}
return 0.0;
}
/**
* Cosine function where multiples of 90 degrees come out exact.
*
* @param x Angle in degrees.
*/
inline double cosd(double x) { return sind(x + 90.0); }
/** @} */
/** @addtogroup Structs
* @ingroup Core
* @brief Miscellaneous data structures for interfacing with this library.
* @{
*/
/**
* @brief Single polygon contour, wound CCW. First and last point are implicitly
* connected. Should ensure all input is
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).
*/
using SimplePolygon = std::vector<vec2>;
/**
* @brief Set of polygons with holes. Order of contours is arbitrary. Can
* contain any depth of nested holes and any number of separate polygons. Should
* ensure all input is
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).
*/
using Polygons = std::vector<SimplePolygon>;
/**
* @brief Defines which edges to sharpen and how much for the Manifold.Smooth()
* constructor.
*/
struct Smoothness {
/// The halfedge index = 3 * tri + i, referring to Mesh.triVerts[tri][i].
size_t halfedge;
/// A value between 0 and 1, where 0 is sharp and 1 is the default and the
/// curvature is interpolated between these values. The two paired halfedges
/// can have different values while maintaining C-1 continuity (except for 0).
double smoothness;
};
/**
* @brief Axis-aligned 3D box, primarily for bounding.
*/
struct Box {
vec3 min = vec3(std::numeric_limits<double>::infinity());
vec3 max = vec3(-std::numeric_limits<double>::infinity());
/**
* Default constructor is an infinite box that contains all space.
*/
constexpr Box() {}
/**
* Creates a box that contains the two given points.
*/
constexpr Box(const vec3 p1, const vec3 p2) {
min = la::min(p1, p2);
max = la::max(p1, p2);
}
/**
* Returns the dimensions of the Box.
*/
constexpr vec3 Size() const { return max - min; }
/**
* Returns the center point of the Box.
*/
constexpr vec3 Center() const { return 0.5 * (max + min); }
/**
* Returns the absolute-largest coordinate value of any contained
* point.
*/
constexpr double Scale() const {
vec3 absMax = la::max(la::abs(min), la::abs(max));
return la::max(absMax.x, la::max(absMax.y, absMax.z));
}
/**
* Does this box contain (includes equal) the given point?
*/
constexpr bool Contains(const vec3& p) const {
return la::all(la::gequal(p, min)) && la::all(la::gequal(max, p));
}
/**
* Does this box contain (includes equal) the given box?
*/
constexpr bool Contains(const Box& box) const {
return la::all(la::gequal(box.min, min)) &&
la::all(la::gequal(max, box.max));
}
/**
* Expand this box to include the given point.
*/
void Union(const vec3 p) {
min = la::min(min, p);
max = la::max(max, p);
}
/**
* Expand this box to include the given box.
*/
constexpr Box Union(const Box& box) const {
Box out;
out.min = la::min(min, box.min);
out.max = la::max(max, box.max);
return out;
}
/**
* Transform the given box by the given axis-aligned affine transform.
*
* Ensure the transform passed in is axis-aligned (rotations are all
* multiples of 90 degrees), or else the resulting bounding box will no longer
* bound properly.
*/
constexpr Box Transform(const mat3x4& transform) const {
Box out;
vec3 minT = transform * vec4(min, 1.0);
vec3 maxT = transform * vec4(max, 1.0);
out.min = la::min(minT, maxT);
out.max = la::max(minT, maxT);
return out;
}
/**
* Shift this box by the given vector.
*/
constexpr Box operator+(vec3 shift) const {
Box out;
out.min = min + shift;
out.max = max + shift;
return out;
}
/**
* Shift this box in-place by the given vector.
*/
Box& operator+=(vec3 shift) {
min += shift;
max += shift;
return *this;
}
/**
* Scale this box by the given vector.
*/
constexpr Box operator*(vec3 scale) const {
Box out;
out.min = min * scale;
out.max = max * scale;
return out;
}
/**
* Scale this box in-place by the given vector.
*/
Box& operator*=(vec3 scale) {
min *= scale;
max *= scale;
return *this;
}
/**
* Does this box overlap the one given (including equality)?
*/
constexpr bool DoesOverlap(const Box& box) const {
return min.x <= box.max.x && min.y <= box.max.y && min.z <= box.max.z &&
max.x >= box.min.x && max.y >= box.min.y && max.z >= box.min.z;
}
/**
* Does the given point project within the XY extent of this box
* (including equality)?
*/
constexpr bool DoesOverlap(vec3 p) const { // projected in z
return p.x <= max.x && p.x >= min.x && p.y <= max.y && p.y >= min.y;
}
/**
* Does this box have finite bounds?
*/
constexpr bool IsFinite() const {
return la::all(la::isfinite(min)) && la::all(la::isfinite(max));
}
};
/**
* @brief Axis-aligned 2D box, primarily for bounding.
*/
struct Rect {
vec2 min = vec2(std::numeric_limits<double>::infinity());
vec2 max = vec2(-std::numeric_limits<double>::infinity());
/**
* Default constructor is an empty rectangle..
*/
constexpr Rect() {}
/**
* Create a rectangle that contains the two given points.
*/
constexpr Rect(const vec2 a, const vec2 b) {
min = la::min(a, b);
max = la::max(a, b);
}
/** @name Information
* Details of the rectangle
*/
///@{
/**
* Return the dimensions of the rectangle.
*/
constexpr vec2 Size() const { return max - min; }
/**
* Return the area of the rectangle.
*/
constexpr double Area() const {
auto sz = Size();
return sz.x * sz.y;
}
/**
* Returns the absolute-largest coordinate value of any contained
* point.
*/
constexpr double Scale() const {
vec2 absMax = la::max(la::abs(min), la::abs(max));
return la::max(absMax.x, absMax.y);
}
/**
* Returns the center point of the rectangle.
*/
constexpr vec2 Center() const { return 0.5 * (max + min); }
/**
* Does this rectangle contain (includes on border) the given point?
*/
constexpr bool Contains(const vec2& p) const {
return la::all(la::gequal(p, min)) && la::all(la::gequal(max, p));
}
/**
* Does this rectangle contain (includes equal) the given rectangle?
*/
constexpr bool Contains(const Rect& rect) const {
return la::all(la::gequal(rect.min, min)) &&
la::all(la::gequal(max, rect.max));
}
/**
* Does this rectangle overlap the one given (including equality)?
*/
constexpr bool DoesOverlap(const Rect& rect) const {
return min.x <= rect.max.x && min.y <= rect.max.y && max.x >= rect.min.x &&
max.y >= rect.min.y;
}
/**
* Is the rectangle empty (containing no space)?
*/
constexpr bool IsEmpty() const { return max.y <= min.y || max.x <= min.x; };
/**
* Does this recangle have finite bounds?
*/
constexpr bool IsFinite() const {
return la::all(la::isfinite(min)) && la::all(la::isfinite(max));
}
///@}
/** @name Modification
*/
///@{
/**
* Expand this rectangle (in place) to include the given point.
*/
void Union(const vec2 p) {
min = la::min(min, p);
max = la::max(max, p);
}
/**
* Expand this rectangle to include the given Rect.
*/
constexpr Rect Union(const Rect& rect) const {
Rect out;
out.min = la::min(min, rect.min);
out.max = la::max(max, rect.max);
return out;
}
/**
* Shift this rectangle by the given vector.
*/
constexpr Rect operator+(const vec2 shift) const {
Rect out;
out.min = min + shift;
out.max = max + shift;
return out;
}
/**
* Shift this rectangle in-place by the given vector.
*/
Rect& operator+=(const vec2 shift) {
min += shift;
max += shift;
return *this;
}
/**
* Scale this rectangle by the given vector.
*/
constexpr Rect operator*(const vec2 scale) const {
Rect out;
out.min = min * scale;
out.max = max * scale;
return out;
}
/**
* Scale this rectangle in-place by the given vector.
*/
Rect& operator*=(const vec2 scale) {
min *= scale;
max *= scale;
return *this;
}
/**
* Transform the rectangle by the given axis-aligned affine transform.
*
* Ensure the transform passed in is axis-aligned (rotations are all
* multiples of 90 degrees), or else the resulting rectangle will no longer
* bound properly.
*/
constexpr Rect Transform(const mat2x3& m) const {
Rect rect;
rect.min = m * vec3(min, 1);
rect.max = m * vec3(max, 1);
return rect;
}
///@}
};
/**
* @brief Boolean operation type: Add (Union), Subtract (Difference), and
* Intersect.
*/
enum class OpType { Add, Subtract, Intersect };
constexpr int DEFAULT_SEGMENTS = 0;
constexpr double DEFAULT_ANGLE = 10.0;
constexpr double DEFAULT_LENGTH = 1.0;
/**
* @brief These static properties control how circular shapes are quantized by
* default on construction.
*
* If circularSegments is specified, it takes
* precedence. If it is zero, then instead the minimum is used of the segments
* calculated based on edge length and angle, rounded up to the nearest
* multiple of four. To get numbers not divisible by four, circularSegments
* must be specified.
*/
class Quality {
private:
inline static int circularSegments_ = DEFAULT_SEGMENTS;
inline static double circularAngle_ = DEFAULT_ANGLE;
inline static double circularEdgeLength_ = DEFAULT_LENGTH;
public:
/**
* Sets an angle constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param angle The minimum angle in degrees between consecutive segments. The
* angle will increase if the the segments hit the minimum edge length.
* Default is 10 degrees.
*/
static void SetMinCircularAngle(double angle) {
if (angle <= 0) return;
circularAngle_ = angle;
}
/**
* Sets a length constraint the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. The number of segments will be rounded up
* to the nearest factor of four.
*
* @param length The minimum length of segments. The length will
* increase if the the segments hit the minimum angle. Default is 1.0.
*/
static void SetMinCircularEdgeLength(double length) {
if (length <= 0) return;
circularEdgeLength_ = length;
}
/**
* Sets the default number of circular segments for the
* CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and
* Manifold::Revolve() constructors. Overrides the edge length and angle
* constraints and sets the number of segments to exactly this value.
*
* @param number Number of circular segments. Default is 0, meaning no
* constraint is applied.
*/
static void SetCircularSegments(int number) {
if (number < 3 && number != 0) return;
circularSegments_ = number;
}
/**
* Determine the result of the SetMinCircularAngle(),
* SetMinCircularEdgeLength(), and SetCircularSegments() defaults.
*
* @param radius For a given radius of circle, determine how many default
* segments there will be.
*/
static int GetCircularSegments(double radius) {
if (circularSegments_ > 0) return circularSegments_;
int nSegA = 360.0 / circularAngle_;
int nSegL = 2.0 * radius * kPi / circularEdgeLength_;
int nSeg = fmin(nSegA, nSegL) + 3;
nSeg -= nSeg % 4;
return std::max(nSeg, 3);
}
/**
* Resets the circular construction parameters to their defaults if
* SetMinCircularAngle, SetMinCircularEdgeLength, or SetCircularSegments have
* been called.
*/
static void ResetToDefaults() {
circularSegments_ = DEFAULT_SEGMENTS;
circularAngle_ = DEFAULT_ANGLE;
circularEdgeLength_ = DEFAULT_LENGTH;
}
};
/** @} */
/** @addtogroup Debug
* @ingroup Optional
* @{
*/
/**
* @brief Global parameters that control debugging output. Only has an
* effect when compiled with the MANIFOLD_DEBUG flag.
*/
struct ExecutionParams {
/// Perform extra sanity checks and assertions on the intermediate data
/// structures.
bool intermediateChecks = false;
/// Verbose output primarily of the Boolean, including timing info and vector
/// sizes.
bool verbose = false;
/// If processOverlaps is false, a geometric check will be performed to assert
/// all triangles are CCW.
bool processOverlaps = true;
/// Suppresses printed errors regarding CW triangles. Has no effect if
/// processOverlaps is true.
bool suppressErrors = false;
/// Perform optional but recommended triangle cleanups in SimplifyTopology()
bool cleanupTriangles = true;
};
/** @} */
#ifdef MANIFOLD_DEBUG
inline std::ostream& operator<<(std::ostream& stream, const Box& box) {
return stream << "min: " << box.min << ", "
<< "max: " << box.max;
}
inline std::ostream& operator<<(std::ostream& stream, const Rect& box) {
return stream << "min: " << box.min << ", "
<< "max: " << box.max;
}
/**
* Print the contents of this vector to standard output. Only exists if compiled
* with MANIFOLD_DEBUG flag.
*/
template <typename T>
void Dump(const std::vector<T>& vec) {
std::cout << "Vec = " << std::endl;
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << i << ", " << vec[i] << ", " << std::endl;
}
std::cout << std::endl;
}
template <typename T>
void Diff(const std::vector<T>& a, const std::vector<T>& b) {
std::cout << "Diff = " << std::endl;
if (a.size() != b.size()) {
std::cout << "a and b must have the same length, aborting Diff"
<< std::endl;
return;
}
for (size_t i = 0; i < a.size(); ++i) {
if (a[i] != b[i])
std::cout << i << ": " << a[i] << ", " << b[i] << std::endl;
}
std::cout << std::endl;
}
struct Timer {
std::chrono::high_resolution_clock::time_point start, end;
void Start() { start = std::chrono::high_resolution_clock::now(); }
void Stop() { end = std::chrono::high_resolution_clock::now(); }
float Elapsed() {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
.count();
}
void Print(std::string message) {
std::cout << "----------- " << std::round(Elapsed()) << " ms for "
<< message << std::endl;
}
};
#endif
} // namespace manifold

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,443 @@
// Copyright 2021 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <functional>
#include <memory>
#ifdef MANIFOLD_EXPORT
#include <iostream>
#endif
#include "manifold/common.h"
#include "manifold/vec_view.h"
namespace manifold {
/**
* @ingroup Debug
*
* Allows modification of the assertions checked in MANIFOLD_DEBUG mode.
*
* @return ExecutionParams&
*/
ExecutionParams& ManifoldParams();
class CsgNode;
class CsgLeafNode;
/** @addtogroup Core
* @brief The central classes of the library
* @{
*/
/**
* @brief Mesh input/output suitable for pushing directly into graphics
* libraries.
*
* This may not be manifold since the verts are duplicated along property
* boundaries that do not match. The additional merge vectors store this missing
* information, allowing the manifold to be reconstructed. MeshGL is an alias
* for the standard single-precision version. Use MeshGL64 to output the full
* double precision that Manifold uses internally.
*/
template <typename Precision, typename I = uint32_t>
struct MeshGLP {
/// Number of property vertices
I NumVert() const { return vertProperties.size() / numProp; };
/// Number of triangles
I NumTri() const { return triVerts.size() / 3; };
/// Number of properties per vertex, always >= 3.
I numProp = 3;
/// Flat, GL-style interleaved list of all vertex properties: propVal =
/// vertProperties[vert * numProp + propIdx]. The first three properties are
/// always the position x, y, z.
std::vector<Precision> vertProperties;
/// The vertex indices of the three triangle corners in CCW (from the outside)
/// order, for each triangle.
std::vector<I> triVerts;
/// Optional: A list of only the vertex indicies that need to be merged to
/// reconstruct the manifold.
std::vector<I> mergeFromVert;
/// Optional: The same length as mergeFromVert, and the corresponding value
/// contains the vertex to merge with. It will have an identical position, but
/// the other properties may differ.
std::vector<I> mergeToVert;
/// Optional: Indicates runs of triangles that correspond to a particular
/// input mesh instance. The runs encompass all of triVerts and are sorted
/// by runOriginalID. Run i begins at triVerts[runIndex[i]] and ends at
/// triVerts[runIndex[i+1]]. All runIndex values are divisible by 3. Returned
/// runIndex will always be 1 longer than runOriginalID, but same length is
/// also allowed as input: triVerts.size() will be automatically appended in
/// this case.
std::vector<I> runIndex;
/// Optional: The OriginalID of the mesh this triangle run came from. This ID
/// is ideal for reapplying materials to the output mesh. Multiple runs may
/// have the same ID, e.g. representing different copies of the same input
/// mesh. If you create an input MeshGL that you want to be able to reference
/// as one or more originals, be sure to set unique values from ReserveIDs().
std::vector<uint32_t> runOriginalID;
/// Optional: For each run, a 3x4 transform is stored representing how the
/// corresponding original mesh was transformed to create this triangle run.
/// This matrix is stored in column-major order and the length of the overall
/// vector is 12 * runOriginalID.size().
std::vector<Precision> runTransform;
/// Optional: Length NumTri, contains the source face ID this
/// triangle comes from. When auto-generated, this ID will be a triangle index
/// into the original mesh. This index/ID is purely for external use (e.g.
/// recreating polygonal faces) and will not affect Manifold's algorithms.
std::vector<I> faceID;
/// Optional: The X-Y-Z-W weighted tangent vectors for smooth Refine(). If
/// non-empty, must be exactly four times as long as Mesh.triVerts. Indexed
/// as 4 * (3 * tri + i) + j, i < 3, j < 4, representing the tangent value
/// Mesh.triVerts[tri][i] along the CCW edge. If empty, mesh is faceted.
std::vector<Precision> halfedgeTangent;
/// Tolerance for mesh simplification. When creating a Manifold, the tolerance
/// used will be the maximum of this and a baseline tolerance from the size of
/// the bounding box. Any edge shorter than tolerance may be collapsed.
/// Tolerance may be enlarged when floating point error accumulates.
Precision tolerance = 0;
MeshGLP() = default;
/**
* Updates the mergeFromVert and mergeToVert vectors in order to create a
* manifold solid. If the MeshGL is already manifold, no change will occur and
* the function will return false. Otherwise, this will merge verts along open
* edges within tolerance (the maximum of the MeshGL tolerance and the
* baseline bounding-box tolerance), keeping any from the existing merge
* vectors, and return true.
*
* There is no guarantee the result will be manifold - this is a best-effort
* helper function designed primarily to aid in the case where a manifold
* multi-material MeshGL was produced, but its merge vectors were lost due to
* a round-trip through a file format. Constructing a Manifold from the result
* will report an error status if it is not manifold.
*/
bool Merge();
/**
* Returns the x, y, z position of the ith vertex.
*
* @param v vertex index.
*/
la::vec<Precision, 3> GetVertPos(size_t v) const {
size_t offset = v * numProp;
return la::vec<Precision, 3>(vertProperties[offset],
vertProperties[offset + 1],
vertProperties[offset + 2]);
}
/**
* Returns the three vertex indices of the ith triangle.
*
* @param t triangle index.
*/
la::vec<I, 3> GetTriVerts(size_t t) const {
size_t offset = 3 * t;
return la::vec<I, 3>(triVerts[offset], triVerts[offset + 1],
triVerts[offset + 2]);
}
/**
* Returns the x, y, z, w tangent of the ith halfedge.
*
* @param h halfedge index (3 * triangle_index + [0|1|2]).
*/
la::vec<Precision, 4> GetTangent(size_t h) const {
size_t offset = 4 * h;
return la::vec<Precision, 4>(
halfedgeTangent[offset], halfedgeTangent[offset + 1],
halfedgeTangent[offset + 2], halfedgeTangent[offset + 3]);
}
};
/**
* @brief Single-precision - ideal for most uses, especially graphics.
*/
using MeshGL = MeshGLP<float>;
/**
* @brief Double-precision, 64-bit indices - best for huge meshes.
*/
using MeshGL64 = MeshGLP<double, uint64_t>;
/**
* @brief This library's internal representation of an oriented, 2-manifold,
* triangle mesh - a simple boundary-representation of a solid object. Use this
* class to store and operate on solids, and use MeshGL for input and output.
*
* In addition to storing geometric data, a Manifold can also store an arbitrary
* number of vertex properties. These could be anything, e.g. normals, UV
* coordinates, colors, etc, but this library is completely agnostic. All
* properties are merely float values indexed by channel number. It is up to the
* user to associate channel numbers with meaning.
*
* Manifold allows vertex properties to be shared for efficient storage, or to
* have multiple property verts associated with a single geometric vertex,
* allowing sudden property changes, e.g. at Boolean intersections, without
* sacrificing manifoldness.
*
* Manifolds also keep track of their relationships to their inputs, via
* OriginalIDs and the faceIDs and transforms accessible through MeshGL. This
* allows object-level properties to be re-associated with the output after many
* operations, particularly useful for materials. Since separate object's
* properties are not mixed, there is no requirement that channels have
* consistent meaning between different inputs.
*/
class Manifold {
public:
/** @name Basics
* Copy / move / assignment
*/
///@{
Manifold();
~Manifold();
Manifold(const Manifold& other);
Manifold& operator=(const Manifold& other);
Manifold(Manifold&&) noexcept;
Manifold& operator=(Manifold&&) noexcept;
///@}
/** @name Input & Output
* Create and retrieve arbitrary manifolds
*/
///@{
Manifold(const MeshGL&);
Manifold(const MeshGL64&);
MeshGL GetMeshGL(int normalIdx = -1) const;
MeshGL64 GetMeshGL64(int normalIdx = -1) const;
///@}
/** @name Constructors
* Topological ops, primitives, and SDF
*/
///@{
std::vector<Manifold> Decompose() const;
static Manifold Compose(const std::vector<Manifold>&);
static Manifold Tetrahedron();
static Manifold Cube(vec3 size = vec3(1.0), bool center = false);
static Manifold Cylinder(double height, double radiusLow,
double radiusHigh = -1.0, int circularSegments = 0,
bool center = false);
static Manifold Sphere(double radius, int circularSegments = 0);
static Manifold LevelSet(std::function<double(vec3)> sdf, Box bounds,
double edgeLength, double level = 0,
double tolerance = -1, bool canParallel = true);
///@}
/** @name Polygons
* 3D to 2D and 2D to 3D
*/
///@{
Polygons Slice(double height = 0) const;
Polygons Project() const;
static Manifold Extrude(const Polygons& crossSection, double height,
int nDivisions = 0, double twistDegrees = 0.0,
vec2 scaleTop = vec2(1.0));
static Manifold Revolve(const Polygons& crossSection,
int circularSegments = 0,
double revolveDegrees = 360.0f);
///@}
enum class Error {
NoError,
NonFiniteVertex,
NotManifold,
VertexOutOfBounds,
PropertiesWrongLength,
MissingPositionProperties,
MergeVectorsDifferentLengths,
MergeIndexOutOfBounds,
TransformWrongLength,
RunIndexWrongLength,
FaceIDWrongLength,
InvalidConstruction,
};
/** @name Information
* Details of the manifold
*/
///@{
Error Status() const;
bool IsEmpty() const;
size_t NumVert() const;
size_t NumEdge() const;
size_t NumTri() const;
size_t NumProp() const;
size_t NumPropVert() const;
Box BoundingBox() const;
int Genus() const;
double GetTolerance() const;
///@}
/** @name Measurement
*/
///@{
double SurfaceArea() const;
double Volume() const;
double MinGap(const Manifold& other, double searchLength) const;
///@}
/** @name Mesh ID
* Details of the manifold's relation to its input meshes, for the purposes
* of reapplying mesh properties.
*/
///@{
int OriginalID() const;
Manifold AsOriginal() const;
static uint32_t ReserveIDs(uint32_t);
///@}
/** @name Transformations
*/
///@{
Manifold Translate(vec3) const;
Manifold Scale(vec3) const;
Manifold Rotate(double xDegrees, double yDegrees = 0.0,
double zDegrees = 0.0) const;
Manifold Mirror(vec3) const;
Manifold Transform(const mat3x4&) const;
Manifold Warp(std::function<void(vec3&)>) const;
Manifold WarpBatch(std::function<void(VecView<vec3>)>) const;
Manifold SetTolerance(double) const;
///@}
/** @name Boolean
* Combine two manifolds
*/
///@{
Manifold Boolean(const Manifold& second, OpType op) const;
static Manifold BatchBoolean(const std::vector<Manifold>& manifolds,
OpType op);
// Boolean operation shorthand
Manifold operator+(const Manifold&) const; // Add (Union)
Manifold& operator+=(const Manifold&);
Manifold operator-(const Manifold&) const; // Subtract (Difference)
Manifold& operator-=(const Manifold&);
Manifold operator^(const Manifold&) const; // Intersect
Manifold& operator^=(const Manifold&);
std::pair<Manifold, Manifold> Split(const Manifold&) const;
std::pair<Manifold, Manifold> SplitByPlane(vec3 normal,
double originOffset) const;
Manifold TrimByPlane(vec3 normal, double originOffset) const;
///@}
/** @name Properties
* Create and modify vertex properties.
*/
///@{
Manifold SetProperties(
int numProp,
std::function<void(double*, vec3, const double*)> propFunc) const;
Manifold CalculateCurvature(int gaussianIdx, int meanIdx) const;
Manifold CalculateNormals(int normalIdx, double minSharpAngle = 60) const;
///@}
/** @name Smoothing
* Smooth meshes by calculating tangent vectors and refining to a higher
* triangle count.
*/
///@{
Manifold Refine(int) const;
Manifold RefineToLength(double) const;
Manifold RefineToTolerance(double) const;
Manifold SmoothByNormals(int normalIdx) const;
Manifold SmoothOut(double minSharpAngle = 60, double minSmoothness = 0) const;
static Manifold Smooth(const MeshGL&,
const std::vector<Smoothness>& sharpenedEdges = {});
static Manifold Smooth(const MeshGL64&,
const std::vector<Smoothness>& sharpenedEdges = {});
///@}
/** @name Convex Hull
*/
///@{
Manifold Hull() const;
static Manifold Hull(const std::vector<Manifold>& manifolds);
static Manifold Hull(const std::vector<vec3>& pts);
///@}
/** @name Testing Hooks
* These are just for internal testing.
*/
///@{
bool MatchesTriNormals() const;
size_t NumDegenerateTris() const;
size_t NumOverlaps(const Manifold& second) const;
double GetEpsilon() const;
///@}
struct Impl;
#ifdef MANIFOLD_EXPORT
static Manifold ImportMeshGL64(std::istream& stream);
#endif
private:
Manifold(std::shared_ptr<CsgNode> pNode_);
Manifold(std::shared_ptr<Impl> pImpl_);
static Manifold Invalid();
mutable std::shared_ptr<CsgNode> pNode_;
CsgLeafNode& GetCsgLeafNode() const;
};
/** @} */
/** @addtogroup Debug
* @ingroup Optional
* @brief Debugging features
*
* The features require compiler flags to be enabled. Assertions are enabled
* with the MANIFOLD_DEBUG flag and then controlled with ExecutionParams.
* @{
*/
#ifdef MANIFOLD_DEBUG
inline std::string ToString(const Manifold::Error& error) {
switch (error) {
case Manifold::Error::NoError:
return "No Error";
case Manifold::Error::NonFiniteVertex:
return "Non Finite Vertex";
case Manifold::Error::NotManifold:
return "Not Manifold";
case Manifold::Error::VertexOutOfBounds:
return "Vertex Out Of Bounds";
case Manifold::Error::PropertiesWrongLength:
return "Properties Wrong Length";
case Manifold::Error::MissingPositionProperties:
return "Missing Position Properties";
case Manifold::Error::MergeVectorsDifferentLengths:
return "Merge Vectors Different Lengths";
case Manifold::Error::MergeIndexOutOfBounds:
return "Merge Index Out Of Bounds";
case Manifold::Error::TransformWrongLength:
return "Transform Wrong Length";
case Manifold::Error::RunIndexWrongLength:
return "Run Index Wrong Length";
case Manifold::Error::FaceIDWrongLength:
return "Face ID Wrong Length";
case Manifold::Error::InvalidConstruction:
return "Invalid Construction";
default:
return "Unknown Error";
};
}
inline std::ostream& operator<<(std::ostream& stream,
const Manifold::Error& error) {
return stream << ToString(error);
}
#endif
/** @} */
} // namespace manifold

View file

@ -0,0 +1,66 @@
// Copyright 2022 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef MANIFOLD_DEBUG
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
/** @addtogroup Debug
* @{
*/
struct userErr : public virtual std::runtime_error {
using std::runtime_error::runtime_error;
};
struct topologyErr : public virtual std::runtime_error {
using std::runtime_error::runtime_error;
};
struct geometryErr : public virtual std::runtime_error {
using std::runtime_error::runtime_error;
};
using logicErr = std::logic_error;
template <typename Ex>
void AssertFail(const char* file, int line, const char* cond, const char* msg) {
std::ostringstream output;
output << "Error in file: " << file << " (" << line << "): \'" << cond
<< "\' is false: " << msg;
throw Ex(output.str());
}
template <typename Ex>
void AssertFail(const char* file, int line, const std::string& cond,
const std::string& msg) {
std::ostringstream output;
output << "Error in file: " << file << " (" << line << "): \'" << cond
<< "\' is false: " << msg;
throw Ex(output.str());
}
// DEBUG_ASSERT is slightly slower due to the function call, but gives more
// detailed info.
#define DEBUG_ASSERT(condition, EX, msg) \
if (!(condition)) AssertFail<EX>(__FILE__, __LINE__, #condition, msg);
// ASSERT has almost no overhead, so better to use for frequent calls like
// vector bounds checking.
#define ASSERT(condition, EX) \
if (!(condition)) throw(EX);
#else
#define DEBUG_ASSERT(condition, EX, msg)
#define ASSERT(condition, EX)
#endif
/** @} */

View file

@ -0,0 +1,62 @@
// Copyright 2021 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "manifold/common.h"
namespace manifold {
/** @addtogroup Structs
* @{
*/
/**
* @brief Polygon vertex.
*/
struct PolyVert {
/// X-Y position
vec2 pos;
/// ID or index into another vertex vector
int idx;
};
/**
* @brief Single polygon contour, wound CCW, with indices. First and last point
* are implicitly connected. Should ensure all input is
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).
*/
using SimplePolygonIdx = std::vector<PolyVert>;
/**
* @brief Set of indexed polygons with holes. Order of contours is arbitrary.
* Can contain any depth of nested holes and any number of separate polygons.
* Should ensure all input is
* [&epsilon;-valid](https://github.com/elalish/manifold/wiki/Manifold-Library#definition-of-%CE%B5-valid).
*/
using PolygonsIdx = std::vector<SimplePolygonIdx>;
/** @} */
/** @addtogroup Triangulation
* @ingroup Core
* @brief Polygon triangulation
* @{
*/
std::vector<ivec3> TriangulateIdx(const PolygonsIdx &polys,
double epsilon = -1);
std::vector<ivec3> Triangulate(const Polygons &polygons, double epsilon = -1);
ExecutionParams &PolygonParams();
/** @} */
} // namespace manifold

View file

@ -0,0 +1,151 @@
// Copyright 2023 The Manifold Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cstddef>
#include <limits>
#include <type_traits>
#include <vector>
#include "manifold/optional_assert.h"
namespace manifold {
/**
* View for Vec, can perform offset operation.
* This will be invalidated when the original vector is dropped or changes
* length. Roughly equivalent to std::span<T> from c++20
*/
template <typename T>
class VecView {
public:
using Iter = T *;
using IterC = const T *;
VecView() : ptr_(nullptr), size_(0) {}
VecView(T *ptr, size_t size) : ptr_(ptr), size_(size) {}
VecView(const std::vector<std::remove_cv_t<T>> &v)
: ptr_(v.data()), size_(v.size()) {}
VecView(const VecView &other) {
ptr_ = other.ptr_;
size_ = other.size_;
}
VecView &operator=(const VecView &other) {
ptr_ = other.ptr_;
size_ = other.size_;
return *this;
}
// allows conversion to a const VecView
operator VecView<const T>() const { return {ptr_, size_}; }
inline const T &operator[](size_t i) const {
ASSERT(i < size_, std::out_of_range("Vec out of range"));
return ptr_[i];
}
inline T &operator[](size_t i) {
ASSERT(i < size_, std::out_of_range("Vec out of range"));
return ptr_[i];
}
IterC cbegin() const { return ptr_; }
IterC cend() const { return ptr_ + size_; }
IterC begin() const { return cbegin(); }
IterC end() const { return cend(); }
Iter begin() { return ptr_; }
Iter end() { return ptr_ + size_; }
const T &front() const {
ASSERT(size_ != 0,
std::out_of_range("Attempt to take the front of an empty vector"));
return ptr_[0];
}
const T &back() const {
ASSERT(size_ != 0,
std::out_of_range("Attempt to take the back of an empty vector"));
return ptr_[size_ - 1];
}
T &front() {
ASSERT(size_ != 0,
std::out_of_range("Attempt to take the front of an empty vector"));
return ptr_[0];
}
T &back() {
ASSERT(size_ != 0,
std::out_of_range("Attempt to take the back of an empty vector"));
return ptr_[size_ - 1];
}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
VecView<T> view(size_t offset = 0,
size_t length = std::numeric_limits<size_t>::max()) {
if (length == std::numeric_limits<size_t>::max())
length = this->size_ - offset;
ASSERT(length >= 0, std::out_of_range("Vec::view out of range"));
ASSERT(offset + length <= this->size_ && offset >= 0,
std::out_of_range("Vec::view out of range"));
return VecView<T>(this->ptr_ + offset, length);
}
VecView<const T> cview(
size_t offset = 0,
size_t length = std::numeric_limits<size_t>::max()) const {
if (length == std::numeric_limits<size_t>::max())
length = this->size_ - offset;
ASSERT(length >= 0, std::out_of_range("Vec::cview out of range"));
ASSERT(offset + length <= this->size_ && offset >= 0,
std::out_of_range("Vec::cview out of range"));
return VecView<const T>(this->ptr_ + offset, length);
}
VecView<const T> view(
size_t offset = 0,
size_t length = std::numeric_limits<size_t>::max()) const {
return cview(offset, length);
}
T *data() { return this->ptr_; }
const T *data() const { return this->ptr_; }
#ifdef MANIFOLD_DEBUG
void Dump() const {
std::cout << "Vec = " << std::endl;
for (size_t i = 0; i < size(); ++i) {
std::cout << i << ", " << ptr_[i] << ", " << std::endl;
}
std::cout << std::endl;
}
#endif
protected:
T *ptr_ = nullptr;
size_t size_ = 0;
};
} // namespace manifold