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
141
142
143
use std::f32;
use std::ops::{Index, Mul, Add};
pub use self::vector::Vector;
pub use self::normal::Normal;
pub use self::point::Point;
pub use self::ray::Ray;
pub use self::matrix4::Matrix4;
pub use self::transform::Transform;
pub use self::quaternion::Quaternion;
pub use self::keyframe::Keyframe;
pub use self::animated_transform::AnimatedTransform;
pub mod vector;
pub mod normal;
pub mod point;
pub mod ray;
pub mod matrix4;
pub mod transform;
pub mod quaternion;
pub mod keyframe;
pub mod animated_transform;
#[derive(Copy, Clone, Debug)]
pub enum Axis { X, Y, Z }
pub fn to_radians(d: f32) -> f32 {
f32::consts::PI / 180.0 * d
}
pub fn cross<A: Index<usize, Output = f32>, B: Index<usize, Output = f32>>(a: &A, b: &B) -> vector::Vector {
Vector::new(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0])
}
pub fn dot<A: Index<usize, Output = f32>, B: Index<usize, Output = f32>>(a: &A, b: &B) -> f32 {
a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
pub fn lerp<T: Mul<f32, Output = T> + Add<Output = T> + Copy>(t: f32, a: &T, b: &T) -> T {
*a * (1.0 - t) + *b * t
}
pub fn clamp<T: PartialOrd>(x: T, min: T, max: T) -> T {
if x < min { min } else if x > max { max } else { x }
}
pub fn spherical_dir(sin_theta: f32, cos_theta: f32, phi: f32) -> Vector {
Vector::new(sin_theta * f32::cos(phi), sin_theta * f32::sin(phi),
cos_theta)
}
pub fn spherical_dir_coords(sin_theta: f32, cos_theta: f32, phi: f32, x: &Vector, y: &Vector, z: &Vector)
-> Vector {
sin_theta * f32::cos(phi) * *x + sin_theta * f32::sin(phi) * *y + cos_theta * *z
}
pub fn spherical_theta(v: &vector::Vector) -> f32 {
f32::acos(clamp(v.z, -1f32, 1f32))
}
pub fn spherical_phi(v: &vector::Vector) -> f32 {
match f32::atan2(v.y, v.x) {
x if x < 0f32 => x + f32::consts::PI * 2.0,
x => x,
}
}
pub fn solve_quadratic(a: f32, b: f32, c: f32) -> Option<(f32, f32)> {
let discrim_sqr = b * b - 4.0 * a * c;
if discrim_sqr < 0.0 {
None
} else {
let discrim = f32::sqrt(discrim_sqr);
let q = if b < 0.0 {
-0.5 * (b - discrim)
} else {
-0.5 * (b + discrim)
};
match (q / a, c / q) {
(x, y) if x > y => Some((y, x)),
(x, y) => Some((x, y)),
}
}
}
pub fn coordinate_system(e1: &Vector) -> (Vector, Vector) {
let e2 =
if f32::abs(e1.x) > f32::abs(e1.y) {
let inv_len = 1.0 / f32::sqrt(e1.x * e1.x + e1.z * e1.z);
Vector::new(-e1.z * inv_len, 0.0, e1.x * inv_len)
}
else {
let inv_len = 1.0 / f32::sqrt(e1.y * e1.y + e1.z * e1.z);
Vector::new(0.0, e1.z * inv_len, -e1.y * inv_len)
};
let e3 = cross(e1, &e2);
(e2, e3)
}
pub fn reflect(w: &Vector, v: &Vector) -> Vector {
2.0 * dot(w, v) * *v - *w
}
pub fn refract(w: &Vector, n: &Vector, eta: f32) -> Option<Vector> {
let cos_t1 = dot(n, w);
let sin_t1_sqr = f32::max(0.0, 1.0 - f32::powf(cos_t1, 2.0));
let sin_t2_sqr = f32::powf(eta, 2.0) * sin_t1_sqr;
if sin_t2_sqr >= 1.0 {
None
} else {
let cos_t2 = f32::sqrt(1.0 - sin_t2_sqr);
Some(eta * -*w + (eta * cos_t1 - cos_t2) * *n)
}
}
#[test]
fn test_cross() {
let a = Vector::new(1f32, 0f32, 0f32);
let b = Vector::new(0f32, 1f32, 0f32);
let c = cross(&a, &b);
assert!(c == Vector::new(0f32, 0f32, 1f32));
}
#[test]
fn test_dot() {
let a = Vector::new(1f32, 2f32, 3f32);
let b = Vector::new(4f32, 5f32, 6f32);
assert!(dot(&a, &b) == 1f32 * 4f32 + 2f32 * 5f32 + 3f32 * 6f32);
}