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
33#[cfg(test)]
34#[macro_use]
35extern crate approx;
36
37extern crate nalgebra as na;
38
39pub use bbox::BoundingBox;
40use num_traits::Float;
41use std::fmt::Debug;
42
43/// A Combination of alga::general::RealField and na::RealField.
44pub trait RealField: alga::general::RealField + na::RealField {}
45impl RealField for f64 {}
46impl RealField for f32 {}
47
48mod transformer;
49pub use self::transformer::AffineTransformer;
50
51mod twister;
52pub use self::twister::Twister;
53
54mod bender;
55pub use self::bender::Bender;
56
57mod boolean;
58pub use self::boolean::{Intersection, Union};
59
60mod sphere;
61pub use self::sphere::Sphere;
62
63mod cylinder;
64pub use self::cylinder::{Cone, Cylinder};
65
66mod plane;
67pub use self::plane::{NormalPlane, PlaneNegX, PlaneNegY, PlaneNegZ, PlaneX, PlaneY, PlaneZ};
68
69mod mesh;
70pub use self::mesh::Mesh;
71
72#[cfg(test)]
73mod test;
74
75/// This struct configures evaluation of rounded edges between object.
76/// The edge is evaluated in a different more computationally expensive way.
77pub struct PrimitiveParameters<S> {
78    /// Fade from standard object evaluation to edge evaluation on this fraction of the edge.
79    pub fade_range: S,
80    /// How much to extend the radius for edge evaluation mode.
81    pub r_multiplier: S,
82}
83
84const ALWAYS_PRECISE: f32 = 1.;
85const EPSILON: f32 = 1e-10;
86
87/// Get a normal from an Object a some point. Do this using approximating the derivative with
88/// deltas.
89fn normal_from_object<S: Debug + RealField + Float + From<f32>>(
90    f: &dyn Object<S>,
91    p: &na::Point3<S>,
92) -> na::Vector3<S> {
93    let null: S = From::from(0.0);
94    let e: S = From::from(EPSILON);
95    let a: S = From::from(ALWAYS_PRECISE);
96    let epsilon_x = na::Vector3::<S>::new(e, null, null);
97    let epsilon_y = na::Vector3::<S>::new(null, e, null);
98    let epsilon_z = na::Vector3::<S>::new(null, null, e);
99    let center = f.approx_value(p, a);
100    let dx = f.approx_value(&(p + epsilon_x), a) - center;
101    let dy = f.approx_value(&(p + epsilon_y), a) - center;
102    let dz = f.approx_value(&(p + epsilon_z), a) - center;
103    na::Vector3::<S>::new(dx, dy, dz).normalize()
104}
105
106/// Object is the basic trait for any 3d implicit function.
107pub trait Object<S: RealField + Float + From<f32>>: ObjectClone<S> + Debug + Sync + Send {
108    /// Get the Bounding Box of this Object.
109    fn bbox(&self) -> &BoundingBox<S>;
110    /// Explicitly set the Bounding Box.
111    fn set_bbox(&mut self, _: &BoundingBox<S>) {
112        unimplemented!();
113    }
114    /// Allows to set parameters.
115    fn set_parameters(&mut self, _: &PrimitiveParameters<S>) {}
116    /// Value is 0 on object surfaces, negative inside and positive outside of objects.
117    /// If positive, value is guarateed to be the minimum distance to the object surface.
118    /// return some approximation (which is always larger then the proper value).
119    /// Only do a proper calculation, for values smaller then slack.
120    fn approx_value(&self, _: &na::Point3<S>, _: S) -> S {
121        unimplemented!();
122    }
123    /// Evaluate the normal of ```self``` at the given point.
124    fn normal(&self, _: &na::Point3<S>) -> na::Vector3<S> {
125        unimplemented!();
126    }
127    /// Return a translated version of ```self```.
128    fn translate(&self, v: &na::Vector3<S>) -> Box<dyn Object<S>> {
129        AffineTransformer::new_translate(self.clone_box(), v)
130    }
131    /// Return a rotated version of ```self```.
132    fn rotate(&self, r: &na::Vector3<S>) -> Box<dyn Object<S>> {
133        AffineTransformer::new_rotate(self.clone_box(), r)
134    }
135    /// Return a scaled version of ```self```.
136    fn scale(&self, s: &na::Vector3<S>) -> Box<dyn Object<S>> {
137        AffineTransformer::new_scale(self.clone_box(), s)
138    }
139}
140
141/// Trait to allow cloning of ```Box<Object<_>>```.
142pub trait ObjectClone<S> {
143    /// Clone ```Box<Object<_>>```.
144    fn clone_box(&self) -> Box<dyn Object<S>>;
145}
146
147impl<S: RealField + Float + From<f32>, T> ObjectClone<S> for T
148where
149    T: 'static + Object<S> + Clone,
150{
151    fn clone_box(&self) -> Box<dyn Object<S>> {
152        Box::new(self.clone())
153    }
154}
155
156// We can now implement Clone manually by forwarding to clone_box.
157impl<S> Clone for Box<dyn Object<S>> {
158    fn clone(&self) -> Box<dyn Object<S>> {
159        self.clone_box()
160    }
161}
162
163// Objects never equal each other
164impl<S> PartialEq for Box<dyn Object<S>> {
165    fn eq(&self, _: &Box<dyn Object<S>>) -> bool {
166        false
167    }
168}
169
170// Objects are never ordered
171impl<S> PartialOrd for Box<dyn Object<S>> {
172    fn partial_cmp(&self, _: &Box<dyn Object<S>>) -> Option<::std::cmp::Ordering> {
173        None
174    }
175}