rust/lib-hedgewars-engine/src/render/atlas.rs
changeset 14722 16024046d458
child 14725 b110cbe52e51
equal deleted inserted replaced
14721:8e74d4eb89f5 14722:16024046d458
       
     1 use integral_geometry::{Rect, Size};
       
     2 use std::cmp::{max, min, Ordering};
       
     3 
       
     4 pub struct Atlas {
       
     5     size: Size,
       
     6     free_rects: Vec<Rect>,
       
     7     used_rects: Vec<Rect>,
       
     8 }
       
     9 
       
    10 #[derive(PartialEq, Eq, PartialOrd, Ord)]
       
    11 struct Fit {
       
    12     short_size: u32,
       
    13     long_size: u32,
       
    14 }
       
    15 
       
    16 impl Fit {
       
    17     fn new() -> Self {
       
    18         Self {
       
    19             short_size: u32::max_value(),
       
    20             long_size: u32::max_value(),
       
    21         }
       
    22     }
       
    23 
       
    24     fn measure(container: Size, size: Size) -> Option<Self> {
       
    25         if container.contains(size) {
       
    26             let x_leftover = container.width - size.width;
       
    27             let y_leftover = container.height - size.height;
       
    28             Some(Self {
       
    29                 short_size: min(x_leftover, y_leftover) as u32,
       
    30                 long_size: max(x_leftover, y_leftover) as u32,
       
    31             })
       
    32         } else {
       
    33             None
       
    34         }
       
    35     }
       
    36 }
       
    37 
       
    38 fn split_rect(free_rect: Rect, rect: Rect) -> Vec<Rect> {
       
    39     let mut result = vec![];
       
    40     if free_rect.intersects(&rect) {
       
    41         if rect.left() > free_rect.left() {
       
    42             let trim = free_rect.right() - rect.left() + 1;
       
    43             result.push(free_rect.with_margins(0, -trim, 0, 0))
       
    44         }
       
    45         if rect.right() < free_rect.right() {
       
    46             let trim = rect.right() - free_rect.left() + 1;
       
    47             result.push(free_rect.with_margins(-trim, 0, 0, 0))
       
    48         }
       
    49         if rect.top() > free_rect.top() {
       
    50             let trim = free_rect.bottom() - rect.top() + 1;
       
    51             result.push(free_rect.with_margins(0, 0, 0, -trim));
       
    52         }
       
    53         if rect.bottom() < free_rect.bottom() {
       
    54             let trim = rect.bottom() - free_rect.top() + 1;
       
    55             result.push(free_rect.with_margins(0, 0, -trim, 0));
       
    56         }
       
    57     }
       
    58     result
       
    59 }
       
    60 
       
    61 impl Atlas {
       
    62     pub fn new(size: Size) -> Self {
       
    63         Self {
       
    64             size,
       
    65             free_rects: vec![Rect::at_origin(size)],
       
    66             used_rects: vec![],
       
    67         }
       
    68     }
       
    69 
       
    70     fn find_position(&self, size: Size) -> Option<(Rect, Fit)> {
       
    71         let mut best_rect = Rect::EMPTY;
       
    72         let mut best_fit = Fit::new();
       
    73 
       
    74         for rect in &self.free_rects {
       
    75             if let Some(fit) = Fit::measure(rect.size(), size) {
       
    76                 if fit < best_fit {
       
    77                     best_fit = fit;
       
    78                     best_rect = Rect::from_size(rect.top_left(), size);
       
    79                 }
       
    80             }
       
    81 
       
    82             if let Some(fit) = Fit::measure(rect.size(), size.transpose()) {
       
    83                 if fit < best_fit {
       
    84                     best_fit = fit;
       
    85                     best_rect = Rect::from_size(rect.top_left(), size.transpose());
       
    86                 }
       
    87             }
       
    88         }
       
    89 
       
    90         if best_rect == Rect::EMPTY {
       
    91             None
       
    92         } else {
       
    93             Some((best_rect, best_fit))
       
    94         }
       
    95     }
       
    96 
       
    97     fn prune(&mut self) {
       
    98         self.free_rects = self
       
    99             .free_rects
       
   100             .iter()
       
   101             .filter(|inner| {
       
   102                 self.free_rects
       
   103                     .iter()
       
   104                     .all(|outer| outer == *inner || !outer.contains_rect(inner))
       
   105             })
       
   106             .cloned()
       
   107             .collect();
       
   108     }
       
   109 
       
   110     pub fn insert_adaptive(&mut self, size: Size) -> Option<Rect> {
       
   111         let (rect, fit) = self.find_position(size)?;
       
   112 
       
   113         let mut rects_to_process = self.free_rects.len();
       
   114         let mut i = 0;
       
   115 
       
   116         while i < rects_to_process {
       
   117             let rects = split_rect(self.free_rects[i], rect);
       
   118             if !rects.is_empty() {
       
   119                 self.free_rects.remove(i);
       
   120                 self.free_rects.extend(rects);
       
   121                 rects_to_process -= 1
       
   122             } else {
       
   123                 i += 1;
       
   124             }
       
   125         }
       
   126 
       
   127         self.used_rects.push(rect);
       
   128         self.prune();
       
   129         Some(rect)
       
   130     }
       
   131 
       
   132     pub fn insert_set<Iter>(&mut self, sizes: Iter) -> Vec<Rect>
       
   133     where
       
   134         Iter: Iterator<Item = Size>,
       
   135     {
       
   136         unimplemented!()
       
   137     }
       
   138 
       
   139     pub fn reset(&mut self) {
       
   140         self.free_rects.clear();
       
   141         self.used_rects.clear();
       
   142         self.free_rects.push(Rect::at_origin(self.size));
       
   143     }
       
   144 }
       
   145 
       
   146 #[cfg(test)]
       
   147 mod tests {
       
   148     use super::Atlas;
       
   149     use integral_geometry::{Rect, Size};
       
   150 
       
   151     #[test]
       
   152     fn insert() {
       
   153         let atlas_size = Size::square(16);
       
   154         let mut atlas = Atlas::new(atlas_size);
       
   155 
       
   156         assert_eq!(None, atlas.insert_adaptive(Size::square(20)));
       
   157 
       
   158         let rect_size = Size::new(11, 3);
       
   159         let rect = atlas.insert_adaptive(rect_size).unwrap();
       
   160         assert_eq!(rect, Rect::at_origin(rect_size));
       
   161         assert_eq!(2, atlas.free_rects.len());
       
   162     }
       
   163 }