Skip to content

Commit

Permalink
Merge pull request #557 from hannobraun/line
Browse files Browse the repository at this point in the history
Add `fj_math::Line`
  • Loading branch information
hannobraun authored May 10, 2022
2 parents 3f8afda + 046b125 commit 72f601d
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 1 deletion.
2 changes: 2 additions & 0 deletions crates/fj-math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

mod aabb;
mod coordinates;
mod line;
mod point;
mod poly_chain;
mod scalar;
Expand All @@ -48,6 +49,7 @@ mod vector;
pub use self::{
aabb::Aabb,
coordinates::{Uv, Xyz, T},
line::Line,
point::Point,
poly_chain::PolyChain,
scalar::Scalar,
Expand Down
117 changes: 117 additions & 0 deletions crates/fj-math/src/line.rs
Original file line number Diff line number Diff line change
@@ -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<const D: usize> {
/// 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<D>,

/// 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<D>,
}

impl<const D: usize> Line<D> {
/// Create a line from two points
pub fn from_points([a, b]: [Point<D>; 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<D>) -> 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<D>,
) -> 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<D> {
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<D> {
self.direction * vector.t
}
}

impl<const D: usize> approx::AbsDiffEq for Line<D> {
type Epsilon = <f64 as approx::AbsDiffEq>::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);
}
}
}
40 changes: 39 additions & 1 deletion crates/fj-math/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::ops;

use nalgebra::Perspective3;

use crate::Scalar;
use crate::{Line, Scalar};

use super::{Aabb, Point, Segment, Triangle, Vector};

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -124,3 +132,33 @@ impl ops::Mul<Self> 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,
);
}
}

0 comments on commit 72f601d

Please sign in to comment.