Merge pull request #221 from hannobraun/coords

Make math code more convenient to use
This commit is contained in:
Hanno Braun 2022-02-20 13:46:20 +01:00 committed by GitHub
commit 4ae6e66137
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 201 additions and 126 deletions

View File

@ -45,19 +45,15 @@ impl Camera {
// //
// To do that, first compute the model's highest point, as well as // To do that, first compute the model's highest point, as well as
// the furthest point from the origin, in x and y. // the furthest point from the origin, in x and y.
let highest_point = aabb.max.z(); let highest_point = aabb.max.z;
let furthest_point = [ let furthest_point =
aabb.min.x().abs(), [aabb.min.x.abs(), aabb.max.x, aabb.min.y.abs(), aabb.max.y]
aabb.max.x(), .into_iter()
aabb.min.y().abs(), .reduce(Scalar::max)
aabb.max.y(), // `reduce` can only return `None`, if there are no items in
] // the iterator. And since we're creating an array full of
.into_iter() // items above, we know this can't panic.
.reduce(Scalar::max) .unwrap();
// `reduce` can only return `None`, if there are no items in
// the iterator. And since we're creating an array full of
// items above, we know this can't panic.
.unwrap();
// The actual furthest point is not far enough. We don't want the // The actual furthest point is not far enough. We don't want the
// model to fill the whole screen. // model to fill the whole screen.
@ -74,7 +70,7 @@ impl Camera {
let initial_offset = { let initial_offset = {
let mut offset = aabb.center(); let mut offset = aabb.center();
*offset.z_mut() = Scalar::ZERO; offset.z = Scalar::ZERO;
-offset -offset
}; };
@ -84,8 +80,8 @@ impl Camera {
rotation: Transform::identity(), rotation: Transform::identity(),
translation: Translation::from([ translation: Translation::from([
initial_offset.x().into_f64(), initial_offset.x.into_f64(),
initial_offset.y().into_f64(), initial_offset.y.into_f64(),
-initial_distance.into_f64(), -initial_distance.into_f64(),
]), ]),
} }

View File

@ -90,22 +90,22 @@ impl Approximation {
// As this is a cycle, neighboring edges are going to share vertices. // As this is a cycle, neighboring edges are going to share vertices.
// Let's remove all those duplicates. // Let's remove all those duplicates.
points.sort_by(|a, b| { points.sort_by(|a, b| {
if a.x() < b.x() { if a.x < b.x {
return Ordering::Less; return Ordering::Less;
} }
if a.x() > b.x() { if a.x > b.x {
return Ordering::Greater; return Ordering::Greater;
} }
if a.y() < b.y() { if a.y < b.y {
return Ordering::Less; return Ordering::Less;
} }
if a.y() > b.y() { if a.y > b.y {
return Ordering::Greater; return Ordering::Greater;
} }
if a.z() < b.z() { if a.z < b.z {
return Ordering::Less; return Ordering::Less;
} }
if a.z() > b.z() { if a.z > b.z {
return Ordering::Greater; return Ordering::Greater;
} }

View File

@ -48,7 +48,7 @@ impl Circle {
/// error. /// error.
pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> { pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> {
let v = point - self.center; let v = point - self.center;
let atan = Scalar::atan2(v.y(), v.x()); let atan = Scalar::atan2(v.y, v.x);
let coord = if atan >= Scalar::ZERO { let coord = if atan >= Scalar::ZERO {
atan atan
} else { } else {
@ -59,13 +59,13 @@ impl Circle {
/// Convert a point on the curve into model coordinates /// Convert a point on the curve into model coordinates
pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> { pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> {
self.center + self.vector_curve_to_model(&point.coords()) self.center + self.vector_curve_to_model(&point.coords)
} }
/// Convert a vector on the curve into model coordinates /// Convert a vector on the curve into model coordinates
pub fn vector_curve_to_model(&self, point: &Vector<1>) -> Vector<3> { pub fn vector_curve_to_model(&self, vector: &Vector<1>) -> Vector<3> {
let radius = self.radius.magnitude(); let radius = self.radius.magnitude();
let angle = point.t(); let angle = vector.t;
let (sin, cos) = angle.sin_cos(); let (sin, cos) = angle.sin_cos();

View File

@ -1,5 +1,3 @@
use approx::AbsDiffEq;
use crate::math::{Point, Transform, Vector}; use crate::math::{Point, Transform, Vector};
/// A line, defined by a point and a vector /// A line, defined by a point and a vector
@ -50,17 +48,17 @@ impl Line {
/// Convert a point on the curve into model coordinates /// Convert a point on the curve into model coordinates
pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> { pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> {
self.origin + self.vector_curve_to_model(&point.coords()) self.origin + self.vector_curve_to_model(&point.coords)
} }
/// Convert a vector on the curve into model coordinates /// Convert a vector on the curve into model coordinates
pub fn vector_curve_to_model(&self, point: &Vector<1>) -> Vector<3> { pub fn vector_curve_to_model(&self, vector: &Vector<1>) -> Vector<3> {
self.direction * point.t() self.direction * vector.t
} }
} }
impl AbsDiffEq for Line { impl approx::AbsDiffEq for Line {
type Epsilon = <f64 as AbsDiffEq>::Epsilon; type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon { fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon() f64::default_epsilon()

View File

@ -24,7 +24,7 @@ impl Swept {
/// Convert a point in model coordinates to surface coordinates /// Convert a point in model coordinates to surface coordinates
pub fn point_model_to_surface(&self, point: &Point<3>) -> Point<2> { pub fn point_model_to_surface(&self, point: &Point<3>) -> Point<2> {
let u = self.curve.point_model_to_curve(point).t(); let u = self.curve.point_model_to_curve(point).t;
let v = (point - self.curve.origin()).dot(&self.path.normalize()) let v = (point - self.curve.origin()).dot(&self.path.normalize())
/ self.path.magnitude(); / self.path.magnitude();
@ -33,13 +33,12 @@ impl Swept {
/// Convert a point in surface coordinates to model coordinates /// Convert a point in surface coordinates to model coordinates
pub fn point_surface_to_model(&self, point: &Point<2>) -> Point<3> { pub fn point_surface_to_model(&self, point: &Point<2>) -> Point<3> {
self.curve.point_curve_to_model(&point.to_t()) + self.path * point.v() self.curve.point_curve_to_model(&point.to_t()) + self.path * point.v
} }
/// Convert a vector in surface coordinates to model coordinates /// Convert a vector in surface coordinates to model coordinates
pub fn vector_surface_to_model(&self, vector: &Vector<2>) -> Vector<3> { pub fn vector_surface_to_model(&self, vector: &Vector<2>) -> Vector<3> {
self.curve.vector_curve_to_model(&vector.to_t()) self.curve.vector_curve_to_model(&vector.to_t()) + self.path * vector.v
+ self.path * vector.v()
} }
} }

View File

@ -20,7 +20,7 @@ use crate::{
impl Shape for fj::Sweep { impl Shape for fj::Sweep {
fn bounding_volume(&self) -> Aabb<3> { fn bounding_volume(&self) -> Aabb<3> {
let mut aabb = self.shape.bounding_volume(); let mut aabb = self.shape.bounding_volume();
*aabb.max.z_mut() = self.length.into(); aabb.max.z = self.length.into();
aabb aabb
} }

View File

@ -40,8 +40,8 @@ impl HasPosition for SurfacePoint {
fn position(&self) -> spade::Point2<Self::Scalar> { fn position(&self) -> spade::Point2<Self::Scalar> {
spade::Point2 { spade::Point2 {
x: self.value.u(), x: self.value.u,
y: self.value.v(), y: self.value.v,
} }
} }
} }

22
src/math/coordinates.rs Normal file
View File

@ -0,0 +1,22 @@
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,
}

View File

@ -1,4 +1,5 @@
pub mod aabb; pub mod aabb;
pub mod coordinates;
pub mod point; pub mod point;
pub mod scalar; pub mod scalar;
pub mod segment; pub mod segment;

View File

@ -1,8 +1,9 @@
use std::ops; use std::ops;
use approx::AbsDiffEq; use super::{
coordinates::{Uv, Xyz, T},
use super::{Scalar, Vector}; Scalar, Vector,
};
/// An n-dimensional point /// An n-dimensional point
/// ///
@ -13,7 +14,9 @@ use super::{Scalar, Vector};
/// The goal of this type is to eventually implement `Eq` and `Hash`, making it /// 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. /// easier to work with vectors. This is a work in progress.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Point<const D: usize>([Scalar; D]); pub struct Point<const D: usize> {
pub coords: Vector<D>,
}
impl<const D: usize> Point<D> { impl<const D: usize> Point<D> {
/// Construct a `Point` at the origin of the coordinate system /// Construct a `Point` at the origin of the coordinate system
@ -23,74 +26,80 @@ impl<const D: usize> Point<D> {
/// Construct a `Point` from an array /// Construct a `Point` from an array
pub fn from_array(array: [f64; D]) -> Self { pub fn from_array(array: [f64; D]) -> Self {
Self(array.map(Scalar::from_f64)) Self {
coords: array.map(Scalar::from_f64).into(),
}
} }
/// Construct a `Point` from an nalgebra vector /// Construct a `Point` from an nalgebra vector
pub fn from_na(point: nalgebra::Point<f64, D>) -> Self { pub fn from_na(point: nalgebra::Point<f64, D>) -> Self {
Self(point.coords.data.0[0].map(Scalar::from_f64)) Self {
coords: point.coords.into(),
}
} }
/// Convert the point into an nalgebra point /// Convert the point into an nalgebra point
pub fn to_na(&self) -> nalgebra::Point<f64, D> { pub fn to_na(&self) -> nalgebra::Point<f64, D> {
self.0.map(Scalar::into_f64).into() nalgebra::Point {
coords: self.coords.into(),
}
} }
/// Convert to a 1-dimensional point /// Convert to a 1-dimensional point
pub fn to_t(&self) -> Point<1> { pub fn to_t(&self) -> Point<1> {
Point([self.0[0]]) Point {
} coords: self.coords.to_t(),
}
/// Access a mutable reference to the point's z coordinate
pub fn z_mut(&mut self) -> &mut Scalar {
&mut self.0[2]
}
/// Access the point's coordinates as a vector
pub fn coords(&self) -> Vector<D> {
Vector::from(self.0)
} }
} }
impl Point<1> { impl ops::Deref for Point<1> {
/// Access the curve point's t coordinate type Target = T;
pub fn t(&self) -> Scalar {
self.0[0] fn deref(&self) -> &Self::Target {
self.coords.deref()
} }
} }
impl Point<2> { impl ops::Deref for Point<2> {
/// Access the point's x coordinate type Target = Uv;
pub fn u(&self) -> Scalar {
self.0[0]
}
/// Access the point's y coordinate fn deref(&self) -> &Self::Target {
pub fn v(&self) -> Scalar { self.coords.deref()
self.0[1]
} }
} }
impl Point<3> { impl ops::Deref for Point<3> {
/// Access the point's x coordinate type Target = Xyz;
pub fn x(&self) -> Scalar {
self.0[0]
}
/// Access the point's y coordinate fn deref(&self) -> &Self::Target {
pub fn y(&self) -> Scalar { self.coords.deref()
self.0[1]
} }
}
/// Access the point's z coordinate impl ops::DerefMut for Point<1> {
pub fn z(&self) -> Scalar { fn deref_mut(&mut self) -> &mut Self::Target {
self.0[2] self.coords.deref_mut()
}
}
impl ops::DerefMut for Point<2> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.coords.deref_mut()
}
}
impl ops::DerefMut for Point<3> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.coords.deref_mut()
} }
} }
impl<const D: usize> From<[Scalar; D]> for Point<D> { impl<const D: usize> From<[Scalar; D]> for Point<D> {
fn from(array: [Scalar; D]) -> Self { fn from(array: [Scalar; D]) -> Self {
Self(array) Self {
coords: array.into(),
}
} }
} }
@ -108,13 +117,13 @@ impl<const D: usize> From<nalgebra::Point<f64, D>> for Point<D> {
impl<const D: usize> From<Point<D>> for [f32; D] { impl<const D: usize> From<Point<D>> for [f32; D] {
fn from(point: Point<D>) -> Self { fn from(point: Point<D>) -> Self {
point.0.map(|scalar| scalar.into_f32()) point.coords.into()
} }
} }
impl<const D: usize> From<Point<D>> for [f64; D] { impl<const D: usize> From<Point<D>> for [f64; D] {
fn from(point: Point<D>) -> Self { fn from(point: Point<D>) -> Self {
point.0.map(|scalar| scalar.into_f64()) point.coords.into()
} }
} }
@ -158,14 +167,14 @@ impl<const D: usize> ops::Mul<f64> for Point<D> {
} }
} }
impl<const D: usize> AbsDiffEq for Point<D> { impl<const D: usize> approx::AbsDiffEq for Point<D> {
type Epsilon = <f64 as AbsDiffEq>::Epsilon; type Epsilon = <Vector<D> as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon { fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon() f64::default_epsilon()
} }
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.coords.abs_diff_eq(&other.coords, epsilon)
} }
} }

View File

@ -1,7 +1,5 @@
use std::{cmp, f64::consts::PI, hash::Hash, ops}; use std::{cmp, f64::consts::PI, hash::Hash, ops};
use approx::AbsDiffEq;
/// A rational, finite scalar value /// A rational, finite scalar value
/// ///
/// This is a wrapper around `f64`. On construction, it checks that the `f64` /// This is a wrapper around `f64`. On construction, it checks that the `f64`
@ -272,8 +270,8 @@ impl num_traits::Signed for Scalar {
} }
} }
impl AbsDiffEq for Scalar { impl approx::AbsDiffEq for Scalar {
type Epsilon = <f64 as AbsDiffEq>::Epsilon; type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon { fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon() f64::default_epsilon()

View File

@ -1,8 +1,9 @@
use std::ops; use std::ops;
use approx::AbsDiffEq; use super::{
coordinates::{Uv, Xyz, T},
use super::Scalar; Scalar,
};
/// An n-dimensional vector /// An n-dimensional vector
/// ///
@ -13,7 +14,7 @@ use super::Scalar;
/// The goal of this type is to eventually implement `Eq` and `Hash`, making it /// 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. /// easier to work with vectors. This is a work in progress.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Vector<const D: usize>([Scalar; D]); pub struct Vector<const D: usize>(pub [Scalar; D]);
impl<const D: usize> Vector<D> { impl<const D: usize> Vector<D> {
/// Construct a `Vector` from an array /// Construct a `Vector` from an array
@ -62,44 +63,83 @@ impl<const D: usize> Vector<D> {
} }
} }
impl Vector<1> {
/// Access the curve vector's t coordinate
pub fn t(&self) -> Scalar {
self.0[0]
}
}
impl Vector<2> { impl Vector<2> {
/// Access the surface vector's u coordinate
pub fn u(&self) -> Scalar {
self.0[0]
}
/// Access the surface vector's v coordinate
pub fn v(&self) -> Scalar {
self.0[1]
}
/// Extend a 2-dimensional vector into a 3-dimensional one /// Extend a 2-dimensional vector into a 3-dimensional one
pub fn to_xyz(&self, z: Scalar) -> Vector<3> { pub fn to_xyz(&self, z: Scalar) -> Vector<3> {
Vector::from([self.u(), self.v(), z]) Vector::from([self.u, self.v, z])
} }
} }
impl Vector<3> { impl Vector<3> {
/// Access the vector's x coordinate
pub fn x(&self) -> Scalar {
self.0[0]
}
/// Access the vector's y coordinate
pub fn y(&self) -> Scalar {
self.0[1]
}
/// Construct a new vector from this vector's x and y components /// Construct a new vector from this vector's x and y components
pub fn xy(&self) -> Vector<2> { pub fn xy(&self) -> Vector<2> {
Vector::from([self.x(), self.y()]) Vector::from([self.x, self.y])
}
}
impl ops::Deref for Vector<1> {
type Target = T;
fn deref(&self) -> &Self::Target {
let ptr = self.0.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.
unsafe { &*ptr }
}
}
impl ops::Deref for Vector<2> {
type Target = Uv;
fn deref(&self) -> &Self::Target {
let ptr = self.0.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.
unsafe { &*ptr }
}
}
impl ops::Deref for Vector<3> {
type Target = Xyz;
fn deref(&self) -> &Self::Target {
let ptr = self.0.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.
unsafe { &*ptr }
}
}
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;
// This is sound. We've created this pointer from a valid instance, that
// has the same size and layout as the target.
unsafe { &mut *ptr }
}
}
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;
// This is sound. We've created this pointer from a valid instance, that
// has the same size and layout as the target.
unsafe { &mut *ptr }
}
}
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;
// This is sound. We've created this pointer from a valid instance, that
// has the same size and layout as the target.
unsafe { &mut *ptr }
} }
} }
@ -127,6 +167,18 @@ impl<const D: usize> From<Vector<D>> for [f32; D] {
} }
} }
impl<const D: usize> From<Vector<D>> for [f64; D] {
fn from(vector: Vector<D>) -> Self {
vector.0.map(|scalar| scalar.into_f64())
}
}
impl<const D: usize> From<Vector<D>> for nalgebra::SVector<f64, D> {
fn from(vector: Vector<D>) -> Self {
vector.to_na()
}
}
impl<const D: usize> ops::Add<Self> for Vector<D> { impl<const D: usize> ops::Add<Self> for Vector<D> {
type Output = Self; type Output = Self;
@ -151,8 +203,8 @@ impl<const D: usize> ops::Div<Scalar> for Vector<D> {
} }
} }
impl<const D: usize> AbsDiffEq for Vector<D> { impl<const D: usize> approx::AbsDiffEq for Vector<D> {
type Epsilon = <f64 as AbsDiffEq>::Epsilon; type Epsilon = <Scalar as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon { fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon() f64::default_epsilon()