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 mut weights = Weights::new(costs, &nodes, region)?;
355 weights.reach = reach;
356 weights.check_crossings(&obstacles, &walls, reach)?;
357 let mut kinds = vec![Kind::Vertex; nodes.len()];
358 let mut spans: Vec<(Point2, Point2, u32)> = Vec::new();
359 for piece in &weights.pieces {
360 let (p, q) = if (piece.p.x, piece.p.y) <= (piece.q.x, piece.q.y) {
361 (piece.p, piece.q)
362 } else {
363 (piece.q, piece.p)
364 };
365 if !spans.iter().any(|(u, v, _)| *u == p && *v == q) {
367 spans.push((p, q, piece.line));
368 }
369 }
370 for (piece, (p, q, line)) in spans.into_iter().enumerate() {
371 if weights.beyond_wall(p, q, reach)? {
375 continue;
376 }
377 let count = ((q - p).length() / spacing).ceil().max(1.0) as usize;
378 let mut cuts: Vec<f64> = (0..=count).map(|k| k as f64 / count as f64).collect();
381 let first = 1.0 / count as f64;
382 for level in 1..=GRADING {
383 let t = first / f64::from(1u32 << level);
384 cuts.push(t);
385 cuts.push(1.0 - t);
386 }
387 cuts.sort_by(f64::total_cmp);
388 cuts.dedup();
389 let count = cuts.len() - 1;
390 if nodes.len() + count > budget {
391 return Err(RouteError::TooManyVertices {
392 supplied: nodes.len() + count,
393 budget,
394 lower_bound: 0.0,
395 }
396 .into());
397 }
398 let at = |k: usize| {
399 if k == 0 {
400 p
401 } else if k == count {
402 q
403 } else {
404 let t = cuts[k];
405 Point2::new(p.x + (q.x - p.x) * t, p.y + (q.y - p.y) * t)
406 }
407 };
408 for k in 0..count {
409 let (a, b) = (at(k), at(k + 1));
410 nodes.push(Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y));
411 let (left, right) = weights.sides(p, q, a, b)?;
412 let along = left.min(right);
413 kinds.push(Kind::Interval {
414 a,
415 b,
416 p,
417 q,
418 line,
419 piece: piece as u32,
420 left,
421 right,
422 along,
423 a_vertex: k == 0,
426 b_vertex: k + 1 == count,
427 });
428 }
429 }
430 if nodes.len() > budget {
431 return Err(RouteError::TooManyVertices {
432 supplied: nodes.len(),
433 budget,
434 lower_bound: 0.0,
435 }
436 .into());
437 }
438 let lines: Vec<Vec<u32>> = nodes
439 .iter()
440 .zip(&kinds)
441 .map(|(v, kind)| match kind {
442 Kind::Interval { line, .. } => Ok(vec![*line]),
443 Kind::Vertex => weights.lines_through(*v),
444 })
445 .collect::<Result<_, RouteError>>()?;
446 let graph = Graph::build(&nodes, region, barriers, &obstacles)?;
447 let scale = nodes
448 .iter()
449 .fold(1.0f64, |m, p| m.max(p.x.abs()).max(p.y.abs()));
450
451 let mut map = WeightedMap {
452 region: region.to_vec(),
453 walls,
454 obstacles,
455 weights,
456 nodes,
457 kinds,
458 lines,
459 graph,
460 upper: Vec::new(),
461 next: Vec::new(),
462 target: Vec::new(),
463 lower: Vec::new(),
464 sites: targets.clone(),
465 seeds: seeds.clone(),
466 costs: costs.to_vec(),
467 scale,
468 };
469 let mut sources = Vec::new();
472 let mut seed = vec![usize::MAX; map.graph.adjacency.len()];
473 for (i, node) in map.nodes.iter().enumerate() {
474 let lightest = (0..targets.len())
475 .filter(|&t| targets[t] == *node)
476 .min_by(|&a, &b| seeds[a].total_cmp(&seeds[b]).then(a.cmp(&b)));
477 if let Some(t) = lightest {
478 for state in map.graph.states(i) {
479 sources.push((state, seeds[t]));
480 seed[state] = t;
481 }
482 }
483 }
484 map.upper_bounds(&sources, &seed)?;
485 map.lower_bounds(&sources)?;
486 Ok(map)
487}
488
489impl WeightedMap {
490 #[must_use]
492 pub fn graph_vertices(&self) -> usize {
493 self.nodes.len()
494 }
495
496 #[must_use]
498 pub fn targets(&self) -> usize {
499 self.sites.len()
500 }
501
502 pub fn nearest(&self, point: Point2) -> Result<Result<WeightedReach, Unreachable>, RouteError> {
512 if !point.is_finite() {
513 return Err(RouteError::NonFinitePoint);
514 }
515 if !contains(&self.region, point)? {
516 return Ok(Err(Unreachable::StartOutside));
517 }
518 let Some((upper, first)) = self.upper_at(point)? else {
519 return Ok(Err(Unreachable::DisconnectedComponents));
520 };
521 let lower = self.lower_at(point, upper)?.min(upper);
522 let mut polyline = vec![point];
523 let mut state = first;
524 loop {
525 let at = self.nodes[self.graph.node(state)];
526 if polyline.last() != Some(&at) {
527 polyline.push(at);
528 }
529 if self.next[state] == usize::MAX {
530 break;
531 }
532 state = self.next[state];
533 }
534 let length = polyline.windows(2).map(|w| (w[1] - w[0]).length()).sum();
535 Ok(Ok(WeightedReach {
536 target: self.target[first],
537 cost: LengthInterval { lower, upper },
538 route: Route {
539 polyline,
540 length,
541 graph_vertices: self.nodes.len(),
542 },
543 }))
544 }
545
546 pub(crate) fn region(&self) -> &[Polygon] {
548 &self.region
549 }
550
551 pub(crate) fn obstacles(&self) -> &[(Point2, Point2)] {
553 &self.obstacles
554 }
555
556 pub(crate) fn walls(&self) -> &[(Point2, Point2)] {
558 &self.walls
559 }
560
561 pub(crate) fn seeded(&self) -> impl Iterator<Item = (Point2, f64)> + '_ {
563 self.sites.iter().copied().zip(self.seeds.iter().copied())
564 }
565
566 pub(crate) fn same_space(&self, other: &Self) -> bool {
568 self.region == other.region && self.walls == other.walls && self.costs == other.costs
569 }
570
571 pub(crate) fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
574 self.weights.steepest(t)
575 }
576
577 pub(crate) fn inside_factor(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
580 for piece in &self.weights.pieces {
581 if meets_triangle(piece.p, piece.q, t)? {
582 return Ok(1.0);
583 }
584 }
585 let mut best = 1.0f64;
586 for (polygon, &factor) in self.weights.polygons.iter().zip(&self.weights.factors) {
587 if factor > best && in_polygon(polygon, t[0])? {
588 best = factor;
589 }
590 }
591 Ok(best)
592 }
593
594 pub(crate) fn spans(&self) -> Vec<((Point2, Point2), f64)> {
597 (0..self.nodes.len())
598 .filter_map(|i| {
599 let least = self
600 .graph
601 .states(i)
602 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
603 .fold(f64::INFINITY, f64::min);
604 least.is_finite().then(|| (self.span(i), least))
605 })
606 .collect()
607 }
608
609 pub(crate) fn bracket(&self, point: Point2) -> Result<Option<(f64, f64)>, RouteError> {
612 let Some((upper, _)) = self.upper_at(point)? else {
613 return Ok(None);
614 };
615 Ok(Some((self.lower_at(point, upper)?.min(upper), upper)))
616 }
617
618 fn upper_bounds(&mut self, sources: &[(usize, f64)], seed: &[usize]) -> Result<(), RouteError> {
621 let states = self.graph.adjacency.len();
622 let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
623 let mut adjacency: Vec<Vec<(usize, f64)>> = vec![Vec::new(); states];
624 for (u, edges) in self.graph.adjacency.iter().enumerate() {
625 let i = self.graph.node(u);
626 for &(w, _) in edges {
627 let j = self.graph.node(w);
628 let key = (i.min(j), i.max(j));
629 let c = match cost.get(&key) {
630 Some(c) => *c,
631 None => {
632 let c = self
633 .weights
634 .segment(self.nodes[i], self.nodes[j], self.scale)?
635 .1;
636 cost.insert(key, c);
637 c
638 }
639 };
640 adjacency[u].push((w, c));
641 }
642 }
643 let (distance, previous) = dijkstra(&adjacency, sources);
644 let mut target = seed.to_vec();
647 for (state, t) in target.iter_mut().enumerate() {
648 let mut at = state;
649 let mut steps = 0;
650 while previous[at] != usize::MAX && steps <= seed.len() {
651 at = previous[at];
652 steps += 1;
653 }
654 *t = seed[at];
655 }
656 self.upper = distance;
657 self.next = previous;
658 self.target = target;
659 Ok(())
660 }
661
662 fn hop_states(&self, i: usize, b1: Point2, b2: Point2) -> Result<Vec<usize>, RouteError> {
666 match self.kinds[i] {
667 Kind::Vertex => self.graph.cone_states(i, self.nodes[i], b1, b2),
668 Kind::Interval { .. } => Ok(self.graph.states(i).collect()),
669 }
670 }
671
672 fn span(&self, i: usize) -> (Point2, Point2) {
674 match self.kinds[i] {
675 Kind::Vertex => (self.nodes[i], self.nodes[i]),
676 Kind::Interval { a, b, .. } => (a, b),
677 }
678 }
679
680 fn shared_line(&self, i: usize, j: usize) -> u32 {
682 self.lines[i]
683 .iter()
684 .find(|l| self.lines[j].contains(l))
685 .copied()
686 .unwrap_or(NO_LINE)
687 }
688
689 fn hop(
696 &self,
697 (a1, a2): (Point2, Point2),
698 (b1, b2): (Point2, Point2),
699 ka: Kind,
700 kb: Kind,
701 ) -> Result<Option<f64>, RouteError> {
702 let (ea, eb) = (vertex_ends(ka), vertex_ends(kb));
703 let blocks = |p: Point2, q: Point2| -> Result<bool, RouteError> {
704 for (u, skip_u) in [(a1, ea.0), (a2, ea.1)] {
705 for (v, skip_v) in [(b1, eb.0), (b2, eb.1)] {
706 if !crosses(u, v, p, q)? && !((skip_u || skip_v) && through_end(u, v, p, q)?) {
707 return Ok(false);
708 }
709 }
710 }
711 Ok(true)
712 };
713 for &(p, q) in &self.obstacles {
714 if blocks(p, q)? {
715 return Ok(None);
716 }
717 }
718 if convex_hull(&[a1, a2, b1, b2])?.is_none() {
719 let Some((u, v)) = gap((a1, a2), (b1, b2)) else {
723 return Ok(Some(0.0));
724 };
725 return Ok(Some(self.weights.segment(u, v, self.scale)?.0));
726 }
727 let own = |kind: Kind| match kind {
730 Kind::Interval { line, .. } => line,
731 Kind::Vertex => NO_LINE,
732 };
733 let (la, lb) = (own(ka), own(kb));
734 for piece in &self.weights.pieces {
735 if piece.line != la && piece.line != lb && blocks(piece.p, piece.q)? {
736 return Ok(None);
737 }
738 }
739 let factor = self
743 .weights
744 .hull_factor(&[a1, a2, b1, b2])?
745 .max(leaving(ka, (b1, b2), eb)?)
746 .max(leaving(kb, (a1, a2), ea)?);
747 let d = span_distance((a1, a2), (b1, b2))?;
748 Ok(Some(round_down(factor * d, self.scale)))
749 }
750
751 fn side_of(&self, k: usize, n: usize) -> Result<u8, RouteError> {
756 let (u, v) = self.span(n);
757 let ends = match self.kinds[n] {
758 Kind::Interval {
759 a_vertex, b_vertex, ..
760 } => (a_vertex, b_vertex),
761 Kind::Vertex => (false, false),
762 };
763 self.side_of_span(k, (u, v), ends)
764 }
765
766 fn side_of_span(
767 &self,
768 k: usize,
769 (u, v): (Point2, Point2),
770 (skip_u, skip_v): (bool, bool),
771 ) -> Result<u8, RouteError> {
772 let Kind::Interval { p, q, .. } = self.kinds[k] else {
773 return Ok(NONE);
774 };
775 let (su, sv) = (side(p, q, u)?, side(p, q, v)?);
776 let su = if su == Sign::Zero && skip_u { sv } else { su };
777 let sv = if sv == Sign::Zero && skip_v { su } else { sv };
778 Ok(match (su, sv) {
779 (Sign::Positive, Sign::Positive) => LEFT,
780 (Sign::Negative, Sign::Negative) => RIGHT,
781 _ => NONE,
782 })
783 }
784
785 fn edge(&self, n: usize, k: usize, from: usize, weight: f64) -> Result<Edge, RouteError> {
788 let line = self.shared_line(n, k);
789 let (leave, arrive) = if line == NO_LINE {
790 (self.side_of(n, k)?, self.side_of(k, n)?)
791 } else {
792 (NONE, NONE)
793 };
794 let same_piece = match (self.kinds[n], self.kinds[k]) {
795 (Kind::Interval { piece: pn, .. }, Kind::Interval { piece: pk, .. }) => pn == pk,
796 _ => false,
797 };
798 Ok(Edge {
799 from,
800 weight,
801 line,
802 leave,
803 arrive,
804 same_piece,
805 })
806 }
807
808 fn lower_bounds(&mut self, sources: &[(usize, f64)]) -> Result<(), RouteError> {
813 let states = self.graph.adjacency.len();
814 let mut incoming: Vec<Vec<Edge>> = vec![Vec::new(); states];
815 let is_vertex = |i: usize, kinds: &[Kind]| matches!(kinds[i], Kind::Vertex);
816 let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
817 for (u, edges) in self.graph.adjacency.iter().enumerate() {
818 let i = self.graph.node(u);
819 if !is_vertex(i, &self.kinds) {
820 continue;
821 }
822 for &(w, _) in edges {
823 let j = self.graph.node(w);
824 if !is_vertex(j, &self.kinds) {
825 continue;
826 }
827 let key = (i.min(j), i.max(j));
828 let c = match cost.get(&key) {
829 Some(c) => *c,
830 None => {
831 let c = self
832 .weights
833 .segment(self.nodes[i], self.nodes[j], self.scale)?
834 .0;
835 cost.insert(key, c);
836 c
837 }
838 };
839 incoming[w].push(self.edge(i, j, u, c)?);
841 }
842 }
843 let n = self.nodes.len();
844 for i in 0..n {
845 for j in i + 1..n {
846 if is_vertex(i, &self.kinds) && is_vertex(j, &self.kinds) {
847 continue;
848 }
849 let (si, sj) = (self.span(i), self.span(j));
850 let Some(c) = self.hop(si, sj, self.kinds[i], self.kinds[j])? else {
851 continue;
852 };
853 let from = self.hop_states(i, sj.0, sj.1)?;
854 let to = self.hop_states(j, si.0, si.1)?;
855 for &u in &from {
856 for &w in &to {
857 incoming[w].push(self.edge(i, j, u, c)?);
858 incoming[u].push(self.edge(j, i, w, c)?);
859 }
860 }
861 }
862 }
863 let graph = &self.graph;
864 let kinds = &self.kinds;
865 self.lower = tagged_dijkstra(&incoming, sources, |s| kinds[graph.node(s)]);
866 Ok(())
867 }
868
869 fn upper_at(&self, point: Point2) -> Result<Option<(f64, usize)>, RouteError> {
871 let mut best: Option<(f64, usize)> = None;
872 let offer = |value: f64, state: usize, best: &mut Option<(f64, usize)>| {
873 if best.is_none_or(|(b, s)| value < b || (value == b && state < s)) {
874 *best = Some((value, state));
875 }
876 };
877 for (i, node) in self.nodes.iter().enumerate() {
878 let nearest = self
879 .graph
880 .states(i)
881 .map(|s| self.upper[s])
882 .fold(f64::INFINITY, f64::min);
883 if nearest.is_infinite() {
884 continue;
885 }
886 if *node == point {
887 for s in self.graph.states(i) {
888 offer(self.upper[s], s, &mut best);
889 }
890 continue;
891 }
892 if best.is_some_and(|(b, _)| (*node - point).length() + nearest > b) {
894 continue;
895 }
896 let ok = crate::graph::sides(
897 point,
898 *node,
899 &self.region,
900 &self.obstacles,
901 &self.nodes,
902 &self.graph.stars,
903 &self.graph.rayed,
904 )?;
905 if ok == [false, false] {
906 continue;
907 }
908 let leg = self.weights.segment(point, *node, self.scale)?.1;
909 for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
910 if !ok[k] {
911 continue;
912 }
913 if let Some(s) = self.graph.arrival(i, point, on)? {
914 if self.upper[s].is_finite() {
915 offer(leg + self.upper[s], s, &mut best);
916 }
917 }
918 }
919 }
920 Ok(best)
921 }
922
923 fn lower_at(&self, point: Point2, ceiling: f64) -> Result<f64, RouteError> {
927 let mut best = ceiling;
928 let point_lines = self.weights.lines_through(point)?;
929 for (i, node) in self.nodes.iter().enumerate() {
930 let least = self
931 .graph
932 .states(i)
933 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
934 .fold(f64::INFINITY, f64::min);
935 let kind = self.kinds[i];
936 if least.is_infinite() {
937 continue;
938 }
939 let line = self.lines[i]
940 .iter()
941 .find(|l| point_lines.contains(l))
942 .copied()
943 .unwrap_or(NO_LINE);
944 let first = Edge {
947 from: usize::MAX,
948 weight: 0.0,
949 line,
950 leave: NONE,
951 arrive: if line == NO_LINE {
952 self.side_of_span(i, (point, point), (false, false))?
953 } else {
954 NONE
955 },
956 same_piece: false,
957 };
958 let from = |states: &mut dyn Iterator<Item = usize>| -> f64 {
959 states
960 .flat_map(|s| self.lower[s].iter())
961 .filter(|(tag, _)| allowed(kind, *tag, &first))
962 .map(|(_, d)| *d)
963 .fold(f64::INFINITY, f64::min)
964 };
965 if *node == point {
966 let any = self
968 .graph
969 .states(i)
970 .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
971 .fold(f64::INFINITY, f64::min);
972 best = best.min(any);
973 continue;
974 }
975 let (a, b) = self.span(i);
976 if span_distance((point, point), (a, b))? + least >= best {
977 continue;
978 }
979 match self.kinds[i] {
980 Kind::Vertex => {
981 let ok = crate::graph::sides(
982 point,
983 *node,
984 &self.region,
985 &self.obstacles,
986 &self.nodes,
987 &self.graph.stars,
988 &self.graph.rayed,
989 )?;
990 if ok == [false, false] {
991 continue;
992 }
993 let leg = self.weights.segment(point, *node, self.scale)?.0;
994 for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
995 if !ok[k] {
996 continue;
997 }
998 if let Some(s) = self.graph.arrival(i, point, on)? {
999 best = best.min(leg + from(&mut core::iter::once(s)));
1000 }
1001 }
1002 }
1003 Kind::Interval { .. } => {
1004 let Some(leg) =
1005 self.hop((point, point), (a, b), Kind::Vertex, self.kinds[i])?
1006 else {
1007 continue;
1008 };
1009 best = best.min(leg + from(&mut self.graph.states(i)));
1010 }
1011 }
1012 }
1013 Ok(best)
1014 }
1015}
1016
1017fn dijkstra(adjacency: &[Vec<(usize, f64)>], sources: &[(usize, f64)]) -> (Vec<f64>, Vec<usize>) {
1021 let n = adjacency.len();
1022 let mut distance = vec![f64::INFINITY; n];
1023 let mut previous = vec![usize::MAX; n];
1024 let mut heap = BinaryHeap::new();
1025 for &(s, start) in sources {
1026 if start < distance[s] {
1027 distance[s] = start;
1028 heap.push(Reverse((Ordered(start), s)));
1029 }
1030 }
1031 while let Some(Reverse((Ordered(d), u))) = heap.pop() {
1032 if d > distance[u] {
1033 continue;
1034 }
1035 for &(w, c) in &adjacency[u] {
1036 let candidate = d + c;
1037 if candidate < distance[w] || (candidate == distance[w] && u < previous[w]) {
1038 if candidate < distance[w] {
1039 heap.push(Reverse((Ordered(candidate), w)));
1040 }
1041 distance[w] = candidate;
1042 previous[w] = u;
1043 }
1044 }
1045 }
1046 (distance, previous)
1047}
1048
1049const NONE: u8 = 0;
1051const LEFT: u8 = 1;
1052const RIGHT: u8 = 2;
1053const ALONG: u8 = 3;
1056
1057#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1062struct Tag {
1063 line: u32,
1064 side: u8,
1065}
1066
1067#[derive(Debug, Clone, Copy)]
1069struct Edge {
1070 from: usize,
1071 weight: f64,
1072 line: u32,
1074 leave: u8,
1076 arrive: u8,
1078 same_piece: bool,
1080}
1081
1082fn allowed(kind: Kind, tag: Tag, edge: &Edge) -> bool {
1089 if edge.line != NO_LINE && tag.line == edge.line {
1090 return false;
1091 }
1092 if let Kind::Interval {
1093 left, right, along, ..
1094 } = kind
1095 {
1096 if edge.line == NO_LINE && edge.arrive != NONE {
1097 let factor = if edge.arrive == LEFT { left } else { right };
1098 if tag.side == edge.arrive {
1099 return false;
1100 }
1101 if tag.side > ALONG && tag.side - ALONG == edge.arrive && along >= factor {
1102 return false;
1103 }
1104 }
1105 }
1106 true
1107}
1108
1109fn tag_before(source: Kind, tag: Tag, edge: &Edge) -> Tag {
1112 let side = match source {
1113 Kind::Vertex => NONE,
1114 Kind::Interval { .. } if edge.line != NO_LINE => {
1115 if edge.same_piece && (tag.side == LEFT || tag.side == RIGHT) {
1116 ALONG + tag.side
1117 } else {
1118 ALONG
1119 }
1120 }
1121 Kind::Interval { .. } => edge.leave,
1122 };
1123 Tag {
1124 line: edge.line,
1125 side,
1126 }
1127}
1128
1129fn tagged_dijkstra(
1133 incoming: &[Vec<Edge>],
1134 sources: &[(usize, f64)],
1135 kind: impl Fn(usize) -> Kind,
1136) -> Vec<Vec<(Tag, f64)>> {
1137 let mut settled: Vec<Vec<(Tag, f64)>> = vec![Vec::new(); incoming.len()];
1138 let mut best: HashMap<(usize, Tag), f64> = HashMap::new();
1139 let mut heap = BinaryHeap::new();
1140 let start = Tag {
1141 line: NO_LINE,
1142 side: NONE,
1143 };
1144 for &(s, weight) in sources {
1145 if best.get(&(s, start)).is_none_or(|b| weight < *b) {
1146 best.insert((s, start), weight);
1147 heap.push(Reverse((Ordered(weight), s, start)));
1148 }
1149 }
1150 while let Some(Reverse((Ordered(d), k, tag))) = heap.pop() {
1151 if settled[k].iter().any(|(t, _)| *t == tag) {
1152 continue;
1153 }
1154 settled[k].push((tag, d));
1155 let here = kind(k);
1156 for edge in &incoming[k] {
1157 if !allowed(here, tag, edge) {
1158 continue;
1159 }
1160 let before = tag_before(kind(edge.from), tag, edge);
1161 let candidate = d + edge.weight;
1162 let key = (edge.from, before);
1163 if best.get(&key).is_none_or(|b| candidate < *b) {
1164 best.insert(key, candidate);
1165 heap.push(Reverse((Ordered(candidate), edge.from, before)));
1166 }
1167 }
1168 }
1169 settled
1170}
1171
1172#[derive(Debug, Clone, Copy, PartialEq)]
1174struct Ordered(f64);
1175
1176impl Eq for Ordered {}
1177
1178impl PartialOrd for Ordered {
1179 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1180 Some(self.cmp(other))
1181 }
1182}
1183
1184impl Ord for Ordered {
1185 fn cmp(&self, other: &Self) -> Ordering {
1186 self.0.total_cmp(&other.0)
1187 }
1188}
1189
1190fn round_down(value: f64, scale: f64) -> f64 {
1193 (value * (1.0 - 16.0 * f64::EPSILON) - 16.0 * f64::EPSILON * scale).max(0.0)
1194}
1195
1196fn round_up(value: f64, scale: f64) -> f64 {
1197 value * (1.0 + 16.0 * f64::EPSILON) + 16.0 * f64::EPSILON * scale
1198}
1199
1200fn leaving(
1205 kind: Kind,
1206 (o1, o2): (Point2, Point2),
1207 (skip1, skip2): (bool, bool),
1208) -> Result<f64, RouteError> {
1209 let Kind::Interval {
1210 p, q, left, right, ..
1211 } = kind
1212 else {
1213 return Ok(1.0);
1214 };
1215 let (s1, s2) = (side(p, q, o1)?, side(p, q, o2)?);
1216 let s1 = if s1 == Sign::Zero && skip1 { s2 } else { s1 };
1218 let s2 = if s2 == Sign::Zero && skip2 { s1 } else { s2 };
1219 Ok(match (s1, s2) {
1220 (Sign::Positive, Sign::Positive) => left,
1221 (Sign::Negative, Sign::Negative) => right,
1222 _ => left.min(right),
1223 })
1224}
1225
1226fn vertex_ends(kind: Kind) -> (bool, bool) {
1229 match kind {
1230 Kind::Interval {
1231 a_vertex, b_vertex, ..
1232 } => (a_vertex, b_vertex),
1233 Kind::Vertex => (false, false),
1234 }
1235}
1236
1237fn through_end(u: Point2, v: Point2, p: Point2, q: Point2) -> Result<bool, RouteError> {
1242 if side(p, q, u)? == Sign::Zero || side(p, q, v)? == Sign::Zero {
1243 return Ok(false);
1244 }
1245 for e in [p, q] {
1246 if within(u, v, e) && side(u, v, e)? == Sign::Zero {
1247 return Ok(true);
1248 }
1249 }
1250 Ok(false)
1251}
1252
1253fn gap((a1, a2): (Point2, Point2), (b1, b2): (Point2, Point2)) -> Option<(Point2, Point2)> {
1256 let d = if a1 != a2 { a2 - a1 } else { b2 - b1 };
1257 let key = |p: Point2| (p - a1).dot(d);
1258 let (alo, ahi) = if key(a1) <= key(a2) {
1259 (a1, a2)
1260 } else {
1261 (a2, a1)
1262 };
1263 let (blo, bhi) = if key(b1) <= key(b2) {
1264 (b1, b2)
1265 } else {
1266 (b2, b1)
1267 };
1268 if key(ahi) < key(blo) {
1269 Some((ahi, blo))
1270 } else if key(bhi) < key(alo) {
1271 Some((bhi, alo))
1272 } else {
1273 None
1274 }
1275}
1276
1277pub(crate) fn span_distance(
1280 (a1, a2): (Point2, Point2),
1281 (b1, b2): (Point2, Point2),
1282) -> Result<f64, RouteError> {
1283 if segments_meet(a1, a2, b1, b2)? {
1284 return Ok(0.0);
1285 }
1286 Ok(point_segment(a1, b1, b2)
1287 .min(point_segment(a2, b1, b2))
1288 .min(point_segment(b1, a1, a2))
1289 .min(point_segment(b2, a1, a2)))
1290}
1291
1292fn point_segment(v: Point2, a: Point2, b: Point2) -> f64 {
1293 let d = b - a;
1294 let length2 = d.dot(d);
1295 let s = if length2 > 0.0 {
1296 ((v - a).dot(d) / length2).clamp(0.0, 1.0)
1297 } else {
1298 0.0
1299 };
1300 (v - (a + d * s)).length()
1301}
1302
1303#[derive(Debug, Clone, Copy)]
1306struct Piece {
1307 p: Point2,
1308 q: Point2,
1309 polygon: usize,
1310 inside_left: bool,
1312 line: u32,
1314}
1315
1316#[derive(Debug, Clone)]
1318struct Weights {
1319 polygons: Vec<Polygon>,
1320 factors: Vec<f64>,
1321 pieces: Vec<Piece>,
1322 lines: Vec<(Point2, Point2)>,
1324 walls: Vec<(Point2, Point2, bool)>,
1327 greatest: f64,
1328 reach: f64,
1331}
1332
1333impl Weights {
1334 fn new(costs: &[CostRegion], cuts: &[Point2], region: &[Polygon]) -> Result<Self, RouteError> {
1335 let mut walls = Vec::new();
1336 for polygon in region {
1337 for (hole, ring) in core::iter::once((false, &polygon.outer))
1338 .chain(polygon.holes.iter().map(|h| (true, h)))
1339 {
1340 let left = region_left(ring, hole)?;
1341 for (p, q) in ring_edges(ring) {
1342 walls.push((p, q, left));
1343 }
1344 }
1345 }
1346 let polygons: Vec<Polygon> = costs.iter().map(|c| c.polygon.clone()).collect();
1347 let factors: Vec<f64> = costs.iter().map(|c| c.factor).collect();
1348 let mut vertices: Vec<Point2> = cuts.to_vec();
1349 for polygon in &polygons {
1350 for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1351 vertices.extend(ring.points.iter().copied());
1352 }
1353 }
1354 dedup_points(&mut vertices);
1355 let mut pieces = Vec::new();
1356 let mut lines: Vec<(Point2, Point2)> = Vec::new();
1357 for (index, polygon) in polygons.iter().enumerate() {
1358 for (hole, ring) in core::iter::once((false, &polygon.outer))
1359 .chain(polygon.holes.iter().map(|h| (true, h)))
1360 {
1361 let left = region_left(ring, hole)?;
1362 for (p, q) in ring_edges(ring) {
1363 if p == q {
1364 continue;
1365 }
1366 let d = q - p;
1367 let mut cuts = vec![p, q];
1368 for &v in &vertices {
1369 if v != p && v != q && within(p, q, v) && side(p, q, v)? == Sign::Zero {
1370 cuts.push(v);
1371 }
1372 }
1373 cuts.sort_by(|u, v| (*u - p).dot(d).total_cmp(&(*v - p).dot(d)));
1374 let mut line = NO_LINE;
1375 for (k, &(r, s)) in lines.iter().enumerate() {
1376 if side(r, s, p)? == Sign::Zero && side(r, s, q)? == Sign::Zero {
1377 line = k as u32;
1378 break;
1379 }
1380 }
1381 if line == NO_LINE {
1382 line = lines.len() as u32;
1383 lines.push((p, q));
1384 }
1385 for pair in cuts.windows(2) {
1386 if pair[0] != pair[1] {
1387 pieces.push(Piece {
1388 p: pair[0],
1389 q: pair[1],
1390 polygon: index,
1391 inside_left: left,
1392 line,
1393 });
1394 }
1395 }
1396 }
1397 }
1398 }
1399 let greatest = factors.iter().copied().fold(1.0, f64::max);
1400 Ok(Self {
1401 polygons,
1402 factors,
1403 pieces,
1404 lines,
1405 walls,
1406 greatest,
1407 reach: 0.0,
1408 })
1409 }
1410
1411 fn check_crossings(
1416 &self,
1417 obstacles: &[(Point2, Point2)],
1418 walls: &[(Point2, Point2)],
1419 reach: f64,
1420 ) -> Result<(), MapError> {
1421 for piece in &self.pieces {
1422 let index = piece.polygon;
1423 for &(r, s) in obstacles {
1424 if crosses(piece.p, piece.q, r, s)? {
1425 let region_edge = self
1426 .walls
1427 .iter()
1428 .any(|&(p, q, _)| (p, q) == (r, s) || (p, q) == (s, r));
1429 let near = point_segment(piece.p, r, s) <= reach
1430 || point_segment(piece.q, r, s) <= reach
1431 || point_segment(r, piece.p, piece.q) <= reach
1432 || point_segment(s, piece.p, piece.q) <= reach;
1433 if region_edge && near {
1434 continue;
1435 }
1436 return Err(MapError::CostCrossing { index });
1437 }
1438 }
1439 for &(r, s) in walls {
1440 let collinear =
1441 side(r, s, piece.p)? == Sign::Zero && side(r, s, piece.q)? == Sign::Zero;
1442 if collinear && segments_meet(piece.p, piece.q, r, s)? {
1443 let overlap = [piece.p, piece.q, r, s]
1444 .iter()
1445 .filter(|v| within(r, s, **v) && within(piece.p, piece.q, **v))
1446 .count();
1447 if overlap >= 2 {
1448 return Err(MapError::CostCrossing { index });
1449 }
1450 }
1451 }
1452 for other in &self.pieces {
1453 if crosses(piece.p, piece.q, other.p, other.q)? {
1454 return Err(MapError::CostCrossing { index });
1455 }
1456 }
1457 }
1458 Ok(())
1459 }
1460
1461 fn sides(&self, p: Point2, q: Point2, a: Point2, b: Point2) -> Result<(f64, f64), RouteError> {
1467 let m = Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y);
1468 let d = q - p;
1469 let (a, b) = (p, q);
1470 let (mut left, mut right) = (1.0f64, 1.0f64);
1471 for (index, polygon) in self.polygons.iter().enumerate() {
1472 let factor = self.factors[index];
1473 let mut on = false;
1474 for piece in self.pieces.iter().filter(|p| p.polygon == index) {
1475 if side(piece.p, piece.q, a)? == Sign::Zero
1476 && side(piece.p, piece.q, b)? == Sign::Zero
1477 && within(piece.p, piece.q, m)
1478 {
1479 on = true;
1480 let same = (piece.q - piece.p).dot(d) > 0.0;
1481 if piece.inside_left == same {
1482 left = left.max(factor);
1483 } else {
1484 right = right.max(factor);
1485 }
1486 }
1487 }
1488 if !on && in_polygon(polygon, m)? {
1489 left = left.max(factor);
1490 right = right.max(factor);
1491 }
1492 }
1493 Ok((left, right))
1494 }
1495
1496 fn beyond_wall(&self, p: Point2, q: Point2, reach: f64) -> Result<bool, RouteError> {
1500 for &(r, s, left) in &self.walls {
1501 let free = if left { Sign::Positive } else { Sign::Negative };
1502 if point_segment(p, r, s) <= reach
1503 && point_segment(q, r, s) <= reach
1504 && side(r, s, p)? != free
1505 && side(r, s, q)? != free
1506 {
1507 return Ok(true);
1508 }
1509 }
1510 Ok(false)
1511 }
1512
1513 fn lines_through(&self, v: Point2) -> Result<Vec<u32>, RouteError> {
1515 let mut out = Vec::new();
1516 for (k, &(p, q)) in self.lines.iter().enumerate() {
1517 if side(p, q, v)? == Sign::Zero {
1518 out.push(k as u32);
1519 }
1520 }
1521 Ok(out)
1522 }
1523
1524 fn hull_factor(&self, corners: &[Point2]) -> Result<f64, RouteError> {
1527 let Some(hull) = convex_hull(corners)? else {
1528 return Ok(1.0);
1529 };
1530 let mut best = 1.0f64;
1531 for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1532 if factor <= best {
1533 continue;
1534 }
1535 if holds(polygon, &hull)? {
1536 best = factor;
1537 }
1538 }
1539 Ok(best)
1540 }
1541
1542 fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
1545 let mut best = 1.0f64;
1546 for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1547 if factor <= best {
1548 continue;
1549 }
1550 let meets = t.iter().try_fold(false, |m, c| {
1551 Ok::<_, RouteError>(m || in_polygon(polygon, *c)?)
1552 })? || core::iter::once(&polygon.outer)
1553 .chain(polygon.holes.iter())
1554 .flat_map(ring_edges)
1555 .try_fold(false, |m, (p, q)| {
1556 Ok::<_, RouteError>(m || meets_triangle(p, q, t)?)
1557 })?;
1558 if meets {
1559 best = factor;
1560 }
1561 }
1562 Ok(best)
1563 }
1564
1565 fn segment(&self, a: Point2, b: Point2, scale: f64) -> Result<(f64, f64), RouteError> {
1577 let length = (b - a).length();
1578 if length == 0.0 {
1579 return Ok((0.0, 0.0));
1580 }
1581 if self.polygons.is_empty() {
1582 return Ok((round_down(length, scale), round_up(length, scale)));
1583 }
1584 let d = b - a;
1585 let param = |v: Point2| (v - a).dot(d) / d.dot(d);
1586 let mut breaks = vec![0.0, 1.0];
1587 let mut along: Vec<(f64, f64, usize, bool)> = Vec::new();
1589 let mut near: Vec<(Point2, Point2, usize, bool)> = Vec::new();
1591 let mut walled: Vec<(f64, f64, bool)> = Vec::new();
1593 let mut near_walls: Vec<(Point2, Point2, bool)> = Vec::new();
1595 for &(p, q, left) in &self.walls {
1596 if p != q && side(a, b, p)? == Sign::Zero && side(a, b, q)? == Sign::Zero {
1597 let (tp, tq) = (param(p), param(q));
1598 let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1599 if hi > lo {
1600 walled.push((lo, hi, left == ((q - p).dot(d) > 0.0)));
1601 breaks.extend([lo, hi]);
1602 }
1603 } else if p != q {
1604 near_walls.push((p, q, left));
1605 }
1606 }
1607 for piece in &self.pieces {
1608 let (sp, sq) = (side(a, b, piece.p)?, side(a, b, piece.q)?);
1609 if sp == Sign::Zero && sq == Sign::Zero {
1610 let (tp, tq) = (param(piece.p), param(piece.q));
1611 let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1612 if hi > lo {
1613 let same = (piece.q - piece.p).dot(d) > 0.0;
1614 along.push((lo, hi, piece.polygon, piece.inside_left == same));
1615 breaks.extend([lo, hi]);
1616 }
1617 continue;
1618 }
1619 near.push((piece.p, piece.q, piece.polygon, piece.inside_left));
1620 let (sa, sb) = (side(piece.p, piece.q, a)?, side(piece.p, piece.q, b)?);
1621 if sp != sq && sa != sb {
1622 let e = piece.q - piece.p;
1625 let denom = d.perp_dot(e);
1626 if denom != 0.0 {
1627 let t = (piece.p - a).perp_dot(e) / denom;
1628 if t > 0.0 && t < 1.0 {
1629 breaks.push(t);
1630 }
1631 }
1632 }
1633 }
1634 breaks.sort_by(f64::total_cmp);
1635 breaks.dedup();
1636 let (mut lower, mut upper) = (0.0, 0.0);
1637 let tiny = 64.0 * f64::EPSILON * scale;
1638 let mut hugged = false;
1639 for pair in breaks.windows(2) {
1640 let (t0, t1) = (pair[0], pair[1]);
1641 let stretch = (t1 - t0) * length;
1642 if stretch <= 0.0 {
1643 continue;
1644 }
1645 let tm = 0.5 * t0 + 0.5 * t1;
1646 let at = |t: f64| Point2::new(a.x + d.x * t, a.y + d.y * t);
1647 let m = at(tm);
1648 let mut on: Vec<(usize, bool)> = along
1651 .iter()
1652 .filter(|(lo, hi, ..)| *lo <= tm && tm <= *hi)
1653 .map(|(_, _, p, l)| (*p, *l))
1654 .collect();
1655 let mut free: Vec<bool> = walled
1657 .iter()
1658 .filter(|(lo, hi, _)| *lo <= tm && tm <= *hi)
1659 .map(|(_, _, l)| *l)
1660 .collect();
1661 let doubtful = stretch <= tiny
1662 || near
1663 .iter()
1664 .any(|&(p, q, ..)| point_segment(m, p, q) <= tiny);
1665 let mut resolved = !doubtful;
1666 if doubtful {
1667 let (s0, s1) = (at(t0), at(t1));
1674 let hugs = |p: Point2, q: Point2| {
1675 point_segment(s0, p, q) <= tiny && point_segment(s1, p, q) <= tiny
1676 };
1677 let mut all_hug = stretch > tiny;
1678 let beyond = match (free.contains(&true), free.contains(&false)) {
1683 (true, false) => Some(Sign::Negative),
1684 (false, true) => Some(Sign::Positive),
1685 _ => None,
1686 };
1687 let mut known = beyond.is_some();
1688 for &(p, q, polygon, inside_left) in &near {
1689 if point_segment(m, p, q) > tiny {
1690 continue;
1691 }
1692 if !hugs(p, q) {
1693 all_hug = false;
1694 break;
1695 }
1696 if let Some(solid) = beyond {
1697 for end in [p, q] {
1698 let s = side(a, b, end)?;
1699 known &= s == Sign::Zero || s == solid;
1700 }
1701 }
1702 on.push((polygon, inside_left == ((q - p).dot(d) > 0.0)));
1703 }
1704 if !all_hug {
1705 lower += stretch;
1706 upper += self.greatest * stretch;
1707 continue;
1708 }
1709 resolved = known;
1710 if !resolved {
1711 lower += stretch;
1712 }
1713 for &(p, q, left) in &near_walls {
1714 if hugs(p, q) {
1715 free.push(left == ((q - p).dot(d) > 0.0));
1716 }
1717 }
1718 hugged = true;
1719 }
1720 if !on.is_empty() {
1725 let (s0, s1) = (at(t0), at(t1));
1726 for &(p, q, left) in &near_walls {
1727 let solid = if left { Sign::Negative } else { Sign::Positive };
1728 let off = |s: Point2| -> Result<bool, RouteError> {
1729 let at = side(p, q, s)?;
1730 Ok(point_segment(s, p, q) <= self.reach
1731 && (at == Sign::Zero || at == solid))
1732 };
1733 if off(s0)? && off(s1)? {
1734 free.push(left == ((q - p).dot(d) > 0.0));
1735 }
1736 }
1737 }
1738 let (mut left, mut right) = (0.0f64, 0.0f64);
1740 for (index, polygon) in self.polygons.iter().enumerate() {
1741 let extra = self.factors[index] - 1.0;
1742 if extra <= left.min(right) {
1743 continue;
1744 }
1745 let sides: Vec<bool> = on
1746 .iter()
1747 .filter(|(p, _)| *p == index)
1748 .map(|(_, l)| *l)
1749 .collect();
1750 if sides.is_empty() {
1751 if in_polygon(polygon, m)? {
1752 left = left.max(extra);
1753 right = right.max(extra);
1754 }
1755 } else {
1756 if sides.contains(&true) {
1759 left = left.max(extra);
1760 }
1761 if sides.contains(&false) {
1762 right = right.max(extra);
1763 }
1764 }
1765 }
1766 let (free_left, free_right) = if free.is_empty() {
1769 (true, true)
1770 } else {
1771 (free.contains(&true), free.contains(&false))
1772 };
1773 let factor = 1.0
1774 + if on.is_empty() {
1775 left
1776 } else {
1777 match (free_left, free_right) {
1778 (true, true) => left.min(right),
1779 (true, false) => left,
1780 (false, true) => right,
1781 (false, false) => left.max(right),
1782 }
1783 };
1784 if resolved {
1785 lower += factor * stretch;
1786 }
1787 upper += factor * stretch;
1788 }
1789 if hugged {
1790 upper += 4.0 * self.greatest * tiny * breaks.len() as f64;
1792 }
1793 let slack = (self.greatest - 1.0) * breaks.len() as f64 * 8.0 * f64::EPSILON * length;
1796 Ok((
1797 round_down(lower - slack, scale),
1798 round_up(upper + slack, scale),
1799 ))
1800 }
1801}
1802
1803fn touch_reach(region: &[Polygon]) -> f64 {
1807 let (mut lo, mut hi) = (
1808 Point2::new(f64::INFINITY, f64::INFINITY),
1809 Point2::new(f64::NEG_INFINITY, f64::NEG_INFINITY),
1810 );
1811 let mut scale = 0.0f64;
1812 for polygon in region {
1813 for p in core::iter::once(&polygon.outer)
1814 .chain(polygon.holes.iter())
1815 .flat_map(|r| r.points.iter())
1816 {
1817 lo = Point2::new(lo.x.min(p.x), lo.y.min(p.y));
1818 hi = Point2::new(hi.x.max(p.x), hi.y.max(p.y));
1819 scale = scale.max(p.x.abs()).max(p.y.abs());
1820 }
1821 }
1822 let extent = (hi.x - lo.x).max(hi.y - lo.y).max(0.0);
1823 extent / f64::from(1u32 << 24) + 64.0 * f64::EPSILON * scale
1824}
1825
1826fn touch_walls(
1835 costs: &[CostRegion],
1836 region: &[Polygon],
1837 reach: f64,
1838) -> Result<Vec<CostRegion>, RouteError> {
1839 let mut edges = Vec::new();
1840 for polygon in region {
1841 for (hole, ring) in
1842 core::iter::once((false, &polygon.outer)).chain(polygon.holes.iter().map(|h| (true, h)))
1843 {
1844 let left = region_left(ring, hole)?;
1845 for (p, q) in ring_edges(ring) {
1846 if p != q {
1847 edges.push((p, q, left));
1848 }
1849 }
1850 }
1851 }
1852 let mut out = costs.to_vec();
1853 for cost in &mut out {
1854 for ring in core::iter::once(&mut cost.polygon.outer).chain(cost.polygon.holes.iter_mut()) {
1855 for v in &mut ring.points {
1856 for &(p, q, left) in edges.iter().chain(edges.iter()) {
1857 let free = if left { Sign::Positive } else { Sign::Negative };
1858 if point_segment(*v, p, q) > reach || side(p, q, *v)? != free {
1859 continue;
1860 }
1861 let d = q - p;
1864 let t = (*v - p).dot(d) / d.dot(d);
1865 let foot = Point2::new(p.x + d.x * t, p.y + d.y * t);
1866 let out = if left {
1867 Point2::new(d.y, -d.x)
1868 } else {
1869 Point2::new(-d.y, d.x)
1870 };
1871 let unit = Point2::new(out.x / d.length(), out.y / d.length());
1872 let mut step = f64::EPSILON * foot.x.abs().max(foot.y.abs()).max(reach);
1873 let mut moved = foot;
1874 while side(p, q, moved)? == free {
1875 moved = Point2::new(foot.x + unit.x * step, foot.y + unit.y * step);
1876 step *= 2.0;
1877 }
1878 *v = moved;
1879 }
1880 }
1881 }
1882 }
1883 Ok(out)
1884}
1885
1886fn convex_hull(points: &[Point2]) -> Result<Option<Vec<Point2>>, RouteError> {
1889 let mut pts: Vec<Point2> = points.to_vec();
1890 pts.sort_by(|p, q| p.x.total_cmp(&q.x).then(p.y.total_cmp(&q.y)));
1891 pts.dedup();
1892 if pts.len() < 3 {
1893 return Ok(None);
1894 }
1895 let mut hull: Vec<Point2> = Vec::new();
1897 for pass in 0..2 {
1898 let start = hull.len();
1899 let iter: Box<dyn Iterator<Item = &Point2>> = if pass == 0 {
1900 Box::new(pts.iter())
1901 } else {
1902 Box::new(pts.iter().rev())
1903 };
1904 for &p in iter {
1905 while hull.len() >= start + 2
1906 && side(hull[hull.len() - 2], hull[hull.len() - 1], p)? != Sign::Positive
1907 {
1908 hull.pop();
1909 }
1910 hull.push(p);
1911 }
1912 hull.pop();
1913 }
1914 Ok((hull.len() >= 3).then_some(hull))
1915}
1916
1917fn holds(polygon: &Polygon, hull: &[Point2]) -> Result<bool, RouteError> {
1925 for &c in hull {
1926 if !in_polygon(polygon, c)? {
1927 return Ok(false);
1928 }
1929 }
1930 let n = hull.len();
1931 for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1932 for (p, q) in ring_edges(ring) {
1933 if meets_open_convex(p, q, hull, n)? {
1934 return Ok(false);
1935 }
1936 }
1937 }
1938 let inv = 1.0 / n as f64;
1939 let centre = hull.iter().fold(Point2::ZERO, |sum, c| sum + *c * inv);
1940 for i in 0..n {
1941 if side(hull[i], hull[(i + 1) % n], centre)? != Sign::Positive {
1942 return Ok(false);
1943 }
1944 }
1945 let on_boundary = core::iter::once(&polygon.outer)
1946 .chain(polygon.holes.iter())
1947 .flat_map(ring_edges)
1948 .try_fold(false, |on, (p, q)| {
1949 Ok::<_, RouteError>(on || (within(p, q, centre) && side(p, q, centre)? == Sign::Zero))
1950 })?;
1951 Ok(!on_boundary && in_polygon(polygon, centre)?)
1952}
1953
1954fn meets_open_convex(p: Point2, q: Point2, hull: &[Point2], n: usize) -> Result<bool, RouteError> {
1958 for i in 0..n {
1959 let (u, v) = (hull[i], hull[(i + 1) % n]);
1960 if side(u, v, p)? != Sign::Positive && side(u, v, q)? != Sign::Positive {
1961 return Ok(false);
1962 }
1963 }
1964 if p != q {
1965 let mut all_left = true;
1966 let mut all_right = true;
1967 for &c in hull {
1968 let s = side(p, q, c)?;
1969 all_left &= s != Sign::Negative;
1970 all_right &= s != Sign::Positive;
1971 }
1972 if all_left || all_right {
1973 return Ok(false);
1974 }
1975 }
1976 Ok(true)
1977}
1978
1979pub fn weighted_farthest_point(
1988 map: &WeightedMap,
1989 subregion: &Polygon,
1990 tolerance: f64,
1991) -> Result<Farthest, FarthestError> {
1992 weighted_farthest_point_within(map, subregion, tolerance, MAX_CELLS)
1993}
1994
1995#[derive(Debug, Clone, Copy)]
1996struct Cell {
1997 corners: [Point2; 3],
1998 root: usize,
1999 anchor: (Point2, f64),
2000 upper: f64,
2001 order: usize,
2002 depth: u32,
2003}
2004
2005impl PartialEq for Cell {
2006 fn eq(&self, other: &Self) -> bool {
2007 self.cmp(other) == Ordering::Equal
2008 }
2009}
2010
2011impl Eq for Cell {}
2012
2013impl PartialOrd for Cell {
2014 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2015 Some(self.cmp(other))
2016 }
2017}
2018
2019impl Ord for Cell {
2020 fn cmp(&self, other: &Self) -> Ordering {
2021 self.upper
2022 .total_cmp(&other.upper)
2023 .then(other.order.cmp(&self.order))
2024 }
2025}
2026
2027pub fn weighted_farthest_point_within(
2033 map: &WeightedMap,
2034 subregion: &Polygon,
2035 tolerance: f64,
2036 max_cells: usize,
2037) -> Result<Farthest, FarthestError> {
2038 if !(tolerance.is_finite() && tolerance >= 0.0) {
2039 return Err(FarthestError::InvalidTolerance);
2040 }
2041 validate_region(core::slice::from_ref(subregion), &[])?;
2042 let roots = free_triangles_in(&map.region, &map.obstacles)?;
2043 let steep: Vec<f64> = roots
2044 .iter()
2045 .map(|t| map.weights.steepest(t))
2046 .collect::<Result<_, _>>()?;
2047 let slack = |depth: u32, radius: f64| {
2048 f64::from(depth + 4) * 4.0 * f64::EPSILON * map.scale + 4.0 * f64::EPSILON * radius
2049 };
2050 let mut evaluated: HashMap<(u64, u64), Option<(f64, f64)>> = HashMap::new();
2051 let mut lower: Option<(f64, Point2)> = None;
2052 let mut heap = BinaryHeap::new();
2053 let mut cells = 0usize;
2054 let mut cell = |corners: [Point2; 3],
2055 root: usize,
2056 inherited: Option<(Point2, f64)>,
2057 depth: u32,
2058 order: usize,
2059 lower: &mut Option<(f64, Point2)>|
2060 -> Result<Option<Cell>, FarthestError> {
2061 let centroid = Point2::new(
2062 (corners[0].x + corners[1].x + corners[2].x) / 3.0,
2063 (corners[0].y + corners[1].y + corners[2].y) / 3.0,
2064 );
2065 let radius = |a: Point2| {
2066 corners
2067 .iter()
2068 .map(|c| (*c - a).length())
2069 .fold(0.0, f64::max)
2070 };
2071 let k = steep[root];
2072 let mut best: Option<(f64, (Point2, f64))> =
2073 inherited.map(|(a, d)| (d + k * radius(a), (a, d)));
2074 for a in [corners[0], corners[1], corners[2], centroid] {
2075 if !in_triangle(&roots[root], a)? {
2076 continue;
2077 }
2078 if map.walls.iter().try_fold(false, |m, &(p, q)| {
2079 Ok::<_, RouteError>(m || (within(p, q, a) && side(p, q, a)? == Sign::Zero))
2080 })? {
2081 continue;
2082 }
2083 let key = (a.x.to_bits(), a.y.to_bits());
2084 let bracket = match evaluated.get(&key) {
2085 Some(b) => *b,
2086 None => {
2087 let b = map.bracket(a)?;
2088 evaluated.insert(key, b);
2089 b
2090 }
2091 };
2092 let Some((lo, hi)) = bracket else {
2093 return Err(FarthestError::Unreachable {
2094 triangle: roots[root],
2095 });
2096 };
2097 if in_polygon(subregion, a)? && lower.is_none_or(|(l, _)| lo > l) {
2098 *lower = Some((lo, a));
2099 }
2100 let bound = hi + k * radius(a);
2101 if best.is_none_or(|(b, _)| bound < b) {
2102 best = Some((bound, (a, hi)));
2103 }
2104 }
2105 Ok(best.map(|(bound, anchor)| Cell {
2106 corners,
2107 root,
2108 anchor,
2109 upper: bound + k * slack(depth, radius(anchor.0)),
2110 order,
2111 depth,
2112 }))
2113 };
2114 for (root, corners) in roots.iter().enumerate() {
2115 if outside(subregion, corners)? {
2116 continue;
2117 }
2118 cells += 1;
2119 let Some(c) = cell(*corners, root, None, 0, cells, &mut lower)? else {
2120 return Err(FarthestError::Triangulation);
2121 };
2122 heap.push(c);
2123 }
2124 if cells == 0 {
2125 return Err(FarthestError::Empty);
2126 }
2127 let best = |lower: &Option<(f64, Point2)>| lower.map_or(f64::NEG_INFINITY, |(d, _)| d);
2128 while let Some(top) = heap.peek() {
2129 if top.upper <= best(&lower) + tolerance || cells >= max_cells {
2130 break;
2131 }
2132 let parent = heap.pop().expect("peeked");
2133 let [a, b, c] = parent.corners;
2134 let mid = |p: Point2, q: Point2| Point2::new(0.5 * p.x + 0.5 * q.x, 0.5 * p.y + 0.5 * q.y);
2135 let (ab, bc, ca) = (mid(a, b), mid(b, c), mid(c, a));
2136 for corners in [[a, ab, ca], [ab, b, bc], [ca, bc, c], [ab, bc, ca]] {
2137 if outside(subregion, &corners)? {
2138 continue;
2139 }
2140 cells += 1;
2141 if let Some(child) = cell(
2142 corners,
2143 parent.root,
2144 Some(parent.anchor),
2145 parent.depth + 1,
2146 cells,
2147 &mut lower,
2148 )? {
2149 if child.upper > best(&lower) {
2150 heap.push(child);
2151 }
2152 }
2153 }
2154 }
2155 let Some((lo, witness)) = lower else {
2156 if heap.is_empty() {
2157 return Err(FarthestError::Empty);
2158 }
2159 return Ok(Farthest {
2160 distance: LengthInterval {
2161 lower: 0.0,
2162 upper: heap.peek().map_or(0.0, |c| c.upper),
2163 },
2164 witness: None,
2165 converged: false,
2166 cells,
2167 });
2168 };
2169 let hi = heap.peek().map_or(lo, |c| c.upper.max(lo));
2170 Ok(Farthest {
2171 distance: LengthInterval {
2172 lower: lo,
2173 upper: hi,
2174 },
2175 witness: Some(witness),
2176 converged: hi - lo <= tolerance,
2177 cells,
2178 })
2179}
2180
2181#[cfg(test)]
2182mod tests {
2183 use super::*;
2184 use axiolid_overlay::Ring;
2185
2186 fn square() -> Weights {
2187 let p = |x: f64, y: f64| Point2::new(x, y);
2188 let polygon = Polygon {
2189 outer: Ring {
2190 points: vec![p(0.0, 0.0), p(1.0, 0.0), p(1.0, 1.0), p(0.0, 1.0)],
2191 },
2192 holes: Vec::new(),
2193 };
2194 Weights::new(&[CostRegion::new(polygon, 2.0)], &[], &[]).unwrap()
2195 }
2196
2197 #[test]
2201 fn a_stretch_within_rounding_of_a_cost_edge_is_bracketed_both_ways() {
2202 let weights = square();
2203 let tiny = 1e-17;
2204 let (lo, hi) = weights
2208 .segment(Point2::new(-1.0, tiny), Point2::new(2.0, tiny), 2.0)
2209 .unwrap();
2210 assert!(lo <= 3.0 && (hi - 3.0).abs() < 1e-12, "[{lo}, {hi}]");
2211 let (lo, hi) = weights
2213 .segment(Point2::new(-1.0, -tiny), Point2::new(2.0, -tiny), 2.0)
2214 .unwrap();
2215 assert!(lo <= 3.0 && hi >= 3.0, "[{lo}, {hi}]");
2216 let p = |x: f64, y: f64| Point2::new(x, y);
2220 let notched = Polygon {
2221 outer: Ring {
2222 points: vec![
2223 p(0.0, 0.0),
2224 p(4.0, 0.0),
2225 p(4.0, 2.0),
2226 p(2.5, 2.0),
2227 p(2.0, 1.0 + f64::EPSILON),
2228 p(1.5, 2.0),
2229 p(0.0, 2.0),
2230 ],
2231 },
2232 holes: Vec::new(),
2233 };
2234 let weights = Weights::new(&[CostRegion::new(notched, 2.0)], &[], &[]).unwrap();
2235 let (lo, hi) = weights.segment(p(0.5, 1.0), p(3.5, 1.0), 4.0).unwrap();
2236 assert!(lo <= 6.0 && hi >= 6.0, "[{lo}, {hi}]");
2237 let weights = square();
2238 let (lo, hi) = weights
2241 .segment(Point2::new(0.5, -tiny), Point2::new(0.5, tiny), 2.0)
2242 .unwrap();
2243 assert!(lo <= 2.0 * tiny && hi >= 2.0 * 2.0 * tiny, "[{lo}, {hi}]");
2244 let (lo, hi) = weights
2246 .segment(Point2::new(-1.0, 0.5), Point2::new(2.0, 0.5), 2.0)
2247 .unwrap();
2248 assert!(
2249 (lo - 4.0).abs() < 1e-12 && (hi - 4.0).abs() < 1e-12,
2250 "[{lo}, {hi}]"
2251 );
2252 }
2253
2254 fn p(x: f64, y: f64) -> Point2 {
2255 Point2::new(x, y)
2256 }
2257
2258 fn rect(x0: f64, y0: f64, x1: f64, y1: f64) -> Polygon {
2259 Polygon {
2260 outer: Ring {
2261 points: vec![p(x0, y0), p(x1, y0), p(x1, y1), p(x0, y1)],
2262 },
2263 holes: Vec::new(),
2264 }
2265 }
2266
2267 fn interval(map: &WeightedMap, end: Point2, toward: Point2) -> usize {
2270 (0..map.nodes.len())
2271 .find(|&i| match map.kinds[i] {
2272 Kind::Interval { a, b, .. } => {
2273 (a == end && (b - a).dot(toward - a) > 0.0)
2274 || (b == end && (a - b).dot(toward - b) > 0.0)
2275 }
2276 Kind::Vertex => false,
2277 })
2278 .expect("an interval")
2279 }
2280
2281 #[test]
2287 fn a_touch_blocks_a_hop_only_at_a_vertex_end() {
2288 let room = [rect(0.0, 0.0, 10.0, 4.0)];
2289 let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2290 let barrier = vec![vec![p(5.0, 1.0), p(5.0, 3.0)]];
2292 let map = weighted_distance_map(&room, &barrier, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2293 let above = interval(&map, p(4.0, 1.0), p(4.0, 2.0));
2294 let q = p(6.0, 1.0);
2295 let hop = map
2296 .hop(map.span(above), (q, q), map.kinds[above], Kind::Vertex)
2297 .unwrap();
2298 assert!(hop.is_some(), "the inner end's pieces graze the foot");
2299 let walled = vec![vec![p(5.0, 3.0), p(5.0, 1.0)]];
2304 let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2305 let map = weighted_distance_map(&room, &walled, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2306 let corner = interval(&map, p(4.0, 4.0), p(4.0, 3.0));
2307 let q = p(6.0, 2.0);
2308 let hop = map
2312 .hop(map.span(corner), (q, q), map.kinds[corner], Kind::Vertex)
2313 .unwrap();
2314 assert!(hop.is_none(), "{hop:?}");
2315 }
2316
2317 #[test]
2321 fn a_hull_is_held_only_whole() {
2322 let square = rect(0.0, 0.0, 4.0, 4.0);
2323 let hull = [p(1.0, 1.0), p(3.0, 1.0), p(3.0, 3.0)];
2324 assert!(holds(&square, &hull).unwrap());
2325 let notched = Polygon {
2328 outer: Ring {
2329 points: vec![
2330 p(0.0, 0.0),
2331 p(4.0, 0.0),
2332 p(4.0, 4.0),
2333 p(0.0, 4.0),
2334 p(0.0, 1.9),
2335 p(2.2, 1.6),
2336 p(0.0, 1.5),
2337 ],
2338 },
2339 holes: Vec::new(),
2340 };
2341 for c in hull {
2342 assert!(in_polygon(¬ched, c).unwrap());
2343 }
2344 assert!(in_polygon(¬ched, p(7.0 / 3.0, 5.0 / 3.0)).unwrap());
2345 assert!(!holds(¬ched, &hull).unwrap());
2346 }
2347
2348 #[test]
2351 fn a_segment_along_a_blocker_does_not_pass_through_its_end() {
2352 assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(3.0, 0.0)).unwrap());
2353 assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(1.0, 0.0)).unwrap());
2354 assert!(through_end(p(0.0, -1.0), p(0.0, 1.0), p(0.0, 0.0), p(1.0, 0.0)).unwrap());
2356 }
2357}