mirror of
https://github.com/hannobraun/Fornjot
synced 2025-02-27 09:35: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",
|
"approx 0.5.1",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"clap",
|
"clap",
|
||||||
"decorum",
|
|
||||||
"figment",
|
"figment",
|
||||||
"fj",
|
"fj",
|
||||||
|
"fj-math",
|
||||||
"futures",
|
"futures",
|
||||||
"libloading",
|
"libloading",
|
||||||
"map-macro",
|
"map-macro",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
"notify",
|
"notify",
|
||||||
"num-traits",
|
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.0",
|
||||||
"parry2d-f64",
|
"parry2d-f64",
|
||||||
"parry3d-f64",
|
"parry3d-f64",
|
||||||
@ -652,6 +651,18 @@ dependencies = [
|
|||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fj-math"
|
||||||
|
version = "0.5.0"
|
||||||
|
dependencies = [
|
||||||
|
"approx 0.5.1",
|
||||||
|
"decorum",
|
||||||
|
"nalgebra",
|
||||||
|
"num-traits",
|
||||||
|
"parry2d-f64",
|
||||||
|
"parry3d-f64",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
|
@ -3,6 +3,7 @@ resolver = "2"
|
|||||||
members = [
|
members = [
|
||||||
"fj",
|
"fj",
|
||||||
"fj-app",
|
"fj-app",
|
||||||
|
"fj-math",
|
||||||
|
|
||||||
"models/cuboid",
|
"models/cuboid",
|
||||||
"models/group",
|
"models/group",
|
||||||
@ -11,4 +12,7 @@ members = [
|
|||||||
|
|
||||||
"release-operator",
|
"release-operator",
|
||||||
]
|
]
|
||||||
default-members = ["fj-app"]
|
default-members = [
|
||||||
|
"fj-app",
|
||||||
|
"fj-math",
|
||||||
|
]
|
||||||
|
@ -4,7 +4,7 @@ version = "0.5.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
description = "The world needs another CAD program."
|
description = "The world needs another CAD program."
|
||||||
readme = "README.md"
|
readme = "../README.md"
|
||||||
repository = "https://github.com/hannobraun/fornjot"
|
repository = "https://github.com/hannobraun/fornjot"
|
||||||
license = "0BSD"
|
license = "0BSD"
|
||||||
keywords = ["cad", "programmatic", "code-cad"]
|
keywords = ["cad", "programmatic", "code-cad"]
|
||||||
@ -15,13 +15,11 @@ categories = ["mathematics", "rendering"]
|
|||||||
anyhow = "1.0.56"
|
anyhow = "1.0.56"
|
||||||
approx = "0.5.1"
|
approx = "0.5.1"
|
||||||
bytemuck = "1.8.0"
|
bytemuck = "1.8.0"
|
||||||
decorum = "0.3.1"
|
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
libloading = "0.7.2"
|
libloading = "0.7.2"
|
||||||
map-macro = "0.2.0"
|
map-macro = "0.2.0"
|
||||||
nalgebra = "0.30.0"
|
nalgebra = "0.30.0"
|
||||||
notify = "5.0.0-pre.14"
|
notify = "5.0.0-pre.14"
|
||||||
num-traits = "0.2.14"
|
|
||||||
parking_lot = "0.12.0"
|
parking_lot = "0.12.0"
|
||||||
parry2d-f64 = "0.8.0"
|
parry2d-f64 = "0.8.0"
|
||||||
parry3d-f64 = "0.8.0"
|
parry3d-f64 = "0.8.0"
|
||||||
@ -45,6 +43,10 @@ features = ["env", "toml"]
|
|||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
path = "../fj"
|
path = "../fj"
|
||||||
|
|
||||||
|
[dependencies.fj-math]
|
||||||
|
version = "0.5.0"
|
||||||
|
path = "../fj-math"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0.136"
|
version = "1.0.136"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
use std::f64::consts::FRAC_PI_2;
|
use std::f64::consts::FRAC_PI_2;
|
||||||
|
|
||||||
|
use fj_math::{Aabb, Scalar, Triangle};
|
||||||
use nalgebra::{Point, TAffine, Transform, Translation, Vector};
|
use nalgebra::{Point, TAffine, Transform, Translation, Vector};
|
||||||
use parry3d_f64::query::{Ray, RayCast as _};
|
use parry3d_f64::query::{Ray, RayCast as _};
|
||||||
use winit::dpi::PhysicalPosition;
|
use winit::dpi::PhysicalPosition;
|
||||||
|
|
||||||
use crate::{
|
use crate::window::Window;
|
||||||
math::{Aabb, Scalar, Triangle},
|
|
||||||
window::Window,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The camera abstraction
|
/// The camera abstraction
|
||||||
///
|
///
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use fj_math::Aabb;
|
||||||
use wgpu::util::StagingBelt;
|
use wgpu::util::StagingBelt;
|
||||||
use wgpu_glyph::{
|
use wgpu_glyph::{
|
||||||
ab_glyph::{FontArc, InvalidFont},
|
ab_glyph::{FontArc, InvalidFont},
|
||||||
GlyphBrush, GlyphBrushBuilder, Section, Text,
|
GlyphBrush, GlyphBrushBuilder, Section, Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::math::Aabb;
|
|
||||||
|
|
||||||
use super::draw_config::DrawConfig;
|
use super::draw_config::DrawConfig;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -70,7 +69,7 @@ impl ConfigUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Render size of model bounding box */
|
/* Render size of model bounding box */
|
||||||
let bbsize = aabb.size().components();
|
let bbsize = aabb.size().components;
|
||||||
let info = format!(
|
let info = format!(
|
||||||
"Model bounding box size: {:0.1} {:0.1} {:0.1}",
|
"Model bounding box size: {:0.1} {:0.1} {:0.1}",
|
||||||
bbsize[0].into_f32(),
|
bbsize[0].into_f32(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::math::Aabb;
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use fj_math::Aabb;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use super::vertices::{Vertex, Vertices};
|
use super::vertices::{Vertex, Vertices};
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use std::{io, mem::size_of};
|
use std::{io, mem::size_of};
|
||||||
|
|
||||||
|
use fj_math::{Aabb, Point};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use wgpu::util::DeviceExt as _;
|
use wgpu::util::DeviceExt as _;
|
||||||
use wgpu_glyph::ab_glyph::InvalidFont;
|
use wgpu_glyph::ab_glyph::InvalidFont;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
use crate::{camera::Camera, math::Aabb, math::Point, window::Window};
|
use crate::{camera::Camera, window::Window};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
config_ui::ConfigUi, draw_config::DrawConfig, drawables::Drawables,
|
config_ui::ConfigUi, draw_config::DrawConfig, drawables::Drawables,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use fj_math::Triangle;
|
||||||
use nalgebra::{vector, Point};
|
use nalgebra::{vector, Point};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
math::Triangle,
|
|
||||||
mesh::{Index, MeshMaker},
|
mesh::{Index, MeshMaker},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use fj_math::Triangle;
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalPosition,
|
dpi::PhysicalPosition,
|
||||||
event::{
|
event::{
|
||||||
@ -10,7 +11,6 @@ use winit::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::{Camera, FocusPoint},
|
camera::{Camera, FocusPoint},
|
||||||
math::Triangle,
|
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{
|
use fj_math::{Point, Scalar, Segment};
|
||||||
kernel::topology::{
|
|
||||||
|
use crate::kernel::topology::{
|
||||||
edges::{Cycle, Edge},
|
edges::{Cycle, Edge},
|
||||||
faces::Face,
|
faces::Face,
|
||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
|
||||||
math::{Point, Scalar, Segment},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An approximation of an edge, multiple edges, or a face
|
/// An approximation of an edge, multiple edges, or a face
|
||||||
@ -132,15 +131,13 @@ fn approximate_edge(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use fj_math::{Point, Scalar, Segment};
|
||||||
use map_macro::set;
|
use map_macro::set;
|
||||||
|
|
||||||
use crate::{
|
use crate::kernel::{
|
||||||
kernel::{
|
|
||||||
geometry::Surface,
|
geometry::Surface,
|
||||||
shape::Shape,
|
shape::Shape,
|
||||||
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
||||||
},
|
|
||||||
math::{Point, Scalar, Segment},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{approximate_edge, Approximation};
|
use super::{approximate_edge, Approximation};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use fj_math::{Scalar, Transform, Triangle, Vector};
|
||||||
kernel::{
|
|
||||||
|
use crate::kernel::{
|
||||||
geometry::{surfaces::Swept, Surface},
|
geometry::{surfaces::Swept, Surface},
|
||||||
shape::{Handle, Shape},
|
shape::{Handle, Shape},
|
||||||
topology::{
|
topology::{
|
||||||
@ -9,8 +10,6 @@ use crate::{
|
|||||||
faces::Face,
|
faces::Face,
|
||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
math::{Scalar, Transform, Triangle, Vector},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::approximation::Approximation;
|
use super::approximation::Approximation;
|
||||||
@ -322,13 +321,12 @@ impl Relation {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use fj_math::{Point, Scalar, Vector};
|
||||||
kernel::{
|
|
||||||
|
use crate::kernel::{
|
||||||
geometry::{surfaces::Swept, Surface},
|
geometry::{surfaces::Swept, Surface},
|
||||||
shape::{Handle, Shape},
|
shape::{Handle, Shape},
|
||||||
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
||||||
},
|
|
||||||
math::{Point, Scalar, Vector},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::sweep_shape;
|
use super::sweep_shape;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
use fj_math::Scalar;
|
||||||
use parry2d_f64::utils::point_in_triangle::{corner_direction, Orientation};
|
use parry2d_f64::utils::point_in_triangle::{corner_direction, Orientation};
|
||||||
use spade::HasPosition;
|
use spade::HasPosition;
|
||||||
|
|
||||||
use crate::{kernel::geometry, math::Scalar};
|
use crate::kernel::geometry;
|
||||||
|
|
||||||
/// Create a Delaunay triangulation of all points
|
/// Create a Delaunay triangulation of all points
|
||||||
pub fn triangulate(
|
pub fn triangulate(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
use crate::math::{Point, Scalar, Transform, Vector};
|
use fj_math::{Point, Scalar, Transform, Vector};
|
||||||
|
|
||||||
/// A circle
|
/// A circle
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
@ -109,7 +109,7 @@ impl Circle {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::f64::consts::{FRAC_PI_2, PI};
|
use std::f64::consts::{FRAC_PI_2, PI};
|
||||||
|
|
||||||
use crate::math::{Point, Scalar, Vector};
|
use fj_math::{Point, Scalar, Vector};
|
||||||
|
|
||||||
use super::Circle;
|
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
|
/// A line, defined by a point and a vector
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
@ -83,11 +83,10 @@ mod tests {
|
|||||||
use std::f64::consts::FRAC_PI_2;
|
use std::f64::consts::FRAC_PI_2;
|
||||||
|
|
||||||
use approx::assert_abs_diff_eq;
|
use approx::assert_abs_diff_eq;
|
||||||
|
use fj_math::{Point, Vector};
|
||||||
use nalgebra::UnitQuaternion;
|
use nalgebra::UnitQuaternion;
|
||||||
use parry3d_f64::math::{Isometry, Translation};
|
use parry3d_f64::math::{Isometry, Translation};
|
||||||
|
|
||||||
use crate::math::{Point, Vector};
|
|
||||||
|
|
||||||
use super::Line;
|
use super::Line;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod circle;
|
mod circle;
|
||||||
mod line;
|
mod line;
|
||||||
|
|
||||||
use crate::math::{Point, Scalar, Transform, Vector};
|
use fj_math::{Point, Scalar, Transform, Vector};
|
||||||
|
|
||||||
pub use self::{circle::Circle, line::Line};
|
pub use self::{circle::Circle, line::Line};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::ops::{Add, Sub};
|
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
|
/// 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
|
/// The native form of the point is its representation in its native
|
||||||
/// coordinate system. This could be a 1-dimensional curve, 2-dimensional
|
/// coordinate system. This could be a 1-dimensional curve, 2-dimensional
|
||||||
/// surface, or 3-dimensional model coordinate system.
|
/// surface, or 3-dimensional model coordinate system.
|
||||||
native: math::Point<D>,
|
native: fj_math::Point<D>,
|
||||||
|
|
||||||
/// The canonical form of the point
|
/// 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
|
/// kept here, unchanged, as the point is converted into other coordinate
|
||||||
/// systems, it allows for a lossless conversion back into 3D coordinates,
|
/// systems, it allows for a lossless conversion back into 3D coordinates,
|
||||||
/// unaffected by floating point accuracy issues.
|
/// unaffected by floating point accuracy issues.
|
||||||
canonical: math::Point<3>,
|
canonical: fj_math::Point<3>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> Point<D> {
|
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
|
/// Both the native and the canonical form must be provide. The caller must
|
||||||
/// guarantee that both of them match.
|
/// 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 }
|
Self { native, canonical }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the point's native form
|
/// Access the point's native form
|
||||||
pub fn native(&self) -> math::Point<D> {
|
pub fn native(&self) -> fj_math::Point<D> {
|
||||||
self.native
|
self.native
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the point's canonical form
|
/// Access the point's canonical form
|
||||||
pub fn canonical(&self) -> math::Point<3> {
|
pub fn canonical(&self) -> fj_math::Point<3> {
|
||||||
self.canonical
|
self.canonical
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<math::Point<3>> for Point<3> {
|
impl From<fj_math::Point<3>> for Point<3> {
|
||||||
fn from(point: math::Point<3>) -> Self {
|
fn from(point: fj_math::Point<3>) -> Self {
|
||||||
Self::new(point, point)
|
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.
|
// `Point`, or the conversion back to 3D would be broken.
|
||||||
|
|
||||||
impl<const D: usize> Add<Vector<D>> for Point<D> {
|
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 {
|
fn add(self, rhs: Vector<D>) -> Self::Output {
|
||||||
self.native.add(rhs)
|
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>;
|
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)
|
self.native.sub(rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,10 @@ pub mod swept;
|
|||||||
|
|
||||||
pub use self::swept::Swept;
|
pub use self::swept::Swept;
|
||||||
|
|
||||||
|
use fj_math::{Point, Transform, Vector};
|
||||||
use nalgebra::vector;
|
use nalgebra::vector;
|
||||||
|
|
||||||
use crate::{
|
use crate::kernel::geometry;
|
||||||
kernel::geometry,
|
|
||||||
math::{Point, Transform, Vector},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Curve, Line};
|
use super::{Curve, Line};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use fj_math::{Point, Transform, Vector};
|
||||||
kernel::geometry::Curve,
|
|
||||||
math::{Point, Transform, Vector},
|
use crate::kernel::geometry::Curve;
|
||||||
};
|
|
||||||
|
|
||||||
/// A surface that was swept from a curve
|
/// A surface that was swept from a curve
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
@ -56,10 +55,9 @@ impl Swept {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use fj_math::{Point, Vector};
|
||||||
kernel::geometry::{Curve, Line},
|
|
||||||
math::{Point, Vector},
|
use crate::kernel::geometry::{Curve, Line};
|
||||||
};
|
|
||||||
|
|
||||||
use super::Swept;
|
use super::Swept;
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::{
|
use fj_math::{Point, Transform};
|
||||||
kernel::{
|
|
||||||
|
use crate::kernel::{
|
||||||
geometry::{Curve, Surface},
|
geometry::{Curve, Surface},
|
||||||
topology::faces::Face,
|
topology::faces::Face,
|
||||||
},
|
|
||||||
math::{Point, Transform},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -4,6 +4,8 @@ pub mod iter;
|
|||||||
pub mod topology;
|
pub mod topology;
|
||||||
pub mod validate;
|
pub mod validate;
|
||||||
|
|
||||||
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
geometry::Geometry,
|
geometry::Geometry,
|
||||||
handle::Handle,
|
handle::Handle,
|
||||||
@ -12,8 +14,6 @@ pub use self::{
|
|||||||
validate::{ValidationError, ValidationResult},
|
validate::{ValidationError, ValidationResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::math::{Point, Scalar};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
geometry::{Curve, Surface},
|
geometry::{Curve, Surface},
|
||||||
topology::{
|
topology::{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use fj_math::{Point, Scalar, Triangle, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{
|
kernel::{
|
||||||
@ -10,7 +12,6 @@ use crate::{
|
|||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
math::{Point, Scalar, Triangle, Vector},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -260,8 +261,9 @@ impl Topology<'_> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::{
|
use fj_math::{Point, Scalar};
|
||||||
kernel::{
|
|
||||||
|
use crate::kernel::{
|
||||||
geometry::{Curve, Line, Surface},
|
geometry::{Curve, Line, Surface},
|
||||||
shape::{handle::Handle, Shape, ValidationError},
|
shape::{handle::Handle, Shape, ValidationError},
|
||||||
topology::{
|
topology::{
|
||||||
@ -269,8 +271,6 @@ mod tests {
|
|||||||
faces::Face,
|
faces::Face,
|
||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
math::{Point, Scalar},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIN_DISTANCE: f64 = 5e-7;
|
const MIN_DISTANCE: f64 = 5e-7;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use fj_math::{Aabb, Point, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{
|
kernel::{
|
||||||
@ -5,7 +7,6 @@ use crate::{
|
|||||||
shape::Shape,
|
shape::Shape,
|
||||||
topology::{edges::Cycle, faces::Face},
|
topology::{edges::Cycle, faces::Face},
|
||||||
},
|
},
|
||||||
math::{Aabb, Point, Scalar},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use fj_math::{Aabb, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{
|
kernel::{
|
||||||
@ -10,7 +12,6 @@ use crate::{
|
|||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
math::{Aabb, Scalar},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use fj_math::{Aabb, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{
|
kernel::{
|
||||||
@ -10,7 +12,6 @@ use crate::{
|
|||||||
vertices::Vertex,
|
vertices::Vertex,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
math::{Aabb, Scalar},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
@ -5,10 +5,9 @@ pub mod sketch;
|
|||||||
pub mod sweep;
|
pub mod sweep;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
use crate::{
|
use fj_math::{Aabb, Scalar};
|
||||||
debug::DebugInfo,
|
|
||||||
math::{Aabb, Scalar},
|
use crate::debug::DebugInfo;
|
||||||
};
|
|
||||||
|
|
||||||
use super::shape::Shape;
|
use super::shape::Shape;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use fj_math::{Aabb, Point, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{
|
kernel::{
|
||||||
@ -5,7 +7,6 @@ use crate::{
|
|||||||
shape::Shape,
|
shape::Shape,
|
||||||
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
topology::{edges::Cycle, faces::Face, vertices::Vertex},
|
||||||
},
|
},
|
||||||
math::{Aabb, Point, Scalar},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
use fj_math::{Aabb, Scalar, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug::DebugInfo,
|
debug::DebugInfo,
|
||||||
kernel::{algorithms::sweep::sweep_shape, shape::Shape},
|
kernel::{algorithms::sweep::sweep_shape, shape::Shape},
|
||||||
math::{Aabb, Scalar, Vector},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
|
use fj_math::{Aabb, Scalar, Transform};
|
||||||
use parry3d_f64::math::Isometry;
|
use parry3d_f64::math::Isometry;
|
||||||
|
|
||||||
use crate::{
|
use crate::{debug::DebugInfo, kernel::shape::Shape};
|
||||||
debug::DebugInfo,
|
|
||||||
kernel::shape::Shape,
|
|
||||||
math::{Aabb, Scalar, Transform},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ToShape;
|
use super::ToShape;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ use std::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use fj_math::{Aabb, Scalar, Segment, Triangle};
|
||||||
use parry2d_f64::query::{Ray as Ray2, RayCast as _};
|
use parry2d_f64::query::{Ray as Ray2, RayCast as _};
|
||||||
use parry3d_f64::query::Ray as Ray3;
|
use parry3d_f64::query::Ray as Ray3;
|
||||||
|
|
||||||
@ -15,7 +16,6 @@ use crate::{
|
|||||||
geometry::Surface,
|
geometry::Surface,
|
||||||
shape::Handle,
|
shape::Handle,
|
||||||
},
|
},
|
||||||
math::{Aabb, Scalar, Segment, Triangle},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::edges::Cycle;
|
use super::edges::Cycle;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{kernel::shape::Handle, math::Point};
|
use fj_math::Point;
|
||||||
|
|
||||||
|
use crate::kernel::shape::Handle;
|
||||||
|
|
||||||
/// A vertex
|
/// A vertex
|
||||||
///
|
///
|
||||||
|
@ -5,7 +5,6 @@ mod debug;
|
|||||||
mod graphics;
|
mod graphics;
|
||||||
mod input;
|
mod input;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
mod math;
|
|
||||||
mod mesh;
|
mod mesh;
|
||||||
mod model;
|
mod model;
|
||||||
mod window;
|
mod window;
|
||||||
@ -15,6 +14,7 @@ use std::ffi::OsStr;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{collections::HashMap, sync::mpsc, time::Instant};
|
use std::{collections::HashMap, sync::mpsc, time::Instant};
|
||||||
|
|
||||||
|
use fj_math::Scalar;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
use notify::Watcher as _;
|
use notify::Watcher as _;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
@ -25,7 +25,6 @@ use winit::{
|
|||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::math::Scalar;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
args::Args,
|
args::Args,
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
@ -105,7 +104,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
// look at the smallest non-zero extent of the bounding box and divide that
|
// look at the smallest non-zero extent of the bounding box and divide that
|
||||||
// by some value.
|
// by some value.
|
||||||
let mut min_extent = Scalar::MAX;
|
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 {
|
if extent > Scalar::ZERO && extent < min_extent {
|
||||||
min_extent = 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
|
/// An n-dimensional point
|
||||||
///
|
///
|
||||||
/// The dimensionality is defined by the const generic argument `D`.
|
/// The dimensionality of the point is defined by the const generic `D`
|
||||||
///
|
/// parameter.
|
||||||
/// # 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.
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Point<const D: usize> {
|
pub struct Point<const D: usize> {
|
||||||
|
/// The coordinates of the point
|
||||||
pub coords: Vector<D>,
|
pub coords: Vector<D>,
|
||||||
}
|
}
|
||||||
|
|
@ -8,23 +8,6 @@ use decorum::R64;
|
|||||||
/// value is not NaN. This allows `Scalar` to provide implementations of [`Eq`],
|
/// 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
|
/// [`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.
|
/// 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)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Scalar(f64);
|
pub struct Scalar(f64);
|
||||||
|
|
||||||
@ -46,6 +29,8 @@ impl Scalar {
|
|||||||
|
|
||||||
/// Construct a `Scalar` from an `f64`
|
/// Construct a `Scalar` from an `f64`
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
/// Panics, if `scalar` is NaN.
|
/// Panics, if `scalar` is NaN.
|
||||||
pub fn from_f64(scalar: f64) -> Self {
|
pub fn from_f64(scalar: f64) -> Self {
|
||||||
if scalar.is_nan() {
|
if scalar.is_nan() {
|
@ -3,48 +3,56 @@ use std::fmt;
|
|||||||
use super::Point;
|
use super::Point;
|
||||||
|
|
||||||
/// A line segment, defined by its two end points
|
/// 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)]
|
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Segment<const D: usize> {
|
pub struct Segment<const D: usize> {
|
||||||
a: Point<D>,
|
points: [Point<D>; 2],
|
||||||
b: Point<D>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> Segment<D> {
|
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
|
/// Access the points of the segment
|
||||||
pub fn points(&self) -> [Point<D>; 2] {
|
pub fn points(&self) -> [Point<D>; 2] {
|
||||||
[self.a, self.b]
|
self.points
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Segment<2> {
|
impl Segment<2> {
|
||||||
/// Convert the 2-dimensional segment to a Parry segment
|
/// Convert the 2-dimensional segment to a Parry segment
|
||||||
pub fn to_parry(self) -> parry2d_f64::shape::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> {
|
impl Segment<3> {
|
||||||
/// Convert the 3-dimensional segment to a Parry segment
|
/// Convert the 3-dimensional segment to a Parry segment
|
||||||
pub fn to_parry(self) -> parry3d_f64::shape::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> {
|
impl<const D: usize> From<[Point<D>; 2]> for Segment<D> {
|
||||||
fn from(points: [Point<D>; 2]) -> Self {
|
fn from(points: [Point<D>; 2]) -> Self {
|
||||||
let [a, b] = points;
|
Self::from_points(points)
|
||||||
|
|
||||||
assert!(a != b, "Invalid segment; both points are identical {a:?}");
|
|
||||||
|
|
||||||
Self {
|
|
||||||
a: points[0],
|
|
||||||
b: points[1],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> fmt::Debug for Segment<D> {
|
impl<const D: usize> fmt::Debug for Segment<D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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};
|
use super::{Point, Scalar};
|
||||||
|
|
||||||
/// A triangle
|
/// A triangle
|
||||||
|
///
|
||||||
|
/// The dimensionality of the triangle is defined by the const generic `D`
|
||||||
|
/// parameter.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Triangle<const D: usize> {
|
pub struct Triangle<const D: usize> {
|
||||||
points: [Point<D>; 3],
|
points: [Point<D>; 3],
|
||||||
@ -8,6 +11,28 @@ pub struct Triangle<const D: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> Triangle<D> {
|
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
|
/// Access the triangle's points
|
||||||
pub fn points(&self) -> [Point<D>; 3] {
|
pub fn points(&self) -> [Point<D>; 3] {
|
||||||
self.points
|
self.points
|
||||||
@ -33,27 +58,15 @@ impl Triangle<3> {
|
|||||||
|
|
||||||
impl<const D: usize> From<[Point<D>; 3]> for Triangle<D> {
|
impl<const D: usize> From<[Point<D>; 3]> for Triangle<D> {
|
||||||
fn from(points: [Point<D>; 3]) -> Self {
|
fn from(points: [Point<D>; 3]) -> Self {
|
||||||
let area = {
|
Self::from_points(points)
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::Point;
|
||||||
|
|
||||||
use super::Triangle;
|
use super::Triangle;
|
||||||
use crate::math::Point;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_triangle_2d() {
|
fn valid_triangle_2d() {
|
@ -7,34 +7,42 @@ use super::{
|
|||||||
|
|
||||||
/// An n-dimensional vector
|
/// An n-dimensional vector
|
||||||
///
|
///
|
||||||
/// The dimensionality is defined by the const generic argument `D`.
|
/// The dimensionality of the vector is defined by the const generic `D`
|
||||||
///
|
/// parameter.
|
||||||
/// # 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.
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[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> {
|
impl<const D: usize> Vector<D> {
|
||||||
/// Construct a `Vector` from an array
|
/// Construct a `Vector` from `f64` components
|
||||||
pub fn from_array(array: [f64; D]) -> Self {
|
///
|
||||||
Self(array.map(Scalar::from_f64))
|
/// # 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
|
/// Construct a `Vector` from an nalgebra vector
|
||||||
pub fn from_na(vector: nalgebra::SVector<f64, D>) -> Self {
|
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
|
/// Convert the vector into an nalgebra vector
|
||||||
pub fn to_na(self) -> nalgebra::SVector<f64, D> {
|
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
|
/// Convert to a 1-dimensional vector
|
||||||
pub fn to_t(self) -> Vector<1> {
|
pub fn to_t(self) -> Vector<1> {
|
||||||
Vector([self.0[0]])
|
Vector {
|
||||||
|
components: [self.components[0]],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the vector into a 3-dimensional vector
|
/// 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> {
|
pub fn to_xyz(self) -> Vector<3> {
|
||||||
let zero = Scalar::ZERO;
|
let zero = Scalar::ZERO;
|
||||||
|
|
||||||
let components = match self.0.as_slice() {
|
let components = match self.components.as_slice() {
|
||||||
[] => [zero, zero, zero],
|
[] => [zero, zero, zero],
|
||||||
&[t] => [t, zero, zero],
|
&[t] => [t, zero, zero],
|
||||||
&[u, v] => [u, v, zero],
|
&[u, v] => [u, v, zero],
|
||||||
&[x, y, z, ..] => [x, y, z],
|
&[x, y, z, ..] => [x, y, z],
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector(components)
|
Vector { components }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the magnitude of the vector
|
/// Compute the magnitude of the vector
|
||||||
@ -71,11 +79,6 @@ impl<const D: usize> Vector<D> {
|
|||||||
pub fn dot(&self, other: &Self) -> Scalar {
|
pub fn dot(&self, other: &Self) -> Scalar {
|
||||||
self.to_na().dot(&other.to_na()).into()
|
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> {
|
impl Vector<3> {
|
||||||
@ -94,7 +97,7 @@ impl ops::Deref for Vector<1> {
|
|||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// has the same size and layout as the target.
|
||||||
@ -106,7 +109,7 @@ impl ops::Deref for Vector<2> {
|
|||||||
type Target = Uv;
|
type Target = Uv;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// has the same size and layout as the target.
|
||||||
@ -118,7 +121,7 @@ impl ops::Deref for Vector<3> {
|
|||||||
type Target = Xyz;
|
type Target = Xyz;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// 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> {
|
impl ops::DerefMut for Vector<1> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// 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> {
|
impl ops::DerefMut for Vector<2> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// 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> {
|
impl ops::DerefMut for Vector<3> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
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
|
// This is sound. We've created this pointer from a valid instance, that
|
||||||
// has the same size and layout as the target.
|
// 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> {
|
impl<const D: usize> From<[Scalar; D]> for Vector<D> {
|
||||||
fn from(array: [Scalar; D]) -> Self {
|
fn from(components: [Scalar; D]) -> Self {
|
||||||
Self(array)
|
Self { components }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> From<[f64; D]> for Vector<D> {
|
impl<const D: usize> From<[f64; D]> for Vector<D> {
|
||||||
fn from(array: [f64; D]) -> Self {
|
fn from(components: [f64; D]) -> Self {
|
||||||
Self::from_array(array)
|
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] {
|
impl<const D: usize> From<Vector<D>> for [f32; D] {
|
||||||
fn from(vector: Vector<D>) -> Self {
|
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] {
|
impl<const D: usize> From<Vector<D>> for [f64; D] {
|
||||||
fn from(vector: Vector<D>) -> Self {
|
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] {
|
impl<const D: usize> From<Vector<D>> for [Scalar; D] {
|
||||||
fn from(vector: Vector<D>) -> Self {
|
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> {
|
impl<const D: usize> fmt::Debug for Vector<D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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 {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::math::Vector;
|
use crate::Vector;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_xyz() {
|
fn to_xyz() {
|
Loading…
Reference in New Issue
Block a user