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
// the furthest point from the origin, in x and y.
let highest_point = aabb.max.z();
let furthest_point = [
aabb.min.x().abs(),
aabb.max.x(),
aabb.min.y().abs(),
aabb.max.y(),
]
.into_iter()
.reduce(Scalar::max)
// `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();
let highest_point = aabb.max.z;
let furthest_point =
[aabb.min.x.abs(), aabb.max.x, aabb.min.y.abs(), aabb.max.y]
.into_iter()
.reduce(Scalar::max)
// `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
// model to fill the whole screen.
@ -74,7 +70,7 @@ impl Camera {
let initial_offset = {
let mut offset = aabb.center();
*offset.z_mut() = Scalar::ZERO;
offset.z = Scalar::ZERO;
-offset
};
@ -84,8 +80,8 @@ impl Camera {
rotation: Transform::identity(),
translation: Translation::from([
initial_offset.x().into_f64(),
initial_offset.y().into_f64(),
initial_offset.x.into_f64(),
initial_offset.y.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.
// Let's remove all those duplicates.
points.sort_by(|a, b| {
if a.x() < b.x() {
if a.x < b.x {
return Ordering::Less;
}
if a.x() > b.x() {
if a.x > b.x {
return Ordering::Greater;
}
if a.y() < b.y() {
if a.y < b.y {
return Ordering::Less;
}
if a.y() > b.y() {
if a.y > b.y {
return Ordering::Greater;
}
if a.z() < b.z() {
if a.z < b.z {
return Ordering::Less;
}
if a.z() > b.z() {
if a.z > b.z {
return Ordering::Greater;
}

View File

@ -48,7 +48,7 @@ impl Circle {
/// error.
pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> {
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 {
atan
} else {
@ -59,13 +59,13 @@ impl Circle {
/// Convert a point on the curve into model coordinates
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
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 angle = point.t();
let angle = vector.t;
let (sin, cos) = angle.sin_cos();

View File

@ -1,5 +1,3 @@
use approx::AbsDiffEq;
use crate::math::{Point, Transform, 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
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
pub fn vector_curve_to_model(&self, point: &Vector<1>) -> Vector<3> {
self.direction * point.t()
pub fn vector_curve_to_model(&self, vector: &Vector<1>) -> Vector<3> {
self.direction * vector.t
}
}
impl AbsDiffEq for Line {
type Epsilon = <f64 as AbsDiffEq>::Epsilon;
impl approx::AbsDiffEq for Line {
type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon()

View File

@ -24,7 +24,7 @@ impl Swept {
/// Convert a point in model coordinates to surface coordinates
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())
/ self.path.magnitude();
@ -33,13 +33,12 @@ impl Swept {
/// Convert a point in surface coordinates to model coordinates
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
pub fn vector_surface_to_model(&self, vector: &Vector<2>) -> Vector<3> {
self.curve.vector_curve_to_model(&vector.to_t())
+ self.path * vector.v()
self.curve.vector_curve_to_model(&vector.to_t()) + self.path * vector.v
}
}

View File

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

View File

@ -40,8 +40,8 @@ impl HasPosition for SurfacePoint {
fn position(&self) -> spade::Point2<Self::Scalar> {
spade::Point2 {
x: self.value.u(),
y: self.value.v(),
x: self.value.u,
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 coordinates;
pub mod point;
pub mod scalar;
pub mod segment;

View File

@ -1,8 +1,9 @@
use std::ops;
use approx::AbsDiffEq;
use super::{Scalar, Vector};
use super::{
coordinates::{Uv, Xyz, T},
Scalar, Vector,
};
/// 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
/// easier to work with vectors. This is a work in progress.
#[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> {
/// 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
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
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
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
pub fn to_t(&self) -> Point<1> {
Point([self.0[0]])
}
/// 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)
Point {
coords: self.coords.to_t(),
}
}
}
impl Point<1> {
/// Access the curve point's t coordinate
pub fn t(&self) -> Scalar {
self.0[0]
impl ops::Deref for Point<1> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.coords.deref()
}
}
impl Point<2> {
/// Access the point's x coordinate
pub fn u(&self) -> Scalar {
self.0[0]
}
impl ops::Deref for Point<2> {
type Target = Uv;
/// Access the point's y coordinate
pub fn v(&self) -> Scalar {
self.0[1]
fn deref(&self) -> &Self::Target {
self.coords.deref()
}
}
impl Point<3> {
/// Access the point's x coordinate
pub fn x(&self) -> Scalar {
self.0[0]
}
impl ops::Deref for Point<3> {
type Target = Xyz;
/// Access the point's y coordinate
pub fn y(&self) -> Scalar {
self.0[1]
fn deref(&self) -> &Self::Target {
self.coords.deref()
}
}
/// Access the point's z coordinate
pub fn z(&self) -> Scalar {
self.0[2]
impl ops::DerefMut for Point<1> {
fn deref_mut(&mut self) -> &mut Self::Target {
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> {
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] {
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] {
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> {
type Epsilon = <f64 as AbsDiffEq>::Epsilon;
impl<const D: usize> approx::AbsDiffEq for Point<D> {
type Epsilon = <Vector<D> as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon()
}
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 approx::AbsDiffEq;
/// A rational, finite scalar value
///
/// 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 {
type Epsilon = <f64 as AbsDiffEq>::Epsilon;
impl approx::AbsDiffEq for Scalar {
type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon()

View File

@ -1,8 +1,9 @@
use std::ops;
use approx::AbsDiffEq;
use super::Scalar;
use super::{
coordinates::{Uv, Xyz, T},
Scalar,
};
/// 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
/// easier to work with vectors. This is a work in progress.
#[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> {
/// 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> {
/// 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
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> {
/// 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
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> {
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> {
type Epsilon = <f64 as AbsDiffEq>::Epsilon;
impl<const D: usize> approx::AbsDiffEq for Vector<D> {
type Epsilon = <Scalar as approx::AbsDiffEq>::Epsilon;
fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon()