use crate::common::boolean::Boolean;
#[cfg(feature = "color")]
use crate::common::color::Color;
use crate::common::error::Error;
use crate::common::mesh::Mesh;
use glam::{DMat3, DQuat, DVec3};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Tessellation {
pub deflection_linear: f64,
pub deflection_angular: f64,
pub relative_linear: bool,
}
impl Default for Tessellation {
fn default() -> Self {
Self { deflection_linear: 0.004, deflection_angular: 0.5, relative_linear: true }
}
}
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 EdgeStruct: Sized + Clone + Transform {
fn id(&self) -> u64;
fn start_point(&self) -> DVec3;
fn end_point(&self) -> DVec3;
fn start_tangent(&self) -> DVec3;
fn end_tangent(&self) -> DVec3;
fn is_closed(&self) -> bool;
fn approximation_segments(&self, tessellation: Tessellation) -> Vec<DVec3>;
fn project(&self, p: DVec3) -> (DVec3, DVec3);
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 + Transform {
type Edge: EdgeStruct;
type Face: FaceStruct;
fn id(&self) -> u64;
fn cube(corner0: DVec3, corner1: DVec3) -> Self;
fn sphere(radius: f64) -> Self;
fn cylinder(r: f64, height: DVec3) -> Self;
fn cone(r1: f64, r2: f64, height: DVec3) -> 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 volume(&self) -> f64;
fn area(&self) -> f64;
fn center(&self) -> DVec3;
fn inertia(&self) -> DMat3;
fn contains(&self, point: DVec3) -> bool;
fn bounding_box(&self) -> [DVec3; 2];
#[cfg(feature = "color")]
fn color(self, color: impl Into<Color>) -> Self;
#[cfg(feature = "color")]
fn color_clear(self) -> Self;
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, I: IntoIterator<Item = &'a Self::Edge>, S: IntoIterator<Item = I>>(sections: S, ruled: bool) -> Result<Self, Error>
where
Self::Edge: 'a;
fn sew<'a>(faces: impl IntoIterator<Item = &'a Self::Face>, tolerance: f64) -> Result<Self, Error>
where
Self::Face: 'a;
fn offset_surface(&self, offset: f64, tolerance: f64) -> Result<Self, Error>;
fn bspline(u: usize, v: usize, u_periodic: bool, point: impl Fn(usize, usize) -> DVec3) -> Result<Self, Error>;
fn boolean<'a>(solids: impl IntoIterator<Item = &'a Self>, clauses: impl IntoIterator<Item = i64>) -> Boolean<Self>
where
Self: 'a;
fn boolean_build(b: &Boolean<Self>) -> Result<Vec<Self>, Error>;
fn read_step<R: std::io::Read>(reader: &mut R) -> Result<Vec<Self>, Error>;
fn read_brep<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<'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>, options: Tessellation) -> Result<Mesh, Error>
where
Self: 'a;
#[cfg(feature = "png")]
fn write_multiview_png<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
let bbox = <Self as SolidStruct>::bounding_box(self);
let diag = (bbox[1] - bbox[0]).length();
let tol = if diag > 0.0 { diag * 0.001 } else { 0.001 };
let mesh = Self::mesh(std::iter::once(self), Tessellation { deflection_linear: tol, relative_linear: false, ..Default::default() })?;
mesh.write_multiview_png(writer)
}
}