mirror of
https://github.com/hannobraun/Fornjot
synced 2025-02-27 01:25:52 +00:00
Merge pull request #374 from hannobraun/math
Extract math code into dedicated crate
This commit is contained in:
commit
15294c2ca2
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -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"
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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"]
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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(),
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
|
@ -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},
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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<const D: usize> {
|
||||
/// 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<D>,
|
||||
native: fj_math::Point<D>,
|
||||
|
||||
/// The canonical form of the point
|
||||
///
|
||||
@ -21,7 +21,7 @@ pub struct Point<const D: usize> {
|
||||
/// 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<const D: usize> Point<D> {
|
||||
@ -29,23 +29,26 @@ impl<const D: usize> Point<D> {
|
||||
///
|
||||
/// 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<D>, canonical: math::Point<3>) -> Self {
|
||||
pub fn new(
|
||||
native: fj_math::Point<D>,
|
||||
canonical: fj_math::Point<3>,
|
||||
) -> Self {
|
||||
Self { native, canonical }
|
||||
}
|
||||
|
||||
/// Access the point's native form
|
||||
pub fn native(&self) -> math::Point<D> {
|
||||
pub fn native(&self) -> fj_math::Point<D> {
|
||||
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<math::Point<3>> for Point<3> {
|
||||
fn from(point: math::Point<3>) -> Self {
|
||||
impl From<fj_math::Point<3>> for Point<3> {
|
||||
fn from(point: fj_math::Point<3>) -> Self {
|
||||
Self::new(point, point)
|
||||
}
|
||||
}
|
||||
@ -54,7 +57,7 @@ impl From<math::Point<3>> for Point<3> {
|
||||
// `Point`, or the conversion back to 3D would be broken.
|
||||
|
||||
impl<const D: usize> Add<Vector<D>> for Point<D> {
|
||||
type Output = math::Point<D>;
|
||||
type Output = fj_math::Point<D>;
|
||||
|
||||
fn add(self, rhs: Vector<D>) -> Self::Output {
|
||||
self.native.add(rhs)
|
||||
@ -69,10 +72,10 @@ impl<const D: usize> Sub<Self> for Point<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> Sub<math::Point<D>> for Point<D> {
|
||||
impl<const D: usize> Sub<fj_math::Point<D>> for Point<D> {
|
||||
type Output = Vector<D>;
|
||||
|
||||
fn sub(self, rhs: math::Point<D>) -> Self::Output {
|
||||
fn sub(self, rhs: fj_math::Point<D>) -> Self::Output {
|
||||
self.native.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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::{
|
||||
|
@ -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::{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
@ -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,
|
||||
};
|
19
fj-math/Cargo.toml
Normal file
19
fj-math/Cargo.toml
Normal file
@ -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"
|
31
fj-math/src/coordinates.rs
Normal file
31
fj-math/src/coordinates.rs
Normal file
@ -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,
|
||||
}
|
48
fj-math/src/lib.rs
Normal file
48
fj-math/src/lib.rs
Normal file
@ -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,
|
||||
};
|
@ -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<const D: usize> {
|
||||
/// The coordinates of the point
|
||||
pub coords: Vector<D>,
|
||||
}
|
||||
|
@ -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() {
|
@ -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<const D: usize> {
|
||||
a: Point<D>,
|
||||
b: Point<D>,
|
||||
points: [Point<D>; 2],
|
||||
}
|
||||
|
||||
impl<const D: usize> Segment<D> {
|
||||
/// Construct a segment from two points
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics, if the points are coincident.
|
||||
pub fn from_points(points: [Point<D>; 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<D>; 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<const D: usize> From<[Point<D>; 2]> for Segment<D> {
|
||||
fn from(points: [Point<D>; 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<const D: usize> fmt::Debug for Segment<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "[{:?} -> {:?}]", self.a, self.b)
|
||||
write!(f, "[{:?} -> {:?}]", self.points[0], self.points[1])
|
||||
}
|
||||
}
|
@ -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<const D: usize> {
|
||||
points: [Point<D>; 3],
|
||||
@ -8,6 +11,28 @@ pub struct Triangle<const D: usize> {
|
||||
}
|
||||
|
||||
impl<const D: usize> Triangle<D> {
|
||||
/// Construct a triangle from three points
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics, if the points don't form a triangle.
|
||||
pub fn from_points(points: [Point<D>; 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<D>; 3] {
|
||||
self.points
|
||||
@ -33,27 +58,15 @@ impl Triangle<3> {
|
||||
|
||||
impl<const D: usize> From<[Point<D>; 3]> for Triangle<D> {
|
||||
fn from(points: [Point<D>; 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() {
|
@ -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<const D: usize>(pub [Scalar; D]);
|
||||
pub struct Vector<const D: usize> {
|
||||
/// The vector components
|
||||
pub components: [Scalar; D],
|
||||
}
|
||||
|
||||
impl<const D: usize> Vector<D> {
|
||||
/// 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<f64, D>) -> 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<f64, D> {
|
||||
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<const D: usize> Vector<D> {
|
||||
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<const D: usize> Vector<D> {
|
||||
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<const D: usize> From<[Scalar; D]> for Vector<D> {
|
||||
fn from(array: [Scalar; D]) -> Self {
|
||||
Self(array)
|
||||
fn from(components: [Scalar; D]) -> Self {
|
||||
Self { components }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> From<[f64; D]> for Vector<D> {
|
||||
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<const D: usize> From<nalgebra::SVector<f64, D>> for Vector<D> {
|
||||
|
||||
impl<const D: usize> From<Vector<D>> for [f32; D] {
|
||||
fn from(vector: Vector<D>) -> Self {
|
||||
vector.0.map(|scalar| scalar.into_f32())
|
||||
vector.components.map(|scalar| scalar.into_f32())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> From<Vector<D>> for [f64; D] {
|
||||
fn from(vector: Vector<D>) -> Self {
|
||||
vector.0.map(|scalar| scalar.into_f64())
|
||||
vector.components.map(|scalar| scalar.into_f64())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: usize> From<Vector<D>> for [Scalar; D] {
|
||||
fn from(vector: Vector<D>) -> Self {
|
||||
vector.0
|
||||
vector.components
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +227,7 @@ impl<const D: usize> ops::Div<Scalar> for Vector<D> {
|
||||
|
||||
impl<const D: usize> fmt::Debug for Vector<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.components.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,13 +239,13 @@ impl<const D: usize> approx::AbsDiffEq for Vector<D> {
|
||||
}
|
||||
|
||||
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() {
|
Loading…
Reference in New Issue
Block a user