rust/landgen/src/outline.rs
changeset 14131 c416d32764b7
parent 14126 32383b888309
child 14132 95360f56db38
equal deleted inserted replaced
14129:703242ade2c1 14131:c416d32764b7
     1 use itertools::Itertools;
     1 use itertools::Itertools;
     2 use std::cmp::min;
     2 use std::cmp::min;
     3 
     3 
     4 use integral_geometry::{Line, Point, Polygon, Rect, Size};
     4 use integral_geometry::{Line, Ray, Point, Polygon, Rect, Size};
     5 use land2d::Land2D;
     5 use land2d::Land2D;
     6 
     6 
     7 use outline_template::OutlineTemplate;
     7 use outline_template::OutlineTemplate;
     8 
     8 
     9 pub struct OutlinePoints {
     9 pub struct OutlinePoints {
    29                 .iter()
    29                 .iter()
    30                 .map(|i| {
    30                 .map(|i| {
    31                     i.iter()
    31                     i.iter()
    32                         .zip(random_numbers.tuples())
    32                         .zip(random_numbers.tuples())
    33                         .map(|(rect, (rnd_a, rnd_b))| {
    33                         .map(|(rect, (rnd_a, rnd_b))| {
    34                             rect.top_left()
    34                             play_box.top_left() + rect.quotient(rnd_a, rnd_b)
    35                                 + Point::new(
       
    36                                     (rnd_a % rect.width) as i32,
       
    37                                     (rnd_b % rect.height) as i32,
       
    38                                 )
       
    39                                 + play_box.top_left()
       
    40                         })
    35                         })
    41                         .collect::<Vec<_>>()
    36                         .collect::<Vec<_>>()
    42                         .into()
    37                         .into()
    43                 })
    38                 })
    44                 .collect(),
    39                 .collect(),
    71         segment: Line,
    66         segment: Line,
    72         distance_divisor: u32,
    67         distance_divisor: u32,
    73         random_numbers: &mut I,
    68         random_numbers: &mut I,
    74     ) -> Option<Point> {
    69     ) -> Option<Point> {
    75         #[inline]
    70         #[inline]
    76         fn intersect(p: Point, m: Point, p1: Point, p2: Point) -> bool {
    71         fn intersects(ray: &Ray, edge: &Line) -> bool {
    77             let t1 = (m - p1).cross(p);
    72             ray.orientation(edge.start) != ray.orientation(edge.end)
    78             let t2 = (m - p2).cross(p);
       
    79 
       
    80             (t1 > 0) != (t2 > 0)
       
    81         }
    73         }
    82 
    74 
    83         #[inline]
    75         #[inline]
    84         fn solve_intersection(
    76         fn solve_intersection(intersections_box: &Rect, ray: &Ray, edge: &Line) -> Option<(i32, u32)>
    85             intersections_box: &Rect,
    77         {
    86             p: Point,
    78             let edge_dir = edge.scaled_direction();
    87             m: Point,
    79             let aqpb = ray.direction.cross(edge_dir) as i64;
    88             s: Point,
       
    89             e: Point,
       
    90         ) -> Option<(i32, u32)> {
       
    91             let f = e - s;
       
    92             let aqpb = p.cross(f) as i64;
       
    93 
    80 
    94             if aqpb != 0 {
    81             if aqpb != 0 {
    95                 let mut iy = ((((s.x - m.x) as i64 * p.y as i64 + m.y as i64 * p.x as i64)
    82                 let mut iy =
    96                     * f.y as i64
    83                     ((((edge.start.x - ray.start.x) as i64 * ray.direction.y as i64
    97                     - s.y as i64 * f.x as i64 * p.y as i64)
    84                         + ray.start.y as i64 * ray.direction.x as i64)
       
    85                     * edge_dir.y as i64
       
    86                     - edge.start.y as i64 * edge_dir.x as i64 * ray.direction.y as i64)
    98                     / aqpb) as i32;
    87                     / aqpb) as i32;
    99 
    88 
   100                 // is there better way to do it?
    89                 // is there better way to do it?
   101                 if iy < intersections_box.top() {
    90                 if iy < intersections_box.top() {
   102                     iy = intersections_box.top();
    91                     iy = intersections_box.top();
   103                 } else if iy > intersections_box.bottom() {
    92                 } else if iy > intersections_box.bottom() {
   104                     iy = intersections_box.bottom();
    93                     iy = intersections_box.bottom();
   105                 }
    94                 }
   106 
    95 
   107                 let ix = if p.y.abs() > f.y.abs() {
    96                 let ix = if ray.direction.y.abs() > edge_dir.y.abs() {
   108                     (iy - m.y) * p.x / p.y + m.x
    97                     (iy - ray.start.y) * ray.direction.cotangent() + ray.start.x
   109                 } else {
    98                 } else {
   110                     (iy - s.y) * f.x / f.y + s.x
    99                     (iy - edge.start.y) * edge_dir.cotangent() + edge.start.x
   111                 };
   100                 };
   112 
   101 
   113                 let intersection_point = Point::new(ix, iy);
   102                 let intersection_point = Point::new(ix, iy);
   114                 let diff_point = m - intersection_point;
   103                 let diff_point = ray.start - intersection_point;
   115                 let t = p.dot(diff_point);
   104                 let t = ray.direction.dot(diff_point);
   116                 if diff_point.max_norm() >= std::i16::MAX as i32 {
   105                 if diff_point.max_norm() >= std::i16::MAX as i32 {
   117                     Some((t, std::i32::MAX as u32))
   106                     Some((t, std::i32::MAX as u32))
   118                 } else {
   107                 } else {
   119                     let d = diff_point.integral_norm();
   108                     let d = diff_point.integral_norm();
   120 
   109 
   127 
   116 
   128         let min_distance = 40;
   117         let min_distance = 40;
   129         // new point should fall inside this box
   118         // new point should fall inside this box
   130         let map_box = self.play_box.with_margin(min_distance);
   119         let map_box = self.play_box.with_margin(min_distance);
   131 
   120 
   132         let p = segment.scaled_normal();
   121         let normal = segment.scaled_normal();
   133         let p_norm = p.integral_norm();
   122         let normal_len = normal.integral_norm();
   134         let mid_point = segment.center();
   123         let mid_point = segment.center();
   135 
   124 
   136         if (p_norm < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) {
   125         if (normal_len < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) {
   137             return None;
   126             return None;
   138         }
   127         }
   139 
   128 
       
   129         let normal_ray = Ray::new(mid_point, normal);
   140         let mut dist_left = (self.size.width + self.size.height) as u32;
   130         let mut dist_left = (self.size.width + self.size.height) as u32;
   141         let mut dist_right = dist_left;
   131         let mut dist_right = dist_left;
   142 
   132 
   143         // find distances to map borders
   133         // find distances to map borders
   144         if p.x != 0 {
   134         if normal.x != 0 {
   145             // where the normal line intersects the left map border
   135             // where the normal line intersects the left map border
   146             let left_intersection = Point::new(
   136             let left_intersection = Point::new(
   147                 map_box.left(),
   137                 map_box.left(),
   148                 (map_box.left() - mid_point.x) * p.y / p.x + mid_point.y,
   138                 (map_box.left() - mid_point.x) * normal.tangent() + mid_point.y,
   149             );
   139             );
   150             dist_left = (mid_point - left_intersection).integral_norm();
   140             dist_left = (mid_point - left_intersection).integral_norm();
   151 
   141 
   152             // same for the right border
   142             // same for the right border
   153             let right_intersection = Point::new(
   143             let right_intersection = Point::new(
   154                 map_box.right(),
   144                 map_box.right(),
   155                 (map_box.right() - mid_point.x) * p.y / p.x + mid_point.y,
   145                 (map_box.right() - mid_point.x) * normal.tangent() + mid_point.y,
   156             );
   146             );
   157             dist_right = (mid_point - right_intersection).integral_norm();
   147             dist_right = (mid_point - right_intersection).integral_norm();
   158 
   148 
   159             if p.x > 0 {
   149             if normal.x > 0 {
   160                 std::mem::swap(&mut dist_left, &mut dist_right);
   150                 std::mem::swap(&mut dist_left, &mut dist_right);
   161             }
   151             }
   162         }
   152         }
   163 
   153 
   164         if p.y != 0 {
   154         if normal.y != 0 {
   165             // where the normal line intersects the top map border
   155             // where the normal line intersects the top map border
   166             let top_intersection = Point::new(
   156             let top_intersection = Point::new(
   167                 (map_box.top() - mid_point.y) * p.x / p.y + mid_point.x,
   157                 (map_box.top() - mid_point.y) * normal.cotangent() + mid_point.x,
   168                 map_box.top(),
   158                 map_box.top(),
   169             );
   159             );
   170             let dl = (mid_point - top_intersection).integral_norm();
   160             let dl = (mid_point - top_intersection).integral_norm();
   171 
   161 
   172             // same for the bottom border
   162             // same for the bottom border
   173             let bottom_intersection = Point::new(
   163             let bottom_intersection = Point::new(
   174                 (map_box.bottom() - mid_point.y) * p.x / p.y + mid_point.x,
   164                 (map_box.bottom() - mid_point.y) * normal.cotangent() + mid_point.x,
   175                 map_box.bottom(),
   165                 map_box.bottom(),
   176             );
   166             );
   177             let dr = (mid_point - bottom_intersection).integral_norm();
   167             let dr = (mid_point - bottom_intersection).integral_norm();
   178 
   168 
   179             if p.y < 0 {
   169             if normal.y < 0 {
   180                 dist_left = min(dist_left, dl);
   170                 dist_left = min(dist_left, dl);
   181                 dist_right = min(dist_right, dr);
   171                 dist_right = min(dist_right, dr);
   182             } else {
   172             } else {
   183                 dist_left = min(dist_left, dr);
   173                 dist_left = min(dist_left, dr);
   184                 dist_right = min(dist_right, dl);
   174                 dist_right = min(dist_right, dl);
   186         }
   176         }
   187 
   177 
   188         // now go through all other segments
   178         // now go through all other segments
   189         for s in self.segments_iter() {
   179         for s in self.segments_iter() {
   190             if s != segment {
   180             if s != segment {
   191                 if intersect(p, mid_point, s.start, s.end) {
   181                 if intersects(&normal_ray, &s) {
       
   182                     if let Some((t, d)) =
       
   183                         solve_intersection(&self.intersections_box, &normal_ray, &s)
       
   184                     {
       
   185                         if t > 0 {
       
   186                             dist_right = min(dist_right, d);
       
   187                         } else {
       
   188                             dist_left = min(dist_left, d);
       
   189                         }
       
   190                     }
       
   191                 }
       
   192             }
       
   193         }
       
   194 
       
   195         // go through all points, including fill points
       
   196         for pi in self.iter().cloned() {
       
   197             if pi != segment.start && pi != segment.end {
       
   198                 if intersects(&pi.ray_to(normal), &segment) {
       
   199                     // ray from segment.start
   192                     if let Some((t, d)) = solve_intersection(
   200                     if let Some((t, d)) = solve_intersection(
   193                         &self.intersections_box,
   201                         &self.intersections_box, &normal_ray, &segment.start.line_to(pi),
   194                         p,
       
   195                         mid_point,
       
   196                         s.start,
       
   197                         s.end,
       
   198                     ) {
   202                     ) {
   199                         if t > 0 {
   203                         if t > 0 {
   200                             dist_right = min(dist_right, d);
   204                             dist_right = min(dist_right, d);
   201                         } else {
   205                         } else {
   202                             dist_left = min(dist_left, d);
   206                             dist_left = min(dist_left, d);
   203                         }
   207                         }
   204                     }
   208                     }
   205                 }
   209 
   206             }
   210                     // ray from segment.end
   207         }
       
   208 
       
   209         // go through all points, including fill points
       
   210         for pi in self.iter().cloned() {
       
   211             if pi != segment.start && pi != segment.end {
       
   212                 if intersect(p, pi, segment.start, segment.end) {
       
   213                     // ray from segment.start
       
   214                     if let Some((t, d)) = solve_intersection(
   211                     if let Some((t, d)) = solve_intersection(
   215                         &self.intersections_box,
   212                         &self.intersections_box, &normal_ray, &segment.end.line_to(pi)
   216                         p,
       
   217                         mid_point,
       
   218                         segment.start,
       
   219                         pi,
       
   220                     ) {
   213                     ) {
   221                         if t > 0 {
   214                         if t > 0 {
   222                             dist_right = min(dist_right, d);
   215                             dist_right = min(dist_right, d);
   223                         } else {
   216                         } else {
   224                             dist_left = min(dist_left, d);
   217                             dist_left = min(dist_left, d);
   225                         }
   218                         }
   226                     }
   219                     }
   227 
   220                 }
   228                     // ray from segment.end
   221             }
   229                     if let Some((t, d)) = solve_intersection(
   222         }
   230                         &self.intersections_box,
   223 
   231                         p,
   224         let max_dist = normal_len * 100 / distance_divisor;
   232                         mid_point,
       
   233                         segment.end,
       
   234                         pi,
       
   235                     ) {
       
   236                         if t > 0 {
       
   237                             dist_right = min(dist_right, d);
       
   238                         } else {
       
   239                             dist_left = min(dist_left, d);
       
   240                         }
       
   241                     }
       
   242                 }
       
   243             }
       
   244         }
       
   245 
       
   246         let max_dist = p_norm * 100 / distance_divisor;
       
   247         dist_left = min(dist_left, max_dist);
   225         dist_left = min(dist_left, max_dist);
   248         dist_right = min(dist_right, max_dist);
   226         dist_right = min(dist_right, max_dist);
   249 
   227 
   250         if dist_right + dist_left < min_distance as u32 * 2 + 10 {
   228         if dist_right + dist_left < min_distance as u32 * 2 + 10 {
   251             // limits are too narrow, just divide
   229             // limits are too narrow, just divide
   255             let d = -(dist_right as i32)
   233             let d = -(dist_right as i32)
   256                 + min_distance
   234                 + min_distance
   257                 + random_numbers.next().unwrap() as i32
   235                 + random_numbers.next().unwrap() as i32
   258                     % (dist_right as i32 + dist_left as i32 - min_distance * 2);
   236                     % (dist_right as i32 + dist_left as i32 - min_distance * 2);
   259 
   237 
   260             Some(Point::new(
   238             Some(mid_point + normal * d / normal_len as i32)
   261                 mid_point.x + p.x * d / p_norm as i32,
       
   262                 mid_point.y + p.y * d / p_norm as i32,
       
   263             ))
       
   264         }
   239         }
   265     }
   240     }
   266 
   241 
   267     fn divide_edges<I: Iterator<Item = u32>>(
   242     fn divide_edges<I: Iterator<Item = u32>>(
   268         &mut self,
   243         &mut self,