From 5bbf0740b9f31d1ec616e989e57d9a8f36da3f30 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 10 May 2022 13:58:20 +0200 Subject: [PATCH 1/2] Add `fj_math::Line` This is an improved version of `fj-kernel`'s `Line`, which this is meant to replace. --- crates/fj-math/src/lib.rs | 2 + crates/fj-math/src/line.rs | 117 +++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 crates/fj-math/src/line.rs diff --git a/crates/fj-math/src/lib.rs b/crates/fj-math/src/lib.rs index 355852426..b9ef5ad6f 100644 --- a/crates/fj-math/src/lib.rs +++ b/crates/fj-math/src/lib.rs @@ -37,6 +37,7 @@ mod aabb; mod coordinates; +mod line; mod point; mod poly_chain; mod scalar; @@ -48,6 +49,7 @@ mod vector; pub use self::{ aabb::Aabb, coordinates::{Uv, Xyz, T}, + line::Line, point::Point, poly_chain::PolyChain, scalar::Scalar, diff --git a/crates/fj-math/src/line.rs b/crates/fj-math/src/line.rs new file mode 100644 index 000000000..02f34a8c3 --- /dev/null +++ b/crates/fj-math/src/line.rs @@ -0,0 +1,117 @@ +use crate::{Point, Vector}; + +/// An n-dimensional line, defined by an origin and a direction +/// +/// The dimensionality of the line is defined by the const generic `D` +/// parameter. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[repr(C)] +pub struct Line { + /// The origin of the line + /// + /// The origin is a point on the line which, together with the `direction` + /// field, defines the line fully. The origin also defines the origin of the + /// line's 1-dimensional coordinate system. + pub origin: Point, + + /// The direction of the line + /// + /// The length of this vector defines the unit of the line's curve + /// coordinate system. The coordinate `1.` is always were the direction + /// vector points, from `origin`. + pub direction: Vector, +} + +impl Line { + /// Create a line from two points + pub fn from_points([a, b]: [Point; 2]) -> Self { + Self { + origin: a, + direction: b - a, + } + } + + /// Create a new instance that is reversed + #[must_use] + pub fn reverse(mut self) -> Self { + self.direction = -self.direction; + self + } + + /// Convert a `D`-dimensional point to line coordinates + /// + /// Projects the point onto the line before the conversion. This is done to + /// make this method robust against floating point accuracy issues. + /// + /// Callers are advised to be careful about the points they pass, as the + /// point not being on the line, intentional or not, will never result in an + /// error. + pub fn convert_point_to_line_coords(&self, point: &Point) -> Point<1> { + Point { + coords: self.convert_vector_to_line_coords(&(point - self.origin)), + } + } + + /// Convert a `D`-dimensional vector to line coordinates + pub fn convert_vector_to_line_coords( + &self, + vector: &Vector, + ) -> Vector<1> { + let t = vector.scalar_projection_onto(&self.direction) + / self.direction.magnitude(); + Vector::from([t]) + } + + /// Convert a point in line coordinates into a `D`-dimensional point + pub fn convert_point_from_line_coords(&self, point: &Point<1>) -> Point { + self.origin + self.convert_vector_from_line_coords(&point.coords) + } + + /// Convert a vector in line coordinates into a `D`-dimensional vector + pub fn convert_vector_from_line_coords( + &self, + vector: &Vector<1>, + ) -> Vector { + self.direction * vector.t + } +} + +impl approx::AbsDiffEq for Line { + type Epsilon = ::Epsilon; + + fn default_epsilon() -> Self::Epsilon { + f64::default_epsilon() + } + + fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { + self.origin.abs_diff_eq(&other.origin, epsilon) + && self.direction.abs_diff_eq(&other.direction, epsilon) + } +} + +#[cfg(test)] +mod tests { + use crate::{Point, Vector}; + + use super::Line; + + #[test] + fn convert_point_to_line_coords() { + let line = Line { + origin: Point::from([1., 0., 0.]), + direction: Vector::from([2., 0., 0.]), + }; + + verify(line, -1.); + verify(line, 0.); + verify(line, 1.); + verify(line, 2.); + + fn verify(line: Line<3>, t: f64) { + let point = line.convert_point_from_line_coords(&Point::from([t])); + let t_result = line.convert_point_to_line_coords(&point); + + assert_eq!(Point::from([t]), t_result); + } + } +} From 046b125938f4bbbaa0af9088ad0c8c345985e5d2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 10 May 2022 13:59:03 +0200 Subject: [PATCH 2/2] Add `Transform::transform_line` --- crates/fj-math/src/transform.rs | 40 ++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/crates/fj-math/src/transform.rs b/crates/fj-math/src/transform.rs index 164044f12..2827adf6f 100644 --- a/crates/fj-math/src/transform.rs +++ b/crates/fj-math/src/transform.rs @@ -2,7 +2,7 @@ use std::ops; use nalgebra::Perspective3; -use crate::Scalar; +use crate::{Line, Scalar}; use super::{Aabb, Point, Segment, Triangle, Vector}; @@ -55,6 +55,14 @@ impl Transform { Vector::from(self.0.transform_vector(&vector.to_na())) } + /// Transform the given line + pub fn transform_line(&self, line: &Line<3>) -> Line<3> { + Line { + origin: self.transform_point(&line.origin), + direction: self.transform_vector(&line.direction), + } + } + /// Transform the given segment pub fn transform_segment(&self, segment: &Segment<3>) -> Segment<3> { let [a, b] = &segment.points(); @@ -124,3 +132,33 @@ impl ops::Mul for Transform { Self(self.0.mul(rhs.0)) } } + +#[cfg(test)] +mod tests { + use approx::assert_abs_diff_eq; + + use crate::{Line, Point, Scalar, Vector}; + + use super::Transform; + + #[test] + fn transform() { + let line = Line { + origin: Point::from([1., 0., 0.]), + direction: Vector::from([0., 1., 0.]), + }; + + let transform = Transform::translation([1., 2., 3.]) + * Transform::rotation(Vector::unit_z() * (Scalar::PI / 2.)); + let line = transform.transform_line(&line); + + assert_abs_diff_eq!( + line, + Line { + origin: Point::from([1., 3., 3.]), + direction: Vector::from([-1., 0., 0.]), + }, + epsilon = 1e-8, + ); + } +}