1use crate::{Point, Rect, Size};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq)]
7pub struct WindowCoordinates {
8 pub size: Size,
10 pub local_to_window: ProjectiveTransform,
12}
13
14impl WindowCoordinates {
15 pub fn bounds(self) -> Rect {
17 self.local_to_window
18 .bounds_for_rect(Rect::from_size(self.size))
19 }
20}
21
22pub const NEAR_W: f32 = 1.0 / 64.0;
27
28#[derive(Clone, Copy, Debug)]
30pub struct ProjectiveTransform {
31 matrix: [[f32; 3]; 3],
32}
33
34#[derive(Clone, Copy, Debug, PartialEq)]
38pub struct FrontPolygon {
39 corners: [[f32; 2]; 5],
40 len: u8,
41}
42
43impl FrontPolygon {
44 pub fn of_rect(rect: Rect) -> Self {
47 let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
48 Self {
49 corners: [
50 [rect.x, rect.y],
51 [right, rect.y],
52 [right, bottom],
53 [rect.x, bottom],
54 [0.0; 2],
55 ],
56 len: 4,
57 }
58 }
59
60 fn push(&mut self, corner: [f32; 2]) {
61 self.corners[usize::from(self.len)] = corner;
62 self.len += 1;
63 }
64
65 pub fn corners(&self) -> &[[f32; 2]] {
67 &self.corners[..usize::from(self.len)]
68 }
69
70 pub fn is_empty(&self) -> bool {
72 self.len < 3
73 }
74
75 pub fn strip(&self) -> [[f32; 2]; 5] {
78 let c = self.corners;
79 match self.len {
80 5 => [c[0], c[1], c[4], c[2], c[3]],
81 4 => [c[0], c[1], c[3], c[2], c[2]],
82 _ => c,
83 }
84 }
85
86 pub fn bounds(&self) -> Rect {
89 if self.is_empty() {
90 return Rect::from_size(Size::ZERO);
91 }
92 let (mut min, mut max) = ([f32::INFINITY; 2], [f32::NEG_INFINITY; 2]);
93 for corner in self.corners() {
94 min = [min[0].min(corner[0]), min[1].min(corner[1])];
95 max = [max[0].max(corner[0]), max[1].max(corner[1])];
96 }
97 Rect {
98 x: min[0],
99 y: min[1],
100 width: (max[0] - min[0]).max(0.0),
101 height: (max[1] - min[1]).max(0.0),
102 }
103 }
104}
105
106impl PartialEq for ProjectiveTransform {
107 fn eq(&self, other: &Self) -> bool {
108 let [[a0, a1, a2], [a3, a4, a5], [a6, a7, a8]] = self.matrix;
111 let [[b0, b1, b2], [b3, b4, b5], [b6, b7, b8]] = other.matrix;
112 a0 == b0
113 && a1 == b1
114 && a2 == b2
115 && a3 == b3
116 && a4 == b4
117 && a5 == b5
118 && a6 == b6
119 && a7 == b7
120 && a8 == b8
121 }
122}
123
124impl ProjectiveTransform {
125 pub const fn identity() -> Self {
127 Self {
128 matrix: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
129 }
130 }
131
132 pub fn translation(tx: f32, ty: f32) -> Self {
134 Self {
135 matrix: [[1.0, 0.0, tx], [0.0, 1.0, ty], [0.0, 0.0, 1.0]],
136 }
137 }
138
139 pub fn uniform_scale(scale: f32) -> Self {
142 Self {
143 matrix: [[scale, 0.0, 0.0], [0.0, scale, 0.0], [0.0, 0.0, 1.0]],
144 }
145 }
146
147 pub fn from_rect_to_quad(rect: Rect, quad: [[f32; 2]; 4]) -> Self {
150 if rect.width.abs() <= f32::EPSILON || rect.height.abs() <= f32::EPSILON {
151 return Self::translation(quad[0][0], quad[0][1]);
152 }
153
154 if let Some(axis_aligned) = axis_aligned_rect_from_quad(quad) {
155 let scale_x = axis_aligned.width / rect.width;
156 let scale_y = axis_aligned.height / rect.height;
157 return Self {
158 matrix: [
159 [scale_x, 0.0, axis_aligned.x - rect.x * scale_x],
160 [0.0, scale_y, axis_aligned.y - rect.y * scale_y],
161 [0.0, 0.0, 1.0],
162 ],
163 };
164 }
165
166 let source = [
167 [rect.x, rect.y],
168 [rect.x + rect.width, rect.y],
169 [rect.x, rect.y + rect.height],
170 [rect.x + rect.width, rect.y + rect.height],
171 ];
172 let Some(coefficients) = solve_homography(source, quad) else {
173 return Self::identity();
174 };
175
176 let solved = Self {
177 matrix: [
178 [coefficients[0], coefficients[1], coefficients[2]],
179 [coefficients[3], coefficients[4], coefficients[5]],
180 [coefficients[6], coefficients[7], 1.0],
181 ],
182 };
183 if solved.w_at(rect.x + rect.width * 0.5, rect.y + rect.height * 0.5) >= 0.0 {
184 return solved;
185 }
186 Self {
187 matrix: solved.matrix.map(|row| row.map(|value| -value)),
188 }
189 }
190
191 pub const fn from_rows(matrix: [[f32; 3]; 3]) -> Self {
194 Self { matrix }
195 }
196
197 pub fn from_homogeneous(matrix: [[f32; 3]; 3]) -> Self {
203 let w = matrix[2][2].abs();
204 if w == 1.0 || w <= f32::EPSILON {
205 return Self { matrix };
206 }
207 Self {
208 matrix: matrix.map(|row| row.map(|value| value / w)),
209 }
210 }
211
212 pub fn then(self, next: Self) -> Self {
214 Self {
215 matrix: multiply_matrices(next.matrix, self.matrix),
216 }
217 }
218
219 pub fn inverse(self) -> Option<Self> {
221 let m = self.matrix;
222 let a = m[0][0];
223 let b = m[0][1];
224 let c = m[0][2];
225 let d = m[1][0];
226 let e = m[1][1];
227 let f = m[1][2];
228 let g = m[2][0];
229 let h = m[2][1];
230 let i = m[2][2];
231
232 let cofactor00 = e * i - f * h;
233 let cofactor01 = -(d * i - f * g);
234 let cofactor02 = d * h - e * g;
235 let cofactor10 = -(b * i - c * h);
236 let cofactor11 = a * i - c * g;
237 let cofactor12 = -(a * h - b * g);
238 let cofactor20 = b * f - c * e;
239 let cofactor21 = -(a * f - c * d);
240 let cofactor22 = a * e - b * d;
241
242 let determinant = a * cofactor00 + b * cofactor01 + c * cofactor02;
243 if determinant.abs() <= f32::EPSILON {
244 return None;
245 }
246 let inverse_determinant = 1.0 / determinant;
247
248 Some(Self {
249 matrix: [
250 [
251 cofactor00 * inverse_determinant,
252 cofactor10 * inverse_determinant,
253 cofactor20 * inverse_determinant,
254 ],
255 [
256 cofactor01 * inverse_determinant,
257 cofactor11 * inverse_determinant,
258 cofactor21 * inverse_determinant,
259 ],
260 [
261 cofactor02 * inverse_determinant,
262 cofactor12 * inverse_determinant,
263 cofactor22 * inverse_determinant,
264 ],
265 ],
266 })
267 }
268
269 pub fn matrix(self) -> [[f32; 3]; 3] {
271 self.matrix
272 }
273
274 pub fn map_point(&self, point: Point) -> Point {
276 let x = point.x;
277 let y = point.y;
278 let w = self.matrix[2][0] * x + self.matrix[2][1] * y + self.matrix[2][2];
279 let mapped_x = self.matrix[0][0] * x + self.matrix[0][1] * y + self.matrix[0][2];
280 let mapped_y = self.matrix[1][0] * x + self.matrix[1][1] * y + self.matrix[1][2];
281 if w == 1.0 {
284 return Point {
285 x: mapped_x,
286 y: mapped_y,
287 };
288 }
289 let safe_w = if w.abs() <= f32::EPSILON { 1.0 } else { w };
290
291 Point {
292 x: mapped_x / safe_w,
293 y: mapped_y / safe_w,
294 }
295 }
296
297 pub fn map_rect(&self, rect: Rect) -> [[f32; 2]; 4] {
299 let right = rect.x + rect.width;
300 let bottom = rect.y + rect.height;
301 let corner = |x, y| {
302 let point = self.map_point(Point { x, y });
303 [point.x, point.y]
304 };
305 [
306 corner(rect.x, rect.y),
307 corner(right, rect.y),
308 corner(rect.x, bottom),
309 corner(right, bottom),
310 ]
311 }
312
313 fn w_at(&self, x: f32, y: f32) -> f32 {
314 self.matrix[2][0] * x + self.matrix[2][1] * y + self.matrix[2][2]
315 }
316
317 pub fn in_front(&self, point: Point) -> bool {
321 self.w_at(point.x, point.y) >= NEAR_W
322 }
323
324 pub fn rect_in_front(&self, rect: Rect) -> bool {
326 let right = rect.x + rect.width;
327 let bottom = rect.y + rect.height;
328 [
329 (rect.x, rect.y),
330 (right, rect.y),
331 (rect.x, bottom),
332 (right, bottom),
333 ]
334 .into_iter()
335 .all(|(x, y)| self.w_at(x, y) >= NEAR_W)
336 }
337
338 pub fn map_point_in_front(&self, point: Point) -> Option<Point> {
344 let w = self.w_at(point.x, point.y);
345 (w > 0.0 && w * NEAR_W <= 1.0).then(|| self.map_point(point))
346 }
347
348 pub fn front_polygon(&self, rect: Rect) -> FrontPolygon {
352 let right = rect.x + rect.width;
353 let bottom = rect.y + rect.height;
354 let around = [
355 [rect.x, rect.y],
356 [right, rect.y],
357 [right, bottom],
358 [rect.x, bottom],
359 ];
360 let mut polygon = FrontPolygon {
361 corners: [[0.0; 2]; 5],
362 len: 0,
363 };
364 let mapped = |[x, y]: [f32; 2]| {
365 let point = self.map_point(Point { x, y });
366 [point.x, point.y]
367 };
368 for (index, &from) in around.iter().enumerate() {
369 let to = around[(index + 1) % around.len()];
370 let (w_from, w_to) = (self.w_at(from[0], from[1]), self.w_at(to[0], to[1]));
371 if w_from >= NEAR_W {
372 polygon.push(mapped(from));
373 }
374 if (w_from >= NEAR_W) != (w_to >= NEAR_W) {
375 let t = (NEAR_W - w_from) / (w_to - w_from);
376 polygon.push(mapped([
377 from[0] + (to[0] - from[0]) * t,
378 from[1] + (to[1] - from[1]) * t,
379 ]));
380 }
381 }
382 polygon
383 }
384
385 pub fn bounds_for_rect(&self, rect: Rect) -> Rect {
388 let [[sx, kx, tx], [ky, sy, ty], [px, py, w]] = self.matrix;
389 if kx != 0.0 || ky != 0.0 || px != 0.0 || py != 0.0 || w != 1.0 {
390 return self.turned_bounds_for_rect(rect);
391 }
392 let x0 = sx * rect.x + tx;
395 let x1 = sx * (rect.x + rect.width) + tx;
396 let y0 = sy * rect.y + ty;
397 let y1 = sy * (rect.y + rect.height) + ty;
398 Rect {
399 x: x0.min(x1),
400 y: y0.min(y1),
401 width: (x1 - x0).abs(),
402 height: (y1 - y0).abs(),
403 }
404 }
405
406 #[inline(never)]
410 fn turned_bounds_for_rect(&self, rect: Rect) -> Rect {
411 let [_, _, [px, py, w]] = self.matrix;
412 if px != 0.0 || py != 0.0 || w != 1.0 {
413 return self.front_polygon(rect).bounds();
414 }
415 quad_bounds(self.map_rect(rect))
416 }
417}
418
419fn axis_aligned_rect_from_quad(quad: [[f32; 2]; 4]) -> Option<Rect> {
420 let top_left = quad[0];
421 let top_right = quad[1];
422 let bottom_left = quad[2];
423 let bottom_right = quad[3];
424 let x_epsilon = 1e-4;
425 let y_epsilon = 1e-4;
426
427 if (top_left[1] - top_right[1]).abs() > y_epsilon
428 || (bottom_left[1] - bottom_right[1]).abs() > y_epsilon
429 || (top_left[0] - bottom_left[0]).abs() > x_epsilon
430 || (top_right[0] - bottom_right[0]).abs() > x_epsilon
431 {
432 return None;
433 }
434
435 Some(Rect {
436 x: top_left[0],
437 y: top_left[1],
438 width: top_right[0] - top_left[0],
439 height: bottom_left[1] - top_left[1],
440 })
441}
442
443impl Default for ProjectiveTransform {
444 fn default() -> Self {
445 Self::identity()
446 }
447}
448
449pub fn quad_bounds(quad: [[f32; 2]; 4]) -> Rect {
451 let [[x0, y0], [x1, y1], [x2, y2], [x3, y3]] = quad;
454 let min_x = f32::INFINITY.min(x0).min(x1).min(x2).min(x3);
455 let min_y = f32::INFINITY.min(y0).min(y1).min(y2).min(y3);
456 let max_x = f32::NEG_INFINITY.max(x0).max(x1).max(x2).max(x3);
457 let max_y = f32::NEG_INFINITY.max(y0).max(y1).max(y2).max(y3);
458
459 Rect {
460 x: min_x,
461 y: min_y,
462 width: (max_x - min_x).max(0.0),
463 height: (max_y - min_y).max(0.0),
464 }
465}
466
467fn multiply_matrices(lhs: [[f32; 3]; 3], rhs: [[f32; 3]; 3]) -> [[f32; 3]; 3] {
468 let cell = |row: usize, col: usize| {
471 lhs[row][0] * rhs[0][col] + lhs[row][1] * rhs[1][col] + lhs[row][2] * rhs[2][col]
472 };
473 [
474 [cell(0, 0), cell(0, 1), cell(0, 2)],
475 [cell(1, 0), cell(1, 1), cell(1, 2)],
476 [cell(2, 0), cell(2, 1), cell(2, 2)],
477 ]
478}
479
480fn solve_homography(source: [[f32; 2]; 4], target: [[f32; 2]; 4]) -> Option<[f32; 8]> {
481 let mut matrix = [[0.0f32; 9]; 8];
482 for (index, (src, dst)) in source.into_iter().zip(target).enumerate() {
483 let row = index * 2;
484 let x = src[0];
485 let y = src[1];
486 let u = dst[0];
487 let v = dst[1];
488
489 matrix[row] = [x, y, 1.0, 0.0, 0.0, 0.0, -u * x, -u * y, u];
490 matrix[row + 1] = [0.0, 0.0, 0.0, x, y, 1.0, -v * x, -v * y, v];
491 }
492
493 for pivot in 0..8 {
494 let mut pivot_row = pivot;
495 let mut pivot_value = matrix[pivot][pivot].abs();
496 let mut candidate = pivot + 1;
497 while candidate < 8 {
498 let candidate_value = matrix[candidate][pivot].abs();
499 if candidate_value > pivot_value {
500 pivot_row = candidate;
501 pivot_value = candidate_value;
502 }
503 candidate += 1;
504 }
505
506 if pivot_value <= f32::EPSILON {
507 return None;
508 }
509
510 if pivot_row != pivot {
511 matrix.swap(pivot, pivot_row);
512 }
513
514 let divisor = matrix[pivot][pivot];
515 let mut col = pivot;
516 while col < 9 {
517 matrix[pivot][col] /= divisor;
518 col += 1;
519 }
520
521 for row in 0..8 {
522 if row == pivot {
523 continue;
524 }
525 let factor = matrix[row][pivot];
526 if factor.abs() <= f32::EPSILON {
527 continue;
528 }
529 let mut col = pivot;
530 while col < 9 {
531 matrix[row][col] -= factor * matrix[pivot][col];
532 col += 1;
533 }
534 }
535 }
536
537 let mut solution = [0.0f32; 8];
538 for index in 0..8 {
539 solution[index] = matrix[index][8];
540 }
541 Some(solution)
542}