1use core::cmp::{Ordering, Reverse};
98use std::collections::{BinaryHeap, HashMap};
99
100use axiolid_contracts::Sign;
101use axiolid_core::Point2;
102use axiolid_overlay::Polygon;
103
104use crate::graph::{region_left, Graph, Side};
105use crate::map::{
106 free_triangles_in, in_polygon, in_triangle, meets_triangle, outside, segments_meet,
107};
108use crate::{
109 contains, crosses, dedup_points, obstacle_segments, ring_edges, side, validate_region, within,
110 Farthest, FarthestError, LengthInterval, MapError, Route, RouteError, Unreachable, MAX_CELLS,
111};
112
113const GRADING: u32 = 6;
115
116pub const MAX_WEIGHTED_NODES: usize = 2048;
119
120#[derive(Debug, Clone, PartialEq)]
122pub struct CostRegion {
123 pub polygon: Polygon,
125 pub factor: f64,
127}
128
129impl CostRegion {
130 #[must_use]
132 pub fn new(polygon: Polygon, factor: f64) -> Self {
133 Self { polygon, factor }
134 }
135}
136
137#[derive(Debug, Clone, PartialEq)]
139#[non_exhaustive]
140pub struct WeightedReach {
141 pub target: usize,
143 pub cost: LengthInterval,
148 pub route: Route,
151}
152
153#[derive(Debug, Clone, Copy)]
155enum Kind {
156 Vertex,
158 Interval {
163 a: Point2,
164 b: Point2,
165 p: Point2,
168 q: Point2,
169 line: u32,
170 piece: u32,
171 left: f64,
172 right: f64,
173 along: f64,
174 a_vertex: bool,
175 b_vertex: bool,
176 },
177}
178
179const NO_LINE: u32 = u32::MAX;
180
181#[derive(Debug, Clone)]
183pub struct WeightedMap {
184 region: Vec<Polygon>,
185 walls: Vec<(Point2, Point2)>,
186 obstacles: Vec<(Point2, Point2)>,
187 weights: Weights,
188 nodes: Vec<Point2>,
189 kinds: Vec<Kind>,
190 lines: Vec<Vec<u32>>,
192 graph: Graph,
193 upper: Vec<f64>,
195 next: Vec<usize>,
196 target: Vec<usize>,
197 lower: Vec<Vec<(Tag, f64)>>,
200 sites: Vec<Point2>,
201 seeds: Vec<f64>,
203 costs: Vec<CostRegion>,
205 scale: f64,
206}
207
208pub fn weighted_distance_map(
229 region: &[Polygon],
230 barriers: &[Vec<Point2>],
231 targets: &[Point2],
232 costs: &[CostRegion],
233 spacing: f64,
234) -> Result<WeightedMap, MapError> {
235 weighted_distance_map_within(
236 region,
237 barriers,
238 targets,
239 costs,
240 spacing,
241 MAX_WEIGHTED_NODES,
242 )
243}
244
245pub fn weighted_distance_map_within(
251 region: &[Polygon],
252 barriers: &[Vec<Point2>],
253 targets: &[Point2],
254 costs: &[CostRegion],
255 spacing: f64,
256 budget: usize,
257) -> Result<WeightedMap, MapError> {
258 let seeded: Vec<(Point2, f64)> = targets.iter().map(|t| (*t, 0.0)).collect();
259 weighted_distance_map_seeded_within(region, barriers, &seeded, costs, spacing, budget)
260}
261
262pub fn weighted_distance_map_seeded(
273 region: &[Polygon],
274 barriers: &[Vec<Point2>],
275 targets: &[(Point2, f64)],
276 costs: &[CostRegion],
277 spacing: f64,
278) -> Result<WeightedMap, MapError> {
279 weighted_distance_map_seeded_within(
280 region,
281 barriers,
282 targets,
283 costs,
284 spacing,
285 MAX_WEIGHTED_NODES,
286 )
287}
288
289pub fn weighted_distance_map_seeded_within(
296 region: &[Polygon],
297 barriers: &[Vec<Point2>],
298 seeded: &[(Point2, f64)],
299 costs: &[CostRegion],
300 spacing: f64,
301 budget: usize,
302) -> Result<WeightedMap, MapError> {
303 validate_region(region, barriers)?;
304 if seeded.is_empty() {
305 return Err(MapError::NoTargets);
306 }
307 let targets: Vec<Point2> = seeded.iter().map(|(t, _)| *t).collect();
308 let seeds: Vec<f64> = seeded.iter().map(|(_, w)| *w).collect();
309 if !targets.iter().all(|t| t.is_finite()) {
310 return Err(RouteError::NonFinitePoint.into());
311 }
312 if let Some(index) = seeds.iter().position(|w| !(w.is_finite() && *w >= 0.0)) {
313 return Err(MapError::InvalidWeight { index });
314 }
315 if !(spacing.is_finite() && spacing > 0.0) {
316 return Err(MapError::InvalidSpacing);
317 }
318 for (index, cost) in costs.iter().enumerate() {
319 if !(cost.factor.is_finite() && cost.factor >= 1.0) {
320 return Err(MapError::InvalidFactor { index });
321 }
322 }
323 validate_region(
324 &costs.iter().map(|c| c.polygon.clone()).collect::<Vec<_>>(),
325 &[],
326 )?;
327 let reach = touch_reach(region);
328 let costs = &touch_walls(costs, region, reach)?;
329 let polygons: Vec<Polygon> = costs.iter().map(|c| c.polygon.clone()).collect();
330 validate_region(&polygons, &[])?;
331 for (index, t) in targets.iter().enumerate() {
332 if !contains(region, *t)? {
333 return Err(MapError::TargetOutside { index });
334 }
335 }
336 let obstacles = obstacle_segments(region, barriers);
337 let walls: Vec<(Point2, Point2)> = barriers
338 .iter()
339 .flat_map(|b| b.windows(2).map(|w| (w[0], w[1])))
340 .collect();
341 let mut nodes = targets.clone();
343 for polygon in region.iter().chain(polygons.iter()) {
344 for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
345 nodes.extend(ring.points.iter().copied());
346 }
347 }
348 for barrier in barriers {
349 nodes.extend(barrier.iter().copied());
350 }
351 dedup_points(&mut nodes);
352 let weights = Weights::new(costs, &nodes, region)?;
355 weights.check_crossings(&obstacles, &walls, reach)?;
356 let mut kinds = vec![Kind::Vertex; nodes.len()];
357 let mut spans: Vec<(Point2, Point2, u32)> = Vec::new();
358 for piece in &weights.pieces {
359 let (p, q) = if (piece.p.x, piece.p.y) <= (piece.q.x, piece.q.y) {
360 (piece.p, piece.q)
361 } else {
362 (piece.q, piece.p)
363 };
364 if !spans.iter().any(|(u, v, _)| *u == p && *v == q) {
366 spans.push((p, q, piece.line));
367 }
368 }
369 for (piece, (p, q, line)) in spans.into_iter().enumerate() {
370 if weights.beyond_wall(p, q, reach)? {
374 continue;
375 }
376 let count = ((q - p).length() / spacing).ceil().max(1.0) as usize;
377 let mut cuts: Vec<f64> = (0..=count).map(|k| k as f64 / count as f64).collect();
380 let first = 1.0 / count as f64;
381 for level in 1..=GRADING {
382 let t = first / f64::from(1u32 << level);
383 cuts.push(t);
384 cuts.push(1.0 - t);
385 }
386 cuts.sort_by(f64::total_cmp);
387 cuts.dedup();
388 let count = cuts.len() - 1;
389 if nodes.len() + count > budget {
390 return Err(RouteError::TooManyVertices {
391 supplied: nodes.len() + count,
392 budget,
393 lower_bound: 0.0,
394 }
395 .into());
396 }
397 let at = |k: usize| {
398 if k == 0 {
399 p
400 } else if k == count {
401 q
402 } else {
403 let t = cuts[k];
404 Point2::new(p.x + (q.x - p.x) * t, p.y + (q.y - p.y) * t)
405 }
406 };
407 for k in 0..count {
408 let (a, b) = (at(k), at(k + 1));
409 nodes.push(Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y));
410 let (left, right) = weights.sides(p, q, a, b)?;
411 let along = left.min(right);
412 kinds.push(Kind::Interval {
413 a,
414 b,
415 p,
416 q,
417 line,
418 piece: piece as u32,
419 left,
420 right,
421 along,
422 a_vertex: k == 0,
425 b_vertex: k + 1 == count,
426 });
427 }
428 }
429 if nodes.len() > budget {
430 return Err(RouteError::TooManyVertices {
431 supplied: nodes.len(),
432 budget,
433 lower_bound: 0.0,
434 }
435 .into());
436 }
437 let lines: Vec<Vec<u32>> = nodes
438 .iter()
439 .zip(&kinds)
440 .map(|(v, kind)| match kind {
441 Kind::Interval { line, .. } => Ok(vec![*line]),
442 Kind::Vertex => weights.lines_through(*v),
443 })
444 .collect::<Result<_, RouteError>>()?;
445 let graph = Graph::build(&nodes, region, barriers, &obstacles)?;
446 let scale = nodes
447 .iter()
448 .fold(1.0f64, |m, p| m.max(p.x.abs()).max(p.y.abs()));
449
450 let mut map = WeightedMap {
451 region: region.to_vec(),
452 walls,
453 obstacles,
454 weights,
455 nodes,
456 kinds,
457 lines,
458 graph,
459 upper: Vec::new(),
460 next: Vec::new(),
461 target: Vec::new(),
462 lower: Vec::new(),
463 sites: targets.clone(),
464 seeds: seeds.clone(),
465 costs: costs.to_vec(),
466 scale,
467 };
468 let mut sources = Vec::new();
471 let mut seed = vec![usize::MAX; map.graph.adjacency.len()];
472 for (i, node) in map.nodes.iter().enumerate() {
473 let lightest = (0..targets.len())
474 .filter(|&t| targets[t] == *node)
475 .min_by(|&a, &b| seeds[a].total_cmp(&seeds[b]).then(a.cmp(&b)));
476 if let Some(t) = lightest {
477 for state in map.graph.states(i) {
478 sources.push((state, seeds[t]));
479 seed[state] = t;
480 }
481 }
482 }
483 map.upper_bounds(&sources, &seed)?;
484 map.lower_bounds(&sources)?;
485 Ok(map)
486}
487
488impl WeightedMap {
489 #[must_use]
491 pub fn graph_vertices(&self) -> usize {
492 self.nodes.len()
493 }
494
495 #[must_use]
497 pub fn targets(&self) -> usize {
498 self.sites.len()
499 }
500
501 pub fn nearest(&self, point: Point2) -> Result<Result<WeightedReach, Unreachable>, RouteError> {
511 if !point.is_finite() {
512 return Err(RouteError::NonFinitePoint);
513 }
514 if !contains(&self.region, point)? {
515 return Ok(Err(Unreachable::StartOutside));
516 }
517 let Some((upper, first)) = self.upper_at(point)? else {
518 return Ok(Err(Unreachable::DisconnectedComponents));
519 };
520 let lower = self.lower_at(point, upper)?.min(upper);
521 let mut polyline = vec![point];
522 let mut state = first;
523 loop {
524 let at = self.nodes[self.graph.node(state)];
525 if polyline.last() != Some(&at) {
526 polyline.push(at);
527 }
528 if self.next[state] == usize::MAX {
529 break;
530 }
531 state = self.next[state];
532 }
533 let length = polyline.windows(2).map(|w| (w[1] - w[0]).length()).sum();
534 Ok(Ok(WeightedReach {
535 target: self.target[first],
536 cost: LengthInterval { lower, upper },
537 route: Route {
538 polyline,
539 length,
540 graph_vertices: self.nodes.len(),
541 },
542 }))
543 }
544
545 pub(crate) fn region(&self) -> &[Polygon] {
547 &self.region
548 }
549
550 pub(crate) fn obstacles(&self) -> &[(Point2, Point2)] {
552 &self.obstacles
553 }
554
555 pub(crate) fn walls(&self) -> &[(Point2, Point2)] {
557 &self.walls
558 }
559
560 pub(crate) fn seeded(&self) -> impl Iterator<Item = (Point2, f64)> + '_ {
562 self.sites.iter().copied().zip(self.seeds.iter().copied())
563 }
564
565 pub(crate) fn same_space(&self, other: &Self) -> bool {
567 self.region == other.region && self.walls == other.walls && self.costs == other.costs
568 }
569
570 pub(crate) fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
573 self.weights.steepest(t)
574 }
575
576 pub(crate) fn inside_factor(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
579 for piece in &self.weights.pieces {
580 if meets_triangle(piece.p, piece.q, t)? {
581 return Ok(1.0);
582 }
583 }
584 let mut best = 1.0f64;
585 for (polygon, &factor) in self.weights.polygons.iter().zip(&self.weights.factors) {
586 if factor > best && in_polygon(polygon, t[0])? {
587 best = factor;
588 }
589 }
590 Ok(best)
591 }
592
593 pub(crate) fn spans(&self) -> Vec<((Point2, Point2), f64)> {
596 (0..self.nodes.len())
597 .filter_map(|i| {
598 let least = self
599 .graph
600 .states(i)
601 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
602 .fold(f64::INFINITY, f64::min);
603 least.is_finite().then(|| (self.span(i), least))
604 })
605 .collect()
606 }
607
608 pub(crate) fn bracket(&self, point: Point2) -> Result<Option<(f64, f64)>, RouteError> {
611 let Some((upper, _)) = self.upper_at(point)? else {
612 return Ok(None);
613 };
614 Ok(Some((self.lower_at(point, upper)?.min(upper), upper)))
615 }
616
617 fn upper_bounds(&mut self, sources: &[(usize, f64)], seed: &[usize]) -> Result<(), RouteError> {
620 let states = self.graph.adjacency.len();
621 let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
622 let mut adjacency: Vec<Vec<(usize, f64)>> = vec![Vec::new(); states];
623 for (u, edges) in self.graph.adjacency.iter().enumerate() {
624 let i = self.graph.node(u);
625 for &(w, _) in edges {
626 let j = self.graph.node(w);
627 let key = (i.min(j), i.max(j));
628 let c = match cost.get(&key) {
629 Some(c) => *c,
630 None => {
631 let c = self
632 .weights
633 .segment(self.nodes[i], self.nodes[j], self.scale)?
634 .1;
635 cost.insert(key, c);
636 c
637 }
638 };
639 adjacency[u].push((w, c));
640 }
641 }
642 let (distance, previous) = dijkstra(&adjacency, sources);
643 let mut target = seed.to_vec();
646 for (state, t) in target.iter_mut().enumerate() {
647 let mut at = state;
648 let mut steps = 0;
649 while previous[at] != usize::MAX && steps <= seed.len() {
650 at = previous[at];
651 steps += 1;
652 }
653 *t = seed[at];
654 }
655 self.upper = distance;
656 self.next = previous;
657 self.target = target;
658 Ok(())
659 }
660
661 fn hop_states(&self, i: usize, b1: Point2, b2: Point2) -> Result<Vec<usize>, RouteError> {
665 match self.kinds[i] {
666 Kind::Vertex => self.graph.cone_states(i, self.nodes[i], b1, b2),
667 Kind::Interval { .. } => Ok(self.graph.states(i).collect()),
668 }
669 }
670
671 fn span(&self, i: usize) -> (Point2, Point2) {
673 match self.kinds[i] {
674 Kind::Vertex => (self.nodes[i], self.nodes[i]),
675 Kind::Interval { a, b, .. } => (a, b),
676 }
677 }
678
679 fn shared_line(&self, i: usize, j: usize) -> u32 {
681 self.lines[i]
682 .iter()
683 .find(|l| self.lines[j].contains(l))
684 .copied()
685 .unwrap_or(NO_LINE)
686 }
687
688 fn hop(
695 &self,
696 (a1, a2): (Point2, Point2),
697 (b1, b2): (Point2, Point2),
698 ka: Kind,
699 kb: Kind,
700 ) -> Result<Option<f64>, RouteError> {
701 let (ea, eb) = (vertex_ends(ka), vertex_ends(kb));
702 let blocks = |p: Point2, q: Point2| -> Result<bool, RouteError> {
703 for (u, skip_u) in [(a1, ea.0), (a2, ea.1)] {
704 for (v, skip_v) in [(b1, eb.0), (b2, eb.1)] {
705 if !crosses(u, v, p, q)? && !((skip_u || skip_v) && through_end(u, v, p, q)?) {
706 return Ok(false);
707 }
708 }
709 }
710 Ok(true)
711 };
712 for &(p, q) in &self.obstacles {
713 if blocks(p, q)? {
714 return Ok(None);
715 }
716 }
717 if convex_hull(&[a1, a2, b1, b2])?.is_none() {
718 let Some((u, v)) = gap((a1, a2), (b1, b2)) else {
722 return Ok(Some(0.0));
723 };
724 return Ok(Some(self.weights.segment(u, v, self.scale)?.0));
725 }
726 let own = |kind: Kind| match kind {
729 Kind::Interval { line, .. } => line,
730 Kind::Vertex => NO_LINE,
731 };
732 let (la, lb) = (own(ka), own(kb));
733 for piece in &self.weights.pieces {
734 if piece.line != la && piece.line != lb && blocks(piece.p, piece.q)? {
735 return Ok(None);
736 }
737 }
738 let factor = self
742 .weights
743 .hull_factor(&[a1, a2, b1, b2])?
744 .max(leaving(ka, (b1, b2), eb)?)
745 .max(leaving(kb, (a1, a2), ea)?);
746 let d = span_distance((a1, a2), (b1, b2))?;
747 Ok(Some(round_down(factor * d, self.scale)))
748 }
749
750 fn side_of(&self, k: usize, n: usize) -> Result<u8, RouteError> {
755 let (u, v) = self.span(n);
756 let ends = match self.kinds[n] {
757 Kind::Interval {
758 a_vertex, b_vertex, ..
759 } => (a_vertex, b_vertex),
760 Kind::Vertex => (false, false),
761 };
762 self.side_of_span(k, (u, v), ends)
763 }
764
765 fn side_of_span(
766 &self,
767 k: usize,
768 (u, v): (Point2, Point2),
769 (skip_u, skip_v): (bool, bool),
770 ) -> Result<u8, RouteError> {
771 let Kind::Interval { p, q, .. } = self.kinds[k] else {
772 return Ok(NONE);
773 };
774 let (su, sv) = (side(p, q, u)?, side(p, q, v)?);
775 let su = if su == Sign::Zero && skip_u { sv } else { su };
776 let sv = if sv == Sign::Zero && skip_v { su } else { sv };
777 Ok(match (su, sv) {
778 (Sign::Positive, Sign::Positive) => LEFT,
779 (Sign::Negative, Sign::Negative) => RIGHT,
780 _ => NONE,
781 })
782 }
783
784 fn edge(&self, n: usize, k: usize, from: usize, weight: f64) -> Result<Edge, RouteError> {
787 let line = self.shared_line(n, k);
788 let (leave, arrive) = if line == NO_LINE {
789 (self.side_of(n, k)?, self.side_of(k, n)?)
790 } else {
791 (NONE, NONE)
792 };
793 let same_piece = match (self.kinds[n], self.kinds[k]) {
794 (Kind::Interval { piece: pn, .. }, Kind::Interval { piece: pk, .. }) => pn == pk,
795 _ => false,
796 };
797 Ok(Edge {
798 from,
799 weight,
800 line,
801 leave,
802 arrive,
803 same_piece,
804 })
805 }
806
807 fn lower_bounds(&mut self, sources: &[(usize, f64)]) -> Result<(), RouteError> {
812 let states = self.graph.adjacency.len();
813 let mut incoming: Vec<Vec<Edge>> = vec![Vec::new(); states];
814 let is_vertex = |i: usize, kinds: &[Kind]| matches!(kinds[i], Kind::Vertex);
815 let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
816 for (u, edges) in self.graph.adjacency.iter().enumerate() {
817 let i = self.graph.node(u);
818 if !is_vertex(i, &self.kinds) {
819 continue;
820 }
821 for &(w, _) in edges {
822 let j = self.graph.node(w);
823 if !is_vertex(j, &self.kinds) {
824 continue;
825 }
826 let key = (i.min(j), i.max(j));
827 let c = match cost.get(&key) {
828 Some(c) => *c,
829 None => {
830 let c = self
831 .weights
832 .segment(self.nodes[i], self.nodes[j], self.scale)?
833 .0;
834 cost.insert(key, c);
835 c
836 }
837 };
838 incoming[w].push(self.edge(i, j, u, c)?);
840 }
841 }
842 let n = self.nodes.len();
843 for i in 0..n {
844 for j in i + 1..n {
845 if is_vertex(i, &self.kinds) && is_vertex(j, &self.kinds) {
846 continue;
847 }
848 let (si, sj) = (self.span(i), self.span(j));
849 let Some(c) = self.hop(si, sj, self.kinds[i], self.kinds[j])? else {
850 continue;
851 };
852 let from = self.hop_states(i, sj.0, sj.1)?;
853 let to = self.hop_states(j, si.0, si.1)?;
854 for &u in &from {
855 for &w in &to {
856 incoming[w].push(self.edge(i, j, u, c)?);
857 incoming[u].push(self.edge(j, i, w, c)?);
858 }
859 }
860 }
861 }
862 let graph = &self.graph;
863 let kinds = &self.kinds;
864 self.lower = tagged_dijkstra(&incoming, sources, |s| kinds[graph.node(s)]);
865 Ok(())
866 }
867
868 fn upper_at(&self, point: Point2) -> Result<Option<(f64, usize)>, RouteError> {
870 let mut best: Option<(f64, usize)> = None;
871 let offer = |value: f64, state: usize, best: &mut Option<(f64, usize)>| {
872 if best.is_none_or(|(b, s)| value < b || (value == b && state < s)) {
873 *best = Some((value, state));
874 }
875 };
876 for (i, node) in self.nodes.iter().enumerate() {
877 let nearest = self
878 .graph
879 .states(i)
880 .map(|s| self.upper[s])
881 .fold(f64::INFINITY, f64::min);
882 if nearest.is_infinite() {
883 continue;
884 }
885 if *node == point {
886 for s in self.graph.states(i) {
887 offer(self.upper[s], s, &mut best);
888 }
889 continue;
890 }
891 if best.is_some_and(|(b, _)| (*node - point).length() + nearest > b) {
893 continue;
894 }
895 let ok = crate::graph::sides(
896 point,
897 *node,
898 &self.region,
899 &self.obstacles,
900 &self.nodes,
901 &self.graph.stars,
902 &self.graph.rayed,
903 )?;
904 if ok == [false, false] {
905 continue;
906 }
907 let leg = self.weights.segment(point, *node, self.scale)?.1;
908 for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
909 if !ok[k] {
910 continue;
911 }
912 if let Some(s) = self.graph.arrival(i, point, on)? {
913 if self.upper[s].is_finite() {
914 offer(leg + self.upper[s], s, &mut best);
915 }
916 }
917 }
918 }
919 Ok(best)
920 }
921
922 fn lower_at(&self, point: Point2, ceiling: f64) -> Result<f64, RouteError> {
926 let mut best = ceiling;
927 let point_lines = self.weights.lines_through(point)?;
928 for (i, node) in self.nodes.iter().enumerate() {
929 let least = self
930 .graph
931 .states(i)
932 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
933 .fold(f64::INFINITY, f64::min);
934 let kind = self.kinds[i];
935 if least.is_infinite() {
936 continue;
937 }
938 let line = self.lines[i]
939 .iter()
940 .find(|l| point_lines.contains(l))
941 .copied()
942 .unwrap_or(NO_LINE);
943 let first = Edge {
946 from: usize::MAX,
947 weight: 0.0,
948 line,
949 leave: NONE,
950 arrive: if line == NO_LINE {
951 self.side_of_span(i, (point, point), (false, false))?
952 } else {
953 NONE
954 },
955 same_piece: false,
956 };
957 let from = |states: &mut dyn Iterator<Item = usize>| -> f64 {
958 states
959 .flat_map(|s| self.lower[s].iter())
960 .filter(|(tag, _)| allowed(kind, *tag, &first))
961 .map(|(_, d)| *d)
962 .fold(f64::INFINITY, f64::min)
963 };
964 if *node == point {
965 let any = self
967 .graph
968 .states(i)
969 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
970 .fold(f64::INFINITY, f64::min);
971 best = best.min(any);
972 continue;
973 }
974 let (a, b) = self.span(i);
975 if span_distance((point, point), (a, b))? + least >= best {
976 continue;
977 }
978 match self.kinds[i] {
979 Kind::Vertex => {
980 let ok = crate::graph::sides(
981 point,
982 *node,
983 &self.region,
984 &self.obstacles,
985 &self.nodes,
986 &self.graph.stars,
987 &self.graph.rayed,
988 )?;
989 if ok == [false, false] {
990 continue;
991 }
992 let leg = self.weights.segment(point, *node, self.scale)?.0;
993 for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
994 if !ok[k] {
995 continue;
996 }
997 if let Some(s) = self.graph.arrival(i, point, on)? {
998 best = best.min(leg + from(&mut core::iter::once(s)));
999 }
1000 }
1001 }
1002 Kind::Interval { .. } => {
1003 let Some(leg) =
1004 self.hop((point, point), (a, b), Kind::Vertex, self.kinds[i])?
1005 else {
1006 continue;
1007 };
1008 best = best.min(leg + from(&mut self.graph.states(i)));
1009 }
1010 }
1011 }
1012 Ok(best)
1013 }
1014}
1015
1016fn dijkstra(adjacency: &[Vec<(usize, f64)>], sources: &[(usize, f64)]) -> (Vec<f64>, Vec<usize>) {
1020 let n = adjacency.len();
1021 let mut distance = vec![f64::INFINITY; n];
1022 let mut previous = vec![usize::MAX; n];
1023 let mut heap = BinaryHeap::new();
1024 for &(s, start) in sources {
1025 if start < distance[s] {
1026 distance[s] = start;
1027 heap.push(Reverse((Ordered(start), s)));
1028 }
1029 }
1030 while let Some(Reverse((Ordered(d), u))) = heap.pop() {
1031 if d > distance[u] {
1032 continue;
1033 }
1034 for &(w, c) in &adjacency[u] {
1035 let candidate = d + c;
1036 if candidate < distance[w] || (candidate == distance[w] && u < previous[w]) {
1037 if candidate < distance[w] {
1038 heap.push(Reverse((Ordered(candidate), w)));
1039 }
1040 distance[w] = candidate;
1041 previous[w] = u;
1042 }
1043 }
1044 }
1045 (distance, previous)
1046}
1047
1048const NONE: u8 = 0;
1050const LEFT: u8 = 1;
1051const RIGHT: u8 = 2;
1052const ALONG: u8 = 3;
1055
1056#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1061struct Tag {
1062 line: u32,
1063 side: u8,
1064}
1065
1066#[derive(Debug, Clone, Copy)]
1068struct Edge {
1069 from: usize,
1070 weight: f64,
1071 line: u32,
1073 leave: u8,
1075 arrive: u8,
1077 same_piece: bool,
1079}
1080
1081fn allowed(kind: Kind, tag: Tag, edge: &Edge) -> bool {
1088 if edge.line != NO_LINE && tag.line == edge.line {
1089 return false;
1090 }
1091 if let Kind::Interval {
1092 left, right, along, ..
1093 } = kind
1094 {
1095 if edge.line == NO_LINE && edge.arrive != NONE {
1096 let factor = if edge.arrive == LEFT { left } else { right };
1097 if tag.side == edge.arrive {
1098 return false;
1099 }
1100 if tag.side > ALONG && tag.side - ALONG == edge.arrive && along >= factor {
1101 return false;
1102 }
1103 }
1104 }
1105 true
1106}
1107
1108fn tag_before(source: Kind, tag: Tag, edge: &Edge) -> Tag {
1111 let side = match source {
1112 Kind::Vertex => NONE,
1113 Kind::Interval { .. } if edge.line != NO_LINE => {
1114 if edge.same_piece && (tag.side == LEFT || tag.side == RIGHT) {
1115 ALONG + tag.side
1116 } else {
1117 ALONG
1118 }
1119 }
1120 Kind::Interval { .. } => edge.leave,
1121 };
1122 Tag {
1123 line: edge.line,
1124 side,
1125 }
1126}
1127
1128fn tagged_dijkstra(
1132 incoming: &[Vec<Edge>],
1133 sources: &[(usize, f64)],
1134 kind: impl Fn(usize) -> Kind,
1135) -> Vec<Vec<(Tag, f64)>> {
1136 let mut settled: Vec<Vec<(Tag, f64)>> = vec![Vec::new(); incoming.len()];
1137 let mut best: HashMap<(usize, Tag), f64> = HashMap::new();
1138 let mut heap = BinaryHeap::new();
1139 let start = Tag {
1140 line: NO_LINE,
1141 side: NONE,
1142 };
1143 for &(s, weight) in sources {
1144 if best.get(&(s, start)).is_none_or(|b| weight < *b) {
1145 best.insert((s, start), weight);
1146 heap.push(Reverse((Ordered(weight), s, start)));
1147 }
1148 }
1149 while let Some(Reverse((Ordered(d), k, tag))) = heap.pop() {
1150 if settled[k].iter().any(|(t, _)| *t == tag) {
1151 continue;
1152 }
1153 settled[k].push((tag, d));
1154 let here = kind(k);
1155 for edge in &incoming[k] {
1156 if !allowed(here, tag, edge) {
1157 continue;
1158 }
1159 let before = tag_before(kind(edge.from), tag, edge);
1160 let candidate = d + edge.weight;
1161 let key = (edge.from, before);
1162 if best.get(&key).is_none_or(|b| candidate < *b) {
1163 best.insert(key, candidate);
1164 heap.push(Reverse((Ordered(candidate), edge.from, before)));
1165 }
1166 }
1167 }
1168 settled
1169}
1170
1171#[derive(Debug, Clone, Copy, PartialEq)]
1173struct Ordered(f64);
1174
1175impl Eq for Ordered {}
1176
1177impl PartialOrd for Ordered {
1178 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1179 Some(self.cmp(other))
1180 }
1181}
1182
1183impl Ord for Ordered {
1184 fn cmp(&self, other: &Self) -> Ordering {
1185 self.0.total_cmp(&other.0)
1186 }
1187}
1188
1189fn round_down(value: f64, scale: f64) -> f64 {
1192 (value * (1.0 - 16.0 * f64::EPSILON) - 16.0 * f64::EPSILON * scale).max(0.0)
1193}
1194
1195fn round_up(value: f64, scale: f64) -> f64 {
1196 value * (1.0 + 16.0 * f64::EPSILON) + 16.0 * f64::EPSILON * scale
1197}
1198
1199fn leaving(
1204 kind: Kind,
1205 (o1, o2): (Point2, Point2),
1206 (skip1, skip2): (bool, bool),
1207) -> Result<f64, RouteError> {
1208 let Kind::Interval {
1209 p, q, left, right, ..
1210 } = kind
1211 else {
1212 return Ok(1.0);
1213 };
1214 let (s1, s2) = (side(p, q, o1)?, side(p, q, o2)?);
1215 let s1 = if s1 == Sign::Zero && skip1 { s2 } else { s1 };
1217 let s2 = if s2 == Sign::Zero && skip2 { s1 } else { s2 };
1218 Ok(match (s1, s2) {
1219 (Sign::Positive, Sign::Positive) => left,
1220 (Sign::Negative, Sign::Negative) => right,
1221 _ => left.min(right),
1222 })
1223}
1224
1225fn vertex_ends(kind: Kind) -> (bool, bool) {
1228 match kind {
1229 Kind::Interval {
1230 a_vertex, b_vertex, ..
1231 } => (a_vertex, b_vertex),
1232 Kind::Vertex => (false, false),
1233 }
1234}
1235
1236fn through_end(u: Point2, v: Point2, p: Point2, q: Point2) -> Result<bool, RouteError> {
1241 if side(p, q, u)? == Sign::Zero || side(p, q, v)? == Sign::Zero {
1242 return Ok(false);
1243 }
1244 for e in [p, q] {
1245 if within(u, v, e) && side(u, v, e)? == Sign::Zero {
1246 return Ok(true);
1247 }
1248 }
1249 Ok(false)
1250}
1251
1252fn gap((a1, a2): (Point2, Point2), (b1, b2): (Point2, Point2)) -> Option<(Point2, Point2)> {
1255 let d = if a1 != a2 { a2 - a1 } else { b2 - b1 };
1256 let key = |p: Point2| (p - a1).dot(d);
1257 let (alo, ahi) = if key(a1) <= key(a2) {
1258 (a1, a2)
1259 } else {
1260 (a2, a1)
1261 };
1262 let (blo, bhi) = if key(b1) <= key(b2) {
1263 (b1, b2)
1264 } else {
1265 (b2, b1)
1266 };
1267 if key(ahi) < key(blo) {
1268 Some((ahi, blo))
1269 } else if key(bhi) < key(alo) {
1270 Some((bhi, alo))
1271 } else {
1272 None
1273 }
1274}
1275
1276pub(crate) fn span_distance(
1279 (a1, a2): (Point2, Point2),
1280 (b1, b2): (Point2, Point2),
1281) -> Result<f64, RouteError> {
1282 if segments_meet(a1, a2, b1, b2)? {
1283 return Ok(0.0);
1284 }
1285 Ok(point_segment(a1, b1, b2)
1286 .min(point_segment(a2, b1, b2))
1287 .min(point_segment(b1, a1, a2))
1288 .min(point_segment(b2, a1, a2)))
1289}
1290
1291fn point_segment(v: Point2, a: Point2, b: Point2) -> f64 {
1292 let d = b - a;
1293 let length2 = d.dot(d);
1294 let s = if length2 > 0.0 {
1295 ((v - a).dot(d) / length2).clamp(0.0, 1.0)
1296 } else {
1297 0.0
1298 };
1299 (v - (a + d * s)).length()
1300}
1301
1302#[derive(Debug, Clone, Copy)]
1305struct Piece {
1306 p: Point2,
1307 q: Point2,
1308 polygon: usize,
1309 inside_left: bool,
1311 line: u32,
1313}
1314
1315#[derive(Debug, Clone)]
1317struct Weights {
1318 polygons: Vec<Polygon>,
1319 factors: Vec<f64>,
1320 pieces: Vec<Piece>,
1321 lines: Vec<(Point2, Point2)>,
1323 walls: Vec<(Point2, Point2, bool)>,
1326 greatest: f64,
1327}
1328
1329impl Weights {
1330 fn new(costs: &[CostRegion], cuts: &[Point2], region: &[Polygon]) -> Result<Self, RouteError> {
1331 let mut walls = Vec::new();
1332 for polygon in region {
1333 for (hole, ring) in core::iter::once((false, &polygon.outer))
1334 .chain(polygon.holes.iter().map(|h| (true, h)))
1335 {
1336 let left = region_left(ring, hole)?;
1337 for (p, q) in ring_edges(ring) {
1338 walls.push((p, q, left));
1339 }
1340 }
1341 }
1342 let polygons: Vec<Polygon> = costs.iter().map(|c| c.polygon.clone()).collect();
1343 let factors: Vec<f64> = costs.iter().map(|c| c.factor).collect();
1344 let mut vertices: Vec<Point2> = cuts.to_vec();
1345 for polygon in &polygons {
1346 for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1347 vertices.extend(ring.points.iter().copied());
1348 }
1349 }
1350 dedup_points(&mut vertices);
1351 let mut pieces = Vec::new();
1352 let mut lines: Vec<(Point2, Point2)> = Vec::new();
1353 for (index, polygon) in polygons.iter().enumerate() {
1354 for (hole, ring) in core::iter::once((false, &polygon.outer))
1355 .chain(polygon.holes.iter().map(|h| (true, h)))
1356 {
1357 let left = region_left(ring, hole)?;
1358 for (p, q) in ring_edges(ring) {
1359 if p == q {
1360 continue;
1361 }
1362 let d = q - p;
1363 let mut cuts = vec![p, q];
1364 for &v in &vertices {
1365 if v != p && v != q && within(p, q, v) && side(p, q, v)? == Sign::Zero {
1366 cuts.push(v);
1367 }
1368 }
1369 cuts.sort_by(|u, v| (*u - p).dot(d).total_cmp(&(*v - p).dot(d)));
1370 let mut line = NO_LINE;
1371 for (k, &(r, s)) in lines.iter().enumerate() {
1372 if side(r, s, p)? == Sign::Zero && side(r, s, q)? == Sign::Zero {
1373 line = k as u32;
1374 break;
1375 }
1376 }
1377 if line == NO_LINE {
1378 line = lines.len() as u32;
1379 lines.push((p, q));
1380 }
1381 for pair in cuts.windows(2) {
1382 if pair[0] != pair[1] {
1383 pieces.push(Piece {
1384 p: pair[0],
1385 q: pair[1],
1386 polygon: index,
1387 inside_left: left,
1388 line,
1389 });
1390 }
1391 }
1392 }
1393 }
1394 }
1395 let greatest = factors.iter().copied().fold(1.0, f64::max);
1396 Ok(Self {
1397 polygons,
1398 factors,
1399 pieces,
1400 lines,
1401 walls,
1402 greatest,
1403 })
1404 }
1405
1406 fn check_crossings(
1411 &self,
1412 obstacles: &[(Point2, Point2)],
1413 walls: &[(Point2, Point2)],
1414 reach: f64,
1415 ) -> Result<(), MapError> {
1416 for piece in &self.pieces {
1417 let index = piece.polygon;
1418 for &(r, s) in obstacles {
1419 if crosses(piece.p, piece.q, r, s)? {
1420 let region_edge = self
1421 .walls
1422 .iter()
1423 .any(|&(p, q, _)| (p, q) == (r, s) || (p, q) == (s, r));
1424 let near = point_segment(piece.p, r, s) <= reach
1425 || point_segment(piece.q, r, s) <= reach
1426 || point_segment(r, piece.p, piece.q) <= reach
1427 || point_segment(s, piece.p, piece.q) <= reach;
1428 if region_edge && near {
1429 continue;
1430 }
1431 return Err(MapError::CostCrossing { index });
1432 }
1433 }
1434 for &(r, s) in walls {
1435 let collinear =
1436 side(r, s, piece.p)? == Sign::Zero && side(r, s, piece.q)? == Sign::Zero;
1437 if collinear && segments_meet(piece.p, piece.q, r, s)? {
1438 let overlap = [piece.p, piece.q, r, s]
1439 .iter()
1440 .filter(|v| within(r, s, **v) && within(piece.p, piece.q, **v))
1441 .count();
1442 if overlap >= 2 {
1443 return Err(MapError::CostCrossing { index });
1444 }
1445 }
1446 }
1447 for other in &self.pieces {
1448 if crosses(piece.p, piece.q, other.p, other.q)? {
1449 return Err(MapError::CostCrossing { index });
1450 }
1451 }
1452 }
1453 Ok(())
1454 }
1455
1456 fn sides(&self, p: Point2, q: Point2, a: Point2, b: Point2) -> Result<(f64, f64), RouteError> {
1462 let m = Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y);
1463 let d = q - p;
1464 let (a, b) = (p, q);
1465 let (mut left, mut right) = (1.0f64, 1.0f64);
1466 for (index, polygon) in self.polygons.iter().enumerate() {
1467 let factor = self.factors[index];
1468 let mut on = false;
1469 for piece in self.pieces.iter().filter(|p| p.polygon == index) {
1470 if side(piece.p, piece.q, a)? == Sign::Zero
1471 && side(piece.p, piece.q, b)? == Sign::Zero
1472 && within(piece.p, piece.q, m)
1473 {
1474 on = true;
1475 let same = (piece.q - piece.p).dot(d) > 0.0;
1476 if piece.inside_left == same {
1477 left = left.max(factor);
1478 } else {
1479 right = right.max(factor);
1480 }
1481 }
1482 }
1483 if !on && in_polygon(polygon, m)? {
1484 left = left.max(factor);
1485 right = right.max(factor);
1486 }
1487 }
1488 Ok((left, right))
1489 }
1490
1491 fn beyond_wall(&self, p: Point2, q: Point2, reach: f64) -> Result<bool, RouteError> {
1495 for &(r, s, left) in &self.walls {
1496 let free = if left { Sign::Positive } else { Sign::Negative };
1497 if point_segment(p, r, s) <= reach
1498 && point_segment(q, r, s) <= reach
1499 && side(r, s, p)? != free
1500 && side(r, s, q)? != free
1501 {
1502 return Ok(true);
1503 }
1504 }
1505 Ok(false)
1506 }
1507
1508 fn lines_through(&self, v: Point2) -> Result<Vec<u32>, RouteError> {
1510 let mut out = Vec::new();
1511 for (k, &(p, q)) in self.lines.iter().enumerate() {
1512 if side(p, q, v)? == Sign::Zero {
1513 out.push(k as u32);
1514 }
1515 }
1516 Ok(out)
1517 }
1518
1519 fn hull_factor(&self, corners: &[Point2]) -> Result<f64, RouteError> {
1522 let Some(hull) = convex_hull(corners)? else {
1523 return Ok(1.0);
1524 };
1525 let mut best = 1.0f64;
1526 for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1527 if factor <= best {
1528 continue;
1529 }
1530 if holds(polygon, &hull)? {
1531 best = factor;
1532 }
1533 }
1534 Ok(best)
1535 }
1536
1537 fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
1540 let mut best = 1.0f64;
1541 for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1542 if factor <= best {
1543 continue;
1544 }
1545 let meets = t.iter().try_fold(false, |m, c| {
1546 Ok::<_, RouteError>(m || in_polygon(polygon, *c)?)
1547 })? || core::iter::once(&polygon.outer)
1548 .chain(polygon.holes.iter())
1549 .flat_map(ring_edges)
1550 .try_fold(false, |m, (p, q)| {
1551 Ok::<_, RouteError>(m || meets_triangle(p, q, t)?)
1552 })?;
1553 if meets {
1554 best = factor;
1555 }
1556 }
1557 Ok(best)
1558 }
1559
1560 fn segment(&self, a: Point2, b: Point2, scale: f64) -> Result<(f64, f64), RouteError> {
1572 let length = (b - a).length();
1573 if length == 0.0 {
1574 return Ok((0.0, 0.0));
1575 }
1576 if self.polygons.is_empty() {
1577 return Ok((round_down(length, scale), round_up(length, scale)));
1578 }
1579 let d = b - a;
1580 let param = |v: Point2| (v - a).dot(d) / d.dot(d);
1581 let mut breaks = vec![0.0, 1.0];
1582 let mut along: Vec<(f64, f64, usize, bool)> = Vec::new();
1584 let mut near: Vec<(Point2, Point2, usize, bool)> = Vec::new();
1586 let mut walled: Vec<(f64, f64, bool)> = Vec::new();
1588 let mut near_walls: Vec<(Point2, Point2, bool)> = Vec::new();
1590 for &(p, q, left) in &self.walls {
1591 if p != q && side(a, b, p)? == Sign::Zero && side(a, b, q)? == Sign::Zero {
1592 let (tp, tq) = (param(p), param(q));
1593 let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1594 if hi > lo {
1595 walled.push((lo, hi, left == ((q - p).dot(d) > 0.0)));
1596 breaks.extend([lo, hi]);
1597 }
1598 } else if p != q {
1599 near_walls.push((p, q, left));
1600 }
1601 }
1602 for piece in &self.pieces {
1603 let (sp, sq) = (side(a, b, piece.p)?, side(a, b, piece.q)?);
1604 if sp == Sign::Zero && sq == Sign::Zero {
1605 let (tp, tq) = (param(piece.p), param(piece.q));
1606 let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1607 if hi > lo {
1608 let same = (piece.q - piece.p).dot(d) > 0.0;
1609 along.push((lo, hi, piece.polygon, piece.inside_left == same));
1610 breaks.extend([lo, hi]);
1611 }
1612 continue;
1613 }
1614 near.push((piece.p, piece.q, piece.polygon, piece.inside_left));
1615 let (sa, sb) = (side(piece.p, piece.q, a)?, side(piece.p, piece.q, b)?);
1616 if sp != sq && sa != sb {
1617 let e = piece.q - piece.p;
1620 let denom = d.perp_dot(e);
1621 if denom != 0.0 {
1622 let t = (piece.p - a).perp_dot(e) / denom;
1623 if t > 0.0 && t < 1.0 {
1624 breaks.push(t);
1625 }
1626 }
1627 }
1628 }
1629 breaks.sort_by(f64::total_cmp);
1630 breaks.dedup();
1631 let (mut lower, mut upper) = (0.0, 0.0);
1632 let tiny = 64.0 * f64::EPSILON * scale;
1633 let mut hugged = false;
1634 for pair in breaks.windows(2) {
1635 let (t0, t1) = (pair[0], pair[1]);
1636 let stretch = (t1 - t0) * length;
1637 if stretch <= 0.0 {
1638 continue;
1639 }
1640 let tm = 0.5 * t0 + 0.5 * t1;
1641 let at = |t: f64| Point2::new(a.x + d.x * t, a.y + d.y * t);
1642 let m = at(tm);
1643 let mut on: Vec<(usize, bool)> = along
1646 .iter()
1647 .filter(|(lo, hi, ..)| *lo <= tm && tm <= *hi)
1648 .map(|(_, _, p, l)| (*p, *l))
1649 .collect();
1650 let mut free: Vec<bool> = walled
1652 .iter()
1653 .filter(|(lo, hi, _)| *lo <= tm && tm <= *hi)
1654 .map(|(_, _, l)| *l)
1655 .collect();
1656 let doubtful = stretch <= tiny
1657 || near
1658 .iter()
1659 .any(|&(p, q, ..)| point_segment(m, p, q) <= tiny);
1660 let mut resolved = !doubtful;
1661 if doubtful {
1662 let (s0, s1) = (at(t0), at(t1));
1669 let hugs = |p: Point2, q: Point2| {
1670 point_segment(s0, p, q) <= tiny && point_segment(s1, p, q) <= tiny
1671 };
1672 let mut all_hug = stretch > tiny;
1673 let beyond = match (free.contains(&true), free.contains(&false)) {
1678 (true, false) => Some(Sign::Negative),
1679 (false, true) => Some(Sign::Positive),
1680 _ => None,
1681 };
1682 let mut known = beyond.is_some();
1683 for &(p, q, polygon, inside_left) in &near {
1684 if point_segment(m, p, q) > tiny {
1685 continue;
1686 }
1687 if !hugs(p, q) {
1688 all_hug = false;
1689 break;
1690 }
1691 if let Some(solid) = beyond {
1692 for end in [p, q] {
1693 let s = side(a, b, end)?;
1694 known &= s == Sign::Zero || s == solid;
1695 }
1696 }
1697 on.push((polygon, inside_left == ((q - p).dot(d) > 0.0)));
1698 }
1699 if !all_hug {
1700 lower += stretch;
1701 upper += self.greatest * stretch;
1702 continue;
1703 }
1704 resolved = known;
1705 if !resolved {
1706 lower += stretch;
1707 }
1708 for &(p, q, left) in &near_walls {
1709 if hugs(p, q) {
1710 free.push(left == ((q - p).dot(d) > 0.0));
1711 }
1712 }
1713 hugged = true;
1714 }
1715 let (mut left, mut right) = (0.0f64, 0.0f64);
1717 for (index, polygon) in self.polygons.iter().enumerate() {
1718 let extra = self.factors[index] - 1.0;
1719 if extra <= left.min(right) {
1720 continue;
1721 }
1722 let sides: Vec<bool> = on
1723 .iter()
1724 .filter(|(p, _)| *p == index)
1725 .map(|(_, l)| *l)
1726 .collect();
1727 if sides.is_empty() {
1728 if in_polygon(polygon, m)? {
1729 left = left.max(extra);
1730 right = right.max(extra);
1731 }
1732 } else {
1733 if sides.contains(&true) {
1736 left = left.max(extra);
1737 }
1738 if sides.contains(&false) {
1739 right = right.max(extra);
1740 }
1741 }
1742 }
1743 let (free_left, free_right) = if free.is_empty() {
1746 (true, true)
1747 } else {
1748 (free.contains(&true), free.contains(&false))
1749 };
1750 let factor = 1.0
1751 + if on.is_empty() {
1752 left
1753 } else {
1754 match (free_left, free_right) {
1755 (true, true) => left.min(right),
1756 (true, false) => left,
1757 (false, true) => right,
1758 (false, false) => left.max(right),
1759 }
1760 };
1761 if resolved {
1762 lower += factor * stretch;
1763 }
1764 upper += factor * stretch;
1765 }
1766 if hugged {
1767 upper += 4.0 * self.greatest * tiny * breaks.len() as f64;
1769 }
1770 let slack = (self.greatest - 1.0) * breaks.len() as f64 * 8.0 * f64::EPSILON * length;
1773 Ok((
1774 round_down(lower - slack, scale),
1775 round_up(upper + slack, scale),
1776 ))
1777 }
1778}
1779
1780fn touch_reach(region: &[Polygon]) -> f64 {
1784 let (mut lo, mut hi) = (
1785 Point2::new(f64::INFINITY, f64::INFINITY),
1786 Point2::new(f64::NEG_INFINITY, f64::NEG_INFINITY),
1787 );
1788 let mut scale = 0.0f64;
1789 for polygon in region {
1790 for p in core::iter::once(&polygon.outer)
1791 .chain(polygon.holes.iter())
1792 .flat_map(|r| r.points.iter())
1793 {
1794 lo = Point2::new(lo.x.min(p.x), lo.y.min(p.y));
1795 hi = Point2::new(hi.x.max(p.x), hi.y.max(p.y));
1796 scale = scale.max(p.x.abs()).max(p.y.abs());
1797 }
1798 }
1799 let extent = (hi.x - lo.x).max(hi.y - lo.y).max(0.0);
1800 extent / f64::from(1u32 << 24) + 64.0 * f64::EPSILON * scale
1801}
1802
1803fn touch_walls(
1812 costs: &[CostRegion],
1813 region: &[Polygon],
1814 reach: f64,
1815) -> Result<Vec<CostRegion>, RouteError> {
1816 let mut edges = Vec::new();
1817 for polygon in region {
1818 for (hole, ring) in
1819 core::iter::once((false, &polygon.outer)).chain(polygon.holes.iter().map(|h| (true, h)))
1820 {
1821 let left = region_left(ring, hole)?;
1822 for (p, q) in ring_edges(ring) {
1823 if p != q {
1824 edges.push((p, q, left));
1825 }
1826 }
1827 }
1828 }
1829 let mut out = costs.to_vec();
1830 for cost in &mut out {
1831 for ring in core::iter::once(&mut cost.polygon.outer).chain(cost.polygon.holes.iter_mut()) {
1832 for v in &mut ring.points {
1833 for &(p, q, left) in edges.iter().chain(edges.iter()) {
1834 let free = if left { Sign::Positive } else { Sign::Negative };
1835 if point_segment(*v, p, q) > reach || side(p, q, *v)? != free {
1836 continue;
1837 }
1838 let d = q - p;
1841 let t = (*v - p).dot(d) / d.dot(d);
1842 let foot = Point2::new(p.x + d.x * t, p.y + d.y * t);
1843 let out = if left {
1844 Point2::new(d.y, -d.x)
1845 } else {
1846 Point2::new(-d.y, d.x)
1847 };
1848 let unit = Point2::new(out.x / d.length(), out.y / d.length());
1849 let mut step = f64::EPSILON * foot.x.abs().max(foot.y.abs()).max(reach);
1850 let mut moved = foot;
1851 while side(p, q, moved)? == free {
1852 moved = Point2::new(foot.x + unit.x * step, foot.y + unit.y * step);
1853 step *= 2.0;
1854 }
1855 *v = moved;
1856 }
1857 }
1858 }
1859 }
1860 Ok(out)
1861}
1862
1863fn convex_hull(points: &[Point2]) -> Result<Option<Vec<Point2>>, RouteError> {
1866 let mut pts: Vec<Point2> = points.to_vec();
1867 pts.sort_by(|p, q| p.x.total_cmp(&q.x).then(p.y.total_cmp(&q.y)));
1868 pts.dedup();
1869 if pts.len() < 3 {
1870 return Ok(None);
1871 }
1872 let mut hull: Vec<Point2> = Vec::new();
1874 for pass in 0..2 {
1875 let start = hull.len();
1876 let iter: Box<dyn Iterator<Item = &Point2>> = if pass == 0 {
1877 Box::new(pts.iter())
1878 } else {
1879 Box::new(pts.iter().rev())
1880 };
1881 for &p in iter {
1882 while hull.len() >= start + 2
1883 && side(hull[hull.len() - 2], hull[hull.len() - 1], p)? != Sign::Positive
1884 {
1885 hull.pop();
1886 }
1887 hull.push(p);
1888 }
1889 hull.pop();
1890 }
1891 Ok((hull.len() >= 3).then_some(hull))
1892}
1893
1894fn holds(polygon: &Polygon, hull: &[Point2]) -> Result<bool, RouteError> {
1902 for &c in hull {
1903 if !in_polygon(polygon, c)? {
1904 return Ok(false);
1905 }
1906 }
1907 let n = hull.len();
1908 for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1909 for (p, q) in ring_edges(ring) {
1910 if meets_open_convex(p, q, hull, n)? {
1911 return Ok(false);
1912 }
1913 }
1914 }
1915 let inv = 1.0 / n as f64;
1916 let centre = hull.iter().fold(Point2::ZERO, |sum, c| sum + *c * inv);
1917 for i in 0..n {
1918 if side(hull[i], hull[(i + 1) % n], centre)? != Sign::Positive {
1919 return Ok(false);
1920 }
1921 }
1922 let on_boundary = core::iter::once(&polygon.outer)
1923 .chain(polygon.holes.iter())
1924 .flat_map(ring_edges)
1925 .try_fold(false, |on, (p, q)| {
1926 Ok::<_, RouteError>(on || (within(p, q, centre) && side(p, q, centre)? == Sign::Zero))
1927 })?;
1928 Ok(!on_boundary && in_polygon(polygon, centre)?)
1929}
1930
1931fn meets_open_convex(p: Point2, q: Point2, hull: &[Point2], n: usize) -> Result<bool, RouteError> {
1935 for i in 0..n {
1936 let (u, v) = (hull[i], hull[(i + 1) % n]);
1937 if side(u, v, p)? != Sign::Positive && side(u, v, q)? != Sign::Positive {
1938 return Ok(false);
1939 }
1940 }
1941 if p != q {
1942 let mut all_left = true;
1943 let mut all_right = true;
1944 for &c in hull {
1945 let s = side(p, q, c)?;
1946 all_left &= s != Sign::Negative;
1947 all_right &= s != Sign::Positive;
1948 }
1949 if all_left || all_right {
1950 return Ok(false);
1951 }
1952 }
1953 Ok(true)
1954}
1955
1956pub fn weighted_farthest_point(
1965 map: &WeightedMap,
1966 subregion: &Polygon,
1967 tolerance: f64,
1968) -> Result<Farthest, FarthestError> {
1969 weighted_farthest_point_within(map, subregion, tolerance, MAX_CELLS)
1970}
1971
1972#[derive(Debug, Clone, Copy)]
1973struct Cell {
1974 corners: [Point2; 3],
1975 root: usize,
1976 anchor: (Point2, f64),
1977 upper: f64,
1978 order: usize,
1979 depth: u32,
1980}
1981
1982impl PartialEq for Cell {
1983 fn eq(&self, other: &Self) -> bool {
1984 self.cmp(other) == Ordering::Equal
1985 }
1986}
1987
1988impl Eq for Cell {}
1989
1990impl PartialOrd for Cell {
1991 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1992 Some(self.cmp(other))
1993 }
1994}
1995
1996impl Ord for Cell {
1997 fn cmp(&self, other: &Self) -> Ordering {
1998 self.upper
1999 .total_cmp(&other.upper)
2000 .then(other.order.cmp(&self.order))
2001 }
2002}
2003
2004pub fn weighted_farthest_point_within(
2010 map: &WeightedMap,
2011 subregion: &Polygon,
2012 tolerance: f64,
2013 max_cells: usize,
2014) -> Result<Farthest, FarthestError> {
2015 if !(tolerance.is_finite() && tolerance >= 0.0) {
2016 return Err(FarthestError::InvalidTolerance);
2017 }
2018 validate_region(core::slice::from_ref(subregion), &[])?;
2019 let roots = free_triangles_in(&map.region, &map.obstacles)?;
2020 let steep: Vec<f64> = roots
2021 .iter()
2022 .map(|t| map.weights.steepest(t))
2023 .collect::<Result<_, _>>()?;
2024 let slack = |depth: u32, radius: f64| {
2025 f64::from(depth + 4) * 4.0 * f64::EPSILON * map.scale + 4.0 * f64::EPSILON * radius
2026 };
2027 let mut evaluated: HashMap<(u64, u64), Option<(f64, f64)>> = HashMap::new();
2028 let mut lower: Option<(f64, Point2)> = None;
2029 let mut heap = BinaryHeap::new();
2030 let mut cells = 0usize;
2031 let mut cell = |corners: [Point2; 3],
2032 root: usize,
2033 inherited: Option<(Point2, f64)>,
2034 depth: u32,
2035 order: usize,
2036 lower: &mut Option<(f64, Point2)>|
2037 -> Result<Option<Cell>, FarthestError> {
2038 let centroid = Point2::new(
2039 (corners[0].x + corners[1].x + corners[2].x) / 3.0,
2040 (corners[0].y + corners[1].y + corners[2].y) / 3.0,
2041 );
2042 let radius = |a: Point2| {
2043 corners
2044 .iter()
2045 .map(|c| (*c - a).length())
2046 .fold(0.0, f64::max)
2047 };
2048 let k = steep[root];
2049 let mut best: Option<(f64, (Point2, f64))> =
2050 inherited.map(|(a, d)| (d + k * radius(a), (a, d)));
2051 for a in [corners[0], corners[1], corners[2], centroid] {
2052 if !in_triangle(&roots[root], a)? {
2053 continue;
2054 }
2055 if map.walls.iter().try_fold(false, |m, &(p, q)| {
2056 Ok::<_, RouteError>(m || (within(p, q, a) && side(p, q, a)? == Sign::Zero))
2057 })? {
2058 continue;
2059 }
2060 let key = (a.x.to_bits(), a.y.to_bits());
2061 let bracket = match evaluated.get(&key) {
2062 Some(b) => *b,
2063 None => {
2064 let b = map.bracket(a)?;
2065 evaluated.insert(key, b);
2066 b
2067 }
2068 };
2069 let Some((lo, hi)) = bracket else {
2070 return Err(FarthestError::Unreachable {
2071 triangle: roots[root],
2072 });
2073 };
2074 if in_polygon(subregion, a)? && lower.is_none_or(|(l, _)| lo > l) {
2075 *lower = Some((lo, a));
2076 }
2077 let bound = hi + k * radius(a);
2078 if best.is_none_or(|(b, _)| bound < b) {
2079 best = Some((bound, (a, hi)));
2080 }
2081 }
2082 Ok(best.map(|(bound, anchor)| Cell {
2083 corners,
2084 root,
2085 anchor,
2086 upper: bound + k * slack(depth, radius(anchor.0)),
2087 order,
2088 depth,
2089 }))
2090 };
2091 for (root, corners) in roots.iter().enumerate() {
2092 if outside(subregion, corners)? {
2093 continue;
2094 }
2095 cells += 1;
2096 let Some(c) = cell(*corners, root, None, 0, cells, &mut lower)? else {
2097 return Err(FarthestError::Triangulation);
2098 };
2099 heap.push(c);
2100 }
2101 if cells == 0 {
2102 return Err(FarthestError::Empty);
2103 }
2104 let best = |lower: &Option<(f64, Point2)>| lower.map_or(f64::NEG_INFINITY, |(d, _)| d);
2105 while let Some(top) = heap.peek() {
2106 if top.upper <= best(&lower) + tolerance || cells >= max_cells {
2107 break;
2108 }
2109 let parent = heap.pop().expect("peeked");
2110 let [a, b, c] = parent.corners;
2111 let mid = |p: Point2, q: Point2| Point2::new(0.5 * p.x + 0.5 * q.x, 0.5 * p.y + 0.5 * q.y);
2112 let (ab, bc, ca) = (mid(a, b), mid(b, c), mid(c, a));
2113 for corners in [[a, ab, ca], [ab, b, bc], [ca, bc, c], [ab, bc, ca]] {
2114 if outside(subregion, &corners)? {
2115 continue;
2116 }
2117 cells += 1;
2118 if let Some(child) = cell(
2119 corners,
2120 parent.root,
2121 Some(parent.anchor),
2122 parent.depth + 1,
2123 cells,
2124 &mut lower,
2125 )? {
2126 if child.upper > best(&lower) {
2127 heap.push(child);
2128 }
2129 }
2130 }
2131 }
2132 let Some((lo, witness)) = lower else {
2133 if heap.is_empty() {
2134 return Err(FarthestError::Empty);
2135 }
2136 return Ok(Farthest {
2137 distance: LengthInterval {
2138 lower: 0.0,
2139 upper: heap.peek().map_or(0.0, |c| c.upper),
2140 },
2141 witness: None,
2142 converged: false,
2143 cells,
2144 });
2145 };
2146 let hi = heap.peek().map_or(lo, |c| c.upper.max(lo));
2147 Ok(Farthest {
2148 distance: LengthInterval {
2149 lower: lo,
2150 upper: hi,
2151 },
2152 witness: Some(witness),
2153 converged: hi - lo <= tolerance,
2154 cells,
2155 })
2156}
2157
2158#[cfg(test)]
2159mod tests {
2160 use super::*;
2161 use axiolid_overlay::Ring;
2162
2163 fn square() -> Weights {
2164 let p = |x: f64, y: f64| Point2::new(x, y);
2165 let polygon = Polygon {
2166 outer: Ring {
2167 points: vec![p(0.0, 0.0), p(1.0, 0.0), p(1.0, 1.0), p(0.0, 1.0)],
2168 },
2169 holes: Vec::new(),
2170 };
2171 Weights::new(&[CostRegion::new(polygon, 2.0)], &[], &[]).unwrap()
2172 }
2173
2174 #[test]
2178 fn a_stretch_within_rounding_of_a_cost_edge_is_bracketed_both_ways() {
2179 let weights = square();
2180 let tiny = 1e-17;
2181 let (lo, hi) = weights
2185 .segment(Point2::new(-1.0, tiny), Point2::new(2.0, tiny), 2.0)
2186 .unwrap();
2187 assert!(lo <= 3.0 && (hi - 3.0).abs() < 1e-12, "[{lo}, {hi}]");
2188 let (lo, hi) = weights
2190 .segment(Point2::new(-1.0, -tiny), Point2::new(2.0, -tiny), 2.0)
2191 .unwrap();
2192 assert!(lo <= 3.0 && hi >= 3.0, "[{lo}, {hi}]");
2193 let p = |x: f64, y: f64| Point2::new(x, y);
2197 let notched = Polygon {
2198 outer: Ring {
2199 points: vec![
2200 p(0.0, 0.0),
2201 p(4.0, 0.0),
2202 p(4.0, 2.0),
2203 p(2.5, 2.0),
2204 p(2.0, 1.0 + f64::EPSILON),
2205 p(1.5, 2.0),
2206 p(0.0, 2.0),
2207 ],
2208 },
2209 holes: Vec::new(),
2210 };
2211 let weights = Weights::new(&[CostRegion::new(notched, 2.0)], &[], &[]).unwrap();
2212 let (lo, hi) = weights.segment(p(0.5, 1.0), p(3.5, 1.0), 4.0).unwrap();
2213 assert!(lo <= 6.0 && hi >= 6.0, "[{lo}, {hi}]");
2214 let weights = square();
2215 let (lo, hi) = weights
2218 .segment(Point2::new(0.5, -tiny), Point2::new(0.5, tiny), 2.0)
2219 .unwrap();
2220 assert!(lo <= 2.0 * tiny && hi >= 2.0 * 2.0 * tiny, "[{lo}, {hi}]");
2221 let (lo, hi) = weights
2223 .segment(Point2::new(-1.0, 0.5), Point2::new(2.0, 0.5), 2.0)
2224 .unwrap();
2225 assert!(
2226 (lo - 4.0).abs() < 1e-12 && (hi - 4.0).abs() < 1e-12,
2227 "[{lo}, {hi}]"
2228 );
2229 }
2230
2231 fn p(x: f64, y: f64) -> Point2 {
2232 Point2::new(x, y)
2233 }
2234
2235 fn rect(x0: f64, y0: f64, x1: f64, y1: f64) -> Polygon {
2236 Polygon {
2237 outer: Ring {
2238 points: vec![p(x0, y0), p(x1, y0), p(x1, y1), p(x0, y1)],
2239 },
2240 holes: Vec::new(),
2241 }
2242 }
2243
2244 fn interval(map: &WeightedMap, end: Point2, toward: Point2) -> usize {
2247 (0..map.nodes.len())
2248 .find(|&i| match map.kinds[i] {
2249 Kind::Interval { a, b, .. } => {
2250 (a == end && (b - a).dot(toward - a) > 0.0)
2251 || (b == end && (a - b).dot(toward - b) > 0.0)
2252 }
2253 Kind::Vertex => false,
2254 })
2255 .expect("an interval")
2256 }
2257
2258 #[test]
2264 fn a_touch_blocks_a_hop_only_at_a_vertex_end() {
2265 let room = [rect(0.0, 0.0, 10.0, 4.0)];
2266 let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2267 let barrier = vec![vec![p(5.0, 1.0), p(5.0, 3.0)]];
2269 let map = weighted_distance_map(&room, &barrier, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2270 let above = interval(&map, p(4.0, 1.0), p(4.0, 2.0));
2271 let q = p(6.0, 1.0);
2272 let hop = map
2273 .hop(map.span(above), (q, q), map.kinds[above], Kind::Vertex)
2274 .unwrap();
2275 assert!(hop.is_some(), "the inner end's pieces graze the foot");
2276 let walled = vec![vec![p(5.0, 3.0), p(5.0, 1.0)]];
2281 let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2282 let map = weighted_distance_map(&room, &walled, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2283 let corner = interval(&map, p(4.0, 4.0), p(4.0, 3.0));
2284 let q = p(6.0, 2.0);
2285 let hop = map
2289 .hop(map.span(corner), (q, q), map.kinds[corner], Kind::Vertex)
2290 .unwrap();
2291 assert!(hop.is_none(), "{hop:?}");
2292 }
2293
2294 #[test]
2298 fn a_hull_is_held_only_whole() {
2299 let square = rect(0.0, 0.0, 4.0, 4.0);
2300 let hull = [p(1.0, 1.0), p(3.0, 1.0), p(3.0, 3.0)];
2301 assert!(holds(&square, &hull).unwrap());
2302 let notched = Polygon {
2305 outer: Ring {
2306 points: vec![
2307 p(0.0, 0.0),
2308 p(4.0, 0.0),
2309 p(4.0, 4.0),
2310 p(0.0, 4.0),
2311 p(0.0, 1.9),
2312 p(2.2, 1.6),
2313 p(0.0, 1.5),
2314 ],
2315 },
2316 holes: Vec::new(),
2317 };
2318 for c in hull {
2319 assert!(in_polygon(¬ched, c).unwrap());
2320 }
2321 assert!(in_polygon(¬ched, p(7.0 / 3.0, 5.0 / 3.0)).unwrap());
2322 assert!(!holds(¬ched, &hull).unwrap());
2323 }
2324
2325 #[test]
2328 fn a_segment_along_a_blocker_does_not_pass_through_its_end() {
2329 assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(3.0, 0.0)).unwrap());
2330 assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(1.0, 0.0)).unwrap());
2331 assert!(through_end(p(0.0, -1.0), p(0.0, 1.0), p(0.0, 0.0), p(1.0, 0.0)).unwrap());
2333 }
2334}