1use std::num::NonZeroUsize;
2
3use approx_derive::RelativeEq;
4use num_traits::AsPrimitive;
5use simba::scalar::RealField;
6
7use crate::Rectangle;
8
9#[allow(non_camel_case_types)]
10enum Decomposition<F> {
11 row(F),
12 col(F),
13}
14
15#[allow(non_snake_case)]
16#[derive(Clone, Debug, RelativeEq, PartialEq)]
17struct KongMountRoscoeValues<F> {
18 p: F,
19 h1: F,
20 h2: F,
21 k1: F,
22 k2: F,
23 A: F,
24 B: F,
25 C: F,
26 S: F,
27}
28
29impl<F> KongMountRoscoeValues<F>
30where
31 F: 'static + Copy + RealField,
32 usize: num_traits::cast::AsPrimitive<F>,
33{
34 #[allow(non_snake_case)]
35 fn calculate(A: F, B: F, n_subdomains: usize) -> Self {
36 let p: F = n_subdomains.as_();
37
38 let h1 = (A * p / B).sqrt().floor();
39 let h2 = (A * p / B).sqrt().ceil();
40 let k1 = (B * p / A).sqrt().floor();
41 let k2 = (B * p / A).sqrt().ceil();
42
43 let mut C = p;
44 let mut S = F::zero();
45 for k in 1..n_subdomains {
46 for h in 1..n_subdomains {
47 let k = k.as_();
48 let h = h.as_();
49 if (h - F::one()) * (k - F::one()) < p {
50 let ci = (A / h).max(B / k);
51 C = C.min(ci);
52 }
53 if p < (h + F::one()) * (k + F::one()) {
54 let si = (A / h).min(B / k);
55 S = S.max(si);
56 }
57 }
58 }
59
60 #[rustfmt::skip]
61 return KongMountRoscoeValues {
62 p,
63 h1, h2, k1, k2,
64 A, B, C, S,
65 };
66 }
67}
68
69impl<F> Decomposition<F> {
70 #[rustfmt::skip]
71 fn figure_out(values: &KongMountRoscoeValues<F>) -> Option<Decomposition<F>>
72 where
73 F: RealField + Copy,
74 {
75 use Decomposition::*;
76 let KongMountRoscoeValues {
77 p,
78 h1, h2, k1, k2,
79 A, B, C, S,
80 } = values.clone();
81 let h0 = (p / k2).floor();
82 let h3 = (p / k1).ceil();
83 let k0 = (p / h2).floor();
84 let k3 = (p / h1).ceil();
85
86 if h1==h2 && k1==k2 {return Some(col(h1));}
87
88 if h0 < h1 && B / k0 <= S {return Some(row(h2));}
90 if k3 > k2 && A / h0 >= C {return Some(row(h1));}
91 if h0 < h1 && A / h0 <= S {return Some(col(k2));}
92 if h3 > h2 && A / h3 >= C {return Some(col(k1));}
93
94 if h0 == h1 && A / h1 <= S && A / h2 >= C {return Some(col(k2));}
96 if h3 == h2 && A / h1 <= S && A / h2 >= C {return Some(col(k1));}
97 if k0 == k1 && B / k1 <= S && B / k2 >= C {return Some(row(h2));}
98 if k3 == k2 && B / k1 <= S && B / k2 >= C {return Some(row(h1));}
99
100 None
104 }
105
106 #[allow(non_snake_case)]
107 fn generate_rectangles(
108 &self,
109 p: F,
110 rectangle: &Rectangle<F>,
111 ) -> impl IntoIterator<Item = Rectangle<F>> + use<F>
112 where
113 F: 'static + RealField + Copy,
114 F: num_traits::cast::AsPrimitive<usize>,
115 usize: num_traits::cast::AsPrimitive<F>,
116 {
117 let min = rectangle.min;
118 let B = rectangle.max[0] - rectangle.min[0];
119 let A = rectangle.max[1] - rectangle.min[1];
120
121 use Decomposition::*;
122 match self {
123 row(hrow) => {
124 let hrow: F = *hrow;
125 let dx_row = A / hrow;
126 let (n_rows1, n_rows2, n_cols1, n_cols2): (usize, usize, F, F) =
127 if (p / hrow).round() == p / hrow {
128 (hrow.as_(), 0, (p / hrow), F::zero())
129 } else {
130 (
131 (p - hrow * (p / hrow).floor()).round().as_(),
132 (hrow * (p / hrow).ceil() - p).round().as_(),
133 (p / hrow).ceil(),
134 (p / hrow).floor(),
135 )
136 };
137 let dx_col1 = B / n_cols1;
138 let dx_col2 = B / n_cols2;
139 let n_cols1: usize = n_cols1.as_();
140 let n_cols2: usize = n_cols2.as_();
141
142 let rects1 =
143 create_rectangles(0..n_rows1, 0..n_cols1, dx_row, dx_col1, min[1], min[0]);
144 let rects2 = create_rectangles(
145 n_rows1..n_rows1 + n_rows2,
146 0..n_cols2,
147 dx_row,
148 dx_col2,
149 min[1],
150 min[0],
151 );
152 rects1.into_iter().chain(rects2)
153 }
154 col(kcol) => {
155 let kcol: F = *kcol;
156 let dx_col = B / kcol;
157 let (n_cols1, n_cols2, n_rows1, n_rows2): (usize, usize, F, F) =
158 if (p / kcol).round() == p / kcol {
159 (kcol.as_(), 0, p / kcol, F::zero())
160 } else {
161 (
162 (p - kcol * (p / kcol).floor()).round().as_(),
163 (kcol * (p / kcol).ceil() - p).round().as_(),
164 (p / kcol).ceil(),
165 (p / kcol).floor(),
166 )
167 };
168 let dx_row1 = A / n_rows1;
169 let dx_row2 = A / n_rows2;
170 let n_rows1: usize = n_rows1.as_();
171 let n_rows2: usize = n_rows2.as_();
172
173 let rects1 =
174 create_rectangles(0..n_rows1, 0..n_cols1, dx_row1, dx_col, min[1], min[0]);
175 let rects2 = create_rectangles(
176 0..n_rows2,
177 n_cols1..n_cols1 + n_cols2,
178 dx_row2,
179 dx_col,
180 min[1],
181 min[0],
182 );
183 rects1.into_iter().chain(rects2)
184 }
185 }
186 }
187}
188
189fn create_rectangles<F>(
190 n_rows_range: std::ops::Range<usize>,
191 n_cols_range: std::ops::Range<usize>,
192 dx_row: F,
193 dx_col: F,
194 x0_row: F,
195 x0_col: F,
196) -> impl IntoIterator<Item = Rectangle<F>>
197where
198 F: 'static + RealField + Copy,
199 F: num_traits::cast::AsPrimitive<usize>,
200 usize: num_traits::cast::AsPrimitive<F>,
201{
202 (n_rows_range).flat_map(move |n| {
203 (n_cols_range).clone().map(move |m| {
204 let n: F = n.as_();
205 let m: F = m.as_();
206 let min = [x0_col + m * dx_col, x0_row + n * dx_row];
207 let max = [
208 x0_col + (m + F::one()) * dx_col,
209 x0_row + (n + F::one()) * dx_row,
210 ];
211 Rectangle { min, max }
212 })
213 })
214}
215
216#[allow(non_snake_case)]
302pub fn kmr_decompose<F>(rectangle: &Rectangle<F>, n_subdomains: NonZeroUsize) -> DecomposedDomain<F>
303where
304 F: 'static + Copy + RealField,
305 F: num_traits::cast::AsPrimitive<usize>,
306 usize: num_traits::cast::AsPrimitive<F>,
307{
308 let n_subdomains = n_subdomains.get();
309 if n_subdomains == 1 {
310 return vec![rectangle.clone()];
311 }
312
313 let n_subdomains_float: F = n_subdomains.as_();
321 let B = rectangle.max[0] - rectangle.min[0];
322 let A = rectangle.max[1] - rectangle.min[1];
323 let ratio_max = (B / A).max(A / B);
324 if n_subdomains_float <= ratio_max {
325 if B >= A {
326 let dx = B / n_subdomains_float;
327 return (0..n_subdomains)
328 .map(|n| Rectangle {
329 min: [rectangle.min[0] + n.as_() * dx, rectangle.min[1]],
330 max: [rectangle.min[0] + (n + 1).as_() * dx, rectangle.max[1]],
331 })
332 .collect();
333 } else {
334 let dx = A / n_subdomains_float;
335 return (0..n_subdomains)
336 .map(|n| Rectangle {
337 min: [rectangle.min[0], rectangle.min[1] + n.as_() * dx],
338 max: [rectangle.max[0], rectangle.min[1] + (n + 1).as_() * dx],
339 })
340 .collect();
341 };
342 }
343
344 let kmr_values = KongMountRoscoeValues::calculate(A, B, n_subdomains);
345 let decomposition = Decomposition::figure_out(&kmr_values);
346
347 decomposition
348 .map(|d| d.generate_rectangles(n_subdomains_float, rectangle))
349 .unwrap()
350 .into_iter()
351 .collect()
352}
353
354pub type DecomposedDomain<F> = Vec<Rectangle<F>>;
356
357#[derive(thiserror::Error, Debug)]
359pub enum Error {
360 #[error("Could not find suitable subdomain for given digit")]
361 Decompose,
362 #[error("Could not assign index to subdomain")]
363 Indexing,
364}
365
366pub type SortedDigits<F, I> = Vec<(Rectangle<F>, Vec<(I, Rectangle<F>)>)>;
368
369pub type Result<T> = std::result::Result<T, Error>;
370
371pub fn kmr_digitize_1<F, I>(
377 rectangle: &Rectangle<F>,
378 n_subdomains: NonZeroUsize,
379 digits: impl IntoIterator<Item = (I, Rectangle<F>)>,
380) -> Result<SortedDigits<F, I>>
381where
382 F: 'static + Copy + RealField,
383 F: num_traits::cast::AsPrimitive<usize>,
384 usize: num_traits::cast::AsPrimitive<F>,
385 I: 'static,
386{
387 let subdomains = kmr_decompose(rectangle, n_subdomains);
388 let mut res: Vec<_> = subdomains
389 .iter()
390 .map(|subdomain| (subdomain.clone(), Vec::new()))
391 .collect();
392 for (digit, rect) in digits.into_iter() {
393 let index = kmr_digitize_1_single(&subdomains, &rect)?;
394 res[index].1.push((digit, rect));
395 }
396 Ok(res)
397}
398
399pub fn kmr_digitize_1_single<F>(
403 decomposed_domain: &DecomposedDomain<F>,
404 subspace: &Rectangle<F>,
405) -> Result<usize>
406where
407 F: 'static + Copy + RealField,
408 F: num_traits::cast::AsPrimitive<usize>,
409 usize: num_traits::cast::AsPrimitive<F>,
410{
411 for (n_subdomain, subdomain) in decomposed_domain.iter().enumerate() {
412 let middle = [
413 (subspace.min[0] + subspace.max[0]) / (F::one() + F::one()),
414 (subspace.min[1] + subspace.max[1]) / (F::one() + F::one()),
415 ];
416 if middle[0] <= subdomain.max[0]
417 && middle[1] <= subdomain.max[1]
418 && middle[0] >= subdomain.min[0]
419 && middle[1] >= subdomain.min[1]
420 {
421 return Ok(n_subdomain);
422 }
423 }
424 Err(Error::Indexing)
425}
426
427#[test]
428fn kmr_digitize_square_2x2_in_4() {
429 let domain = Rectangle {
430 min: [0.0; 2],
431 max: [100.0; 2],
432 };
433 let digits = kmr_decompose(&domain, 16.try_into().unwrap())
434 .into_iter()
435 .enumerate();
436 let sorted = kmr_digitize_1(&domain, 4.try_into().unwrap(), digits).unwrap();
437 for (_, voxels) in sorted.iter() {
438 assert_eq!(voxels.len(), 4);
439 }
440}
441
442#[test]
443fn kmr_decompose_identity() {
444 let rectangle = Rectangle {
445 min: [0.0; 2],
446 max: [10.0; 2],
447 };
448 let rects = kmr_decompose(&rectangle, 1.try_into().unwrap());
449 assert_eq!(rects.len(), 1);
450 assert_eq!(rects[0], rectangle);
451}
452
453#[test]
454fn kmr_decompose_very_wide() {
455 use approx::assert_abs_diff_eq;
456 let rectangle = Rectangle {
457 min: [0.0; 2],
458 max: [100.0, 10.0],
459 };
460 let rects = kmr_decompose(&rectangle, 4.try_into().unwrap());
461 assert_eq!(rects.len(), 4);
462 assert_abs_diff_eq!(
463 rects[0],
464 Rectangle {
465 min: [0.0; 2],
466 max: [25.0, 10.0],
467 }
468 );
469 assert_abs_diff_eq!(
470 rects[1],
471 Rectangle {
472 min: [25.0, 0.0],
473 max: [50.0, 10.0],
474 }
475 );
476 assert_abs_diff_eq!(
477 rects[2],
478 Rectangle {
479 min: [50.0, 0.0],
480 max: [75.0, 10.0],
481 }
482 );
483 assert_abs_diff_eq!(
484 rects[3],
485 Rectangle {
486 min: [75.0, 0.0],
487 max: [100.0, 10.0],
488 }
489 );
490}
491
492#[test]
493fn kmr_decompose_very_long() {
494 use approx::assert_abs_diff_eq;
495 let rectangle = Rectangle {
496 min: [-10f32, -200f32],
497 max: [10f32, 200f32],
498 };
499 let rects = kmr_decompose(&rectangle, 10.try_into().unwrap());
500 let dx = (rectangle.max[1] - rectangle.min[1]) / 10f32;
501 for (n, rect) in rects.into_iter().enumerate() {
502 assert_abs_diff_eq!(
503 rect,
504 Rectangle {
505 min: [rectangle.min[0], rectangle.min[1] + n as f32 * dx],
506 max: [rectangle.max[0], rectangle.min[1] + (n + 1) as f32 * dx],
507 }
508 );
509 }
510}
511
512#[test]
513fn kmr_decompose_5x3_in_7() {
514 let rectangle = Rectangle {
515 min: [0.0; 2],
516 max: [5.0, 3.0],
517 };
518 let rects = kmr_decompose(&rectangle, 7.try_into().unwrap());
519 assert_eq!(rects.len(), 7);
520
521 for i in 0..4 {
522 let dx = (rectangle.max[0] - rectangle.min[0]) / 4.;
523 let i = i as f64;
524 assert!(rects.contains(&Rectangle {
525 min: [i * dx, 0.],
526 max: [(i + 1.) * dx, 1.5],
527 }));
528 }
529 for i in 0..3 {
530 let dx = (rectangle.max[0] - rectangle.min[0]) / 3.;
531 let i = i as f64;
532 assert!(rects.contains(&Rectangle {
533 min: [i * dx, 1.5],
534 max: [(i + 1.) * dx, 3.]
535 }))
536 }
537}
538
539#[test]
540fn kmr_decompose_6x6_in_14() {
541 let rectangle = Rectangle {
542 min: [-60., 0.],
543 max: [0., 60.],
544 };
545 let rects = kmr_decompose(&rectangle, 14.try_into().unwrap());
546 assert_eq!(rects.len(), 14);
547 for i in 0..5 {
548 for j in 0..2 {
549 let dx = 60. / 5.;
550 let i = i as f64;
551 let j = j as f64;
552 let dy = 60. / 3.;
553 let r = Rectangle {
554 min: [i * dx - 60., j * dy],
555 max: [(i + 1.) * dx - 60., (j + 1.) * dy],
556 };
557 assert!(rects.contains(&r));
558 }
559 }
560 for i in 0..4 {
561 let i = i as f64;
562 let j = 2.;
563 let dx = 60. / 4.;
564 let dy = 60. / 3.;
565 let r = Rectangle {
566 min: [i * dx - 60., j * dy],
567 max: [(i + 1.) * dx - 60., (j + 1.) * dy],
568 };
569 assert!(rects.contains(&r));
570 }
571}
572
573#[test]
574fn kmr_decompose_square_into_4() {
575 let rectangle = Rectangle {
576 min: [-40.0; 2],
577 max: [-20.0; 2],
578 };
579 let rects = kmr_decompose(&rectangle, 4.try_into().unwrap());
580 assert_eq!(rects.len(), 4);
581 assert!(rects.contains(&Rectangle {
582 min: [-40.; 2],
583 max: [-30.; 2]
584 }));
585 assert!(rects.contains(&Rectangle {
586 min: [-40.0, -30.0],
587 max: [-30.0, -20.0]
588 }));
589 assert!(rects.contains(&Rectangle {
590 min: [-30.0, -40.0],
591 max: [-20.0, -30.0]
592 }));
593 assert!(rects.contains(&Rectangle {
594 min: [-30.0; 2],
595 max: [-20.0; 2]
596 }));
597}
598
599#[test]
600fn kmr_decompose_square_into_5() {
601 let rectangle = Rectangle {
602 min: [0.0; 2],
603 max: [100.0; 2],
604 };
605 let subdomains = kmr_decompose(&rectangle, 5.try_into().unwrap());
606 assert_eq!(subdomains.len(), 5);
607 for s in subdomains.iter() {
608 println!("{s:7.2?}");
609 }
610 assert!(subdomains.contains(&Rectangle {
611 min: [0.0; 2],
612 max: [50.0, 100.0 / 3.],
613 }));
614 assert!(subdomains.contains(&Rectangle {
615 min: [0.0, 100. / 3.],
616 max: [50., 100. / 3. * 2.]
617 }));
618 assert!(subdomains.contains(&Rectangle {
619 min: [0.0, 100. / 3. * 2.],
620 max: [50., 100.]
621 }));
622 assert!(subdomains.contains(&Rectangle {
623 min: [50.0, 0.],
624 max: [100., 50.]
625 }));
626 assert!(subdomains.contains(&Rectangle {
627 min: [50.0, 50.],
628 max: [100., 100.]
629 }));
630}