rust/lib-hedgewars-engine/src/render/atlas.rs
changeset 14726 75ff5c643004
parent 14725 19d30d96d7d6
child 14727 2cc36cb1c258
equal deleted inserted replaced
14725:19d30d96d7d6 14726:75ff5c643004
    27             None
    27             None
    28         }
    28         }
    29     }
    29     }
    30 }
    30 }
    31 
    31 
       
    32 #[derive(PartialEq, Eq)]
       
    33 pub struct UsedSpace {
       
    34     used_area: usize,
       
    35     total_area: usize,
       
    36 }
       
    37 
       
    38 impl UsedSpace {
       
    39     const fn new(used_area: usize, total_area: usize) -> Self {
       
    40         Self {
       
    41             used_area,
       
    42             total_area,
       
    43         }
       
    44     }
       
    45 
       
    46     const fn used(&self) -> usize {
       
    47         self.used_area
       
    48     }
       
    49 
       
    50     const fn total(&self) -> usize {
       
    51         self.total_area
       
    52     }
       
    53 }
       
    54 
       
    55 impl std::fmt::Debug for UsedSpace {
       
    56     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
       
    57         write!(
       
    58             f,
       
    59             "{:.2}%",
       
    60             self.used() as f32 / self.total() as f32 / 100.0
       
    61         )?;
       
    62         Ok(())
       
    63     }
       
    64 }
       
    65 
    32 pub struct Atlas {
    66 pub struct Atlas {
    33     size: Size,
    67     size: Size,
    34     free_rects: Vec<Rect>,
    68     free_rects: Vec<Rect>,
    35     used_rects: Vec<Rect>,
    69     used_rects: Vec<Rect>,
    36 }
    70 }
    44         }
    78         }
    45     }
    79     }
    46 
    80 
    47     pub fn size(&self) -> Size {
    81     pub fn size(&self) -> Size {
    48         self.size
    82         self.size
       
    83     }
       
    84 
       
    85     pub fn used_space(&self) -> UsedSpace {
       
    86         let used = self.used_rects.iter().map(|r| r.size().area()).sum();
       
    87         UsedSpace::new(used, self.size.area())
    49     }
    88     }
    50 
    89 
    51     fn find_position(&self, size: Size) -> Option<(Rect, Fit)> {
    90     fn find_position(&self, size: Size) -> Option<(Rect, Fit)> {
    52         let mut best_rect = Rect::EMPTY;
    91         let mut best_rect = Rect::EMPTY;
    53         let mut best_fit = Fit::new();
    92         let mut best_fit = Fit::new();
   211 
   250 
   212 #[cfg(test)]
   251 #[cfg(test)]
   213 mod tests {
   252 mod tests {
   214     use super::Atlas;
   253     use super::Atlas;
   215     use integral_geometry::{Rect, Size};
   254     use integral_geometry::{Rect, Size};
       
   255     use itertools::Itertools as _;
   216     use proptest::prelude::*;
   256     use proptest::prelude::*;
   217 
   257 
   218     #[test]
   258     #[test]
   219     fn insert() {
   259     fn insert() {
   220         let atlas_size = Size::square(16);
   260         let atlas_size = Size::square(16);
   276         fn prop_insert(rects in Vec::<TestRect>::arbitrary()) {
   316         fn prop_insert(rects in Vec::<TestRect>::arbitrary()) {
   277             let container = Rect::at_origin(Size::square(2048));
   317             let container = Rect::at_origin(Size::square(2048));
   278             let mut atlas = Atlas::new(container.size());
   318             let mut atlas = Atlas::new(container.size());
   279             let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
   319             let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
   280 
   320 
   281             let mut inserted_pairs = inserted.iter().zip(&inserted);
   321             let mut inserted_pairs = inserted.iter().cartesian_product(inserted.iter());
   282 
   322 
   283             assert!(inserted.iter().all(|r| container.contains_rect(r)));
   323             assert!(inserted.iter().all(|r| container.contains_rect(r)));
   284             assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && r1.intersects(r2)));
   324             assert!(inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
   285 
   325 
   286             assert(inserted.len(), rects.len());
   326             assert_eq!(inserted.len(), rects.len());
   287             assert_eq!(sum_area(&inserted), sum_area(&rects));
   327             assert_eq!(sum_area(&inserted), sum_area(&rects));
   288         }
   328         }
   289     }
   329     }
   290 }
   330 
       
   331     proptest! {
       
   332         #[test]
       
   333         fn prop_insert_set(rects in Vec::<TestRect>::arbitrary()) {
       
   334             let container = Rect::at_origin(Size::square(2048));
       
   335             let mut atlas = Atlas::new(container.size());
       
   336             let mut set_atlas = Atlas::new(container.size());
       
   337 
       
   338             let inserted: Vec<_> = rects.iter().filter_map(|TestRect(size)| atlas.insert(*size)).collect();
       
   339             let set_inserted: Vec<_> = set_atlas.insert_set(rects.iter().map(|TestRect(size)| *size));
       
   340 
       
   341             let mut set_inserted_pairs = set_inserted.iter().cartesian_product(set_inserted.iter());
       
   342 
       
   343             assert!(set_inserted_pairs.all(|(r1, r2)| r1 == r2 || r1 != r2 && !r1.intersects(r2)));
       
   344             assert!(set_atlas.used_space().used() <= atlas.used_space().used());
       
   345 
       
   346             assert_eq!(sum_area(&set_inserted), sum_area(&inserted));
       
   347         }
       
   348     }
       
   349 }