1use num_traits::{Num, NumAssignOps, NumOps};
2
3use crate::{
4 area::Area,
5 axis::{Axis, SizeForAxis},
6 rectangle::RectangleSize,
7 rotate::QuarterRotation,
8 weight::normalize_weights,
9};
10
11pub trait Dividing<T> {
12 fn divide_vertical(&self, x: T) -> (Self, Self)
14 where
15 Self: Sized;
16
17 fn divide_horizontal(&self, y: T) -> (Self, Self)
19 where
20 Self: Sized;
21
22 fn divide(&self, v: T, axis: Axis) -> (Self, Self)
24 where
25 Self: Sized,
26 {
27 match axis {
28 Axis::Vertical => self.divide_vertical(v),
29 Axis::Horizontal => self.divide_horizontal(v),
30 }
31 }
32
33 fn divide_by_values_and_axis(&self, values: &Vec<T>, axis: Axis) -> Vec<Self>
35 where
36 Self: Sized + RectangleSize<T> + Clone,
37 T: Copy + Num + NumAssignOps,
38 {
39 let mut remaining = self.clone();
40 let mut divided: Vec<Self> = Vec::new();
41 for v in values {
42 let (divided1, divided2) = remaining.divide(*v, axis);
43 divided.push(divided1);
44 remaining = divided2;
45 }
46 divided.push(remaining.clone());
47 divided
48 }
49
50 fn divide_by_weights_and_axis(&self, weights: &[T], axis: Axis) -> Vec<Self>
52 where
53 Self: Sized + RectangleSize<T> + Clone + SizeForAxis<T>,
54 T: Copy + for<'a> std::iter::Sum<&'a T> + Num + NumAssignOps + NumOps,
55 {
56 if weights.is_empty() {
57 return vec![];
58 }
59 if weights.len() == 1 {
60 return vec![self.clone()];
61 }
62 let normalized_weights_ = normalize_weights(weights);
63 let size: T = self.size_for_axis(axis);
64 let mut values: Vec<T> = normalized_weights_.iter().map(|w| *w * size).collect();
65 values.pop();
67 self.divide_by_values_and_axis(&values, axis)
68 }
69
70 fn divide_vertical_then_horizontal_with_weights(
71 &self,
72 weights: &[T],
73 aspect_ratio: T,
74 boustrophedon: bool,
75 ) -> Vec<Self>
76 where
77 Self: Sized + RectangleSize<T> + Clone + SizeForAxis<T> + Area<T>,
78 T: Copy + for<'a> std::iter::Sum<&'a T> + Num + NumAssignOps + std::cmp::PartialOrd,
79 {
80 let norm_weights = normalize_weights(weights);
81 let total_area = self.area();
82 let height = self.height();
83
84 let mut dividing_weights: Vec<Vec<T>> = Vec::new();
85
86 let mut remaining_weights = norm_weights;
87 let mut picked_weights: Vec<T> = Vec::new();
88 let mut divided: Vec<Self> = Vec::new();
89
90 remaining_weights.reverse(); while let Some(picked_weight) = remaining_weights.pop() {
93 picked_weights.push(picked_weight);
94 let weights_in_group = picked_weights.iter().sum::<T>();
95 let picked_area: T = total_area * weights_in_group;
96 let width = picked_area / height;
97 let first_item_height = picked_weights[0] / weights_in_group * height;
98 let first_item_aspect_ratio = width / first_item_height;
99 if first_item_aspect_ratio >= aspect_ratio {
100 dividing_weights.push(picked_weights.clone());
101 picked_weights = Vec::new();
102 }
103 }
104 if !picked_weights.is_empty() {
105 dividing_weights.push(picked_weights.clone());
106 }
107
108 let group_weights: Vec<T> = dividing_weights.iter().map(|w| w.iter().sum()).collect();
109 let vertical_divided = self.divide_by_weights_and_axis(&group_weights, Axis::Vertical);
110 let mut forward = true;
111 for (divided_part, weights) in vertical_divided.iter().zip(dividing_weights.iter_mut()) {
112 if !forward {
113 weights.reverse();
114 }
115 let mut horizontal_divided =
116 divided_part.divide_by_weights_and_axis(weights, Axis::Horizontal);
117 if !forward {
118 horizontal_divided.reverse();
119 }
120 divided.extend(horizontal_divided);
121 if boustrophedon {
122 forward = !forward;
123 }
124 }
125 divided
126 }
127
128 fn divide_horizontal_then_vertical_with_weights(
129 &self,
130 weights: &[T],
131 aspect_ratio: T,
132 boustrophedon: bool,
133 ) -> Vec<Self>
134 where
135 Self: Sized + RectangleSize<T> + Clone + SizeForAxis<T> + Area<T> + QuarterRotation,
136 T: Copy
137 + Num
138 + NumOps
139 + NumAssignOps
140 + std::cmp::PartialOrd
141 + for<'a> std::iter::Sum<&'a T>,
142 {
143 let rotated = self.rotate_clockwise();
145 let rotated_aspect_ratio = T::one() / aspect_ratio;
146 let divided = rotated.divide_vertical_then_horizontal_with_weights(
147 weights,
148 rotated_aspect_ratio,
149 boustrophedon,
150 );
151 divided
152 .iter()
153 .map(|r| r.rotate_counter_clockwise())
154 .collect()
155 }
156}
157
158pub(crate) trait VerticalDividingHelper<T> {
159 fn divide_vertical_helper(&self, x: T) -> (Self, Self)
160 where
161 Self: Sized;
162}
163
164impl<T, U> Dividing<T> for U
165where
166 U: QuarterRotation + VerticalDividingHelper<T>,
167 T: Copy,
168{
169 fn divide_vertical(&self, x: T) -> (Self, Self) {
170 self.divide_vertical_helper(x)
171 }
172
173 fn divide_horizontal(&self, y: T) -> (Self, Self) {
174 let rotated = self.rotate_clockwise();
176 let (a, b) = rotated.divide_vertical(y);
177 (a.rotate_counter_clockwise(), b.rotate_counter_clockwise())
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use num_traits::Float;
184
185 use super::*;
186 use crate::aspect_ratio::AspectRatio;
187 use crate::axis_aligned_rectangle::AxisAlignedRectangle;
188 use crate::component::Component;
189 use crate::point::Point;
190 use crate::rectangle::Rectangle;
191 use crate::weight::normalize_weights;
192
193 #[test]
194 fn test_divide_vertical() {
195 let point = Point::new(2, 3);
196 let rect = Rectangle::new(4, 5);
197 let (rect_a, rect_b) = AxisAlignedRectangle::new(&point, &rect).divide_vertical(2);
198 assert_eq!(rect_a.origin(), point);
199 assert_eq!(rect_a.rect(), Rectangle::new(2, 5));
200 assert_eq!(rect_b.origin(), Point::new(4, 3));
201 assert_eq!(rect_b.rect(), Rectangle::new(2, 5));
202
203 let point = Point::new(2, 3);
204 let rect = Rectangle::new(4, 5);
205 let (rect_a, rect_b) = AxisAlignedRectangle::new(&point, &rect).divide_vertical(1);
206 assert_eq!(rect_a.origin(), point);
207 assert_eq!(rect_a.rect(), Rectangle::new(1, 5));
208 assert_eq!(rect_b.origin(), Point::new(3, 3));
209 assert_eq!(rect_b.rect(), Rectangle::new(3, 5));
210 }
211
212 #[test]
213 fn test_divide_horizontal() {
214 let point = Point::new(2, 3);
215 let rect = Rectangle::new(4, 5);
216 let (rect_a, rect_b) = AxisAlignedRectangle::new(&point, &rect).divide_horizontal(1);
217 assert_eq!(rect_a.origin(), point);
218 assert_eq!(rect_a.rect(), Rectangle::new(4, 1));
219 assert_eq!(rect_b.origin(), Point::new(2, 4));
220 assert_eq!(rect_b.rect(), Rectangle::new(4, 4));
221
222 let point = Point::new(2, 3);
223 let rect = Rectangle::new(4, 5);
224 let (rect_a, rect_b) = AxisAlignedRectangle::new(&point, &rect).divide_horizontal(2);
225 assert_eq!(rect_a.origin(), point);
226 assert_eq!(rect_a.rect(), Rectangle::new(4, 2));
227 assert_eq!(rect_b.origin(), Point::new(2, 5));
228 assert_eq!(rect_b.rect(), Rectangle::new(4, 3));
229 }
230
231 #[test]
232 fn test_divide_nth() {
233 let point = Point::new(2.0, 3.0);
235 let rect = Rectangle::new(6.0, 2.0);
236 let a_rect = AxisAlignedRectangle::new(&point, &rect);
237 let divided = a_rect.divide_by_values_and_axis(&vec![1.0, 2.0], Axis::Vertical);
238 assert_eq!(divided[0].origin(), point);
239 assert_eq!(divided[0].rect(), Rectangle::new(1.0, 2.0));
240 assert_eq!(divided[1].origin(), Point::new(3.0, 3.0));
241 assert_eq!(divided[1].rect(), Rectangle::new(2.0, 2.0));
242 assert_eq!(divided[2].origin(), Point::new(5.0, 3.0));
243 assert_eq!(divided[2].rect(), Rectangle::new(3.0, 2.0));
244 assert_no_overlaps(&a_rect, ÷d);
245 assert_eq!(divided.len(), 3);
246 assert_eq!(
248 divided[0].width() + divided[1].width() + divided[2].width(),
249 a_rect.width()
250 );
251 assert_eq!(divided[0].height(), a_rect.height());
253 assert_eq!(divided[1].height(), a_rect.height());
254 assert_eq!(divided[2].height(), a_rect.height());
255 assert_eq!(divided[0].x() + divided[0].width(), divided[1].x());
257 assert_eq!(divided[1].x() + divided[1].width(), divided[2].x());
258 assert_eq!(
259 a_rect.x() + a_rect.width(),
260 divided[2].x() + divided[2].width()
261 );
262
263 let point = Point::new(2.0, 3.0);
265 let rect = Rectangle::new(2.0, 6.0);
266 let a_rect = AxisAlignedRectangle::new(&point, &rect);
267 let divided = a_rect.divide_by_values_and_axis(&vec![3.0, 2.0], Axis::Horizontal);
268 assert_eq!(divided[0].origin(), point);
269 assert_eq!(divided[0].rect(), Rectangle::new(2.0, 3.0));
270 assert_eq!(divided[1].origin(), Point::new(2.0, 6.0));
271 assert_eq!(divided[1].rect(), Rectangle::new(2.0, 2.0));
272 assert_eq!(divided[2].origin(), Point::new(2.0, 8.0));
273 assert_eq!(divided[2].rect(), Rectangle::new(2.0, 1.0));
274 assert_no_overlaps(&a_rect, ÷d);
275 assert_eq!(divided.len(), 3);
276 assert_eq!(
278 divided[0].height() + divided[1].height() + divided[2].height(),
279 a_rect.height()
280 );
281 assert_eq!(divided[0].width(), a_rect.width());
283 assert_eq!(divided[1].width(), a_rect.width());
284 assert_eq!(divided[2].width(), a_rect.width());
285 assert_eq!(divided[0].y() + divided[0].height(), divided[1].y());
287 assert_eq!(divided[1].y() + divided[1].height(), divided[2].y());
288 assert_eq!(
289 a_rect.y() + a_rect.height(),
290 divided[2].y() + divided[2].height()
291 );
292 }
293
294 #[test]
295 fn test_divide_vertical_then_horizontal_with_weights() {
296 let rect = Rectangle::new(100.0, 100.0);
297 let weights = vec![1.0, 1.0, 1.0, 1.0];
298 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.0, false);
299 assert_weights_dividing(&rect, ÷d, &weights);
300 assert_eq!(divided[0], Rectangle::new(50.0, 50.0));
301 assert_eq!(divided[1], Rectangle::new(50.0, 50.0));
302 assert_eq!(divided[2], Rectangle::new(50.0, 50.0));
303 assert_eq!(divided[3], Rectangle::new(50.0, 50.0));
304
305 let rect = Rectangle::new(100.0, 100.0);
307 let weights = vec![1.0];
308 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.0, false);
309 assert_weights_dividing(&rect, ÷d, &weights);
310 assert_eq!(divided[0], rect);
311
312 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(100.0, 100.0));
313 let weights = vec![1.0, 1.0, 1.0, 1.0];
314 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.0, false);
315 assert_weights_dividing(&rect, ÷d, &weights);
316 assert_no_overlaps(&rect, ÷d);
317 assert_respect_aspect_ratio(÷d, &weights, 1.0);
318 assert_eq!(
319 divided[0],
320 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(50.0, 50.0))
321 );
322 assert_eq!(
323 divided[1],
324 AxisAlignedRectangle::new(&Point::new(0.0, 50.0), &Rectangle::new(50.0, 50.0))
325 );
326 assert_eq!(
327 divided[2],
328 AxisAlignedRectangle::new(&Point::new(50.0, 0.0), &Rectangle::new(50.0, 50.0))
329 );
330 assert_eq!(
331 divided[3],
332 AxisAlignedRectangle::new(&Point::new(50.0, 50.0), &Rectangle::new(50.0, 50.0))
333 );
334
335 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(100.0, 100.0));
337 let weights = vec![1.0];
338 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.0, false);
339 assert_weights_dividing(&rect, ÷d, &weights);
340 assert_no_overlaps(&rect, ÷d);
341 assert_eq!(divided[0], rect);
342
343 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(9.0, 8.0));
344 let weights = vec![4.0, 4.0, 1.0, 1.0, 1.0, 1.0];
345 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.5, false);
346 assert_weights_dividing(&rect, ÷d, &weights);
347 assert_no_overlaps(&rect, ÷d);
348 assert_respect_aspect_ratio(÷d, &weights, 1.5);
349 assert_eq!(
350 divided[0].round(),
351 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(6.0, 4.0))
352 );
353 assert_eq!(
354 divided[1].round(),
355 AxisAlignedRectangle::new(&Point::new(0.0, 4.0), &Rectangle::new(6.0, 4.0))
356 );
357 assert_eq!(
358 divided[2].round(),
359 AxisAlignedRectangle::new(&Point::new(6.0, 0.0), &Rectangle::new(3.0, 2.0))
360 );
361 assert_eq!(
362 divided[3].round(),
363 AxisAlignedRectangle::new(&Point::new(6.0, 2.0), &Rectangle::new(3.0, 2.0))
364 );
365 assert_eq!(
366 divided[4].round(),
367 AxisAlignedRectangle::new(&Point::new(6.0, 4.0), &Rectangle::new(3.0, 2.0))
368 );
369 assert_eq!(
370 divided[5].round(),
371 AxisAlignedRectangle::new(&Point::new(6.0, 6.0), &Rectangle::new(3.0, 2.0))
372 );
373
374 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(300.0, 200.0));
375 let weights = vec![4.0, 3.0, 2.0, 1.0];
376 let divided = rect.divide_vertical_then_horizontal_with_weights(&weights, 1.0, false);
377 assert_weights_dividing(&rect, ÷d, &weights);
378 assert_no_overlaps(&rect, ÷d);
379 assert_eq!(
380 divided[0].round(),
381 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(210.0, 114.0))
382 );
383 assert_eq!(
384 divided[1].round(),
385 AxisAlignedRectangle::new(&Point::new(0.0, 115.0), &Rectangle::new(210.0, 85.0))
386 );
387 assert_eq!(
388 divided[2].round(),
389 AxisAlignedRectangle::new(&Point::new(210.0, 0.0), &Rectangle::new(90.0, 133.0))
390 );
391 assert_eq!(
392 divided[3].round(),
393 AxisAlignedRectangle::new(&Point::new(210.0, 134.0), &Rectangle::new(90.0, 66.0))
394 );
395 }
396
397 #[test]
398 fn test_divide_horizontal_then_vertical_with_weights() {
399 let rect = Rectangle::new(100.0, 100.0);
400 let weights = vec![1.0, 1.0, 1.0, 1.0];
401 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0, false);
402 assert_weights_dividing(&rect, ÷d, &weights);
403 assert_eq!(divided[0], Rectangle::new(50.0, 50.0));
404 assert_eq!(divided[1], Rectangle::new(50.0, 50.0));
405 assert_eq!(divided[2], Rectangle::new(50.0, 50.0));
406 assert_eq!(divided[3], Rectangle::new(50.0, 50.0));
407
408 let rect = Rectangle::new(100.0, 100.0);
410 let weights = vec![1.0];
411 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0, false);
412 assert_weights_dividing(&rect, ÷d, &weights);
413 assert_eq!(divided[0], rect);
414
415 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(100.0, 100.0));
416 let weights = vec![1.0, 1.0, 1.0, 1.0];
417 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0, false);
418 assert_weights_dividing(&rect, ÷d, &weights);
419 assert_no_overlaps(&rect, ÷d);
420 assert_respect_aspect_ratio(÷d, &weights, 1.0);
421 assert_eq!(
422 divided[0],
423 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(50.0, 50.0))
424 );
425 assert_eq!(
426 divided[1],
427 AxisAlignedRectangle::new(&Point::new(50.0, 0.0), &Rectangle::new(50.0, 50.0))
428 );
429 assert_eq!(
430 divided[2],
431 AxisAlignedRectangle::new(&Point::new(0.0, 50.0), &Rectangle::new(50.0, 50.0))
432 );
433 assert_eq!(
434 divided[3],
435 AxisAlignedRectangle::new(&Point::new(50.0, 50.0), &Rectangle::new(50.0, 50.0))
436 );
437
438 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(8.0, 9.0));
439 let weights = vec![4.0, 4.0, 1.0, 1.0, 1.0, 1.0];
440 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0 / 1.5, false);
441 assert_weights_dividing(&rect, ÷d, &weights);
442 assert_no_overlaps(&rect, ÷d);
443 assert_respect_aspect_ratio(÷d, &weights, 1.0 / 1.5);
444 assert_eq!(
445 divided[0].round(),
446 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(4.0, 6.0))
447 );
448 assert_eq!(
449 divided[1].round(),
450 AxisAlignedRectangle::new(&Point::new(4.0, 0.0), &Rectangle::new(4.0, 6.0))
451 );
452 assert_eq!(
453 divided[2].round(),
454 AxisAlignedRectangle::new(&Point::new(0.0, 6.0), &Rectangle::new(2.0, 3.0))
455 );
456 assert_eq!(
457 divided[3].round(),
458 AxisAlignedRectangle::new(&Point::new(2.0, 6.0), &Rectangle::new(2.0, 3.0))
459 );
460 assert_eq!(
461 divided[4].round(),
462 AxisAlignedRectangle::new(&Point::new(4.0, 6.0), &Rectangle::new(2.0, 3.0))
463 );
464 assert_eq!(
465 divided[5].round(),
466 AxisAlignedRectangle::new(&Point::new(6.0, 6.0), &Rectangle::new(2.0, 3.0))
467 );
468
469 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(300.0, 200.0));
470 let weights = vec![4.0, 3.0, 2.0, 1.0];
471 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0, false);
472 assert_weights_dividing(&rect, ÷d, &weights);
473 assert_no_overlaps(&rect, ÷d);
474 assert_eq!(
475 divided[0].round(),
476 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(133.0, 180.0))
477 );
478 assert_eq!(
479 divided[1].round(),
480 AxisAlignedRectangle::new(&Point::new(134.0, 0.0), &Rectangle::new(99.0, 180.0))
481 );
482 assert_eq!(
483 divided[2].round(),
484 AxisAlignedRectangle::new(&Point::new(234.0, 0.0), &Rectangle::new(66.0, 180.0))
485 );
486 assert_eq!(
487 divided[3].round(),
488 AxisAlignedRectangle::new(&Point::new(0.0, 180.0), &Rectangle::new(300.0, 20.0))
489 );
490 }
491
492 #[test]
493 fn test_divide_many() {
494 let rects = vec![
496 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(100.0, 100.0)),
497 AxisAlignedRectangle::new(&Point::new(100.0, 0.0), &Rectangle::new(100.0, 300.0)),
498 AxisAlignedRectangle::new(&Point::new(0.0, 100.0), &Rectangle::new(300.0, 100.0)),
499 AxisAlignedRectangle::new(&Point::new(0.0, 100.0), &Rectangle::new(300.0, 300.0)),
500 ];
501 let various_weights = vec![vec![
502 24.0, 23.0, 22.0, 21.0, 20.0, 19.0, 18.0, 17.0, 16.0, 15.0, 14.0, 13.0, 12.0, 11.0,
503 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0,
504 ]];
505 let various_aspect_ratio = vec![0.5, 1.0, 2.0];
506 for rect in &rects {
507 for weights in &various_weights {
508 let divided = rect.divide_by_weights_and_axis(weights, Axis::Vertical);
509 assert_weights_dividing(rect, ÷d, weights);
510 assert_no_overlaps(rect, ÷d);
511 }
512 }
513 for rect in &rects {
514 for weights in &various_weights {
515 for aspect_ratio in &various_aspect_ratio {
516 for boustrophedon in &[false, true] {
517 let divided = rect.divide_vertical_then_horizontal_with_weights(
518 weights,
519 *aspect_ratio,
520 *boustrophedon,
521 );
522 assert_respect_aspect_ratio(÷d, weights, *aspect_ratio);
523 assert_weights_dividing(rect, ÷d, weights);
524 assert_no_overlaps(rect, ÷d);
525 assert_respect_aspect_ratio(÷d, weights, rect.aspect_ratio());
526
527 let divided = rect.divide_horizontal_then_vertical_with_weights(
528 weights,
529 *aspect_ratio,
530 *boustrophedon,
531 );
532 assert_respect_aspect_ratio(÷d, weights, *aspect_ratio);
533 assert_weights_dividing(rect, ÷d, weights);
534 assert_no_overlaps(rect, ÷d);
535 assert_respect_aspect_ratio(÷d, weights, rect.aspect_ratio());
536 }
537 }
538 }
539 }
540 }
541
542 #[test]
543 fn test_boustrophedon() {
544 let rect = AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(8.0, 9.0));
545 let weights = vec![4.0, 4.0, 1.0, 1.0, 1.0, 1.0];
546 let divided = rect.divide_horizontal_then_vertical_with_weights(&weights, 1.0 / 1.5, true);
547 assert_weights_dividing(&rect, ÷d, &weights);
548 assert_no_overlaps(&rect, ÷d);
549 assert_respect_aspect_ratio(÷d, &weights, 1.0 / 1.5);
550 assert_eq!(
551 divided[0].round(),
552 AxisAlignedRectangle::new(&Point::new(0.0, 0.0), &Rectangle::new(4.0, 6.0))
553 );
554 assert_eq!(
555 divided[1].round(),
556 AxisAlignedRectangle::new(&Point::new(4.0, 0.0), &Rectangle::new(4.0, 6.0))
557 );
558 assert_eq!(
559 divided[2].round(),
560 AxisAlignedRectangle::new(&Point::new(6.0, 6.0), &Rectangle::new(2.0, 3.0))
561 );
562 assert_eq!(
563 divided[3].round(),
564 AxisAlignedRectangle::new(&Point::new(4.0, 6.0), &Rectangle::new(2.0, 3.0))
565 );
566 assert_eq!(
567 divided[4].round(),
568 AxisAlignedRectangle::new(&Point::new(2.0, 6.0), &Rectangle::new(2.0, 3.0))
569 );
570 assert_eq!(
571 divided[5].round(),
572 AxisAlignedRectangle::new(&Point::new(0.0, 6.0), &Rectangle::new(2.0, 3.0))
573 );
574 }
575
576 fn assert_weights_dividing<T, D>(original: &D, divided: &[D], weights: &[T])
577 where
578 D: Dividing<T> + Area<T>,
579 T: Copy
580 + std::fmt::Debug
581 + Num
582 + NumAssignOps
583 + NumOps
584 + std::iter::Sum<T>
585 + for<'a> std::iter::Sum<&'a T>
586 + std::cmp::PartialOrd<f64>,
587 {
588 assert_eq!(divided.len(), weights.len());
590
591 let original_area = original.area();
593 let divided_area: T = divided.iter().map(|r| r.area()).sum();
594 assert!((original_area - divided_area) < 0.1);
596
597 let original_normalized_weights = normalize_weights(weights);
599 let divided_areas: Vec<T> = divided.iter().map(|r| r.area()).collect();
600 let divided_area_by_weights = normalize_weights(÷d_areas);
601
602 let diffs: Vec<T> = original_normalized_weights
604 .iter()
605 .zip(divided_area_by_weights.iter())
606 .map(|(w1, w2)| (*w1 - *w2) * (*w1 - *w2))
607 .collect();
608
609 for diff in diffs {
610 assert!(diff < 0.1);
611 }
612
613 }
615
616 fn assert_no_overlaps<T>(
617 original: &AxisAlignedRectangle<T>,
618 divided: &[AxisAlignedRectangle<T>],
619 ) where
620 T: Copy
621 + std::fmt::Debug
622 + Num
623 + NumAssignOps
624 + NumOps
625 + Float
626 + std::iter::Sum<T>
627 + for<'a> std::iter::Sum<&'a T>,
628 {
629 for d in divided {
631 assert!(original.enclodes(&d.round()));
632 }
633 for (d1, d2) in divided.iter().zip(divided.iter().skip(1)) {
635 assert!(!d1.round().overlaps(&d2.round()));
636 }
637 }
638
639 fn assert_respect_aspect_ratio<T>(
640 divided: &[AxisAlignedRectangle<T>],
641 weights: &[T],
642 aspect_ratio: T,
643 ) where
644 T: Copy
645 + std::fmt::Debug
646 + std::cmp::PartialEq
647 + for<'a> std::iter::Sum<&'a T>
648 + Num
649 + NumAssignOps
650 + NumOps
651 + Float
652 + std::cmp::PartialOrd<f64>,
653 {
654 let normalized_weights = normalize_weights(weights);
655 for (d, w) in divided.iter().zip(normalized_weights.iter()) {
656 let asis_aspect_ratio = d.aspect_ratio();
657 let diff = (asis_aspect_ratio - aspect_ratio).abs();
658 assert!(diff * *w < 0.5);
662 }
663 }
664}