Skip to main content

axiolid_route/
weighted.rs

1//! Weighted distance maps: travel costing a factor inside cost polygons
2//! (#195).
3//!
4//! # The cost
5//!
6//! Each [`CostRegion`] is a polygon with a factor of at least 1; a metre
7//! travelled inside it counts `factor` metres. Outside every polygon the
8//! factor is 1, and where polygons overlap the greatest applies. The
9//! weighted distance is the least cost of a walk in the free space to the
10//! nearest target. On a cost polygon's edge the cheaper side applies: a
11//! walk along the edge can always be moved off it, to that side, for as
12//! little extra as wanted.
13//!
14//! Weighted shortest paths bend where they cross a cost edge (Snell's
15//! law), so there is no exact visibility graph. The map brackets the
16//! distance instead.
17//!
18//! # The upper bound
19//!
20//! Every cost edge carries points at most `spacing` apart. The visibility
21//! graph over the region's vertices, the cost polygons' vertices, the
22//! targets and those points is exact, as for [`crate::distance_map`], and
23//! each edge costs its segment's weighted length, integrated piece by piece
24//! across the cost polygons and rounded up. Every path in the graph is a
25//! walk, so its cost bounds the distance from above; the query's route is
26//! that walk. A walk crossing a cost edge between two of its points is only
27//! longer by the square of the offset, so the upper bound is close.
28//!
29//! # The lower bound
30//!
31//! An optimal walk is straight between the points where it bends or
32//! crosses a cost edge. Cut it there: each piece is a segment inside one
33//! cell of constant factor, or along one line, from a vertex or a point of
34//! a cost edge to another. Each cost edge is split into intervals, even
35//! ones at most `spacing` long, graded down to 1/64 of that toward the
36//! edge's vertices; a point of an edge lies in some interval. So every
37//! piece runs between two nodes -- vertices, or intervals -- and costs at
38//! least
39//!
40//! ```text
41//! factor  x  least distance between the two nodes,
42//! ```
43//!
44//! where the factor is the greatest of: that of every polygon holding the
45//! whole hull of the two nodes, and, at an interval, that of the side the
46//! piece leaves or enters it on. A piece along a line costs at least the
47//! weighted length of the gap between its nodes. The graph over the nodes
48//! with those weights has a path no dearer than the walk, so its distances
49//! bound the walk from below. A hop is left out only when one obstacle --
50//! or, across a cell, one cost edge -- certainly crosses every such piece
51//! (a piece from an interval's end that is a vertex is the vertex's own).
52//!
53//! Relaxed like that, a path could enter an interval at one end and leave
54//! at the other, and slide along an edge from interval to interval for
55//! nothing. Three facts about optimal walks bar it; the graph's states
56//! remember the line and side of each first hop to apply them:
57//!
58//! - no two pieces in a row along one line (they would be one piece);
59//! - no touching a cost edge and turning back to the side it came from:
60//!   the chord between the two pieces is shorter;
61//! - no running along a cost edge between two touches on one side unless
62//!   along is cheaper than that side (critical reflection): else the chord
63//!   is no dearer.
64//!
65//! Cost edges along a wall get no intervals: no walk crosses them, and
66//! none bends at a wall except at a vertex, the free side having one
67//! factor.
68//!
69//! What is left is first order in the interval length where a walk
70//! crosses a cost edge obliquely or wraps its corner; square crossings
71//! lose nothing. Halving `spacing` about halves the gap.
72//!
73//! Every exact decision -- sides, crossings, containment -- is made with
74//! certified predicates; lengths are rounded down for the lower bound and
75//! up for the upper. The points cutting a cost edge at an angle are
76//! interpolated, so they lie off its line by rounding: the sides of an
77//! interval are taken of its edge's exact ends, an edge never blocks a
78//! hop from its own intervals, and a hop along an edge is costed above as
79//! the walk along the edge itself, which is that close to it.
80//!
81//! # At the walls
82//!
83//! A cost region clipped to the free region meets the walls only up to
84//! rounding. A vertex left a hair inside would open a sliver along the
85//! wall, costing 1; so a vertex that near a wall, on its free side, is
86//! moved just beyond it, where the region costs nothing (see
87//! [`weighted_distance_map`]). An edge left so on or beyond a wall is
88//! wall-borne: it gets no intervals, and along the wall its free side's
89//! factor counts.
90//!
91//! # Seeded targets
92//!
93//! Each target may start at its own cost (see
94//! [`weighted_distance_map_seeded`]): both searches start there, so both
95//! bounds count it.
96
97use 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
113/// Halvings of the end intervals of each cost edge toward its vertices.
114const GRADING: u32 = 6;
115
116/// Graph nodes a weighted map builds at most: region, barrier, target and
117/// cost-polygon vertices, and the points along cost edges.
118pub const MAX_WEIGHTED_NODES: usize = 2048;
119
120/// A polygon inside which travel costs `factor` times its length.
121#[derive(Debug, Clone, PartialEq)]
122pub struct CostRegion {
123    /// The polygon, closed.
124    pub polygon: Polygon,
125    /// At least 1.
126    pub factor: f64,
127}
128
129impl CostRegion {
130    /// A cost region.
131    #[must_use]
132    pub fn new(polygon: Polygon, factor: f64) -> Self {
133        Self { polygon, factor }
134    }
135}
136
137/// The nearest target by weighted distance, bracketed, and a walk there.
138#[derive(Debug, Clone, PartialEq)]
139#[non_exhaustive]
140pub struct WeightedReach {
141    /// Index of the target the walk reaches, in the targets given.
142    pub target: usize,
143    /// Contains the weighted distance to the nearest target, counting the
144    /// target's start weight (see [`weighted_distance_map_seeded`]).
145    /// `upper` is the weighted cost of `route` plus that weight, rounded
146    /// up.
147    pub cost: LengthInterval,
148    /// The walk, from the query point to the target. Its `length` is its
149    /// plain Euclidean length.
150    pub route: Route,
151}
152
153/// What a graph node is.
154#[derive(Debug, Clone, Copy)]
155enum Kind {
156    /// A region, barrier, target or cost-polygon vertex: exact.
157    Vertex,
158    /// A point on a cost edge standing for the interval `a`-`b` of it, on
159    /// line `line` and cost piece `piece` (no vertex lies strictly inside
160    /// a piece), with the factors just left and right of `a`-`b`, and the
161    /// factor of travel along it: the cheaper free side.
162    Interval {
163        a: Point2,
164        b: Point2,
165        /// The piece's own ends, exact: `a` and `b` are interpolated and
166        /// may lie off its line by rounding, so sides are taken of this.
167        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/// Weighted distances to the nearest of several targets, bracketed.
182#[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 of cost edges through each node.
191    lines: Vec<Vec<u32>>,
192    graph: Graph,
193    /// Per state: upper-bound distance, next state, target.
194    upper: Vec<f64>,
195    next: Vec<usize>,
196    target: Vec<usize>,
197    /// Per state: lower-bound distances by the line of the hop that
198    /// reached the state (`NO_LINE` for none).
199    lower: Vec<Vec<(Tag, f64)>>,
200    sites: Vec<Point2>,
201    /// The distance each target starts at.
202    seeds: Vec<f64>,
203    /// The cost regions, as snapped (see [`weighted_distance_map`]).
204    costs: Vec<CostRegion>,
205    scale: f64,
206}
207
208/// A weighted distance map from `targets` over `region`, avoiding
209/// `barriers`, with travel inside `costs` weighted by their factors and
210/// points along cost edges at most `spacing` apart.
211///
212/// A cost region may touch the region's boundary up to rounding, as one
213/// clipped to the free region does (#198): a cost vertex within 2^-24 of
214/// the region's extent (and a few ulps) of a region edge, on its free
215/// side, is moved just across it, and a cost edge may cross a region edge
216/// that near one of either edge's ends. The map is the one for the
217/// regions so moved: no sliver along a wall is left for a walk to slip
218/// through at factor 1.
219///
220/// # Errors
221///
222/// [`MapError`] for malformed input, no targets, a target outside the
223/// region, a factor below 1 or not finite ([`MapError::InvalidFactor`]), a
224/// spacing not positive and finite ([`MapError::InvalidSpacing`]), a cost
225/// edge that crosses an obstacle or another cost edge other than by such
226/// a touch, or lies along a barrier ([`MapError::CostCrossing`]), or more
227/// than [`MAX_WEIGHTED_NODES`] graph nodes.
228pub 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
245/// [`weighted_distance_map`] with a caller-chosen node budget.
246///
247/// # Errors
248///
249/// As [`weighted_distance_map`], with `budget` for [`MAX_WEIGHTED_NODES`].
250pub 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
262/// A weighted distance map whose targets each start at their own cost:
263/// the distance from a point is the least, over targets, of the weighted
264/// cost of a walk to the target plus the target's weight (#198), as
265/// [`crate::distance_map_weighted`] is for plain maps. With every weight
266/// zero it is [`weighted_distance_map`].
267///
268/// # Errors
269///
270/// As [`weighted_distance_map`], and [`MapError::InvalidWeight`] for a
271/// weight that is negative or not finite.
272pub 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
289/// [`weighted_distance_map_seeded`] with a caller-chosen node budget.
290///
291/// # Errors
292///
293/// As [`weighted_distance_map_seeded`], with `budget` for
294/// [`MAX_WEIGHTED_NODES`].
295pub 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    // Vertices first, then the points along cost edges.
342    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    // Cost edges are cut at every vertex on them, so no interval's point
353    // is a vertex.
354    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        // An edge two polygons share gets its points once.
366        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        // Along a wall (or a hair beyond it) no walk crosses, and none
372        // bends except at a vertex: the one free side has one factor, so a
373        // chord is shorter. Every other piece has both sides free.
374        if weights.beyond_wall(p, q, reach)? {
375            continue;
376        }
377        let count = ((q - p).length() / spacing).ceil().max(1.0) as usize;
378        // Even cuts, graded toward both ends: a walk wrapping the corner
379        // loses at most the length of the interval it touches there.
380        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 piece runs from vertex to vertex; its inner cuts are
424                // not vertices.
425                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    // Every state of every target at its weight; of targets on one point,
470    // the lightest (then the first) seeds it.
471    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    /// Graph nodes: vertices and points along cost edges.
491    #[must_use]
492    pub fn graph_vertices(&self) -> usize {
493        self.nodes.len()
494    }
495
496    /// How many targets the map was built from.
497    #[must_use]
498    pub fn targets(&self) -> usize {
499        self.sites.len()
500    }
501
502    /// The nearest target from `point` by weighted distance, the distance
503    /// bracketed, and a walk there.
504    ///
505    /// `Ok(Err(StartOutside))` for a point outside the region,
506    /// `Ok(Err(DisconnectedComponents))` for one no target can reach.
507    ///
508    /// # Errors
509    ///
510    /// [`RouteError`] for a non-finite point or an undecidable predicate.
511    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    /// The region the map covers.
547    pub(crate) fn region(&self) -> &[Polygon] {
548        &self.region
549    }
550
551    /// Region edges and barrier segments.
552    pub(crate) fn obstacles(&self) -> &[(Point2, Point2)] {
553        &self.obstacles
554    }
555
556    /// Barrier segments.
557    pub(crate) fn walls(&self) -> &[(Point2, Point2)] {
558        &self.walls
559    }
560
561    /// The targets given, and the cost each starts at.
562    pub(crate) fn seeded(&self) -> impl Iterator<Item = (Point2, f64)> + '_ {
563        self.sites.iter().copied().zip(self.seeds.iter().copied())
564    }
565
566    /// Whether two maps cover the same free space at the same costs.
567    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    /// The greatest factor meeting the closed triangle: the most a metre
572    /// can cost in it.
573    pub(crate) fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
574        self.weights.steepest(t)
575    }
576
577    /// The factor all through the closed triangle when no cost edge meets
578    /// it, else 1: what a straight piece inside it costs a metre at least.
579    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    /// Every node's span -- a vertex twice, or an interval -- with the
595    /// least lower bound a walk from a point of it has, when finite.
596    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    /// The bracket at a point known to be inside the region; `None` when
610    /// no target is reached.
611    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    /// Upper-bound Dijkstra over the exact graph, each edge costing its
619    /// segment's weighted length rounded up.
620    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        // Follow each state's chain to its end: a heavy target may itself
645        // be reached from a lighter one, so being seeded does not end it.
646        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    /// The states a hop from node `i` toward the segment `b1`-`b2` may
663    /// leave in: the cone's sectors at a vertex, every free state of an
664    /// interval.
665    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    /// The segment a node stands for: its interval, or the vertex twice.
673    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    /// The line both spans lie on, if they share a cost edge's line.
681    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    /// A lower bound on the cost of a straight piece from a point of span
690    /// `(a1, a2)` to a point of span `(b1, b2)`, or `None` when an obstacle
691    /// certainly blocks every such piece. Spans on one line bound the
692    /// piece by the weighted length of the gap between them, however many
693    /// cells it crosses; any other piece stays in one cell and crosses no
694    /// cost edge.
695    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            // On one line: at least the weighted length of the gap between
720            // the spans, which every such piece covers, whatever it runs
721            // along.
722            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        // A piece on an interval's own line never crosses a piece from it;
728        // its interpolated ends may lie a rounding off that line, beyond.
729        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        // A piece from inside an interval leaves it into the side the
740        // other span lies on, into the cell there; one from the interval's
741        // end, if a vertex, is the vertex's hop.
742        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    /// Which side of interval node `k`'s line node `n`'s span lies
752    /// strictly on: `LEFT`, `RIGHT`, or `NONE` when it touches the line or
753    /// `k` is a vertex. An end of `n`'s interval that is a vertex does not
754    /// count: a walk through it has its event at the vertex's own node.
755    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    /// The edge from node `n` (its span `sn`) into node `k`, with the
786    /// sides it leaves `n` and arrives at `k` on.
787    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    /// Lower-bound Dijkstra: exact vertex-to-vertex edges costing their
809    /// weighted length rounded down, and hops to and between intervals,
810    /// over states that remember the first hop's line and side (see
811    /// [`allowed`]).
812    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                // Travelled from `u` into `w`.
840                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    /// The least upper bound from `point` and the first state on the way.
870    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            // A leg costs at least its length.
893            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    /// The least lower bound from `point`: over its first piece to a node
924    /// and the node's bound, the piece exact to a vertex, fattened to an
925    /// interval. Starts from `ceiling`, a value no less than the answer.
926    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            // The first hop, as an edge into the node: the states it may
945            // go on from.
946            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                // The walk starts here: any way on.
967                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
1017/// Dijkstra with a binary heap from several sources, each at its start
1018/// distance: distance and the previous state. Ties break on the lowest
1019/// state.
1020fn 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
1049/// No side, or unknown.
1050const NONE: u8 = 0;
1051const LEFT: u8 = 1;
1052const RIGHT: u8 = 2;
1053/// A first hop along the interval's piece, the next node's first hop
1054/// leaving on no known side, or on the left or right.
1055const ALONG: u8 = 3;
1056
1057/// How a path leaves a state toward the targets: the line of its first hop
1058/// if that runs along a cost line, and at an interval the side it leaves
1059/// on (`LEFT`, `RIGHT`), or `ALONG + side` for a first hop along the
1060/// interval's own piece followed by one leaving on `side`.
1061#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1062struct Tag {
1063    line: u32,
1064    side: u8,
1065}
1066
1067/// An edge travelled from state `from` into the state it is listed under.
1068#[derive(Debug, Clone, Copy)]
1069struct Edge {
1070    from: usize,
1071    weight: f64,
1072    /// The cost line it runs along, or `NO_LINE`.
1073    line: u32,
1074    /// The side of the source interval's line it leaves on.
1075    leave: u8,
1076    /// The side of the destination interval's line it arrives from.
1077    arrive: u8,
1078    /// Along one cost piece, from one of its intervals to another.
1079    same_piece: bool,
1080}
1081
1082/// Whether a path may arrive at a node of `kind` by `edge` and go on as
1083/// `tag` says. An optimal walk never takes two pieces along one line in a
1084/// row (they would be one piece), never touches a cost edge and turns
1085/// back to the side it came from (the chord is shorter), and never runs
1086/// along a cost piece between two such touches on one side unless along
1087/// is cheaper than that side (else the chord is no dearer).
1088fn 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
1109/// The tag of the source state of `edge`, whose destination went on as
1110/// `tag`.
1111fn 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
1129/// Dijkstra backward from the targets over (state, tag), taking only the
1130/// transitions [`allowed`] permits. Per state, the settled distances by
1131/// tag.
1132fn 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/// A total order on finite and infinite floats for the heaps.
1173#[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
1190/// Round a nonnegative length down past the error of the few operations
1191/// that produced it.
1192fn 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
1200/// The factor a piece from inside an interval node's span toward the span
1201/// `other` must pay: that of the side of the interval `other` lies
1202/// strictly on, or the lesser when it touches the interval's line. 1 for
1203/// a vertex.
1204fn 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    // An end of the other span that is a vertex does not count.
1217    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
1226/// Which ends of a node's span are vertices, whose walks the vertices'
1227/// own nodes carry.
1228fn 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
1237/// Whether the segment `u`-`v` passes through an end of the segment
1238/// `p`-`q`, with neither of its own ends on that segment's line: a touch
1239/// that still separates the points just beside it, when every other corner
1240/// segment crosses properly.
1241fn 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
1253/// The gap between two spans on one line: the facing ends, or `None`
1254/// when the spans overlap.
1255fn 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
1277/// The least distance between two closed segments (either may be a
1278/// point): zero when they meet, decided exactly.
1279pub(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/// A piece of cost edge: part of one polygon's ring, cut at every vertex
1304/// lying on it, with the polygon and which side its inside lies on.
1305#[derive(Debug, Clone, Copy)]
1306struct Piece {
1307    p: Point2,
1308    q: Point2,
1309    polygon: usize,
1310    /// The polygon's inside lies left of `p` to `q`.
1311    inside_left: bool,
1312    /// Index of the line the piece lies on, shared by collinear pieces.
1313    line: u32,
1314}
1315
1316/// The cost polygons, their edges and the lines those lie on.
1317#[derive(Debug, Clone)]
1318struct Weights {
1319    polygons: Vec<Polygon>,
1320    factors: Vec<f64>,
1321    pieces: Vec<Piece>,
1322    /// Two points on each line cost pieces lie on.
1323    lines: Vec<(Point2, Point2)>,
1324    /// Region boundary edges, with whether the region lies left of them:
1325    /// along one, only that side is free.
1326    walls: Vec<(Point2, Point2, bool)>,
1327    greatest: f64,
1328    /// How far off a region edge a cost vertex may lie and still touch
1329    /// it (see [`touch_walls`]); 0 until the map sets it.
1330    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    /// Refuse a cost edge that properly crosses an obstacle or another
1412    /// cost edge, or runs along or through a barrier. A crossing of a
1413    /// region edge within `reach` of an end of either edge is a touch
1414    /// rounding moved (see [`touch_walls`]), and stands.
1415    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    /// The factors just left and right of the stretch `a`-`b` of the cost
1462    /// piece `p`-`q`, which no vertex interrupts. Which edges the stretch
1463    /// runs along is decided on the piece's own exact ends: `a` and `b`
1464    /// are interpolated, and on an edge at an angle rounding puts them off
1465    /// its line.
1466    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    /// Whether the stretch `p`-`q` lies within `reach` of one region edge,
1497    /// on or beyond it: out of the free space, a cost edge touching the
1498    /// wall (see [`touch_walls`]) that no walk crosses, only runs along.
1499    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    /// The lines of cost pieces that pass through `v`.
1514    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    /// The greatest factor over the polygons holding the whole convex hull
1525    /// of `corners`, and 1 if none does; 1 for a hull with no interior.
1526    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    /// The greatest factor over the polygons meeting the closed triangle,
1543    /// and 1: the most a metre can cost in it.
1544    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    /// The weighted length of the segment `a`-`b`, bracketed: the factor
1566    /// is decided piece by piece between the points where the segment
1567    /// meets cost edges. Along a cost edge the cheaper side counts. A piece
1568    /// whose factor rounding could misjudge -- shorter than a few ulps, or
1569    /// whose middle lies within rounding of a cost edge -- counts 1 below
1570    /// and the greatest factor above, with two exceptions for a piece
1571    /// within rounding of a cost edge all along. Above, it counts as the
1572    /// walk along that edge, which is off it by no more than rounding:
1573    /// the upper end bounds a walk that close to the segment, not the
1574    /// segment itself. Below, a piece exactly along a region edge, the
1575    /// cost edge on or beyond it, counts its free side's factor.
1576    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        // Stretches along cost pieces: (from, to, polygon, inside left of a-b).
1588        let mut along: Vec<(f64, f64, usize, bool)> = Vec::new();
1589        // Pieces not along `a`-`b`: (p, q, polygon, inside left of p-q).
1590        let mut near: Vec<(Point2, Point2, usize, bool)> = Vec::new();
1591        // Stretches along region edges: (from, to, region left of a-b).
1592        let mut walled: Vec<(f64, f64, bool)> = Vec::new();
1593        // Region edges not along `a`-`b`: (p, q, region left of p-q).
1594        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                // The lines cross between the ends of both: at the
1623                // parameter where `a`-`b` meets the piece's line.
1624                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            // Along cost pieces: polygon and whether its inside lies left
1649            // of `a`-`b`.
1650            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            // Along region edges: whether the region lies left of `a`-`b`.
1656            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                // A stretch within rounding of a cost piece all along --
1668                // `a`-`b` joins two points interpolated on a cost edge at
1669                // an angle -- may be on either side of it. The walk along
1670                // the piece itself, off the stretch by at most `tiny` at
1671                // each end, costs the piece's along factor: take that one
1672                // instead, above. Otherwise the greatest factor.
1673                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                // Along a region edge, with every hugged piece exactly on
1679                // or beyond it -- as a cost edge touching a wall is left
1680                // (see [`touch_walls`]) -- the free side lies on the
1681                // piece's inside side, and its factor is known below too.
1682                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            // Along a cost edge left on or beyond a wall (see
1721            // [`touch_walls`]), only the region's side of it is free, as
1722            // along the wall itself: the other lies outside the free
1723            // space, where no walk goes (#222).
1724            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            // Extra factor on each side of the segment at this stretch.
1739            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                    // On the polygon's edge: its inside on one side (both,
1757                    // for an edge the polygon has twice).
1758                    if sides.contains(&true) {
1759                        left = left.max(extra);
1760                    }
1761                    if sides.contains(&false) {
1762                        right = right.max(extra);
1763                    }
1764                }
1765            }
1766            // Along a cost edge the cheaper side counts -- of the sides a
1767            // walk can move to: along a wall, only the region's.
1768            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            // The steps between the stretch and the piece it hugs.
1791            upper += 4.0 * self.greatest * tiny * breaks.len() as f64;
1792        }
1793        // Each break's parameter is rounded: a stretch may be off by a few
1794        // ulps of the length at each end.
1795        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
1803/// How far off a region edge a cost vertex may lie and still be taken to
1804/// touch it: 2^-24 of the region's extent, four steps of the grid overlay
1805/// results were once rounded to, and a few ulps of its coordinates.
1806fn 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
1826/// The cost regions with every vertex that lies strictly on the free side
1827/// of a region edge and within `reach` of it moved just across that
1828/// edge's line: a cost region clipped to the free region touches its walls
1829/// only up to rounding, and a vertex left a hair inside would open a
1830/// sliver along the wall a walk could slip through at factor 1. Beyond the
1831/// wall the region costs nothing, so moving out is taking it as touching.
1832/// A vertex in a corner crosses each wall it is that near to, moving by at
1833/// most `reach` and a few ulps for each.
1834fn 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                    // The foot on the line, then out along the normal until
1862                    // the exact side says it is no longer on the free side.
1863                    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
1886/// The corners of the convex hull of `points`, counter-clockwise, or
1887/// `None` when they are collinear. Decided exactly.
1888fn 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    // Andrew's monotone chain with exact turns.
1896    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
1917/// Whether the closed polygon holds the convex polygon `hull`
1918/// (counter-clockwise, with interior). No edge of the polygon may meet the
1919/// hull's interior, which then lies wholly inside the polygon or wholly
1920/// outside, so one point strictly inside the hull decides -- corners on
1921/// the polygon's boundary do not: a hull can span a notch of a polygon,
1922/// touching it only along two sides. A hull too thin to hold a point
1923/// provably inside it is not held.
1924fn 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
1954/// Whether the closed segment `p`-`q` meets the open interior of the
1955/// counter-clockwise convex polygon: no edge line of the polygon, nor the
1956/// segment's line, separates them.
1957fn 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
1979/// The greatest weighted distance from a subregion to the nearest target
1980/// over its points in the free space, bracketed to within `tolerance`
1981/// where the map's own bracket allows: the result is never narrower than
1982/// the gap between the map's bounds at the farthest point.
1983///
1984/// # Errors
1985///
1986/// As [`crate::farthest_point`].
1987pub 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
2027/// [`weighted_farthest_point`] with a caller-chosen cell budget.
2028///
2029/// # Errors
2030///
2031/// As [`weighted_farthest_point`].
2032pub 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    /// A segment a rounding step off a cost edge cannot be told inside
2198    /// from outside by its middle: the stretch counts 1 below and the
2199    /// greatest factor above, whichever side it is on.
2200    #[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        // Just inside the square along its lower edge: 1 + 2 + 1 below.
2205        // Above, the walk along the edge itself, a rounding off the
2206        // segment, where the cheaper side counts: 3.
2207        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        // Just outside it: 3 at factor 1.
2212        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        // Inside a notched polygon, the notch's tip a rounding above the
2217        // middle: doubtful there, and hugging nothing, so the greatest
2218        // factor above, which is the true one.
2219        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        // Crossing the edge within rounding of it, not along it: the
2239        // greatest factor above.
2240        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        // Clear of every edge the stretches are decided.
2245        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    /// The interval of `map` whose span has `end` as one end and runs
2268    /// toward `toward`.
2269    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    /// A barrier's foot on the segment from an interval's inner end: the
2282    /// pieces from just beside that end pass under the foot, so the hop
2283    /// stands. From an end that is a vertex, the same touch is left to the
2284    /// vertex's own node, and the hop is blocked when every other piece
2285    /// crosses the barrier.
2286    #[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        // The stair's east edge x = 4 is cut at y = 1 (spacing 1).
2291        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        // The same geometry with the foot's end a vertex: the stair's
2300        // corner (4, 4) and a barrier foot on the line to the query.
2301        // An end that is a vertex: the stair's corner (4, 4), with a
2302        // barrier's foot on the line from it to the query.
2303        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        // Segments from (4, y), y just under 4, to (6, 2) cross x = 5 just
2309        // under y = 3: through the barrier; from the corner itself, at its
2310        // end (5, 3).
2311        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    /// A notch poking into a hull through its side: every corner and the
2318    /// centre of the hull are inside the polygon, but part of the hull is
2319    /// not, so it is not held.
2320    #[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        // A notch cut in from the west side, its tip at (2.2, 1.6), inside
2326        // the hull, below its diagonal.
2327        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(&notched, c).unwrap());
2343        }
2344        assert!(in_polygon(&notched, p(7.0 / 3.0, 5.0 / 3.0)).unwrap());
2345        assert!(!holds(&notched, &hull).unwrap());
2346    }
2347
2348    /// A segment along a blocker's own line, through its end, runs along
2349    /// it: no touch that separates anything.
2350    #[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        // Across the blocker's line, through its end.
2355        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}