-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
line.rs
140 lines (117 loc) · 3.93 KB
/
line.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use crate::math::{Point, Transform, Vector};
/// A line, defined by a point and a vector
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Line {
/// The origin of the line's coordinate system
pub origin: Point<3>,
/// 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<3>,
}
impl Line {
/// Create a line from two points
pub fn from_points([a, b]: [Point<3>; 2]) -> Self {
Self {
origin: a,
direction: b - a,
}
}
/// Access the origin of the curve's coordinate system
pub fn origin(&self) -> Point<3> {
self.origin
}
/// Transform the line
#[must_use]
pub fn transform(self, transform: &Transform) -> Self {
Self {
origin: transform.transform_point(&self.origin),
direction: transform.transform_vector(&self.direction),
}
}
/// Convert a point in model coordinates to curve coordinates
///
/// Projects the point onto the line before computing curve coordinate. 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 point_model_to_curve(&self, point: &Point<3>) -> Point<1> {
// scalar projection
let t = (point - self.origin).dot(&self.direction.normalize())
/ self.direction.magnitude();
Point::from([t])
}
/// 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)
}
/// Convert a vector on the curve into model coordinates
pub fn vector_curve_to_model(&self, vector: &Vector<1>) -> Vector<3> {
self.direction * vector.t
}
}
impl approx::AbsDiffEq for Line {
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 std::f64::consts::FRAC_PI_2;
use approx::assert_abs_diff_eq;
use nalgebra::UnitQuaternion;
use parry3d_f64::math::{Isometry, Translation};
use crate::math::{Point, Vector};
use super::Line;
#[test]
fn transform() {
let line = Line {
origin: Point::from([1., 0., 0.]),
direction: Vector::from([0., 1., 0.]),
};
let line = line.transform(
&Isometry::from_parts(
Translation::from([1., 2., 3.]),
UnitQuaternion::from_axis_angle(
&nalgebra::Vector::z_axis(),
FRAC_PI_2,
),
)
.into(),
);
assert_abs_diff_eq!(
line,
Line {
origin: Point::from([1., 3., 3.]),
direction: Vector::from([-1., 0., 0.]),
},
epsilon = 1e-8,
);
}
#[test]
fn point_model_to_curve() {
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, t: f64) {
let point = line.point_curve_to_model(&Point::from([t]));
let t_result = line.point_model_to_curve(&point);
assert_eq!(Point::from([t]), t_result);
}
}
}