use crate::boolean3;
use crate::cross_section::CrossSection;
use crate::math;
use crate::impl_mesh::ManifoldImpl;
use crate::linalg::{mat4_to_mat3x4, normalize, scaling_matrix, translation_matrix, Mat3, Mat3x4, Vec3};
use crate::types::{Error, OpType, RayHit};
#[derive(Clone)]
pub struct Manifold {
imp: ManifoldImpl,
}
impl Default for Manifold {
fn default() -> Self {
Self::new()
}
}
impl Manifold {
pub fn new() -> Self {
Self { imp: ManifoldImpl::new() }
}
pub fn empty() -> Self {
Self::new()
}
pub fn make_empty(status: crate::types::Error) -> Self {
let mut imp = ManifoldImpl::new();
imp.make_empty(status);
Self { imp }
}
pub fn from_impl(imp: ManifoldImpl) -> Self {
Self { imp }
}
pub fn as_impl(&self) -> &ManifoldImpl {
&self.imp
}
pub fn num_vert(&self) -> usize { self.imp.num_vert() }
pub fn num_tri(&self) -> usize { self.imp.num_tri() }
pub fn num_edge(&self) -> usize { self.imp.num_edge() }
pub fn num_prop(&self) -> usize { self.imp.num_prop }
pub fn num_prop_vert(&self) -> usize { self.imp.num_prop_vert() }
pub fn is_empty(&self) -> bool { self.imp.is_empty() }
pub fn status(&self) -> Error { self.imp.status }
pub fn volume(&self) -> f64 { self.imp.get_property(crate::properties::Property::Volume).abs() }
pub fn surface_area(&self) -> f64 { self.imp.get_property(crate::properties::Property::SurfaceArea) }
pub fn matches_tri_normals(&self) -> bool { self.imp.matches_tri_normals() }
pub fn num_degenerate_tris(&self) -> i32 { self.imp.num_degenerate_tris() }
pub fn get_tolerance(&self) -> f64 { self.imp.tolerance }
pub fn get_epsilon(&self) -> f64 { self.imp.epsilon }
pub fn genus(&self) -> i32 {
let chi = self.num_vert() as i32 - self.imp.num_edge() as i32 + self.num_tri() as i32;
1 - chi / 2
}
pub fn original_id(&self) -> i32 {
self.imp.mesh_relation.original_id
}
pub fn as_original(&self) -> Self {
if self.is_empty() { return self.clone(); }
let mut out = self.imp.clone();
out.initialize_original();
out.set_normals_and_coplanar();
Self::from_impl(out)
}
pub fn reserve_ids(n: u32) -> u32 {
crate::impl_mesh::reserve_ids(n)
}
pub fn set_tolerance(&self, tolerance: f64) -> Self {
if self.is_empty() { return self.clone(); }
let mut out = self.imp.clone();
if tolerance > out.tolerance {
out.tolerance = tolerance;
out.set_normals_and_coplanar();
crate::edge_op::simplify_topology(&mut out, 0);
out.sort_geometry();
} else {
out.tolerance = out.epsilon.max(tolerance);
}
Self::from_impl(out)
}
pub fn simplify(&self, tolerance: f64) -> Self {
if self.is_empty() { return self.clone(); }
let mut out = self.imp.clone();
let old_tolerance = out.tolerance;
let mut tol = tolerance;
if tol == 0.0 {
tol = old_tolerance;
}
if tol > old_tolerance {
out.tolerance = tol;
out.set_normals_and_coplanar();
}
crate::edge_op::simplify_topology(&mut out, 0);
out.sort_geometry();
out.tolerance = old_tolerance;
Self::from_impl(out)
}
pub fn warp_batch<F: Fn(&mut [Vec3])>(&self, warp_fn: F) -> Self {
if self.is_empty() {
return self.clone();
}
let mut out = self.imp.clone();
warp_fn(&mut out.vert_pos);
out.calculate_bbox();
out.sort_geometry();
out.set_normals_and_coplanar();
out.mesh_relation.original_id = -1;
Self::from_impl(out)
}
pub fn translate(&self, v: Vec3) -> Self {
let t = mat4_to_mat3x4(translation_matrix(v));
Self::from_impl(self.imp.transform(&t))
}
pub fn rotate(&self, x_degrees: f64, y_degrees: f64, z_degrees: f64) -> Self {
use crate::types::{cosd, sind};
let (sx, cx) = (sind(x_degrees), cosd(x_degrees));
let (sy, cy) = (sind(y_degrees), cosd(y_degrees));
let (sz, cz) = (sind(z_degrees), cosd(z_degrees));
let rx = Mat3::from_cols(
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, cx, sx),
Vec3::new(0.0, -sx, cx),
);
let ry = Mat3::from_cols(
Vec3::new(cy, 0.0, -sy),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(sy, 0.0, cy),
);
let rz = Mat3::from_cols(
Vec3::new(cz, sz, 0.0),
Vec3::new(-sz, cz, 0.0),
Vec3::new(0.0, 0.0, 1.0),
);
let m = rz * ry * rx;
let t = Mat3x4::from_cols(m.x, m.y, m.z, Vec3::new(0.0, 0.0, 0.0));
Self::from_impl(self.imp.transform(&t))
}
pub fn scale(&self, v: Vec3) -> Self {
let t = mat4_to_mat3x4(scaling_matrix(v));
Self::from_impl(self.imp.transform(&t))
}
pub fn transform(&self, m: &Mat3x4) -> Self {
Self::from_impl(self.imp.transform(m))
}
pub fn mirror(&self, normal: Vec3) -> Self {
if self.imp.status != Error::NoError {
return self.clone();
}
let len = (normal.x * normal.x + normal.y * normal.y + normal.z * normal.z).sqrt();
if len == 0.0 {
return Self::empty();
}
let n = Vec3::new(normal.x / len, normal.y / len, normal.z / len);
let m = Mat3x4::from_cols(
Vec3::new(1.0 - 2.0 * n.x * n.x, -2.0 * n.x * n.y, -2.0 * n.x * n.z),
Vec3::new(-2.0 * n.y * n.x, 1.0 - 2.0 * n.y * n.y, -2.0 * n.y * n.z),
Vec3::new(-2.0 * n.z * n.x, -2.0 * n.z * n.y, 1.0 - 2.0 * n.z * n.z),
Vec3::new(0.0, 0.0, 0.0),
);
Self::from_impl(self.imp.transform(&m))
}
pub fn warp<F: Fn(&mut Vec3)>(&self, warp_fn: F) -> Self {
if self.is_empty() {
return self.clone();
}
let mut out = self.imp.clone();
for v in out.vert_pos.iter_mut() {
warp_fn(v);
}
out.calculate_bbox();
out.sort_geometry();
out.set_normals_and_coplanar();
out.mesh_relation.original_id = -1;
Self::from_impl(out)
}
pub fn bounding_box(&self) -> crate::types::Box {
crate::types::Box {
min: self.imp.bbox.min,
max: self.imp.bbox.max,
}
}
pub fn split(&self, cutter: &Self) -> (Self, Self) {
let intersection = self.intersection(cutter);
let difference = self.difference(cutter);
(intersection, difference)
}
pub fn split_by_plane(&self, normal: Vec3, origin_offset: f64) -> (Self, Self) {
if self.imp.status != Error::NoError {
return (self.clone(), self.clone());
}
if self.is_empty() {
return (Self::empty(), Self::empty());
}
let halfspace = Self::halfspace(&self.imp.bbox, normal, origin_offset);
self.split(&halfspace)
}
pub fn trim_by_plane(&self, normal: Vec3, origin_offset: f64) -> Self {
if self.is_empty() {
return Self::empty();
}
let halfspace = Self::halfspace(&self.imp.bbox, normal, origin_offset);
self.intersection(&halfspace)
}
pub fn slice(&self, height: f64) -> CrossSection {
if self.is_empty() {
return CrossSection::new(vec![]);
}
let polys = self.imp.slice(height);
CrossSection::new(polys)
}
pub fn project(&self) -> CrossSection {
if self.is_empty() {
return CrossSection::new(vec![]);
}
let polys = self.imp.project();
CrossSection::from_polygons_fill(polys)
}
pub fn batch_boolean(manifolds: &[Self], op: OpType) -> Self {
if manifolds.is_empty() {
return Self::empty();
}
let mut result = manifolds[0].clone();
for m in &manifolds[1..] {
result = result.boolean(m, op);
}
result
}
fn halfspace(bbox: &crate::types::Box, normal: Vec3, origin_offset: f64) -> Self {
let n = normalize(normal);
let cutter = Self::cube(Vec3::splat(2.0), true).translate(Vec3::new(1.0, 0.0, 0.0));
let center = bbox.center();
let size_len = (bbox.size().x * bbox.size().x + bbox.size().y * bbox.size().y + bbox.size().z * bbox.size().z).sqrt();
let dist = ((center.x - n.x * origin_offset).powi(2)
+ (center.y - n.y * origin_offset).powi(2)
+ (center.z - n.z * origin_offset).powi(2)).sqrt()
+ 0.5 * size_len;
let cutter = cutter.scale(Vec3::splat(dist)).translate(Vec3::new(origin_offset, 0.0, 0.0));
let y_deg = -math::asin(n.z).to_degrees();
let z_deg = math::atan2(n.y, n.x).to_degrees();
cutter.rotate(0.0, y_deg, z_deg)
}
pub fn boolean(&self, other: &Self, op: OpType) -> Self {
Self::from_impl(boolean3::boolean(&self.imp, &other.imp, op))
}
pub fn boolean_with_token(
&self,
other: &Self,
op: OpType,
token: Option<&crate::cancel::CancelToken>,
) -> Self {
Self::from_impl(boolean3::boolean_with_token(&self.imp, &other.imp, op, token))
}
pub fn union(&self, other: &Self) -> Self {
self.boolean(other, OpType::Add)
}
pub fn difference(&self, other: &Self) -> Self {
self.boolean(other, OpType::Subtract)
}
pub fn intersection(&self, other: &Self) -> Self {
self.boolean(other, OpType::Intersect)
}
pub fn calculate_curvature(&self, gaussian_idx: i32, mean_idx: i32) -> Self {
if self.is_empty() { return self.clone(); }
let mut out = self.imp.clone();
out.calculate_curvature(gaussian_idx, mean_idx);
Self::from_impl(out)
}
pub fn set_properties<F>(&self, num_prop: usize, prop_func: F) -> Self
where
F: Fn(&mut [f64], Vec3, &[f64]),
{
if self.is_empty() { return self.clone(); }
let mut out = self.imp.clone();
let old_num_prop = out.num_prop;
let old_properties = out.properties.clone();
if num_prop == 0 {
out.properties.clear();
} else {
let num_prop_vert = out.num_prop_vert();
out.properties = vec![0.0; num_prop * num_prop_vert];
let num_tri = out.num_tri();
for tri in 0..num_tri {
for i in 0..3 {
let edge = out.halfedge[3 * tri + i];
let vert = edge.start_vert as usize;
let prop_vert = edge.prop_vert as usize;
let pos = out.vert_pos[vert];
let old_slice = if old_num_prop > 0 && prop_vert * old_num_prop < old_properties.len() {
&old_properties[old_num_prop * prop_vert..old_num_prop * prop_vert + old_num_prop]
} else {
&[]
};
prop_func(
&mut out.properties[num_prop * prop_vert..num_prop * prop_vert + num_prop],
pos,
old_slice,
);
}
}
}
out.num_prop = num_prop;
Self::from_impl(out)
}
pub fn compose(parts: &[Self]) -> Self {
let impls: Vec<_> = parts.iter().map(|m| m.imp.clone()).collect();
Self::from_impl(boolean3::compose_meshes(&impls))
}
pub fn decompose(&self) -> Vec<Self> {
use crate::disjoint_sets::DisjointSets;
let num_vert = self.imp.num_vert();
if num_vert == 0 {
if self.imp.status != Error::NoError {
return vec![self.clone()];
}
return vec![];
}
let uf = DisjointSets::new(num_vert as u32);
for he in &self.imp.halfedge {
if he.is_forward() {
uf.unite(he.start_vert as u32, he.end_vert as u32);
}
}
let mut component_indices = vec![0i32; num_vert];
let num_components = uf.connected_components(&mut component_indices);
if num_components <= 1 {
return vec![self.clone()];
}
let num_tri = self.imp.num_tri();
let mut meshes = Vec::new();
for comp in 0..num_components {
let mut imp = ManifoldImpl::new();
imp.tolerance = self.imp.tolerance;
let vert_new2old: Vec<i32> = (0..num_vert as i32)
.filter(|&v| component_indices[v as usize] == comp)
.collect();
let n_vert = vert_new2old.len();
if n_vert == 0 { continue; }
imp.vert_pos = vert_new2old.iter().map(|&v| self.imp.vert_pos[v as usize]).collect();
if !self.imp.vert_normal.is_empty() {
imp.vert_normal = vert_new2old.iter()
.map(|&v| self.imp.vert_normal[v as usize]).collect();
}
let face_new2old: Vec<usize> = (0..num_tri)
.filter(|&f| {
let sv = self.imp.halfedge[3 * f].start_vert;
sv >= 0 && component_indices[sv as usize] == comp
})
.collect();
if face_new2old.is_empty() { continue; }
imp.halfedge = self.imp.halfedge.clone();
imp.face_normal = self.imp.face_normal.clone();
imp.halfedge_tangent = self.imp.halfedge_tangent.clone();
imp.num_prop = self.imp.num_prop;
imp.properties = self.imp.properties.clone();
imp.mesh_relation = self.imp.mesh_relation.clone();
crate::sort::gather_faces(&mut imp, &face_new2old);
crate::sort::reindex_verts(&mut imp, &vert_new2old, self.imp.num_vert());
imp.calculate_bbox();
imp.sort_geometry();
meshes.push(Self::from_impl(imp));
}
meshes
}
pub fn min_gap(&self, other: &Self, search_length: f64) -> f64 {
self.imp.min_gap(&other.imp, search_length)
}
pub fn ray_cast(&self, origin: Vec3, endpoint: Vec3) -> Vec<RayHit> {
crate::boolean3::ray_cast(&self.imp, origin, endpoint)
}
}
impl std::ops::Add for Manifold {
type Output = Self;
fn add(self, rhs: Self) -> Self { self.union(&rhs) }
}
impl std::ops::Add<&Manifold> for Manifold {
type Output = Self;
fn add(self, rhs: &Self) -> Self { self.union(rhs) }
}
impl std::ops::Add<&Manifold> for &Manifold {
type Output = Manifold;
fn add(self, rhs: &Manifold) -> Manifold { self.union(rhs) }
}
impl std::ops::AddAssign for Manifold {
fn add_assign(&mut self, rhs: Self) { *self = self.union(&rhs); }
}
impl std::ops::AddAssign<&Manifold> for Manifold {
fn add_assign(&mut self, rhs: &Self) { *self = self.union(rhs); }
}
impl std::ops::Sub for Manifold {
type Output = Self;
fn sub(self, rhs: Self) -> Self { self.difference(&rhs) }
}
impl std::ops::Sub<&Manifold> for Manifold {
type Output = Self;
fn sub(self, rhs: &Self) -> Self { self.difference(rhs) }
}
impl std::ops::Sub<&Manifold> for &Manifold {
type Output = Manifold;
fn sub(self, rhs: &Manifold) -> Manifold { self.difference(rhs) }
}
impl std::ops::SubAssign for Manifold {
fn sub_assign(&mut self, rhs: Self) { *self = self.difference(&rhs); }
}
impl std::ops::SubAssign<&Manifold> for Manifold {
fn sub_assign(&mut self, rhs: &Self) { *self = self.difference(rhs); }
}
impl std::ops::BitXor for Manifold {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self { self.intersection(&rhs) }
}
impl std::ops::BitXor<&Manifold> for Manifold {
type Output = Self;
fn bitxor(self, rhs: &Self) -> Self { self.intersection(rhs) }
}
impl std::ops::BitXor<&Manifold> for &Manifold {
type Output = Manifold;
fn bitxor(self, rhs: &Manifold) -> Manifold { self.intersection(rhs) }
}
impl std::ops::BitXorAssign for Manifold {
fn bitxor_assign(&mut self, rhs: Self) { *self = self.intersection(&rhs); }
}
impl std::ops::BitXorAssign<&Manifold> for Manifold {
fn bitxor_assign(&mut self, rhs: &Self) { *self = self.intersection(rhs); }
}
#[path = "manifold_meshgl.rs"]
mod meshgl;
#[path = "manifold_shape.rs"]
mod shape;
#[path = "manifold_smooth.rs"]
mod smooth;
#[cfg(test)]
#[path = "manifold_tests/mod.rs"]
mod tests;