1pub use emath::{Pos2, Rect, Vec2};
2pub use glam::{DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat4, Quat, Vec3, Vec4Swizzles};
3
4#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
5pub struct Transform {
6 pub scale: mint::Vector3<f64>,
7 pub rotation: mint::Quaternion<f64>,
8 pub translation: mint::Vector3<f64>,
9}
10
11impl Default for Transform {
12 fn default() -> Self {
13 Self {
14 scale: DVec3::ONE.into(),
15 rotation: DQuat::IDENTITY.into(),
16 translation: DVec3::ZERO.into(),
17 }
18 }
19}
20
21impl Transform {
22 pub fn from_scale_rotation_translation(
23 scale: impl Into<mint::Vector3<f64>>,
24 rotation: impl Into<mint::Quaternion<f64>>,
25 translation: impl Into<mint::Vector3<f64>>,
26 ) -> Self {
27 Self {
28 scale: scale.into(),
29 rotation: rotation.into(),
30 translation: translation.into(),
31 }
32 }
33}
34
35pub(crate) fn rotation_align(from: DVec3, to: DVec3) -> DMat3 {
39 let v = from.cross(to);
40 let c = from.dot(to);
41 let k = 1.0 / (1.0 + c);
42
43 DMat3::from_cols_array(&[
44 v.x * v.x * k + c,
45 v.x * v.y * k + v.z,
46 v.x * v.z * k - v.y,
47 v.y * v.x * k - v.z,
48 v.y * v.y * k + c,
49 v.y * v.z * k + v.x,
50 v.z * v.x * k + v.y,
51 v.z * v.y * k - v.x,
52 v.z * v.z * k + c,
53 ])
54}
55
56pub(crate) fn ray_to_ray(a1: DVec3, adir: DVec3, b1: DVec3, bdir: DVec3) -> (f64, f64) {
61 let b = adir.dot(bdir);
62 let w = a1 - b1;
63 let d = adir.dot(w);
64 let e = bdir.dot(w);
65 let dot = 1.0 - b * b;
66 let ta;
67 let tb;
68
69 if dot < 1e-8 {
70 ta = 0.0;
71 tb = e;
72 } else {
73 ta = (b * e - d) / dot;
74 tb = (e - b * d) / dot;
75 }
76
77 (ta, tb)
78}
79
80pub(crate) fn segment_to_segment(a1: DVec3, a2: DVec3, b1: DVec3, b2: DVec3) -> (f64, f64) {
85 let da = a2 - a1;
86 let db = b2 - b1;
87 let la = da.length_squared();
88 let lb = db.length_squared();
89 let dd = da.dot(db);
90 let d1 = a1 - b1;
91 let d = da.dot(d1);
92 let e = db.dot(d1);
93 let n = la * lb - dd * dd;
94
95 let mut sn;
96 let mut tn;
97 let mut sd = n;
98 let mut td = n;
99
100 if n < 1e-8 {
101 sn = 0.0;
102 sd = 1.0;
103 tn = e;
104 td = lb;
105 } else {
106 sn = dd * e - lb * d;
107 tn = la * e - dd * d;
108 if sn < 0.0 {
109 sn = 0.0;
110 tn = e;
111 td = lb;
112 } else if sn > sd {
113 sn = sd;
114 tn = e + dd;
115 td = lb;
116 }
117 }
118
119 if tn < 0.0 {
120 tn = 0.0;
121 if -d < 0.0 {
122 sn = 0.0;
123 } else if -d > la {
124 sn = sd;
125 } else {
126 sn = -d;
127 sd = la;
128 }
129 } else if tn > td {
130 tn = td;
131 if (-d + dd) < 0.0 {
132 sn = 0.0;
133 } else if (-d + dd) > la {
134 sn = sd;
135 } else {
136 sn = -d + dd;
137 sd = la;
138 }
139 }
140
141 let ta = if sn.abs() < 1e-8 { 0.0 } else { sn / sd };
142 let tb = if tn.abs() < 1e-8 { 0.0 } else { tn / td };
143
144 (ta, tb)
145}
146
147pub(crate) fn intersect_plane(
149 plane_normal: DVec3,
150 plane_origin: DVec3,
151 ray_origin: DVec3,
152 ray_dir: DVec3,
153 t: &mut f64,
154) -> bool {
155 let denom = plane_normal.dot(ray_dir);
156
157 if denom.abs() < 10e-8 {
158 false
159 } else {
160 *t = (plane_origin - ray_origin).dot(plane_normal) / denom;
161 *t >= 0.0
162 }
163}
164
165pub(crate) fn ray_to_plane_origin(
168 disc_normal: DVec3,
169 disc_origin: DVec3,
170 ray_origin: DVec3,
171 ray_dir: DVec3,
172) -> (f64, f64) {
173 let mut t = 0.0;
174 if intersect_plane(disc_normal, disc_origin, ray_origin, ray_dir, &mut t) {
175 let p = ray_origin + ray_dir * t;
176 let v = p - disc_origin;
177 let d2 = v.dot(v);
178 (t, f64::sqrt(d2))
179 } else {
180 (t, f64::MAX)
181 }
182}
183
184pub(crate) fn round_to_interval(val: f64, interval: f64) -> f64 {
186 (val / interval).round() * interval
187}
188
189pub(crate) fn world_to_screen(viewport: Rect, mvp: DMat4, pos: DVec3) -> Option<Pos2> {
191 let mut pos = mvp * DVec4::from((pos, 1.0));
192
193 if pos.w < 1e-10 {
194 return None;
195 }
196
197 pos /= pos.w;
198 pos.y *= -1.0;
199
200 let center = viewport.center();
201
202 Some(Pos2::new(
203 (center.x as f64 + pos.x * viewport.width() as f64 / 2.0) as f32,
204 (center.y as f64 + pos.y * viewport.height() as f64 / 2.0) as f32,
205 ))
206}
207
208pub(crate) fn screen_to_world(viewport: Rect, mat: DMat4, pos: Pos2, z: f64) -> DVec3 {
210 let x = (((pos.x - viewport.min.x) / viewport.width()) * 2.0 - 1.0) as f64;
211 let y = (((pos.y - viewport.min.y) / viewport.height()) * 2.0 - 1.0) as f64;
212
213 let mut world_pos = mat * DVec4::new(x, -y, z, 1.0);
214
215 if world_pos.w.abs() < 1e-7 {
217 world_pos.w = 1e-7;
218 }
219
220 world_pos /= world_pos.w;
221
222 world_pos.xyz()
223}