diff --git a/Cargo.lock b/Cargo.lock index 4697e0ac2..f5ecb7f28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -629,15 +629,14 @@ dependencies = [ "approx 0.5.1", "bytemuck", "clap", - "decorum", "figment", "fj", + "fj-math", "futures", "libloading", "map-macro", "nalgebra", "notify", - "num-traits", "parking_lot 0.12.0", "parry2d-f64", "parry3d-f64", @@ -652,6 +651,18 @@ dependencies = [ "winit", ] +[[package]] +name = "fj-math" +version = "0.5.0" +dependencies = [ + "approx 0.5.1", + "decorum", + "nalgebra", + "num-traits", + "parry2d-f64", + "parry3d-f64", +] + [[package]] name = "flate2" version = "1.0.22" diff --git a/Cargo.toml b/Cargo.toml index 07cc0b8bd..c9e94951a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "fj", "fj-app", + "fj-math", "models/cuboid", "models/group", @@ -11,4 +12,7 @@ members = [ "release-operator", ] -default-members = ["fj-app"] +default-members = [ + "fj-app", + "fj-math", +] diff --git a/fj-app/Cargo.toml b/fj-app/Cargo.toml index 56744578a..1f2880d39 100644 --- a/fj-app/Cargo.toml +++ b/fj-app/Cargo.toml @@ -4,7 +4,7 @@ version = "0.5.0" edition = "2021" description = "The world needs another CAD program." -readme = "README.md" +readme = "../README.md" repository = "https://github.com/hannobraun/fornjot" license = "0BSD" keywords = ["cad", "programmatic", "code-cad"] @@ -15,13 +15,11 @@ categories = ["mathematics", "rendering"] anyhow = "1.0.56" approx = "0.5.1" bytemuck = "1.8.0" -decorum = "0.3.1" futures = "0.3.21" libloading = "0.7.2" map-macro = "0.2.0" nalgebra = "0.30.0" notify = "5.0.0-pre.14" -num-traits = "0.2.14" parking_lot = "0.12.0" parry2d-f64 = "0.8.0" parry3d-f64 = "0.8.0" @@ -45,6 +43,10 @@ features = ["env", "toml"] version = "0.5.0" path = "../fj" +[dependencies.fj-math] +version = "0.5.0" +path = "../fj-math" + [dependencies.serde] version = "1.0.136" features = ["derive"] diff --git a/fj-app/src/camera.rs b/fj-app/src/camera.rs index 0aee94c15..523e2a916 100644 --- a/fj-app/src/camera.rs +++ b/fj-app/src/camera.rs @@ -1,13 +1,11 @@ use std::f64::consts::FRAC_PI_2; +use fj_math::{Aabb, Scalar, Triangle}; use nalgebra::{Point, TAffine, Transform, Translation, Vector}; use parry3d_f64::query::{Ray, RayCast as _}; use winit::dpi::PhysicalPosition; -use crate::{ - math::{Aabb, Scalar, Triangle}, - window::Window, -}; +use crate::window::Window; /// The camera abstraction /// diff --git a/fj-app/src/graphics/config_ui.rs b/fj-app/src/graphics/config_ui.rs index 60f72f6bc..40c77904a 100644 --- a/fj-app/src/graphics/config_ui.rs +++ b/fj-app/src/graphics/config_ui.rs @@ -1,13 +1,12 @@ use std::collections::HashMap; +use fj_math::Aabb; use wgpu::util::StagingBelt; use wgpu_glyph::{ ab_glyph::{FontArc, InvalidFont}, GlyphBrush, GlyphBrushBuilder, Section, Text, }; -use crate::math::Aabb; - use super::draw_config::DrawConfig; #[derive(Debug)] @@ -70,7 +69,7 @@ impl ConfigUi { } /* Render size of model bounding box */ - let bbsize = aabb.size().components(); + let bbsize = aabb.size().components; let info = format!( "Model bounding box size: {:0.1} {:0.1} {:0.1}", bbsize[0].into_f32(), diff --git a/fj-app/src/graphics/geometries.rs b/fj-app/src/graphics/geometries.rs index 0fae44afe..7738962ce 100644 --- a/fj-app/src/graphics/geometries.rs +++ b/fj-app/src/graphics/geometries.rs @@ -1,6 +1,6 @@ -use crate::math::Aabb; use std::convert::TryInto; +use fj_math::Aabb; use wgpu::util::DeviceExt; use super::vertices::{Vertex, Vertices}; diff --git a/fj-app/src/graphics/renderer.rs b/fj-app/src/graphics/renderer.rs index 425992867..ad6b10d7c 100644 --- a/fj-app/src/graphics/renderer.rs +++ b/fj-app/src/graphics/renderer.rs @@ -1,12 +1,13 @@ use std::{io, mem::size_of}; +use fj_math::{Aabb, Point}; use thiserror::Error; use tracing::debug; use wgpu::util::DeviceExt as _; use wgpu_glyph::ab_glyph::InvalidFont; use winit::dpi::PhysicalSize; -use crate::{camera::Camera, math::Aabb, math::Point, window::Window}; +use crate::{camera::Camera, window::Window}; use super::{ config_ui::ConfigUi, draw_config::DrawConfig, drawables::Drawables, diff --git a/fj-app/src/graphics/vertices.rs b/fj-app/src/graphics/vertices.rs index 795387e11..0afc25b89 100644 --- a/fj-app/src/graphics/vertices.rs +++ b/fj-app/src/graphics/vertices.rs @@ -1,9 +1,9 @@ use bytemuck::{Pod, Zeroable}; +use fj_math::Triangle; use nalgebra::{vector, Point}; use crate::{ debug::DebugInfo, - math::Triangle, mesh::{Index, MeshMaker}, }; diff --git a/fj-app/src/input/handler.rs b/fj-app/src/input/handler.rs index 829580064..d479f1c62 100644 --- a/fj-app/src/input/handler.rs +++ b/fj-app/src/input/handler.rs @@ -1,5 +1,6 @@ use std::time::Instant; +use fj_math::Triangle; use winit::{ dpi::PhysicalPosition, event::{ @@ -10,7 +11,6 @@ use winit::{ use crate::{ camera::{Camera, FocusPoint}, - math::Triangle, window::Window, }; diff --git a/fj-app/src/kernel/algorithms/approximation.rs b/fj-app/src/kernel/algorithms/approximation.rs index f70569895..60baff4e2 100644 --- a/fj-app/src/kernel/algorithms/approximation.rs +++ b/fj-app/src/kernel/algorithms/approximation.rs @@ -1,12 +1,11 @@ use std::collections::HashSet; -use crate::{ - kernel::topology::{ - edges::{Cycle, Edge}, - faces::Face, - vertices::Vertex, - }, - math::{Point, Scalar, Segment}, +use fj_math::{Point, Scalar, Segment}; + +use crate::kernel::topology::{ + edges::{Cycle, Edge}, + faces::Face, + vertices::Vertex, }; /// An approximation of an edge, multiple edges, or a face @@ -132,15 +131,13 @@ fn approximate_edge( #[cfg(test)] mod tests { + use fj_math::{Point, Scalar, Segment}; use map_macro::set; - use crate::{ - kernel::{ - geometry::Surface, - shape::Shape, - topology::{edges::Cycle, faces::Face, vertices::Vertex}, - }, - math::{Point, Scalar, Segment}, + use crate::kernel::{ + geometry::Surface, + shape::Shape, + topology::{edges::Cycle, faces::Face, vertices::Vertex}, }; use super::{approximate_edge, Approximation}; diff --git a/fj-app/src/kernel/algorithms/sweep.rs b/fj-app/src/kernel/algorithms/sweep.rs index 5cd930787..09d5c8f66 100644 --- a/fj-app/src/kernel/algorithms/sweep.rs +++ b/fj-app/src/kernel/algorithms/sweep.rs @@ -1,16 +1,15 @@ use std::collections::HashMap; -use crate::{ - kernel::{ - geometry::{surfaces::Swept, Surface}, - shape::{Handle, Shape}, - topology::{ - edges::{Cycle, Edge}, - faces::Face, - vertices::Vertex, - }, +use fj_math::{Scalar, Transform, Triangle, Vector}; + +use crate::kernel::{ + geometry::{surfaces::Swept, Surface}, + shape::{Handle, Shape}, + topology::{ + edges::{Cycle, Edge}, + faces::Face, + vertices::Vertex, }, - math::{Scalar, Transform, Triangle, Vector}, }; use super::approximation::Approximation; @@ -322,13 +321,12 @@ impl Relation { #[cfg(test)] mod tests { - use crate::{ - kernel::{ - geometry::{surfaces::Swept, Surface}, - shape::{Handle, Shape}, - topology::{edges::Cycle, faces::Face, vertices::Vertex}, - }, - math::{Point, Scalar, Vector}, + use fj_math::{Point, Scalar, Vector}; + + use crate::kernel::{ + geometry::{surfaces::Swept, Surface}, + shape::{Handle, Shape}, + topology::{edges::Cycle, faces::Face, vertices::Vertex}, }; use super::sweep_shape; diff --git a/fj-app/src/kernel/algorithms/triangulation.rs b/fj-app/src/kernel/algorithms/triangulation.rs index 5efbb93ee..435fbf65d 100644 --- a/fj-app/src/kernel/algorithms/triangulation.rs +++ b/fj-app/src/kernel/algorithms/triangulation.rs @@ -1,7 +1,8 @@ +use fj_math::Scalar; use parry2d_f64::utils::point_in_triangle::{corner_direction, Orientation}; use spade::HasPosition; -use crate::{kernel::geometry, math::Scalar}; +use crate::kernel::geometry; /// Create a Delaunay triangulation of all points pub fn triangulate( diff --git a/fj-app/src/kernel/geometry/curves/circle.rs b/fj-app/src/kernel/geometry/curves/circle.rs index 1454321d9..68eceeedb 100644 --- a/fj-app/src/kernel/geometry/curves/circle.rs +++ b/fj-app/src/kernel/geometry/curves/circle.rs @@ -1,6 +1,6 @@ use std::f64::consts::PI; -use crate::math::{Point, Scalar, Transform, Vector}; +use fj_math::{Point, Scalar, Transform, Vector}; /// A circle #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -109,7 +109,7 @@ impl Circle { mod tests { use std::f64::consts::{FRAC_PI_2, PI}; - use crate::math::{Point, Scalar, Vector}; + use fj_math::{Point, Scalar, Vector}; use super::Circle; diff --git a/fj-app/src/kernel/geometry/curves/line.rs b/fj-app/src/kernel/geometry/curves/line.rs index 605d960d8..0ceeb225d 100644 --- a/fj-app/src/kernel/geometry/curves/line.rs +++ b/fj-app/src/kernel/geometry/curves/line.rs @@ -1,4 +1,4 @@ -use crate::math::{Point, Transform, Vector}; +use fj_math::{Point, Transform, Vector}; /// A line, defined by a point and a vector #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -83,11 +83,10 @@ mod tests { use std::f64::consts::FRAC_PI_2; use approx::assert_abs_diff_eq; + use fj_math::{Point, Vector}; use nalgebra::UnitQuaternion; use parry3d_f64::math::{Isometry, Translation}; - use crate::math::{Point, Vector}; - use super::Line; #[test] diff --git a/fj-app/src/kernel/geometry/curves/mod.rs b/fj-app/src/kernel/geometry/curves/mod.rs index 513d4a3c7..c422ee1a0 100644 --- a/fj-app/src/kernel/geometry/curves/mod.rs +++ b/fj-app/src/kernel/geometry/curves/mod.rs @@ -1,7 +1,7 @@ mod circle; mod line; -use crate::math::{Point, Scalar, Transform, Vector}; +use fj_math::{Point, Scalar, Transform, Vector}; pub use self::{circle::Circle, line::Line}; diff --git a/fj-app/src/kernel/geometry/points.rs b/fj-app/src/kernel/geometry/points.rs index 5a986e10d..9e260fb40 100644 --- a/fj-app/src/kernel/geometry/points.rs +++ b/fj-app/src/kernel/geometry/points.rs @@ -1,6 +1,6 @@ use std::ops::{Add, Sub}; -use crate::math::{self, Vector}; +use fj_math::Vector; /// A point that can be losslessly converted into its canonical form /// @@ -13,7 +13,7 @@ pub struct Point { /// The native form of the point is its representation in its native /// coordinate system. This could be a 1-dimensional curve, 2-dimensional /// surface, or 3-dimensional model coordinate system. - native: math::Point, + native: fj_math::Point, /// The canonical form of the point /// @@ -21,7 +21,7 @@ pub struct Point { /// kept here, unchanged, as the point is converted into other coordinate /// systems, it allows for a lossless conversion back into 3D coordinates, /// unaffected by floating point accuracy issues. - canonical: math::Point<3>, + canonical: fj_math::Point<3>, } impl Point { @@ -29,23 +29,26 @@ impl Point { /// /// Both the native and the canonical form must be provide. The caller must /// guarantee that both of them match. - pub fn new(native: math::Point, canonical: math::Point<3>) -> Self { + pub fn new( + native: fj_math::Point, + canonical: fj_math::Point<3>, + ) -> Self { Self { native, canonical } } /// Access the point's native form - pub fn native(&self) -> math::Point { + pub fn native(&self) -> fj_math::Point { self.native } /// Access the point's canonical form - pub fn canonical(&self) -> math::Point<3> { + pub fn canonical(&self) -> fj_math::Point<3> { self.canonical } } -impl From> for Point<3> { - fn from(point: math::Point<3>) -> Self { +impl From> for Point<3> { + fn from(point: fj_math::Point<3>) -> Self { Self::new(point, point) } } @@ -54,7 +57,7 @@ impl From> for Point<3> { // `Point`, or the conversion back to 3D would be broken. impl Add> for Point { - type Output = math::Point; + type Output = fj_math::Point; fn add(self, rhs: Vector) -> Self::Output { self.native.add(rhs) @@ -69,10 +72,10 @@ impl Sub for Point { } } -impl Sub> for Point { +impl Sub> for Point { type Output = Vector; - fn sub(self, rhs: math::Point) -> Self::Output { + fn sub(self, rhs: fj_math::Point) -> Self::Output { self.native.sub(rhs) } } diff --git a/fj-app/src/kernel/geometry/surfaces/mod.rs b/fj-app/src/kernel/geometry/surfaces/mod.rs index d1f3d360c..1d060d0a2 100644 --- a/fj-app/src/kernel/geometry/surfaces/mod.rs +++ b/fj-app/src/kernel/geometry/surfaces/mod.rs @@ -2,12 +2,10 @@ pub mod swept; pub use self::swept::Swept; +use fj_math::{Point, Transform, Vector}; use nalgebra::vector; -use crate::{ - kernel::geometry, - math::{Point, Transform, Vector}, -}; +use crate::kernel::geometry; use super::{Curve, Line}; diff --git a/fj-app/src/kernel/geometry/surfaces/swept.rs b/fj-app/src/kernel/geometry/surfaces/swept.rs index 675443162..b8f6149fd 100644 --- a/fj-app/src/kernel/geometry/surfaces/swept.rs +++ b/fj-app/src/kernel/geometry/surfaces/swept.rs @@ -1,7 +1,6 @@ -use crate::{ - kernel::geometry::Curve, - math::{Point, Transform, Vector}, -}; +use fj_math::{Point, Transform, Vector}; + +use crate::kernel::geometry::Curve; /// A surface that was swept from a curve #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -56,10 +55,9 @@ impl Swept { #[cfg(test)] mod tests { - use crate::{ - kernel::geometry::{Curve, Line}, - math::{Point, Vector}, - }; + use fj_math::{Point, Vector}; + + use crate::kernel::geometry::{Curve, Line}; use super::Swept; diff --git a/fj-app/src/kernel/shape/geometry.rs b/fj-app/src/kernel/shape/geometry.rs index 9b8119157..873ce7d13 100644 --- a/fj-app/src/kernel/shape/geometry.rs +++ b/fj-app/src/kernel/shape/geometry.rs @@ -1,9 +1,8 @@ -use crate::{ - kernel::{ - geometry::{Curve, Surface}, - topology::faces::Face, - }, - math::{Point, Transform}, +use fj_math::{Point, Transform}; + +use crate::kernel::{ + geometry::{Curve, Surface}, + topology::faces::Face, }; use super::{ diff --git a/fj-app/src/kernel/shape/mod.rs b/fj-app/src/kernel/shape/mod.rs index 84b6f3ea9..71965f066 100644 --- a/fj-app/src/kernel/shape/mod.rs +++ b/fj-app/src/kernel/shape/mod.rs @@ -4,6 +4,8 @@ pub mod iter; pub mod topology; pub mod validate; +use fj_math::{Point, Scalar}; + pub use self::{ geometry::Geometry, handle::Handle, @@ -12,8 +14,6 @@ pub use self::{ validate::{ValidationError, ValidationResult}, }; -use crate::math::{Point, Scalar}; - use super::{ geometry::{Curve, Surface}, topology::{ diff --git a/fj-app/src/kernel/shape/topology.rs b/fj-app/src/kernel/shape/topology.rs index c2ef2eab5..147f97a9d 100644 --- a/fj-app/src/kernel/shape/topology.rs +++ b/fj-app/src/kernel/shape/topology.rs @@ -1,5 +1,7 @@ use std::collections::HashSet; +use fj_math::{Point, Scalar, Triangle, Vector}; + use crate::{ debug::DebugInfo, kernel::{ @@ -10,7 +12,6 @@ use crate::{ vertices::Vertex, }, }, - math::{Point, Scalar, Triangle, Vector}, }; use super::{ @@ -260,17 +261,16 @@ impl Topology<'_> { mod tests { use std::ops::{Deref, DerefMut}; - use crate::{ - kernel::{ - geometry::{Curve, Line, Surface}, - shape::{handle::Handle, Shape, ValidationError}, - topology::{ - edges::{Cycle, Edge}, - faces::Face, - vertices::Vertex, - }, + use fj_math::{Point, Scalar}; + + use crate::kernel::{ + geometry::{Curve, Line, Surface}, + shape::{handle::Handle, Shape, ValidationError}, + topology::{ + edges::{Cycle, Edge}, + faces::Face, + vertices::Vertex, }, - math::{Point, Scalar}, }; const MIN_DISTANCE: f64 = 5e-7; diff --git a/fj-app/src/kernel/shapes/circle.rs b/fj-app/src/kernel/shapes/circle.rs index a3a107f4c..5f283a5a7 100644 --- a/fj-app/src/kernel/shapes/circle.rs +++ b/fj-app/src/kernel/shapes/circle.rs @@ -1,3 +1,5 @@ +use fj_math::{Aabb, Point, Scalar}; + use crate::{ debug::DebugInfo, kernel::{ @@ -5,7 +7,6 @@ use crate::{ shape::Shape, topology::{edges::Cycle, faces::Face}, }, - math::{Aabb, Point, Scalar}, }; use super::ToShape; diff --git a/fj-app/src/kernel/shapes/difference_2d.rs b/fj-app/src/kernel/shapes/difference_2d.rs index d4a5976b1..a2ea256f0 100644 --- a/fj-app/src/kernel/shapes/difference_2d.rs +++ b/fj-app/src/kernel/shapes/difference_2d.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use fj_math::{Aabb, Scalar}; + use crate::{ debug::DebugInfo, kernel::{ @@ -10,7 +12,6 @@ use crate::{ vertices::Vertex, }, }, - math::{Aabb, Scalar}, }; use super::ToShape; diff --git a/fj-app/src/kernel/shapes/group.rs b/fj-app/src/kernel/shapes/group.rs index fe63c48ff..b73b2b73c 100644 --- a/fj-app/src/kernel/shapes/group.rs +++ b/fj-app/src/kernel/shapes/group.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use fj_math::{Aabb, Scalar}; + use crate::{ debug::DebugInfo, kernel::{ @@ -10,7 +12,6 @@ use crate::{ vertices::Vertex, }, }, - math::{Aabb, Scalar}, }; use super::ToShape; diff --git a/fj-app/src/kernel/shapes/mod.rs b/fj-app/src/kernel/shapes/mod.rs index 700b07a0c..737993891 100644 --- a/fj-app/src/kernel/shapes/mod.rs +++ b/fj-app/src/kernel/shapes/mod.rs @@ -5,10 +5,9 @@ pub mod sketch; pub mod sweep; pub mod transform; -use crate::{ - debug::DebugInfo, - math::{Aabb, Scalar}, -}; +use fj_math::{Aabb, Scalar}; + +use crate::debug::DebugInfo; use super::shape::Shape; diff --git a/fj-app/src/kernel/shapes/sketch.rs b/fj-app/src/kernel/shapes/sketch.rs index b92d74bfb..d6f718f6f 100644 --- a/fj-app/src/kernel/shapes/sketch.rs +++ b/fj-app/src/kernel/shapes/sketch.rs @@ -1,3 +1,5 @@ +use fj_math::{Aabb, Point, Scalar}; + use crate::{ debug::DebugInfo, kernel::{ @@ -5,7 +7,6 @@ use crate::{ shape::Shape, topology::{edges::Cycle, faces::Face, vertices::Vertex}, }, - math::{Aabb, Point, Scalar}, }; use super::ToShape; diff --git a/fj-app/src/kernel/shapes/sweep.rs b/fj-app/src/kernel/shapes/sweep.rs index 9ba88eb63..0f7f73913 100644 --- a/fj-app/src/kernel/shapes/sweep.rs +++ b/fj-app/src/kernel/shapes/sweep.rs @@ -1,7 +1,8 @@ +use fj_math::{Aabb, Scalar, Vector}; + use crate::{ debug::DebugInfo, kernel::{algorithms::sweep::sweep_shape, shape::Shape}, - math::{Aabb, Scalar, Vector}, }; use super::ToShape; diff --git a/fj-app/src/kernel/shapes/transform.rs b/fj-app/src/kernel/shapes/transform.rs index 966f2a042..2b3e8a26e 100644 --- a/fj-app/src/kernel/shapes/transform.rs +++ b/fj-app/src/kernel/shapes/transform.rs @@ -1,10 +1,7 @@ +use fj_math::{Aabb, Scalar, Transform}; use parry3d_f64::math::Isometry; -use crate::{ - debug::DebugInfo, - kernel::shape::Shape, - math::{Aabb, Scalar, Transform}, -}; +use crate::{debug::DebugInfo, kernel::shape::Shape}; use super::ToShape; diff --git a/fj-app/src/kernel/topology/faces.rs b/fj-app/src/kernel/topology/faces.rs index a2255d8c6..0bdce626a 100644 --- a/fj-app/src/kernel/topology/faces.rs +++ b/fj-app/src/kernel/topology/faces.rs @@ -3,6 +3,7 @@ use std::{ hash::{Hash, Hasher}, }; +use fj_math::{Aabb, Scalar, Segment, Triangle}; use parry2d_f64::query::{Ray as Ray2, RayCast as _}; use parry3d_f64::query::Ray as Ray3; @@ -15,7 +16,6 @@ use crate::{ geometry::Surface, shape::Handle, }, - math::{Aabb, Scalar, Segment, Triangle}, }; use super::edges::Cycle; diff --git a/fj-app/src/kernel/topology/vertices.rs b/fj-app/src/kernel/topology/vertices.rs index e71ef6ecd..76c85f38f 100644 --- a/fj-app/src/kernel/topology/vertices.rs +++ b/fj-app/src/kernel/topology/vertices.rs @@ -1,6 +1,8 @@ use std::hash::Hash; -use crate::{kernel::shape::Handle, math::Point}; +use fj_math::Point; + +use crate::kernel::shape::Handle; /// A vertex /// diff --git a/fj-app/src/main.rs b/fj-app/src/main.rs index 85bd4bb9e..2f8237065 100644 --- a/fj-app/src/main.rs +++ b/fj-app/src/main.rs @@ -5,7 +5,6 @@ mod debug; mod graphics; mod input; mod kernel; -mod math; mod mesh; mod model; mod window; @@ -15,6 +14,7 @@ use std::ffi::OsStr; use std::path::PathBuf; use std::{collections::HashMap, sync::mpsc, time::Instant}; +use fj_math::Scalar; use futures::executor::block_on; use notify::Watcher as _; use tracing::trace; @@ -25,7 +25,6 @@ use winit::{ event_loop::{ControlFlow, EventLoop}, }; -use crate::math::Scalar; use crate::{ args::Args, camera::Camera, @@ -105,7 +104,7 @@ fn main() -> anyhow::Result<()> { // look at the smallest non-zero extent of the bounding box and divide that // by some value. let mut min_extent = Scalar::MAX; - for extent in aabb.size().components() { + for extent in aabb.size().components { if extent > Scalar::ZERO && extent < min_extent { min_extent = extent; } diff --git a/fj-app/src/math/coordinates.rs b/fj-app/src/math/coordinates.rs deleted file mode 100644 index fff693a6e..000000000 --- a/fj-app/src/math/coordinates.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::Scalar; - -/// 1-dimensional curve coordinates -#[repr(C)] -pub struct T { - pub t: Scalar, -} - -/// 2-dimensional surface coordinates -#[repr(C)] -pub struct Uv { - pub u: Scalar, - pub v: Scalar, -} - -/// 3-dimensional model coordinates -#[repr(C)] -pub struct Xyz { - pub x: Scalar, - pub y: Scalar, - pub z: Scalar, -} diff --git a/fj-app/src/math/mod.rs b/fj-app/src/math/mod.rs deleted file mode 100644 index ff312f9cc..000000000 --- a/fj-app/src/math/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod aabb; -pub mod coordinates; -pub mod point; -pub mod scalar; -pub mod segment; -pub mod transform; -pub mod triangle; -pub mod vector; - -pub use self::{ - aabb::Aabb, point::Point, scalar::Scalar, segment::Segment, - transform::Transform, triangle::Triangle, vector::Vector, -}; diff --git a/fj-math/Cargo.toml b/fj-math/Cargo.toml new file mode 100644 index 000000000..34c7dffab --- /dev/null +++ b/fj-math/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "fj-math" +version = "0.5.0" +edition = "2021" + +description = "The world needs another CAD program." +readme = "../README.md" +repository = "https://github.com/hannobraun/fornjot" +license = "0BSD" +keywords = ["cad", "programmatic", "code-cad"] +categories = ["mathematics"] + +[dependencies] +approx = "0.5.1" +decorum = "0.3.1" +nalgebra = "0.30.0" +num-traits = "0.2.14" +parry2d-f64 = "0.8.0" +parry3d-f64 = "0.8.0" diff --git a/fj-app/src/math/aabb.rs b/fj-math/src/aabb.rs similarity index 100% rename from fj-app/src/math/aabb.rs rename to fj-math/src/aabb.rs diff --git a/fj-math/src/coordinates.rs b/fj-math/src/coordinates.rs new file mode 100644 index 000000000..b56cc52c8 --- /dev/null +++ b/fj-math/src/coordinates.rs @@ -0,0 +1,31 @@ +use super::Scalar; + +/// 1-dimensional curve coordinates +#[repr(C)] +pub struct T { + /// The single coordinate of the 1-dimensional curve coordinates + pub t: Scalar, +} + +/// 2-dimensional surface coordinates +#[repr(C)] +pub struct Uv { + /// The first coordinate of the 2-dimensional surface coordinates + pub u: Scalar, + + /// The second coordinate of the 2-dimensional surface coordinates + pub v: Scalar, +} + +/// 3-dimensional model coordinates +#[repr(C)] +pub struct Xyz { + /// The first coordinate of the 3-dimensional model coordinates + pub x: Scalar, + + /// The second coordinate of the 3-dimensional model coordinates + pub y: Scalar, + + /// The third coordinate of the 3-dimensional model coordinates + pub z: Scalar, +} diff --git a/fj-math/src/lib.rs b/fj-math/src/lib.rs new file mode 100644 index 000000000..482580a1c --- /dev/null +++ b/fj-math/src/lib.rs @@ -0,0 +1,48 @@ +//! Math primitives for the Fornjot ecosystem +//! +//! This crates provides basic math types for the Fornjot ecosystem. It is built +//! on [nalgebra] and [Parry], but provides an interface that is specifically +//! tailored to the needs of Fornjot. +//! +//! # Failing [`From`]/[`Into`] implementations +//! +//! Please note that any [`From`]/[`Into`] implementation that convert floating +//! point numbers into [`Scalar`] can panic. These conversions call +//! [`Scalar::from_f64`] internally and panic under the same conditions. This +//! affects [`Scalar`] itself, but also any other types in this crate that +//! provide conversions from types that involve `f64`. +//! +//! This explicitly goes against the mandate of [`From`]/[`Into`], whose +//! documentation states that implementations must not fail. This is a +//! deliberate design decision. The intended use case of `Scalar` is math code +//! that considers NaN results a bug, not a recoverable error. +//! +//! For this use case, having easy conversions available is an advantage, and +//! explicit `unwrap`/`expect` calls would add nothing. In addition, the +//! [`From`]/[`Into`] documentation fails to provide any reasons for its +//! mandate. +//! +//! [nalgebra]: https://nalgebra.org/ +//! [Parry]: https://www.parry.rs/ + +#![deny(missing_docs)] + +mod aabb; +mod coordinates; +mod point; +mod scalar; +mod segment; +mod transform; +mod triangle; +mod vector; + +pub use self::{ + aabb::Aabb, + coordinates::{Uv, Xyz, T}, + point::Point, + scalar::Scalar, + segment::Segment, + transform::Transform, + triangle::Triangle, + vector::Vector, +}; diff --git a/fj-app/src/math/point.rs b/fj-math/src/point.rs similarity index 94% rename from fj-app/src/math/point.rs rename to fj-math/src/point.rs index 76b65799f..5d7ac9891 100644 --- a/fj-app/src/math/point.rs +++ b/fj-math/src/point.rs @@ -7,14 +7,11 @@ use super::{ /// An n-dimensional point /// -/// The dimensionality is defined by the const generic argument `D`. -/// -/// # Implementation Note -/// -/// The goal of this type is to eventually implement `Eq` and `Hash`, making it -/// easier to work with vectors. This is a work in progress. +/// The dimensionality of the point is defined by the const generic `D` +/// parameter. #[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Point { + /// The coordinates of the point pub coords: Vector, } diff --git a/fj-app/src/math/scalar.rs b/fj-math/src/scalar.rs similarity index 92% rename from fj-app/src/math/scalar.rs rename to fj-math/src/scalar.rs index bf25ec3d3..d673739e8 100644 --- a/fj-app/src/math/scalar.rs +++ b/fj-math/src/scalar.rs @@ -8,23 +8,6 @@ use decorum::R64; /// value is not NaN. This allows `Scalar` to provide implementations of [`Eq`], /// [`Ord`], and [`Hash`], enabling `Scalar` (and types built on top of it), to /// be used as keys in hash maps, hash sets, and similar types. -/// -/// # Failing `From`/`Into` implementations -/// -/// Please note that the [`From`]/[`Into`] implementation that convert floating -/// point numbers into `Scalar` can panic. These conversions call -/// [`Scalar::from_f64`] internally and panic under the same conditions. -/// -/// This explicitly goes against the mandate of [`From`]/[`Into`], whose -/// documentation mandate that implementations must not fail. This is a -/// deliberate design decision. The intended use case of `Scalar` is math code -/// that considers non-finite floating point values a bug, not a recoverable -/// error. -/// -/// For this use case, having easy conversions available is an advantage, and -/// explicit `unwrap`/`expect` calls would add nothing. In addition, the mandate -/// not to fail is not motivated in any way, in the [`From`]/[`Into`] -/// documentation. #[derive(Clone, Copy)] pub struct Scalar(f64); @@ -46,6 +29,8 @@ impl Scalar { /// Construct a `Scalar` from an `f64` /// + /// # Panics + /// /// Panics, if `scalar` is NaN. pub fn from_f64(scalar: f64) -> Self { if scalar.is_nan() { diff --git a/fj-app/src/math/segment.rs b/fj-math/src/segment.rs similarity index 62% rename from fj-app/src/math/segment.rs rename to fj-math/src/segment.rs index de3c31d86..eb2d9e3ab 100644 --- a/fj-app/src/math/segment.rs +++ b/fj-math/src/segment.rs @@ -3,48 +3,56 @@ use std::fmt; use super::Point; /// A line segment, defined by its two end points +/// +/// The dimensionality of the segment is defined by the const generic `D` +/// parameter. #[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Segment { - a: Point, - b: Point, + points: [Point; 2], } impl Segment { + /// Construct a segment from two points + /// + /// # Panics + /// + /// Panics, if the points are coincident. + pub fn from_points(points: [Point; 2]) -> Self { + let [a, b] = points; + + assert!(a != b, "Invalid segment; both points are identical {a:?}"); + + Self { points } + } + /// Access the points of the segment pub fn points(&self) -> [Point; 2] { - [self.a, self.b] + self.points } } impl Segment<2> { /// Convert the 2-dimensional segment to a Parry segment pub fn to_parry(self) -> parry2d_f64::shape::Segment { - [self.a.to_na(), self.b.to_na()].into() + self.points.map(|point| point.to_na()).into() } } impl Segment<3> { /// Convert the 3-dimensional segment to a Parry segment pub fn to_parry(self) -> parry3d_f64::shape::Segment { - [self.a.to_na(), self.b.to_na()].into() + self.points.map(|point| point.to_na()).into() } } impl From<[Point; 2]> for Segment { fn from(points: [Point; 2]) -> Self { - let [a, b] = points; - - assert!(a != b, "Invalid segment; both points are identical {a:?}"); - - Self { - a: points[0], - b: points[1], - } + Self::from_points(points) } } impl fmt::Debug for Segment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[{:?} -> {:?}]", self.a, self.b) + write!(f, "[{:?} -> {:?}]", self.points[0], self.points[1]) } } diff --git a/fj-app/src/math/transform.rs b/fj-math/src/transform.rs similarity index 100% rename from fj-app/src/math/transform.rs rename to fj-math/src/transform.rs diff --git a/fj-app/src/math/triangle.rs b/fj-math/src/triangle.rs similarity index 89% rename from fj-app/src/math/triangle.rs rename to fj-math/src/triangle.rs index c2b26b902..8024c6b9d 100644 --- a/fj-app/src/math/triangle.rs +++ b/fj-math/src/triangle.rs @@ -1,6 +1,9 @@ use super::{Point, Scalar}; /// A triangle +/// +/// The dimensionality of the triangle is defined by the const generic `D` +/// parameter. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Triangle { points: [Point; 3], @@ -8,6 +11,28 @@ pub struct Triangle { } impl Triangle { + /// Construct a triangle from three points + /// + /// # Panics + /// + /// Panics, if the points don't form a triangle. + pub fn from_points(points: [Point; 3]) -> Self { + let area = { + let [a, b, c] = points.map(Point::to_xyz); + (b - a).cross(&(c - a)).magnitude() + }; + + // A triangle is not valid if it doesn't span any area + if area != Scalar::from(0.0) { + Self { + points, + color: [255, 0, 0, 255], + } + } else { + panic!("Invalid Triangle specified"); + } + } + /// Access the triangle's points pub fn points(&self) -> [Point; 3] { self.points @@ -33,27 +58,15 @@ impl Triangle<3> { impl From<[Point; 3]> for Triangle { fn from(points: [Point; 3]) -> Self { - let area = { - let [a, b, c] = points.map(Point::to_xyz); - (b - a).cross(&(c - a)).magnitude() - }; - - // A triangle is not valid if it doesn't span any area - if area != Scalar::from(0.0) { - Self { - points, - color: [255, 0, 0, 255], - } - } else { - panic!("Invalid Triangle specified"); - } + Self::from_points(points) } } #[cfg(test)] mod tests { + use crate::Point; + use super::Triangle; - use crate::math::Point; #[test] fn valid_triangle_2d() { diff --git a/fj-app/src/math/vector.rs b/fj-math/src/vector.rs similarity index 77% rename from fj-app/src/math/vector.rs rename to fj-math/src/vector.rs index 82bd89bf4..88adcddc2 100644 --- a/fj-app/src/math/vector.rs +++ b/fj-math/src/vector.rs @@ -7,34 +7,42 @@ use super::{ /// An n-dimensional vector /// -/// The dimensionality is defined by the const generic argument `D`. -/// -/// # Implementation Note -/// -/// The goal of this type is to eventually implement `Eq` and `Hash`, making it -/// easier to work with vectors. This is a work in progress. +/// The dimensionality of the vector is defined by the const generic `D` +/// parameter. #[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct Vector(pub [Scalar; D]); +pub struct Vector { + /// The vector components + pub components: [Scalar; D], +} impl Vector { - /// Construct a `Vector` from an array - pub fn from_array(array: [f64; D]) -> Self { - Self(array.map(Scalar::from_f64)) + /// Construct a `Vector` from `f64` components + /// + /// # Panics + /// + /// Panics, if the components can not be converted to [`Scalar`]. See + /// [`Scalar::from_f64`], which this method uses internally. + pub fn from_components_f64(components: [f64; D]) -> Self { + Self { + components: components.map(Scalar::from_f64), + } } /// Construct a `Vector` from an nalgebra vector pub fn from_na(vector: nalgebra::SVector) -> Self { - Self::from_array(vector.into()) + Self::from_components_f64(vector.into()) } /// Convert the vector into an nalgebra vector pub fn to_na(self) -> nalgebra::SVector { - self.0.map(Scalar::into_f64).into() + self.components.map(Scalar::into_f64).into() } /// Convert to a 1-dimensional vector pub fn to_t(self) -> Vector<1> { - Vector([self.0[0]]) + Vector { + components: [self.components[0]], + } } /// Convert the vector into a 3-dimensional vector @@ -47,14 +55,14 @@ impl Vector { pub fn to_xyz(self) -> Vector<3> { let zero = Scalar::ZERO; - let components = match self.0.as_slice() { + let components = match self.components.as_slice() { [] => [zero, zero, zero], &[t] => [t, zero, zero], &[u, v] => [u, v, zero], &[x, y, z, ..] => [x, y, z], }; - Vector(components) + Vector { components } } /// Compute the magnitude of the vector @@ -71,11 +79,6 @@ impl Vector { pub fn dot(&self, other: &Self) -> Scalar { self.to_na().dot(&other.to_na()).into() } - - /// Access an iterator over the vector's components - pub fn components(&self) -> [Scalar; D] { - self.0 - } } impl Vector<3> { @@ -94,7 +97,7 @@ impl ops::Deref for Vector<1> { type Target = T; fn deref(&self) -> &Self::Target { - let ptr = self.0.as_ptr() as *const Self::Target; + let ptr = self.components.as_ptr() as *const Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -106,7 +109,7 @@ impl ops::Deref for Vector<2> { type Target = Uv; fn deref(&self) -> &Self::Target { - let ptr = self.0.as_ptr() as *const Self::Target; + let ptr = self.components.as_ptr() as *const Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -118,7 +121,7 @@ impl ops::Deref for Vector<3> { type Target = Xyz; fn deref(&self) -> &Self::Target { - let ptr = self.0.as_ptr() as *const Self::Target; + let ptr = self.components.as_ptr() as *const Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -128,7 +131,7 @@ impl ops::Deref for Vector<3> { impl ops::DerefMut for Vector<1> { fn deref_mut(&mut self) -> &mut Self::Target { - let ptr = self.0.as_mut_ptr() as *mut Self::Target; + let ptr = self.components.as_mut_ptr() as *mut Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -138,7 +141,7 @@ impl ops::DerefMut for Vector<1> { impl ops::DerefMut for Vector<2> { fn deref_mut(&mut self) -> &mut Self::Target { - let ptr = self.0.as_mut_ptr() as *mut Self::Target; + let ptr = self.components.as_mut_ptr() as *mut Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -148,7 +151,7 @@ impl ops::DerefMut for Vector<2> { impl ops::DerefMut for Vector<3> { fn deref_mut(&mut self) -> &mut Self::Target { - let ptr = self.0.as_mut_ptr() as *mut Self::Target; + let ptr = self.components.as_mut_ptr() as *mut Self::Target; // This is sound. We've created this pointer from a valid instance, that // has the same size and layout as the target. @@ -157,14 +160,14 @@ impl ops::DerefMut for Vector<3> { } impl From<[Scalar; D]> for Vector { - fn from(array: [Scalar; D]) -> Self { - Self(array) + fn from(components: [Scalar; D]) -> Self { + Self { components } } } impl From<[f64; D]> for Vector { - fn from(array: [f64; D]) -> Self { - Self::from_array(array) + fn from(components: [f64; D]) -> Self { + Self::from_components_f64(components) } } @@ -176,19 +179,19 @@ impl From> for Vector { impl From> for [f32; D] { fn from(vector: Vector) -> Self { - vector.0.map(|scalar| scalar.into_f32()) + vector.components.map(|scalar| scalar.into_f32()) } } impl From> for [f64; D] { fn from(vector: Vector) -> Self { - vector.0.map(|scalar| scalar.into_f64()) + vector.components.map(|scalar| scalar.into_f64()) } } impl From> for [Scalar; D] { fn from(vector: Vector) -> Self { - vector.0 + vector.components } } @@ -224,7 +227,7 @@ impl ops::Div for Vector { impl fmt::Debug for Vector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) + self.components.fmt(f) } } @@ -236,13 +239,13 @@ impl approx::AbsDiffEq for Vector { } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { - self.0.abs_diff_eq(&other.0, epsilon) + self.components.abs_diff_eq(&other.components, epsilon) } } #[cfg(test)] mod tests { - use crate::math::Vector; + use crate::Vector; #[test] fn to_xyz() {