rust/land2d/src/lib.rs
changeset 13934 9c112f2ae02d
parent 13931 9230aed8a32e
child 13936 78c798d655ad
equal deleted inserted replaced
13933:429945a9b8db 13934:9c112f2ae02d
     1 extern crate vec2d;
     1 extern crate vec2d;
     2 
     2 
     3 use std::cmp;
     3 use std::cmp;
       
     4 use std::ops;
     4 
     5 
     5 pub struct Land2D<T> {
     6 pub struct Land2D<T> {
     6     pixels: vec2d::Vec2D<T>,
     7     pixels: vec2d::Vec2D<T>,
     7     width_mask: usize,
     8     width_mask: usize,
     8     height_mask: usize,
     9     height_mask: usize,
    44     pub fn is_valid_coordinate(&self, x: i32, y: i32) -> bool {
    45     pub fn is_valid_coordinate(&self, x: i32, y: i32) -> bool {
    45         self.is_valid_x(x) && self.is_valid_y(y)
    46         self.is_valid_x(x) && self.is_valid_y(y)
    46     }
    47     }
    47 
    48 
    48     #[inline]
    49     #[inline]
    49     pub fn map<U, F: FnOnce(&mut T) -> U>(&mut self, y: i32, x: i32, f: F) {
    50     pub fn map<U: Default, F: FnOnce(&mut T) -> U>(&mut self, y: i32, x: i32, f: F) -> U {
    50         if self.is_valid_coordinate(x, y) {
    51         if self.is_valid_coordinate(x, y) {
    51             unsafe { // hey, I just checked that coordinates are valid!
    52             unsafe {
    52                 f(self.pixels.get_unchecked_mut(y as usize, x as usize));
    53                 // hey, I just checked that coordinates are valid!
    53             }
    54                 f(self.pixels.get_unchecked_mut(y as usize, x as usize))
    54         }
    55             }
    55     }
    56         } else {
    56 
    57             U::default()
    57     pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, value: T) {
    58         }
       
    59     }
       
    60 
       
    61     fn apply_along_line<U: Default + ops::AddAssign, F: FnMut(i32, i32) -> U>(
       
    62         x1: i32,
       
    63         y1: i32,
       
    64         x2: i32,
       
    65         y2: i32,
       
    66         f: &mut F,
       
    67     ) -> U {
       
    68         let mut result = U::default();
    58         let mut e_x: i32 = 0;
    69         let mut e_x: i32 = 0;
    59         let mut e_y: i32 = 0;
    70         let mut e_y: i32 = 0;
    60         let mut d_x: i32 = x2 - x1;
    71         let mut d_x: i32 = x2 - x1;
    61         let mut d_y: i32 = y2 - y1;
    72         let mut d_y: i32 = y2 - y1;
    62 
    73 
    97             if e_y > d {
   108             if e_y > d {
    98                 e_y -= d;
   109                 e_y -= d;
    99                 y += s_y;
   110                 y += s_y;
   100             }
   111             }
   101 
   112 
       
   113             result += f(x, y);
       
   114         }
       
   115 
       
   116         result
       
   117     }
       
   118 
       
   119     fn apply_around_circle<U: Default + ops::AddAssign, F: FnMut(i32, i32) -> U>(
       
   120         radius: i32,
       
   121         f: &mut F,
       
   122     ) -> U {
       
   123         let mut dx: i32 = 0;
       
   124         let mut dy: i32 = radius;
       
   125         let mut d = 3 - 2 * radius;
       
   126         let mut result = U::default();
       
   127 
       
   128         while dx < dy {
       
   129             result += f(dx, dy);
       
   130 
       
   131             if d < 0 {
       
   132                 d += 4 * dx + 6;
       
   133             } else {
       
   134                 d += 4 * (dx - dy) + 10;
       
   135                 dy -= 1;
       
   136             }
       
   137 
       
   138             dx += 1;
       
   139         }
       
   140 
       
   141         if dx == dy {
       
   142             result += f(dx, dy);
       
   143         }
       
   144 
       
   145         result
       
   146     }
       
   147 
       
   148     pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, value: T) -> usize {
       
   149         <Land2D<T>>::apply_along_line(x1, y1, x2, y2, &mut |x, y| {
   102             self.map(y, x, |p| *p = value);
   150             self.map(y, x, |p| *p = value);
   103         }
   151             1
       
   152         })
   104     }
   153     }
   105 
   154 
   106     pub fn fill(&mut self, start_x: i32, start_y: i32, border_value: T, fill_value: T) {
   155     pub fn fill(&mut self, start_x: i32, start_y: i32, border_value: T, fill_value: T) {
   107         debug_assert!(self.is_valid_coordinate(start_x - 1, start_y));
   156         debug_assert!(self.is_valid_coordinate(start_x - 1, start_y));
   108         debug_assert!(self.is_valid_coordinate(start_x, start_y));
   157         debug_assert!(self.is_valid_coordinate(start_x, start_y));
   190     ) -> usize {
   239     ) -> usize {
   191         let mut result = 0;
   240         let mut result = 0;
   192 
   241 
   193         if self.is_valid_y(y) {
   242         if self.is_valid_y(y) {
   194             for i in cmp::min(x_from, 0) as usize..cmp::max(x_to as usize, self.width() - 1) {
   243             for i in cmp::min(x_from, 0) as usize..cmp::max(x_to as usize, self.width() - 1) {
   195                 unsafe { // coordinates are valid at this point
   244                 unsafe {
       
   245                     // coordinates are valid at this point
   196                     result += f(self.pixels.get_unchecked_mut(y as usize, i));
   246                     result += f(self.pixels.get_unchecked_mut(y as usize, i));
   197                 }
   247                 }
   198             }
   248             }
   199         }
   249         }
   200 
   250 
   221         x: i32,
   271         x: i32,
   222         y: i32,
   272         y: i32,
   223         radius: i32,
   273         radius: i32,
   224         f: F,
   274         f: F,
   225     ) -> usize {
   275     ) -> usize {
   226         let mut dx: i32 = 0;
   276         <Land2D<T>>::apply_around_circle(radius, &mut |dx, dy| {
   227         let mut dy: i32 = radius;
   277             self.fill_circle_lines(x, y, dx, dy, &f)
   228         let mut d = 3 - 2 * radius;
   278         })
   229         let mut result = 0;
   279     }
   230 
   280 
   231         while dx < dy {
   281     #[inline]
   232             result += self.fill_circle_lines(x, y, dx, dy, &f);
   282     fn change_dots_around<U: Default + ops::AddAssign, F: FnMut(i32, i32) -> U>(
   233 
   283         x: i32,
   234             if d < 0 {
   284         y: i32,
   235                 d += 4 * dx + 6;
   285         xx: i32,
   236             } else {
   286         yy: i32,
   237                 d += 4 * (dx - dy) + 10;
   287         f: &mut F,
   238                 dy -= 1;
   288     ) -> U {
   239             }
   289         let mut result = U::default();
   240 
   290         result += f(y + yy, x + xx);
   241             dx += 1;
   291         result += f(y - yy, x + xx);
   242         }
   292         result += f(y + yy, x - xx);
   243 
   293         result += f(y - yy, x - xx);
   244         if dx == dy {
   294         result += f(y + xx, x + yy);
   245             result += self.fill_circle_lines(x, y, dx, dy, &f);
   295         result += f(y - xx, x + yy);
   246         }
   296         result += f(y + xx, x - yy);
       
   297         result += f(y - xx, x - yy);
   247 
   298 
   248         result
   299         result
       
   300     }
       
   301 
       
   302     pub fn draw_thick_line(
       
   303         &mut self,
       
   304         x1: i32,
       
   305         y1: i32,
       
   306         x2: i32,
       
   307         y2: i32,
       
   308         radius: i32,
       
   309         value: T,
       
   310     ) -> usize {
       
   311         <Land2D<T>>::apply_around_circle(radius, &mut |dx, dy| {
       
   312             <Land2D<T>>::apply_along_line(x1, y1, x2, y2, &mut |x, y| {
       
   313                 <Land2D<T>>::change_dots_around(x, y, dx, dy, &mut |x, y| {
       
   314                     self.map(x, y, |p| {
       
   315                         if *p != value {
       
   316                             *p = value;
       
   317                             1
       
   318                         } else {
       
   319                             0
       
   320                         }
       
   321                     })
       
   322                 })
       
   323             })
       
   324         })
   249     }
   325     }
   250 }
   326 }
   251 
   327 
   252 #[cfg(test)]
   328 #[cfg(test)]
   253 mod tests {
   329 mod tests {