Skip to main content

implicit3d/
lib.rs

1//! ```implicit3d``` is a crate for creating
2//! [3d implicit functions](https://en.wikipedia.org/wiki/Implicit_function).
3//! Implicit functions evaluate to a scalar value for each point the 3d space.
4//! They can be used to described object surfaces. If the function evaluates to negative values
5//! the point is in the object, if the function evaluates positve this is outside the object.
6//! If the function evaluates to zero the point is on the object surface.
7//! This library allows to create implicit functions for 3d primitives (sphere, cylinder, cone,
8//! box). Those primitives can be combined using
9//! [CSG](https://en.wikipedia.org/wiki/Constructive_solid_geometry) and transformed.
10//!
11//! # Examples
12//!
13//! Create a Sphere:
14//!
15//! ```rust,no_run
16//! let sphere = implicit3d::Sphere::new(1.0);
17//! ```
18//! Create a rounded Cube (as rounded intersection of 6 planes):
19//!
20//! ```rust,no_run
21//! use std::fs::OpenOptions;
22//! let px = Box::new(implicit3d::PlaneX::new(1.0));
23//! let pnx = Box::new(implicit3d::PlaneNegX::new(1.0));
24//! let py = Box::new(implicit3d::PlaneY::new(1.0));
25//! let pny = Box::new(implicit3d::PlaneNegY::new(1.0));
26//! let pz = Box::new(implicit3d::PlaneZ::new(1.0));
27//! let pnz = Box::new(implicit3d::PlaneNegZ::new(1.0));
28//! let cube = implicit3d::Intersection::from_vec(vec![px, pnx, py, pny, pz, pnz], 0.2);
29//! ```
30
31#![warn(missing_docs)]
32
33use nalgebra as na;
34
35/// A 3D bounding box.
36pub type BoundingBox<S> = bbox::BoundingBox<S, 3>;
37use num_traits::Float;
38use std::fmt::Debug;
39
40/// A scalar type suitable for use with implicit3d objects.
41/// Combines nalgebra's RealField with num_traits::Float.
42pub trait RealField: na::RealField + Copy {}
43impl RealField for f64 {}
44impl RealField for f32 {}
45
46mod transformer;
47pub use self::transformer::AffineTransformer;
48
49mod twister;
50pub use self::twister::Twister;
51
52mod bender;
53pub use self::bender::Bender;
54
55mod boolean;
56pub use self::boolean::{Intersection, Union};
57
58mod sphere;
59pub use self::sphere::Sphere;
60
61mod cylinder;
62pub use self::cylinder::{Cone, Cylinder};
63
64mod plane;
65pub use self::plane::{NormalPlane, PlaneNegX, PlaneNegY, PlaneNegZ, PlaneX, PlaneY, PlaneZ};
66
67mod mesh;
68pub use self::mesh::Mesh;
69
70#[cfg(test)]
71mod test;
72
73/// This struct configures evaluation of rounded edges between object.
74/// The edge is evaluated in a different more computationally expensive way.
75pub struct PrimitiveParameters<S> {
76    /// Fade from standard object evaluation to edge evaluation on this fraction of the edge.
77    pub fade_range: S,
78    /// How much to extend the radius for edge evaluation mode.
79    pub r_multiplier: S,
80}
81
82const ALWAYS_PRECISE: f32 = 1.;
83const EPSILON: f32 = 1e-10;
84
85/// Get a normal from an Object a some point. Do this using approximating the derivative with
86/// deltas.
87fn normal_from_object<S: Debug + RealField + Float + From<f32>>(
88    f: &dyn Object<S>,
89    p: &na::Point3<S>,
90) -> na::Vector3<S> {
91    let null: S = From::from(0.0);
92    let e: S = From::from(EPSILON);
93    let a: S = From::from(ALWAYS_PRECISE);
94    let epsilon_x = na::Vector3::<S>::new(e, null, null);
95    let epsilon_y = na::Vector3::<S>::new(null, e, null);
96    let epsilon_z = na::Vector3::<S>::new(null, null, e);
97    let center = f.approx_value(p, a);
98    let dx = f.approx_value(&(p + epsilon_x), a) - center;
99    let dy = f.approx_value(&(p + epsilon_y), a) - center;
100    let dz = f.approx_value(&(p + epsilon_z), a) - center;
101    na::Vector3::<S>::new(dx, dy, dz).normalize()
102}
103
104/// Object is the basic trait for any 3d implicit function.
105pub trait Object<S: RealField + Float + From<f32>>: ObjectClone<S> + Debug + Sync + Send {
106    /// Get the Bounding Box of this Object.
107    fn bbox(&self) -> &BoundingBox<S>;
108    /// Explicitly set the Bounding Box.
109    fn set_bbox(&mut self, _: &BoundingBox<S>) {
110        unimplemented!();
111    }
112    /// Allows to set parameters.
113    fn set_parameters(&mut self, _: &PrimitiveParameters<S>) {}
114    /// Value is 0 on object surfaces, negative inside and positive outside of objects.
115    /// If positive, value is guarateed to be the minimum distance to the object surface.
116    /// return some approximation (which is always larger then the proper value).
117    /// Only do a proper calculation, for values smaller then slack.
118    fn approx_value(&self, _: &na::Point3<S>, _: S) -> S {
119        unimplemented!();
120    }
121    /// Evaluate the normal of ```self``` at the given point.
122    fn normal(&self, _: &na::Point3<S>) -> na::Vector3<S> {
123        unimplemented!();
124    }
125    /// Return a translated version of ```self```.
126    fn translate(&self, v: &na::Vector3<S>) -> Box<dyn Object<S>> {
127        AffineTransformer::new_translate(self.clone_box(), v)
128    }
129    /// Return a rotated version of ```self```.
130    fn rotate(&self, r: &na::Vector3<S>) -> Box<dyn Object<S>> {
131        AffineTransformer::new_rotate(self.clone_box(), r)
132    }
133    /// Return a scaled version of ```self```.
134    fn scale(&self, s: &na::Vector3<S>) -> Box<dyn Object<S>> {
135        AffineTransformer::new_scale(self.clone_box(), s)
136    }
137}
138
139/// Trait to allow cloning of ```Box<Object<_>>```.
140pub trait ObjectClone<S> {
141    /// Clone ```Box<Object<_>>```.
142    fn clone_box(&self) -> Box<dyn Object<S>>;
143}
144
145impl<S: RealField + Float + From<f32>, T> ObjectClone<S> for T
146where
147    T: 'static + Object<S> + Clone,
148{
149    fn clone_box(&self) -> Box<dyn Object<S>> {
150        Box::new(self.clone())
151    }
152}
153
154// We can now implement Clone manually by forwarding to clone_box.
155impl<S> Clone for Box<dyn Object<S>> {
156    fn clone(&self) -> Box<dyn Object<S>> {
157        self.clone_box()
158    }
159}
160
161// Objects never equal each other
162impl<S> PartialEq for Box<dyn Object<S>> {
163    fn eq(&self, _: &Box<dyn Object<S>>) -> bool {
164        false
165    }
166}
167
168// Objects are never ordered
169impl<S> PartialOrd for Box<dyn Object<S>> {
170    fn partial_cmp(&self, _: &Box<dyn Object<S>>) -> Option<std::cmp::Ordering> {
171        None
172    }
173}