#[cfg(feature = "color")]
use crate::common::color::Color;
use crate::common::error::Error;
use crate::common::mesh::Mesh;
use glam::{DMat3, DQuat, DVec3};
pub trait Transform: Sized {
fn translate(self, translation: DVec3) -> Self;
fn rotate(self, axis_origin: DVec3, axis_direction: DVec3, angle: f64) -> Self;
fn rotate_x(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::X, angle) }
fn rotate_y(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::Y, angle) }
fn rotate_z(self, angle: f64) -> Self { self.rotate(DVec3::ZERO, DVec3::Z, angle) }
fn scale(self, center: DVec3, factor: f64) -> Self;
fn mirror(self, plane_origin: DVec3, plane_normal: DVec3) -> Self;
fn align_x(self, new_x: DVec3, y_hint: DVec3) -> Self {
let x = new_x.try_normalize().expect("align_x: new_x is zero");
let z = x.cross(y_hint).try_normalize().expect("align_x: y_hint parallel to new_x");
let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(x, z.cross(x), z)).to_axis_angle();
self.rotate(DVec3::ZERO, axis, angle)
}
fn align_y(self, new_y: DVec3, z_hint: DVec3) -> Self {
let y = new_y.try_normalize().expect("align_y: new_y is zero");
let x = y.cross(z_hint).try_normalize().expect("align_y: z_hint parallel to new_y");
let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(x, y, x.cross(y))).to_axis_angle();
self.rotate(DVec3::ZERO, axis, angle)
}
fn align_z(self, new_z: DVec3, x_hint: DVec3) -> Self {
let z = new_z.try_normalize().expect("align_z: new_z is zero");
let y = z.cross(x_hint).try_normalize().expect("align_z: x_hint parallel to new_z");
let (axis, angle) = DQuat::from_mat3(&DMat3::from_cols(y.cross(z), y, z)).to_axis_angle();
self.rotate(DVec3::ZERO, axis, angle)
}
}
#[derive(Clone, Copy)]
pub enum ProfileOrient<'a> {
Fixed,
Torsion,
Up(DVec3),
Auxiliary(&'a [crate::Edge]),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BSplineEnd {
Periodic,
NotAKnot,
Clamped {
start: DVec3,
end: DVec3,
},
}
pub trait Wire: Transform {
type Elem: Wire;
fn iter_elem(&self) -> impl Iterator<Item = &Self::Elem> + '_;
fn map_elem(self, f: impl FnMut(Self::Elem) -> Self::Elem) -> Self;
fn start_point(&self) -> DVec3 {
self.iter_elem().next().map(|e| <Self::Elem as Wire>::start_point(e)).unwrap_or(DVec3::ZERO)
}
fn end_point(&self) -> DVec3 {
self.iter_elem().last().map(|e| <Self::Elem as Wire>::end_point(e)).unwrap_or(DVec3::ZERO)
}
fn start_tangent(&self) -> DVec3 {
self.iter_elem().next().map(|e| <Self::Elem as Wire>::start_tangent(e)).unwrap_or(DVec3::ZERO)
}
fn end_tangent(&self) -> DVec3 {
self.iter_elem().last().map(|e| <Self::Elem as Wire>::end_tangent(e)).unwrap_or(DVec3::ZERO)
}
fn is_closed(&self) -> bool {
let mut iter = self.iter_elem();
let Some(first) = iter.next() else { return false; };
match iter.last() {
None => <Self::Elem as Wire>::is_closed(first),
Some(last) => (<Self::Elem as Wire>::start_point(first) - <Self::Elem as Wire>::end_point(last)).length() < 1e-6,
}
}
fn approximation_segments(&self, tolerance: f64) -> Vec<DVec3> {
let mut out: Vec<DVec3> = Vec::new();
for e in self.iter_elem() {
let pts = <Self::Elem as Wire>::approximation_segments(e, tolerance);
if let Some((first, rest)) = pts.split_first() {
if out.last().map(|p| (*p - *first).length() < 1e-9).unwrap_or(false) {
out.extend_from_slice(rest);
} else {
out.push(*first);
out.extend_from_slice(rest);
}
}
}
out
}
fn project(&self, p: DVec3) -> (DVec3, DVec3) {
self.iter_elem()
.map(|e| <Self::Elem as Wire>::project(e, p))
.min_by(|(a, _), (b, _)| (a - p).length_squared().partial_cmp(&(b - p).length_squared()).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or((DVec3::ZERO, DVec3::ZERO))
}
fn translate(self, translation: DVec3) -> Self { <Self as Transform>::translate(self, translation) }
fn rotate(self, axis_origin: DVec3, axis_direction: DVec3, angle: f64) -> Self { <Self as Transform>::rotate(self, axis_origin, axis_direction, angle) }
fn rotate_x(self, angle: f64) -> Self { <Self as Transform>::rotate_x(self, angle) }
fn rotate_y(self, angle: f64) -> Self { <Self as Transform>::rotate_y(self, angle) }
fn rotate_z(self, angle: f64) -> Self { <Self as Transform>::rotate_z(self, angle) }
fn scale(self, center: DVec3, factor: f64) -> Self { <Self as Transform>::scale(self, center, factor) }
fn mirror(self, plane_origin: DVec3, plane_normal: DVec3) -> Self { <Self as Transform>::mirror(self, plane_origin, plane_normal) }
fn align_x(self, new_x: DVec3, y_hint: DVec3) -> Self { <Self as Transform>::align_x(self, new_x, y_hint) }
fn align_y(self, new_y: DVec3, z_hint: DVec3) -> Self { <Self as Transform>::align_y(self, new_y, z_hint) }
fn align_z(self, new_z: DVec3, x_hint: DVec3) -> Self { <Self as Transform>::align_z(self, new_z, x_hint) }
}
pub trait EdgeStruct: Sized + Clone + Wire {
fn id(&self) -> u64;
fn helix(radius: f64, pitch: f64, height: f64, axis: DVec3, x_ref: DVec3) -> Result<Self, Error>;
fn polygon<'a>(points: impl IntoIterator<Item = &'a DVec3>) -> Result<Vec<Self>, Error>;
fn circle(radius: f64, axis: DVec3) -> Result<Self, Error>;
fn line(a: DVec3, b: DVec3) -> Result<Self, Error>;
fn arc_3pts(start: DVec3, mid: DVec3, end: DVec3) -> Result<Self, Error>;
fn bspline<'a>(points: impl IntoIterator<Item = &'a DVec3>, end: BSplineEnd) -> Result<Self, Error>;
}
pub trait FaceStruct: Sized {
type Edge: EdgeStruct;
fn id(&self) -> u64;
fn project(&self, p: DVec3) -> (DVec3, DVec3);
fn iter_edge(&self) -> impl Iterator<Item = &Self::Edge> + '_;
}
pub trait SolidStruct: Sized + Clone + Compound {
type Edge: EdgeStruct;
type Face: FaceStruct;
fn id(&self) -> u64;
fn cube(x: f64, y: f64, z: f64) -> Self;
fn sphere(radius: f64) -> Self;
fn cylinder(r: f64, axis: DVec3, h: f64) -> Self;
fn cone(r1: f64, r2: f64, axis: DVec3, h: f64) -> Self;
fn torus(r1: f64, r2: f64, axis: DVec3) -> Self;
fn half_space(plane_origin: DVec3, plane_normal: DVec3) -> Self;
fn iter_edge(&self) -> impl Iterator<Item = &Self::Edge> + '_;
fn iter_face(&self) -> impl Iterator<Item = &Self::Face> + '_;
fn iter_history(&self) -> impl Iterator<Item = [u64; 2]> + '_;
fn clean(&self) -> Result<Self, Error>;
fn extrude<'a>(profile: impl IntoIterator<Item = &'a Self::Edge>, dir: DVec3) -> Result<Self, Error> where Self::Edge: 'a;
fn shell<'a>(&self, thickness: f64, open_faces: impl IntoIterator<Item = &'a Self::Face>) -> Result<Self, Error> where Self::Face: 'a;
fn fillet_edges<'a>(&self, radius: f64, edges: impl IntoIterator<Item = &'a Self::Edge>) -> Result<Self, Error> where Self::Edge: 'a;
fn chamfer_edges<'a>(&self, distance: f64, edges: impl IntoIterator<Item = &'a Self::Edge>) -> Result<Self, Error> where Self::Edge: 'a;
fn sweep<'a, 'b, 'c>(profile: impl IntoIterator<Item = &'a Self::Edge>, spine: impl IntoIterator<Item = &'b Self::Edge>, orient: ProfileOrient<'c>) -> Result<Self, Error> where Self::Edge: 'a + 'b;
fn loft<'a, S, I>(sections: S) -> Result<Self, Error> where S: IntoIterator<Item = I>, I: IntoIterator<Item = &'a Self::Edge>, Self::Edge: 'a;
fn bspline(u: usize, v: usize, u_periodic: bool, point: impl Fn(usize, usize) -> DVec3) -> Result<Self, Error>;
fn boolean_union<'a, 'b>(a: impl IntoIterator<Item = &'a Self>, b: impl IntoIterator<Item = &'b Self>) -> Result<Vec<Self>, Error> where Self: 'a + 'b;
fn boolean_subtract<'a, 'b>(a: impl IntoIterator<Item = &'a Self>, b: impl IntoIterator<Item = &'b Self>) -> Result<Vec<Self>, Error> where Self: 'a + 'b;
fn boolean_intersect<'a, 'b>(a: impl IntoIterator<Item = &'a Self>, b: impl IntoIterator<Item = &'b Self>) -> Result<Vec<Self>, Error> where Self: 'a + 'b;
fn read_step<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
fn read_brep_binary<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
fn read_brep_text<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
fn write_step<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
fn write_brep_binary<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
fn write_brep_text<'a, W: std::io::Write>(solids: impl IntoIterator<Item = &'a Self>, writer: &mut W) -> Result<(), Error> where Self: 'a;
fn mesh<'a>(solids: impl IntoIterator<Item = &'a Self>, tolerance: f64) -> Result<Mesh, Error> where Self: 'a;
}
pub trait Compound: Transform {
type Elem: Compound+SolidStruct;
fn iter_elem(&self) -> impl Iterator<Item = &Self::Elem> + '_;
fn map_elem(self, f: impl FnMut(Self::Elem) -> Self::Elem) -> Self;
fn volume(&self) -> f64 {
self.iter_elem().map(|s| <Self::Elem as Compound>::volume(s)).sum()
}
fn area(&self) -> f64 {
self.iter_elem().map(|s| <Self::Elem as Compound>::area(s)).sum()
}
fn contains(&self, point: DVec3) -> bool {
self.iter_elem().any(|s| <Self::Elem as Compound>::contains(s, point))
}
fn bounding_box(&self) -> [DVec3; 2] {
self.iter_elem()
.map(|s| <Self::Elem as Compound>::bounding_box(s))
.reduce(|[amin, amax], [bmin, bmax]| [amin.min(bmin), amax.max(bmax)])
.unwrap_or([DVec3::ZERO, DVec3::ZERO])
}
fn center(&self) -> DVec3 {
let total: f64 = self.iter_elem().map(|s| <Self::Elem as Compound>::volume(s)).sum();
if total == 0.0 { return DVec3::ZERO; }
self.iter_elem()
.map(|s| <Self::Elem as Compound>::center(s) * <Self::Elem as Compound>::volume(s))
.sum::<DVec3>() / total
}
fn inertia(&self) -> DMat3 {
self.iter_elem().map(|s| <Self::Elem as Compound>::inertia(s)).fold(DMat3::ZERO, |a, b| a + b)
}
#[cfg(feature = "color")]
fn color(self, color: impl Into<Color>) -> Self {
let c: Color = color.into();
self.map_elem(|s| <Self::Elem as Compound>::color(s, c))
}
#[cfg(feature = "color")]
fn color_clear(self) -> Self {
self.map_elem(|s| <Self::Elem as Compound>::color_clear(s))
}
fn union<'a>(&self, tool: impl IntoIterator<Item = &'a Self::Elem>) -> Result<Vec<Self::Elem>, Error> where Self::Elem: 'a {
Self::Elem::boolean_union(self.iter_elem(), tool)
}
fn subtract<'a>(&self, tool: impl IntoIterator<Item = &'a Self::Elem>) -> Result<Vec<Self::Elem>, Error> where Self::Elem: 'a {
Self::Elem::boolean_subtract(self.iter_elem(), tool)
}
fn intersect<'a>(&self, tool: impl IntoIterator<Item = &'a Self::Elem>) -> Result<Vec<Self::Elem>, Error> where Self::Elem: 'a {
Self::Elem::boolean_intersect(self.iter_elem(), tool)
}
fn translate(self, translation: DVec3) -> Self { <Self as Transform>::translate(self, translation) }
fn rotate(self, axis_origin: DVec3, axis_direction: DVec3, angle: f64) -> Self { <Self as Transform>::rotate(self, axis_origin, axis_direction, angle) }
fn rotate_x(self, angle: f64) -> Self { <Self as Transform>::rotate_x(self, angle) }
fn rotate_y(self, angle: f64) -> Self { <Self as Transform>::rotate_y(self, angle) }
fn rotate_z(self, angle: f64) -> Self { <Self as Transform>::rotate_z(self, angle) }
fn scale(self, center: DVec3, factor: f64) -> Self { <Self as Transform>::scale(self, center, factor) }
fn mirror(self, plane_origin: DVec3, plane_normal: DVec3) -> Self { <Self as Transform>::mirror(self, plane_origin, plane_normal) }
fn align_x(self, new_x: DVec3, y_hint: DVec3) -> Self { <Self as Transform>::align_x(self, new_x, y_hint) }
fn align_y(self, new_y: DVec3, z_hint: DVec3) -> Self { <Self as Transform>::align_y(self, new_y, z_hint) }
fn align_z(self, new_z: DVec3, x_hint: DVec3) -> Self { <Self as Transform>::align_z(self, new_z, x_hint) }
}
impl<T: Transform> Transform for Vec<T> {
fn translate(self, v: DVec3) -> Self { self.into_iter().map(|s| s.translate(v)).collect() }
fn rotate(self, o: DVec3, d: DVec3, a: f64) -> Self { self.into_iter().map(|s| s.rotate(o, d, a)).collect() }
fn scale(self, c: DVec3, f: f64) -> Self { self.into_iter().map(|s| s.scale(c, f)).collect() }
fn mirror(self, o: DVec3, n: DVec3) -> Self { self.into_iter().map(|s| s.mirror(o, n)).collect() }
}
impl<T: SolidStruct> Compound for Vec<T> {
type Elem = T;
fn iter_elem(&self) -> impl Iterator<Item = &T> + '_ { self.iter() }
fn map_elem(self, f: impl FnMut(T) -> T) -> Self { self.into_iter().map(f).collect() }
}
impl<T: Transform, const N: usize> Transform for [T; N] {
fn translate(self, v: DVec3) -> Self { self.map(|s| s.translate(v)) }
fn rotate(self, o: DVec3, d: DVec3, a: f64) -> Self { self.map(|s| s.rotate(o, d, a)) }
fn scale(self, c: DVec3, f: f64) -> Self { self.map(|s| s.scale(c, f)) }
fn mirror(self, o: DVec3, n: DVec3) -> Self { self.map(|s| s.mirror(o, n)) }
}
impl<T: SolidStruct, const N: usize> Compound for [T; N] {
type Elem = T;
fn iter_elem(&self) -> impl Iterator<Item = &T> + '_ { self.iter() }
fn map_elem(self, f: impl FnMut(T) -> T) -> Self { self.map(f) }
}
impl<T: EdgeStruct> Wire for Vec<T> {
type Elem = T;
fn iter_elem(&self) -> impl Iterator<Item = &T> + '_ { self.iter() }
fn map_elem(self, f: impl FnMut(T) -> T) -> Self { self.into_iter().map(f).collect() }
}
impl<T: EdgeStruct, const N: usize> Wire for [T; N] {
type Elem = T;
fn iter_elem(&self) -> impl Iterator<Item = &T> + '_ { self.iter() }
fn map_elem(self, f: impl FnMut(T) -> T) -> Self { self.map(f) }
}