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 weights = Weights::new(costs, &nodes, region)?;
355    weights.check_crossings(&obstacles, &walls, reach)?;
356    let mut kinds = vec![Kind::Vertex; nodes.len()];
357    let mut spans: Vec<(Point2, Point2, u32)> = Vec::new();
358    for piece in &weights.pieces {
359        let (p, q) = if (piece.p.x, piece.p.y) <= (piece.q.x, piece.q.y) {
360            (piece.p, piece.q)
361        } else {
362            (piece.q, piece.p)
363        };
364        // An edge two polygons share gets its points once.
365        if !spans.iter().any(|(u, v, _)| *u == p && *v == q) {
366            spans.push((p, q, piece.line));
367        }
368    }
369    for (piece, (p, q, line)) in spans.into_iter().enumerate() {
370        // Along a wall (or a hair beyond it) no walk crosses, and none
371        // bends except at a vertex: the one free side has one factor, so a
372        // chord is shorter. Every other piece has both sides free.
373        if weights.beyond_wall(p, q, reach)? {
374            continue;
375        }
376        let count = ((q - p).length() / spacing).ceil().max(1.0) as usize;
377        // Even cuts, graded toward both ends: a walk wrapping the corner
378        // loses at most the length of the interval it touches there.
379        let mut cuts: Vec<f64> = (0..=count).map(|k| k as f64 / count as f64).collect();
380        let first = 1.0 / count as f64;
381        for level in 1..=GRADING {
382            let t = first / f64::from(1u32 << level);
383            cuts.push(t);
384            cuts.push(1.0 - t);
385        }
386        cuts.sort_by(f64::total_cmp);
387        cuts.dedup();
388        let count = cuts.len() - 1;
389        if nodes.len() + count > budget {
390            return Err(RouteError::TooManyVertices {
391                supplied: nodes.len() + count,
392                budget,
393                lower_bound: 0.0,
394            }
395            .into());
396        }
397        let at = |k: usize| {
398            if k == 0 {
399                p
400            } else if k == count {
401                q
402            } else {
403                let t = cuts[k];
404                Point2::new(p.x + (q.x - p.x) * t, p.y + (q.y - p.y) * t)
405            }
406        };
407        for k in 0..count {
408            let (a, b) = (at(k), at(k + 1));
409            nodes.push(Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y));
410            let (left, right) = weights.sides(p, q, a, b)?;
411            let along = left.min(right);
412            kinds.push(Kind::Interval {
413                a,
414                b,
415                p,
416                q,
417                line,
418                piece: piece as u32,
419                left,
420                right,
421                along,
422                // A piece runs from vertex to vertex; its inner cuts are
423                // not vertices.
424                a_vertex: k == 0,
425                b_vertex: k + 1 == count,
426            });
427        }
428    }
429    if nodes.len() > budget {
430        return Err(RouteError::TooManyVertices {
431            supplied: nodes.len(),
432            budget,
433            lower_bound: 0.0,
434        }
435        .into());
436    }
437    let lines: Vec<Vec<u32>> = nodes
438        .iter()
439        .zip(&kinds)
440        .map(|(v, kind)| match kind {
441            Kind::Interval { line, .. } => Ok(vec![*line]),
442            Kind::Vertex => weights.lines_through(*v),
443        })
444        .collect::<Result<_, RouteError>>()?;
445    let graph = Graph::build(&nodes, region, barriers, &obstacles)?;
446    let scale = nodes
447        .iter()
448        .fold(1.0f64, |m, p| m.max(p.x.abs()).max(p.y.abs()));
449
450    let mut map = WeightedMap {
451        region: region.to_vec(),
452        walls,
453        obstacles,
454        weights,
455        nodes,
456        kinds,
457        lines,
458        graph,
459        upper: Vec::new(),
460        next: Vec::new(),
461        target: Vec::new(),
462        lower: Vec::new(),
463        sites: targets.clone(),
464        seeds: seeds.clone(),
465        costs: costs.to_vec(),
466        scale,
467    };
468    // Every state of every target at its weight; of targets on one point,
469    // the lightest (then the first) seeds it.
470    let mut sources = Vec::new();
471    let mut seed = vec![usize::MAX; map.graph.adjacency.len()];
472    for (i, node) in map.nodes.iter().enumerate() {
473        let lightest = (0..targets.len())
474            .filter(|&t| targets[t] == *node)
475            .min_by(|&a, &b| seeds[a].total_cmp(&seeds[b]).then(a.cmp(&b)));
476        if let Some(t) = lightest {
477            for state in map.graph.states(i) {
478                sources.push((state, seeds[t]));
479                seed[state] = t;
480            }
481        }
482    }
483    map.upper_bounds(&sources, &seed)?;
484    map.lower_bounds(&sources)?;
485    Ok(map)
486}
487
488impl WeightedMap {
489    /// Graph nodes: vertices and points along cost edges.
490    #[must_use]
491    pub fn graph_vertices(&self) -> usize {
492        self.nodes.len()
493    }
494
495    /// How many targets the map was built from.
496    #[must_use]
497    pub fn targets(&self) -> usize {
498        self.sites.len()
499    }
500
501    /// The nearest target from `point` by weighted distance, the distance
502    /// bracketed, and a walk there.
503    ///
504    /// `Ok(Err(StartOutside))` for a point outside the region,
505    /// `Ok(Err(DisconnectedComponents))` for one no target can reach.
506    ///
507    /// # Errors
508    ///
509    /// [`RouteError`] for a non-finite point or an undecidable predicate.
510    pub fn nearest(&self, point: Point2) -> Result<Result<WeightedReach, Unreachable>, RouteError> {
511        if !point.is_finite() {
512            return Err(RouteError::NonFinitePoint);
513        }
514        if !contains(&self.region, point)? {
515            return Ok(Err(Unreachable::StartOutside));
516        }
517        let Some((upper, first)) = self.upper_at(point)? else {
518            return Ok(Err(Unreachable::DisconnectedComponents));
519        };
520        let lower = self.lower_at(point, upper)?.min(upper);
521        let mut polyline = vec![point];
522        let mut state = first;
523        loop {
524            let at = self.nodes[self.graph.node(state)];
525            if polyline.last() != Some(&at) {
526                polyline.push(at);
527            }
528            if self.next[state] == usize::MAX {
529                break;
530            }
531            state = self.next[state];
532        }
533        let length = polyline.windows(2).map(|w| (w[1] - w[0]).length()).sum();
534        Ok(Ok(WeightedReach {
535            target: self.target[first],
536            cost: LengthInterval { lower, upper },
537            route: Route {
538                polyline,
539                length,
540                graph_vertices: self.nodes.len(),
541            },
542        }))
543    }
544
545    /// The region the map covers.
546    pub(crate) fn region(&self) -> &[Polygon] {
547        &self.region
548    }
549
550    /// Region edges and barrier segments.
551    pub(crate) fn obstacles(&self) -> &[(Point2, Point2)] {
552        &self.obstacles
553    }
554
555    /// Barrier segments.
556    pub(crate) fn walls(&self) -> &[(Point2, Point2)] {
557        &self.walls
558    }
559
560    /// The targets given, and the cost each starts at.
561    pub(crate) fn seeded(&self) -> impl Iterator<Item = (Point2, f64)> + '_ {
562        self.sites.iter().copied().zip(self.seeds.iter().copied())
563    }
564
565    /// Whether two maps cover the same free space at the same costs.
566    pub(crate) fn same_space(&self, other: &Self) -> bool {
567        self.region == other.region && self.walls == other.walls && self.costs == other.costs
568    }
569
570    /// The greatest factor meeting the closed triangle: the most a metre
571    /// can cost in it.
572    pub(crate) fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
573        self.weights.steepest(t)
574    }
575
576    /// The factor all through the closed triangle when no cost edge meets
577    /// it, else 1: what a straight piece inside it costs a metre at least.
578    pub(crate) fn inside_factor(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
579        for piece in &self.weights.pieces {
580            if meets_triangle(piece.p, piece.q, t)? {
581                return Ok(1.0);
582            }
583        }
584        let mut best = 1.0f64;
585        for (polygon, &factor) in self.weights.polygons.iter().zip(&self.weights.factors) {
586            if factor > best && in_polygon(polygon, t[0])? {
587                best = factor;
588            }
589        }
590        Ok(best)
591    }
592
593    /// Every node's span -- a vertex twice, or an interval -- with the
594    /// least lower bound a walk from a point of it has, when finite.
595    pub(crate) fn spans(&self) -> Vec<((Point2, Point2), f64)> {
596        (0..self.nodes.len())
597            .filter_map(|i| {
598                let least = self
599                    .graph
600                    .states(i)
601                    .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
602                    .fold(f64::INFINITY, f64::min);
603                least.is_finite().then(|| (self.span(i), least))
604            })
605            .collect()
606    }
607
608    /// The bracket at a point known to be inside the region; `None` when
609    /// no target is reached.
610    pub(crate) fn bracket(&self, point: Point2) -> Result<Option<(f64, f64)>, RouteError> {
611        let Some((upper, _)) = self.upper_at(point)? else {
612            return Ok(None);
613        };
614        Ok(Some((self.lower_at(point, upper)?.min(upper), upper)))
615    }
616
617    /// Upper-bound Dijkstra over the exact graph, each edge costing its
618    /// segment's weighted length rounded up.
619    fn upper_bounds(&mut self, sources: &[(usize, f64)], seed: &[usize]) -> Result<(), RouteError> {
620        let states = self.graph.adjacency.len();
621        let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
622        let mut adjacency: Vec<Vec<(usize, f64)>> = vec![Vec::new(); states];
623        for (u, edges) in self.graph.adjacency.iter().enumerate() {
624            let i = self.graph.node(u);
625            for &(w, _) in edges {
626                let j = self.graph.node(w);
627                let key = (i.min(j), i.max(j));
628                let c = match cost.get(&key) {
629                    Some(c) => *c,
630                    None => {
631                        let c = self
632                            .weights
633                            .segment(self.nodes[i], self.nodes[j], self.scale)?
634                            .1;
635                        cost.insert(key, c);
636                        c
637                    }
638                };
639                adjacency[u].push((w, c));
640            }
641        }
642        let (distance, previous) = dijkstra(&adjacency, sources);
643        // Follow each state's chain to its end: a heavy target may itself
644        // be reached from a lighter one, so being seeded does not end it.
645        let mut target = seed.to_vec();
646        for (state, t) in target.iter_mut().enumerate() {
647            let mut at = state;
648            let mut steps = 0;
649            while previous[at] != usize::MAX && steps <= seed.len() {
650                at = previous[at];
651                steps += 1;
652            }
653            *t = seed[at];
654        }
655        self.upper = distance;
656        self.next = previous;
657        self.target = target;
658        Ok(())
659    }
660
661    /// The states a hop from node `i` toward the segment `b1`-`b2` may
662    /// leave in: the cone's sectors at a vertex, every free state of an
663    /// interval.
664    fn hop_states(&self, i: usize, b1: Point2, b2: Point2) -> Result<Vec<usize>, RouteError> {
665        match self.kinds[i] {
666            Kind::Vertex => self.graph.cone_states(i, self.nodes[i], b1, b2),
667            Kind::Interval { .. } => Ok(self.graph.states(i).collect()),
668        }
669    }
670
671    /// The segment a node stands for: its interval, or the vertex twice.
672    fn span(&self, i: usize) -> (Point2, Point2) {
673        match self.kinds[i] {
674            Kind::Vertex => (self.nodes[i], self.nodes[i]),
675            Kind::Interval { a, b, .. } => (a, b),
676        }
677    }
678
679    /// The line both spans lie on, if they share a cost edge's line.
680    fn shared_line(&self, i: usize, j: usize) -> u32 {
681        self.lines[i]
682            .iter()
683            .find(|l| self.lines[j].contains(l))
684            .copied()
685            .unwrap_or(NO_LINE)
686    }
687
688    /// A lower bound on the cost of a straight piece from a point of span
689    /// `(a1, a2)` to a point of span `(b1, b2)`, or `None` when an obstacle
690    /// certainly blocks every such piece. Spans on one line bound the
691    /// piece by the weighted length of the gap between them, however many
692    /// cells it crosses; any other piece stays in one cell and crosses no
693    /// cost edge.
694    fn hop(
695        &self,
696        (a1, a2): (Point2, Point2),
697        (b1, b2): (Point2, Point2),
698        ka: Kind,
699        kb: Kind,
700    ) -> Result<Option<f64>, RouteError> {
701        let (ea, eb) = (vertex_ends(ka), vertex_ends(kb));
702        let blocks = |p: Point2, q: Point2| -> Result<bool, RouteError> {
703            for (u, skip_u) in [(a1, ea.0), (a2, ea.1)] {
704                for (v, skip_v) in [(b1, eb.0), (b2, eb.1)] {
705                    if !crosses(u, v, p, q)? && !((skip_u || skip_v) && through_end(u, v, p, q)?) {
706                        return Ok(false);
707                    }
708                }
709            }
710            Ok(true)
711        };
712        for &(p, q) in &self.obstacles {
713            if blocks(p, q)? {
714                return Ok(None);
715            }
716        }
717        if convex_hull(&[a1, a2, b1, b2])?.is_none() {
718            // On one line: at least the weighted length of the gap between
719            // the spans, which every such piece covers, whatever it runs
720            // along.
721            let Some((u, v)) = gap((a1, a2), (b1, b2)) else {
722                return Ok(Some(0.0));
723            };
724            return Ok(Some(self.weights.segment(u, v, self.scale)?.0));
725        }
726        // A piece on an interval's own line never crosses a piece from it;
727        // its interpolated ends may lie a rounding off that line, beyond.
728        let own = |kind: Kind| match kind {
729            Kind::Interval { line, .. } => line,
730            Kind::Vertex => NO_LINE,
731        };
732        let (la, lb) = (own(ka), own(kb));
733        for piece in &self.weights.pieces {
734            if piece.line != la && piece.line != lb && blocks(piece.p, piece.q)? {
735                return Ok(None);
736            }
737        }
738        // A piece from inside an interval leaves it into the side the
739        // other span lies on, into the cell there; one from the interval's
740        // end, if a vertex, is the vertex's hop.
741        let factor = self
742            .weights
743            .hull_factor(&[a1, a2, b1, b2])?
744            .max(leaving(ka, (b1, b2), eb)?)
745            .max(leaving(kb, (a1, a2), ea)?);
746        let d = span_distance((a1, a2), (b1, b2))?;
747        Ok(Some(round_down(factor * d, self.scale)))
748    }
749
750    /// Which side of interval node `k`'s line node `n`'s span lies
751    /// strictly on: `LEFT`, `RIGHT`, or `NONE` when it touches the line or
752    /// `k` is a vertex. An end of `n`'s interval that is a vertex does not
753    /// count: a walk through it has its event at the vertex's own node.
754    fn side_of(&self, k: usize, n: usize) -> Result<u8, RouteError> {
755        let (u, v) = self.span(n);
756        let ends = match self.kinds[n] {
757            Kind::Interval {
758                a_vertex, b_vertex, ..
759            } => (a_vertex, b_vertex),
760            Kind::Vertex => (false, false),
761        };
762        self.side_of_span(k, (u, v), ends)
763    }
764
765    fn side_of_span(
766        &self,
767        k: usize,
768        (u, v): (Point2, Point2),
769        (skip_u, skip_v): (bool, bool),
770    ) -> Result<u8, RouteError> {
771        let Kind::Interval { p, q, .. } = self.kinds[k] else {
772            return Ok(NONE);
773        };
774        let (su, sv) = (side(p, q, u)?, side(p, q, v)?);
775        let su = if su == Sign::Zero && skip_u { sv } else { su };
776        let sv = if sv == Sign::Zero && skip_v { su } else { sv };
777        Ok(match (su, sv) {
778            (Sign::Positive, Sign::Positive) => LEFT,
779            (Sign::Negative, Sign::Negative) => RIGHT,
780            _ => NONE,
781        })
782    }
783
784    /// The edge from node `n` (its span `sn`) into node `k`, with the
785    /// sides it leaves `n` and arrives at `k` on.
786    fn edge(&self, n: usize, k: usize, from: usize, weight: f64) -> Result<Edge, RouteError> {
787        let line = self.shared_line(n, k);
788        let (leave, arrive) = if line == NO_LINE {
789            (self.side_of(n, k)?, self.side_of(k, n)?)
790        } else {
791            (NONE, NONE)
792        };
793        let same_piece = match (self.kinds[n], self.kinds[k]) {
794            (Kind::Interval { piece: pn, .. }, Kind::Interval { piece: pk, .. }) => pn == pk,
795            _ => false,
796        };
797        Ok(Edge {
798            from,
799            weight,
800            line,
801            leave,
802            arrive,
803            same_piece,
804        })
805    }
806
807    /// Lower-bound Dijkstra: exact vertex-to-vertex edges costing their
808    /// weighted length rounded down, and hops to and between intervals,
809    /// over states that remember the first hop's line and side (see
810    /// [`allowed`]).
811    fn lower_bounds(&mut self, sources: &[(usize, f64)]) -> Result<(), RouteError> {
812        let states = self.graph.adjacency.len();
813        let mut incoming: Vec<Vec<Edge>> = vec![Vec::new(); states];
814        let is_vertex = |i: usize, kinds: &[Kind]| matches!(kinds[i], Kind::Vertex);
815        let mut cost: HashMap<(usize, usize), f64> = HashMap::new();
816        for (u, edges) in self.graph.adjacency.iter().enumerate() {
817            let i = self.graph.node(u);
818            if !is_vertex(i, &self.kinds) {
819                continue;
820            }
821            for &(w, _) in edges {
822                let j = self.graph.node(w);
823                if !is_vertex(j, &self.kinds) {
824                    continue;
825                }
826                let key = (i.min(j), i.max(j));
827                let c = match cost.get(&key) {
828                    Some(c) => *c,
829                    None => {
830                        let c = self
831                            .weights
832                            .segment(self.nodes[i], self.nodes[j], self.scale)?
833                            .0;
834                        cost.insert(key, c);
835                        c
836                    }
837                };
838                // Travelled from `u` into `w`.
839                incoming[w].push(self.edge(i, j, u, c)?);
840            }
841        }
842        let n = self.nodes.len();
843        for i in 0..n {
844            for j in i + 1..n {
845                if is_vertex(i, &self.kinds) && is_vertex(j, &self.kinds) {
846                    continue;
847                }
848                let (si, sj) = (self.span(i), self.span(j));
849                let Some(c) = self.hop(si, sj, self.kinds[i], self.kinds[j])? else {
850                    continue;
851                };
852                let from = self.hop_states(i, sj.0, sj.1)?;
853                let to = self.hop_states(j, si.0, si.1)?;
854                for &u in &from {
855                    for &w in &to {
856                        incoming[w].push(self.edge(i, j, u, c)?);
857                        incoming[u].push(self.edge(j, i, w, c)?);
858                    }
859                }
860            }
861        }
862        let graph = &self.graph;
863        let kinds = &self.kinds;
864        self.lower = tagged_dijkstra(&incoming, sources, |s| kinds[graph.node(s)]);
865        Ok(())
866    }
867
868    /// The least upper bound from `point` and the first state on the way.
869    fn upper_at(&self, point: Point2) -> Result<Option<(f64, usize)>, RouteError> {
870        let mut best: Option<(f64, usize)> = None;
871        let offer = |value: f64, state: usize, best: &mut Option<(f64, usize)>| {
872            if best.is_none_or(|(b, s)| value < b || (value == b && state < s)) {
873                *best = Some((value, state));
874            }
875        };
876        for (i, node) in self.nodes.iter().enumerate() {
877            let nearest = self
878                .graph
879                .states(i)
880                .map(|s| self.upper[s])
881                .fold(f64::INFINITY, f64::min);
882            if nearest.is_infinite() {
883                continue;
884            }
885            if *node == point {
886                for s in self.graph.states(i) {
887                    offer(self.upper[s], s, &mut best);
888                }
889                continue;
890            }
891            // A leg costs at least its length.
892            if best.is_some_and(|(b, _)| (*node - point).length() + nearest > b) {
893                continue;
894            }
895            let ok = crate::graph::sides(
896                point,
897                *node,
898                &self.region,
899                &self.obstacles,
900                &self.nodes,
901                &self.graph.stars,
902                &self.graph.rayed,
903            )?;
904            if ok == [false, false] {
905                continue;
906            }
907            let leg = self.weights.segment(point, *node, self.scale)?.1;
908            for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
909                if !ok[k] {
910                    continue;
911                }
912                if let Some(s) = self.graph.arrival(i, point, on)? {
913                    if self.upper[s].is_finite() {
914                        offer(leg + self.upper[s], s, &mut best);
915                    }
916                }
917            }
918        }
919        Ok(best)
920    }
921
922    /// The least lower bound from `point`: over its first piece to a node
923    /// and the node's bound, the piece exact to a vertex, fattened to an
924    /// interval. Starts from `ceiling`, a value no less than the answer.
925    fn lower_at(&self, point: Point2, ceiling: f64) -> Result<f64, RouteError> {
926        let mut best = ceiling;
927        let point_lines = self.weights.lines_through(point)?;
928        for (i, node) in self.nodes.iter().enumerate() {
929            let least = self
930                .graph
931                .states(i)
932                .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
933                .fold(f64::INFINITY, f64::min);
934            let kind = self.kinds[i];
935            if least.is_infinite() {
936                continue;
937            }
938            let line = self.lines[i]
939                .iter()
940                .find(|l| point_lines.contains(l))
941                .copied()
942                .unwrap_or(NO_LINE);
943            // The first hop, as an edge into the node: the states it may
944            // go on from.
945            let first = Edge {
946                from: usize::MAX,
947                weight: 0.0,
948                line,
949                leave: NONE,
950                arrive: if line == NO_LINE {
951                    self.side_of_span(i, (point, point), (false, false))?
952                } else {
953                    NONE
954                },
955                same_piece: false,
956            };
957            let from = |states: &mut dyn Iterator<Item = usize>| -> f64 {
958                states
959                    .flat_map(|s| self.lower[s].iter())
960                    .filter(|(tag, _)| allowed(kind, *tag, &first))
961                    .map(|(_, d)| *d)
962                    .fold(f64::INFINITY, f64::min)
963            };
964            if *node == point {
965                // The walk starts here: any way on.
966                let any = self
967                    .graph
968                    .states(i)
969                    .flat_map(|s| self.lower[s].iter().map(|(_, d)| *d))
970                    .fold(f64::INFINITY, f64::min);
971                best = best.min(any);
972                continue;
973            }
974            let (a, b) = self.span(i);
975            if span_distance((point, point), (a, b))? + least >= best {
976                continue;
977            }
978            match self.kinds[i] {
979                Kind::Vertex => {
980                    let ok = crate::graph::sides(
981                        point,
982                        *node,
983                        &self.region,
984                        &self.obstacles,
985                        &self.nodes,
986                        &self.graph.stars,
987                        &self.graph.rayed,
988                    )?;
989                    if ok == [false, false] {
990                        continue;
991                    }
992                    let leg = self.weights.segment(point, *node, self.scale)?.0;
993                    for (k, on) in [Side::Left, Side::Right].into_iter().enumerate() {
994                        if !ok[k] {
995                            continue;
996                        }
997                        if let Some(s) = self.graph.arrival(i, point, on)? {
998                            best = best.min(leg + from(&mut core::iter::once(s)));
999                        }
1000                    }
1001                }
1002                Kind::Interval { .. } => {
1003                    let Some(leg) =
1004                        self.hop((point, point), (a, b), Kind::Vertex, self.kinds[i])?
1005                    else {
1006                        continue;
1007                    };
1008                    best = best.min(leg + from(&mut self.graph.states(i)));
1009                }
1010            }
1011        }
1012        Ok(best)
1013    }
1014}
1015
1016/// Dijkstra with a binary heap from several sources, each at its start
1017/// distance: distance and the previous state. Ties break on the lowest
1018/// state.
1019fn dijkstra(adjacency: &[Vec<(usize, f64)>], sources: &[(usize, f64)]) -> (Vec<f64>, Vec<usize>) {
1020    let n = adjacency.len();
1021    let mut distance = vec![f64::INFINITY; n];
1022    let mut previous = vec![usize::MAX; n];
1023    let mut heap = BinaryHeap::new();
1024    for &(s, start) in sources {
1025        if start < distance[s] {
1026            distance[s] = start;
1027            heap.push(Reverse((Ordered(start), s)));
1028        }
1029    }
1030    while let Some(Reverse((Ordered(d), u))) = heap.pop() {
1031        if d > distance[u] {
1032            continue;
1033        }
1034        for &(w, c) in &adjacency[u] {
1035            let candidate = d + c;
1036            if candidate < distance[w] || (candidate == distance[w] && u < previous[w]) {
1037                if candidate < distance[w] {
1038                    heap.push(Reverse((Ordered(candidate), w)));
1039                }
1040                distance[w] = candidate;
1041                previous[w] = u;
1042            }
1043        }
1044    }
1045    (distance, previous)
1046}
1047
1048/// No side, or unknown.
1049const NONE: u8 = 0;
1050const LEFT: u8 = 1;
1051const RIGHT: u8 = 2;
1052/// A first hop along the interval's piece, the next node's first hop
1053/// leaving on no known side, or on the left or right.
1054const ALONG: u8 = 3;
1055
1056/// How a path leaves a state toward the targets: the line of its first hop
1057/// if that runs along a cost line, and at an interval the side it leaves
1058/// on (`LEFT`, `RIGHT`), or `ALONG + side` for a first hop along the
1059/// interval's own piece followed by one leaving on `side`.
1060#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1061struct Tag {
1062    line: u32,
1063    side: u8,
1064}
1065
1066/// An edge travelled from state `from` into the state it is listed under.
1067#[derive(Debug, Clone, Copy)]
1068struct Edge {
1069    from: usize,
1070    weight: f64,
1071    /// The cost line it runs along, or `NO_LINE`.
1072    line: u32,
1073    /// The side of the source interval's line it leaves on.
1074    leave: u8,
1075    /// The side of the destination interval's line it arrives from.
1076    arrive: u8,
1077    /// Along one cost piece, from one of its intervals to another.
1078    same_piece: bool,
1079}
1080
1081/// Whether a path may arrive at a node of `kind` by `edge` and go on as
1082/// `tag` says. An optimal walk never takes two pieces along one line in a
1083/// row (they would be one piece), never touches a cost edge and turns
1084/// back to the side it came from (the chord is shorter), and never runs
1085/// along a cost piece between two such touches on one side unless along
1086/// is cheaper than that side (else the chord is no dearer).
1087fn allowed(kind: Kind, tag: Tag, edge: &Edge) -> bool {
1088    if edge.line != NO_LINE && tag.line == edge.line {
1089        return false;
1090    }
1091    if let Kind::Interval {
1092        left, right, along, ..
1093    } = kind
1094    {
1095        if edge.line == NO_LINE && edge.arrive != NONE {
1096            let factor = if edge.arrive == LEFT { left } else { right };
1097            if tag.side == edge.arrive {
1098                return false;
1099            }
1100            if tag.side > ALONG && tag.side - ALONG == edge.arrive && along >= factor {
1101                return false;
1102            }
1103        }
1104    }
1105    true
1106}
1107
1108/// The tag of the source state of `edge`, whose destination went on as
1109/// `tag`.
1110fn tag_before(source: Kind, tag: Tag, edge: &Edge) -> Tag {
1111    let side = match source {
1112        Kind::Vertex => NONE,
1113        Kind::Interval { .. } if edge.line != NO_LINE => {
1114            if edge.same_piece && (tag.side == LEFT || tag.side == RIGHT) {
1115                ALONG + tag.side
1116            } else {
1117                ALONG
1118            }
1119        }
1120        Kind::Interval { .. } => edge.leave,
1121    };
1122    Tag {
1123        line: edge.line,
1124        side,
1125    }
1126}
1127
1128/// Dijkstra backward from the targets over (state, tag), taking only the
1129/// transitions [`allowed`] permits. Per state, the settled distances by
1130/// tag.
1131fn tagged_dijkstra(
1132    incoming: &[Vec<Edge>],
1133    sources: &[(usize, f64)],
1134    kind: impl Fn(usize) -> Kind,
1135) -> Vec<Vec<(Tag, f64)>> {
1136    let mut settled: Vec<Vec<(Tag, f64)>> = vec![Vec::new(); incoming.len()];
1137    let mut best: HashMap<(usize, Tag), f64> = HashMap::new();
1138    let mut heap = BinaryHeap::new();
1139    let start = Tag {
1140        line: NO_LINE,
1141        side: NONE,
1142    };
1143    for &(s, weight) in sources {
1144        if best.get(&(s, start)).is_none_or(|b| weight < *b) {
1145            best.insert((s, start), weight);
1146            heap.push(Reverse((Ordered(weight), s, start)));
1147        }
1148    }
1149    while let Some(Reverse((Ordered(d), k, tag))) = heap.pop() {
1150        if settled[k].iter().any(|(t, _)| *t == tag) {
1151            continue;
1152        }
1153        settled[k].push((tag, d));
1154        let here = kind(k);
1155        for edge in &incoming[k] {
1156            if !allowed(here, tag, edge) {
1157                continue;
1158            }
1159            let before = tag_before(kind(edge.from), tag, edge);
1160            let candidate = d + edge.weight;
1161            let key = (edge.from, before);
1162            if best.get(&key).is_none_or(|b| candidate < *b) {
1163                best.insert(key, candidate);
1164                heap.push(Reverse((Ordered(candidate), edge.from, before)));
1165            }
1166        }
1167    }
1168    settled
1169}
1170
1171/// A total order on finite and infinite floats for the heaps.
1172#[derive(Debug, Clone, Copy, PartialEq)]
1173struct Ordered(f64);
1174
1175impl Eq for Ordered {}
1176
1177impl PartialOrd for Ordered {
1178    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1179        Some(self.cmp(other))
1180    }
1181}
1182
1183impl Ord for Ordered {
1184    fn cmp(&self, other: &Self) -> Ordering {
1185        self.0.total_cmp(&other.0)
1186    }
1187}
1188
1189/// Round a nonnegative length down past the error of the few operations
1190/// that produced it.
1191fn round_down(value: f64, scale: f64) -> f64 {
1192    (value * (1.0 - 16.0 * f64::EPSILON) - 16.0 * f64::EPSILON * scale).max(0.0)
1193}
1194
1195fn round_up(value: f64, scale: f64) -> f64 {
1196    value * (1.0 + 16.0 * f64::EPSILON) + 16.0 * f64::EPSILON * scale
1197}
1198
1199/// The factor a piece from inside an interval node's span toward the span
1200/// `other` must pay: that of the side of the interval `other` lies
1201/// strictly on, or the lesser when it touches the interval's line. 1 for
1202/// a vertex.
1203fn leaving(
1204    kind: Kind,
1205    (o1, o2): (Point2, Point2),
1206    (skip1, skip2): (bool, bool),
1207) -> Result<f64, RouteError> {
1208    let Kind::Interval {
1209        p, q, left, right, ..
1210    } = kind
1211    else {
1212        return Ok(1.0);
1213    };
1214    let (s1, s2) = (side(p, q, o1)?, side(p, q, o2)?);
1215    // An end of the other span that is a vertex does not count.
1216    let s1 = if s1 == Sign::Zero && skip1 { s2 } else { s1 };
1217    let s2 = if s2 == Sign::Zero && skip2 { s1 } else { s2 };
1218    Ok(match (s1, s2) {
1219        (Sign::Positive, Sign::Positive) => left,
1220        (Sign::Negative, Sign::Negative) => right,
1221        _ => left.min(right),
1222    })
1223}
1224
1225/// Which ends of a node's span are vertices, whose walks the vertices'
1226/// own nodes carry.
1227fn vertex_ends(kind: Kind) -> (bool, bool) {
1228    match kind {
1229        Kind::Interval {
1230            a_vertex, b_vertex, ..
1231        } => (a_vertex, b_vertex),
1232        Kind::Vertex => (false, false),
1233    }
1234}
1235
1236/// Whether the segment `u`-`v` passes through an end of the segment
1237/// `p`-`q`, with neither of its own ends on that segment's line: a touch
1238/// that still separates the points just beside it, when every other corner
1239/// segment crosses properly.
1240fn through_end(u: Point2, v: Point2, p: Point2, q: Point2) -> Result<bool, RouteError> {
1241    if side(p, q, u)? == Sign::Zero || side(p, q, v)? == Sign::Zero {
1242        return Ok(false);
1243    }
1244    for e in [p, q] {
1245        if within(u, v, e) && side(u, v, e)? == Sign::Zero {
1246            return Ok(true);
1247        }
1248    }
1249    Ok(false)
1250}
1251
1252/// The gap between two spans on one line: the facing ends, or `None`
1253/// when the spans overlap.
1254fn gap((a1, a2): (Point2, Point2), (b1, b2): (Point2, Point2)) -> Option<(Point2, Point2)> {
1255    let d = if a1 != a2 { a2 - a1 } else { b2 - b1 };
1256    let key = |p: Point2| (p - a1).dot(d);
1257    let (alo, ahi) = if key(a1) <= key(a2) {
1258        (a1, a2)
1259    } else {
1260        (a2, a1)
1261    };
1262    let (blo, bhi) = if key(b1) <= key(b2) {
1263        (b1, b2)
1264    } else {
1265        (b2, b1)
1266    };
1267    if key(ahi) < key(blo) {
1268        Some((ahi, blo))
1269    } else if key(bhi) < key(alo) {
1270        Some((bhi, alo))
1271    } else {
1272        None
1273    }
1274}
1275
1276/// The least distance between two closed segments (either may be a
1277/// point): zero when they meet, decided exactly.
1278pub(crate) fn span_distance(
1279    (a1, a2): (Point2, Point2),
1280    (b1, b2): (Point2, Point2),
1281) -> Result<f64, RouteError> {
1282    if segments_meet(a1, a2, b1, b2)? {
1283        return Ok(0.0);
1284    }
1285    Ok(point_segment(a1, b1, b2)
1286        .min(point_segment(a2, b1, b2))
1287        .min(point_segment(b1, a1, a2))
1288        .min(point_segment(b2, a1, a2)))
1289}
1290
1291fn point_segment(v: Point2, a: Point2, b: Point2) -> f64 {
1292    let d = b - a;
1293    let length2 = d.dot(d);
1294    let s = if length2 > 0.0 {
1295        ((v - a).dot(d) / length2).clamp(0.0, 1.0)
1296    } else {
1297        0.0
1298    };
1299    (v - (a + d * s)).length()
1300}
1301
1302/// A piece of cost edge: part of one polygon's ring, cut at every vertex
1303/// lying on it, with the polygon and which side its inside lies on.
1304#[derive(Debug, Clone, Copy)]
1305struct Piece {
1306    p: Point2,
1307    q: Point2,
1308    polygon: usize,
1309    /// The polygon's inside lies left of `p` to `q`.
1310    inside_left: bool,
1311    /// Index of the line the piece lies on, shared by collinear pieces.
1312    line: u32,
1313}
1314
1315/// The cost polygons, their edges and the lines those lie on.
1316#[derive(Debug, Clone)]
1317struct Weights {
1318    polygons: Vec<Polygon>,
1319    factors: Vec<f64>,
1320    pieces: Vec<Piece>,
1321    /// Two points on each line cost pieces lie on.
1322    lines: Vec<(Point2, Point2)>,
1323    /// Region boundary edges, with whether the region lies left of them:
1324    /// along one, only that side is free.
1325    walls: Vec<(Point2, Point2, bool)>,
1326    greatest: f64,
1327}
1328
1329impl Weights {
1330    fn new(costs: &[CostRegion], cuts: &[Point2], region: &[Polygon]) -> Result<Self, RouteError> {
1331        let mut walls = Vec::new();
1332        for polygon in region {
1333            for (hole, ring) in core::iter::once((false, &polygon.outer))
1334                .chain(polygon.holes.iter().map(|h| (true, h)))
1335            {
1336                let left = region_left(ring, hole)?;
1337                for (p, q) in ring_edges(ring) {
1338                    walls.push((p, q, left));
1339                }
1340            }
1341        }
1342        let polygons: Vec<Polygon> = costs.iter().map(|c| c.polygon.clone()).collect();
1343        let factors: Vec<f64> = costs.iter().map(|c| c.factor).collect();
1344        let mut vertices: Vec<Point2> = cuts.to_vec();
1345        for polygon in &polygons {
1346            for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1347                vertices.extend(ring.points.iter().copied());
1348            }
1349        }
1350        dedup_points(&mut vertices);
1351        let mut pieces = Vec::new();
1352        let mut lines: Vec<(Point2, Point2)> = Vec::new();
1353        for (index, polygon) in polygons.iter().enumerate() {
1354            for (hole, ring) in core::iter::once((false, &polygon.outer))
1355                .chain(polygon.holes.iter().map(|h| (true, h)))
1356            {
1357                let left = region_left(ring, hole)?;
1358                for (p, q) in ring_edges(ring) {
1359                    if p == q {
1360                        continue;
1361                    }
1362                    let d = q - p;
1363                    let mut cuts = vec![p, q];
1364                    for &v in &vertices {
1365                        if v != p && v != q && within(p, q, v) && side(p, q, v)? == Sign::Zero {
1366                            cuts.push(v);
1367                        }
1368                    }
1369                    cuts.sort_by(|u, v| (*u - p).dot(d).total_cmp(&(*v - p).dot(d)));
1370                    let mut line = NO_LINE;
1371                    for (k, &(r, s)) in lines.iter().enumerate() {
1372                        if side(r, s, p)? == Sign::Zero && side(r, s, q)? == Sign::Zero {
1373                            line = k as u32;
1374                            break;
1375                        }
1376                    }
1377                    if line == NO_LINE {
1378                        line = lines.len() as u32;
1379                        lines.push((p, q));
1380                    }
1381                    for pair in cuts.windows(2) {
1382                        if pair[0] != pair[1] {
1383                            pieces.push(Piece {
1384                                p: pair[0],
1385                                q: pair[1],
1386                                polygon: index,
1387                                inside_left: left,
1388                                line,
1389                            });
1390                        }
1391                    }
1392                }
1393            }
1394        }
1395        let greatest = factors.iter().copied().fold(1.0, f64::max);
1396        Ok(Self {
1397            polygons,
1398            factors,
1399            pieces,
1400            lines,
1401            walls,
1402            greatest,
1403        })
1404    }
1405
1406    /// Refuse a cost edge that properly crosses an obstacle or another
1407    /// cost edge, or runs along or through a barrier. A crossing of a
1408    /// region edge within `reach` of an end of either edge is a touch
1409    /// rounding moved (see [`touch_walls`]), and stands.
1410    fn check_crossings(
1411        &self,
1412        obstacles: &[(Point2, Point2)],
1413        walls: &[(Point2, Point2)],
1414        reach: f64,
1415    ) -> Result<(), MapError> {
1416        for piece in &self.pieces {
1417            let index = piece.polygon;
1418            for &(r, s) in obstacles {
1419                if crosses(piece.p, piece.q, r, s)? {
1420                    let region_edge = self
1421                        .walls
1422                        .iter()
1423                        .any(|&(p, q, _)| (p, q) == (r, s) || (p, q) == (s, r));
1424                    let near = point_segment(piece.p, r, s) <= reach
1425                        || point_segment(piece.q, r, s) <= reach
1426                        || point_segment(r, piece.p, piece.q) <= reach
1427                        || point_segment(s, piece.p, piece.q) <= reach;
1428                    if region_edge && near {
1429                        continue;
1430                    }
1431                    return Err(MapError::CostCrossing { index });
1432                }
1433            }
1434            for &(r, s) in walls {
1435                let collinear =
1436                    side(r, s, piece.p)? == Sign::Zero && side(r, s, piece.q)? == Sign::Zero;
1437                if collinear && segments_meet(piece.p, piece.q, r, s)? {
1438                    let overlap = [piece.p, piece.q, r, s]
1439                        .iter()
1440                        .filter(|v| within(r, s, **v) && within(piece.p, piece.q, **v))
1441                        .count();
1442                    if overlap >= 2 {
1443                        return Err(MapError::CostCrossing { index });
1444                    }
1445                }
1446            }
1447            for other in &self.pieces {
1448                if crosses(piece.p, piece.q, other.p, other.q)? {
1449                    return Err(MapError::CostCrossing { index });
1450                }
1451            }
1452        }
1453        Ok(())
1454    }
1455
1456    /// The factors just left and right of the stretch `a`-`b` of the cost
1457    /// piece `p`-`q`, which no vertex interrupts. Which edges the stretch
1458    /// runs along is decided on the piece's own exact ends: `a` and `b`
1459    /// are interpolated, and on an edge at an angle rounding puts them off
1460    /// its line.
1461    fn sides(&self, p: Point2, q: Point2, a: Point2, b: Point2) -> Result<(f64, f64), RouteError> {
1462        let m = Point2::new(0.5 * a.x + 0.5 * b.x, 0.5 * a.y + 0.5 * b.y);
1463        let d = q - p;
1464        let (a, b) = (p, q);
1465        let (mut left, mut right) = (1.0f64, 1.0f64);
1466        for (index, polygon) in self.polygons.iter().enumerate() {
1467            let factor = self.factors[index];
1468            let mut on = false;
1469            for piece in self.pieces.iter().filter(|p| p.polygon == index) {
1470                if side(piece.p, piece.q, a)? == Sign::Zero
1471                    && side(piece.p, piece.q, b)? == Sign::Zero
1472                    && within(piece.p, piece.q, m)
1473                {
1474                    on = true;
1475                    let same = (piece.q - piece.p).dot(d) > 0.0;
1476                    if piece.inside_left == same {
1477                        left = left.max(factor);
1478                    } else {
1479                        right = right.max(factor);
1480                    }
1481                }
1482            }
1483            if !on && in_polygon(polygon, m)? {
1484                left = left.max(factor);
1485                right = right.max(factor);
1486            }
1487        }
1488        Ok((left, right))
1489    }
1490
1491    /// Whether the stretch `p`-`q` lies within `reach` of one region edge,
1492    /// on or beyond it: out of the free space, a cost edge touching the
1493    /// wall (see [`touch_walls`]) that no walk crosses, only runs along.
1494    fn beyond_wall(&self, p: Point2, q: Point2, reach: f64) -> Result<bool, RouteError> {
1495        for &(r, s, left) in &self.walls {
1496            let free = if left { Sign::Positive } else { Sign::Negative };
1497            if point_segment(p, r, s) <= reach
1498                && point_segment(q, r, s) <= reach
1499                && side(r, s, p)? != free
1500                && side(r, s, q)? != free
1501            {
1502                return Ok(true);
1503            }
1504        }
1505        Ok(false)
1506    }
1507
1508    /// The lines of cost pieces that pass through `v`.
1509    fn lines_through(&self, v: Point2) -> Result<Vec<u32>, RouteError> {
1510        let mut out = Vec::new();
1511        for (k, &(p, q)) in self.lines.iter().enumerate() {
1512            if side(p, q, v)? == Sign::Zero {
1513                out.push(k as u32);
1514            }
1515        }
1516        Ok(out)
1517    }
1518
1519    /// The greatest factor over the polygons holding the whole convex hull
1520    /// of `corners`, and 1 if none does; 1 for a hull with no interior.
1521    fn hull_factor(&self, corners: &[Point2]) -> Result<f64, RouteError> {
1522        let Some(hull) = convex_hull(corners)? else {
1523            return Ok(1.0);
1524        };
1525        let mut best = 1.0f64;
1526        for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1527            if factor <= best {
1528                continue;
1529            }
1530            if holds(polygon, &hull)? {
1531                best = factor;
1532            }
1533        }
1534        Ok(best)
1535    }
1536
1537    /// The greatest factor over the polygons meeting the closed triangle,
1538    /// and 1: the most a metre can cost in it.
1539    fn steepest(&self, t: &[Point2; 3]) -> Result<f64, RouteError> {
1540        let mut best = 1.0f64;
1541        for (polygon, &factor) in self.polygons.iter().zip(&self.factors) {
1542            if factor <= best {
1543                continue;
1544            }
1545            let meets = t.iter().try_fold(false, |m, c| {
1546                Ok::<_, RouteError>(m || in_polygon(polygon, *c)?)
1547            })? || core::iter::once(&polygon.outer)
1548                .chain(polygon.holes.iter())
1549                .flat_map(ring_edges)
1550                .try_fold(false, |m, (p, q)| {
1551                    Ok::<_, RouteError>(m || meets_triangle(p, q, t)?)
1552                })?;
1553            if meets {
1554                best = factor;
1555            }
1556        }
1557        Ok(best)
1558    }
1559
1560    /// The weighted length of the segment `a`-`b`, bracketed: the factor
1561    /// is decided piece by piece between the points where the segment
1562    /// meets cost edges. Along a cost edge the cheaper side counts. A piece
1563    /// whose factor rounding could misjudge -- shorter than a few ulps, or
1564    /// whose middle lies within rounding of a cost edge -- counts 1 below
1565    /// and the greatest factor above, with two exceptions for a piece
1566    /// within rounding of a cost edge all along. Above, it counts as the
1567    /// walk along that edge, which is off it by no more than rounding:
1568    /// the upper end bounds a walk that close to the segment, not the
1569    /// segment itself. Below, a piece exactly along a region edge, the
1570    /// cost edge on or beyond it, counts its free side's factor.
1571    fn segment(&self, a: Point2, b: Point2, scale: f64) -> Result<(f64, f64), RouteError> {
1572        let length = (b - a).length();
1573        if length == 0.0 {
1574            return Ok((0.0, 0.0));
1575        }
1576        if self.polygons.is_empty() {
1577            return Ok((round_down(length, scale), round_up(length, scale)));
1578        }
1579        let d = b - a;
1580        let param = |v: Point2| (v - a).dot(d) / d.dot(d);
1581        let mut breaks = vec![0.0, 1.0];
1582        // Stretches along cost pieces: (from, to, polygon, inside left of a-b).
1583        let mut along: Vec<(f64, f64, usize, bool)> = Vec::new();
1584        // Pieces not along `a`-`b`: (p, q, polygon, inside left of p-q).
1585        let mut near: Vec<(Point2, Point2, usize, bool)> = Vec::new();
1586        // Stretches along region edges: (from, to, region left of a-b).
1587        let mut walled: Vec<(f64, f64, bool)> = Vec::new();
1588        // Region edges not along `a`-`b`: (p, q, region left of p-q).
1589        let mut near_walls: Vec<(Point2, Point2, bool)> = Vec::new();
1590        for &(p, q, left) in &self.walls {
1591            if p != q && side(a, b, p)? == Sign::Zero && side(a, b, q)? == Sign::Zero {
1592                let (tp, tq) = (param(p), param(q));
1593                let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1594                if hi > lo {
1595                    walled.push((lo, hi, left == ((q - p).dot(d) > 0.0)));
1596                    breaks.extend([lo, hi]);
1597                }
1598            } else if p != q {
1599                near_walls.push((p, q, left));
1600            }
1601        }
1602        for piece in &self.pieces {
1603            let (sp, sq) = (side(a, b, piece.p)?, side(a, b, piece.q)?);
1604            if sp == Sign::Zero && sq == Sign::Zero {
1605                let (tp, tq) = (param(piece.p), param(piece.q));
1606                let (lo, hi) = (tp.min(tq).max(0.0), tp.max(tq).min(1.0));
1607                if hi > lo {
1608                    let same = (piece.q - piece.p).dot(d) > 0.0;
1609                    along.push((lo, hi, piece.polygon, piece.inside_left == same));
1610                    breaks.extend([lo, hi]);
1611                }
1612                continue;
1613            }
1614            near.push((piece.p, piece.q, piece.polygon, piece.inside_left));
1615            let (sa, sb) = (side(piece.p, piece.q, a)?, side(piece.p, piece.q, b)?);
1616            if sp != sq && sa != sb {
1617                // The lines cross between the ends of both: at the
1618                // parameter where `a`-`b` meets the piece's line.
1619                let e = piece.q - piece.p;
1620                let denom = d.perp_dot(e);
1621                if denom != 0.0 {
1622                    let t = (piece.p - a).perp_dot(e) / denom;
1623                    if t > 0.0 && t < 1.0 {
1624                        breaks.push(t);
1625                    }
1626                }
1627            }
1628        }
1629        breaks.sort_by(f64::total_cmp);
1630        breaks.dedup();
1631        let (mut lower, mut upper) = (0.0, 0.0);
1632        let tiny = 64.0 * f64::EPSILON * scale;
1633        let mut hugged = false;
1634        for pair in breaks.windows(2) {
1635            let (t0, t1) = (pair[0], pair[1]);
1636            let stretch = (t1 - t0) * length;
1637            if stretch <= 0.0 {
1638                continue;
1639            }
1640            let tm = 0.5 * t0 + 0.5 * t1;
1641            let at = |t: f64| Point2::new(a.x + d.x * t, a.y + d.y * t);
1642            let m = at(tm);
1643            // Along cost pieces: polygon and whether its inside lies left
1644            // of `a`-`b`.
1645            let mut on: Vec<(usize, bool)> = along
1646                .iter()
1647                .filter(|(lo, hi, ..)| *lo <= tm && tm <= *hi)
1648                .map(|(_, _, p, l)| (*p, *l))
1649                .collect();
1650            // Along region edges: whether the region lies left of `a`-`b`.
1651            let mut free: Vec<bool> = walled
1652                .iter()
1653                .filter(|(lo, hi, _)| *lo <= tm && tm <= *hi)
1654                .map(|(_, _, l)| *l)
1655                .collect();
1656            let doubtful = stretch <= tiny
1657                || near
1658                    .iter()
1659                    .any(|&(p, q, ..)| point_segment(m, p, q) <= tiny);
1660            let mut resolved = !doubtful;
1661            if doubtful {
1662                // A stretch within rounding of a cost piece all along --
1663                // `a`-`b` joins two points interpolated on a cost edge at
1664                // an angle -- may be on either side of it. The walk along
1665                // the piece itself, off the stretch by at most `tiny` at
1666                // each end, costs the piece's along factor: take that one
1667                // instead, above. Otherwise the greatest factor.
1668                let (s0, s1) = (at(t0), at(t1));
1669                let hugs = |p: Point2, q: Point2| {
1670                    point_segment(s0, p, q) <= tiny && point_segment(s1, p, q) <= tiny
1671                };
1672                let mut all_hug = stretch > tiny;
1673                // Along a region edge, with every hugged piece exactly on
1674                // or beyond it -- as a cost edge touching a wall is left
1675                // (see [`touch_walls`]) -- the free side lies on the
1676                // piece's inside side, and its factor is known below too.
1677                let beyond = match (free.contains(&true), free.contains(&false)) {
1678                    (true, false) => Some(Sign::Negative),
1679                    (false, true) => Some(Sign::Positive),
1680                    _ => None,
1681                };
1682                let mut known = beyond.is_some();
1683                for &(p, q, polygon, inside_left) in &near {
1684                    if point_segment(m, p, q) > tiny {
1685                        continue;
1686                    }
1687                    if !hugs(p, q) {
1688                        all_hug = false;
1689                        break;
1690                    }
1691                    if let Some(solid) = beyond {
1692                        for end in [p, q] {
1693                            let s = side(a, b, end)?;
1694                            known &= s == Sign::Zero || s == solid;
1695                        }
1696                    }
1697                    on.push((polygon, inside_left == ((q - p).dot(d) > 0.0)));
1698                }
1699                if !all_hug {
1700                    lower += stretch;
1701                    upper += self.greatest * stretch;
1702                    continue;
1703                }
1704                resolved = known;
1705                if !resolved {
1706                    lower += stretch;
1707                }
1708                for &(p, q, left) in &near_walls {
1709                    if hugs(p, q) {
1710                        free.push(left == ((q - p).dot(d) > 0.0));
1711                    }
1712                }
1713                hugged = true;
1714            }
1715            // Extra factor on each side of the segment at this stretch.
1716            let (mut left, mut right) = (0.0f64, 0.0f64);
1717            for (index, polygon) in self.polygons.iter().enumerate() {
1718                let extra = self.factors[index] - 1.0;
1719                if extra <= left.min(right) {
1720                    continue;
1721                }
1722                let sides: Vec<bool> = on
1723                    .iter()
1724                    .filter(|(p, _)| *p == index)
1725                    .map(|(_, l)| *l)
1726                    .collect();
1727                if sides.is_empty() {
1728                    if in_polygon(polygon, m)? {
1729                        left = left.max(extra);
1730                        right = right.max(extra);
1731                    }
1732                } else {
1733                    // On the polygon's edge: its inside on one side (both,
1734                    // for an edge the polygon has twice).
1735                    if sides.contains(&true) {
1736                        left = left.max(extra);
1737                    }
1738                    if sides.contains(&false) {
1739                        right = right.max(extra);
1740                    }
1741                }
1742            }
1743            // Along a cost edge the cheaper side counts -- of the sides a
1744            // walk can move to: along a wall, only the region's.
1745            let (free_left, free_right) = if free.is_empty() {
1746                (true, true)
1747            } else {
1748                (free.contains(&true), free.contains(&false))
1749            };
1750            let factor = 1.0
1751                + if on.is_empty() {
1752                    left
1753                } else {
1754                    match (free_left, free_right) {
1755                        (true, true) => left.min(right),
1756                        (true, false) => left,
1757                        (false, true) => right,
1758                        (false, false) => left.max(right),
1759                    }
1760                };
1761            if resolved {
1762                lower += factor * stretch;
1763            }
1764            upper += factor * stretch;
1765        }
1766        if hugged {
1767            // The steps between the stretch and the piece it hugs.
1768            upper += 4.0 * self.greatest * tiny * breaks.len() as f64;
1769        }
1770        // Each break's parameter is rounded: a stretch may be off by a few
1771        // ulps of the length at each end.
1772        let slack = (self.greatest - 1.0) * breaks.len() as f64 * 8.0 * f64::EPSILON * length;
1773        Ok((
1774            round_down(lower - slack, scale),
1775            round_up(upper + slack, scale),
1776        ))
1777    }
1778}
1779
1780/// How far off a region edge a cost vertex may lie and still be taken to
1781/// touch it: 2^-24 of the region's extent, four steps of the grid overlay
1782/// results were once rounded to, and a few ulps of its coordinates.
1783fn touch_reach(region: &[Polygon]) -> f64 {
1784    let (mut lo, mut hi) = (
1785        Point2::new(f64::INFINITY, f64::INFINITY),
1786        Point2::new(f64::NEG_INFINITY, f64::NEG_INFINITY),
1787    );
1788    let mut scale = 0.0f64;
1789    for polygon in region {
1790        for p in core::iter::once(&polygon.outer)
1791            .chain(polygon.holes.iter())
1792            .flat_map(|r| r.points.iter())
1793        {
1794            lo = Point2::new(lo.x.min(p.x), lo.y.min(p.y));
1795            hi = Point2::new(hi.x.max(p.x), hi.y.max(p.y));
1796            scale = scale.max(p.x.abs()).max(p.y.abs());
1797        }
1798    }
1799    let extent = (hi.x - lo.x).max(hi.y - lo.y).max(0.0);
1800    extent / f64::from(1u32 << 24) + 64.0 * f64::EPSILON * scale
1801}
1802
1803/// The cost regions with every vertex that lies strictly on the free side
1804/// of a region edge and within `reach` of it moved just across that
1805/// edge's line: a cost region clipped to the free region touches its walls
1806/// only up to rounding, and a vertex left a hair inside would open a
1807/// sliver along the wall a walk could slip through at factor 1. Beyond the
1808/// wall the region costs nothing, so moving out is taking it as touching.
1809/// A vertex in a corner crosses each wall it is that near to, moving by at
1810/// most `reach` and a few ulps for each.
1811fn touch_walls(
1812    costs: &[CostRegion],
1813    region: &[Polygon],
1814    reach: f64,
1815) -> Result<Vec<CostRegion>, RouteError> {
1816    let mut edges = Vec::new();
1817    for polygon in region {
1818        for (hole, ring) in
1819            core::iter::once((false, &polygon.outer)).chain(polygon.holes.iter().map(|h| (true, h)))
1820        {
1821            let left = region_left(ring, hole)?;
1822            for (p, q) in ring_edges(ring) {
1823                if p != q {
1824                    edges.push((p, q, left));
1825                }
1826            }
1827        }
1828    }
1829    let mut out = costs.to_vec();
1830    for cost in &mut out {
1831        for ring in core::iter::once(&mut cost.polygon.outer).chain(cost.polygon.holes.iter_mut()) {
1832            for v in &mut ring.points {
1833                for &(p, q, left) in edges.iter().chain(edges.iter()) {
1834                    let free = if left { Sign::Positive } else { Sign::Negative };
1835                    if point_segment(*v, p, q) > reach || side(p, q, *v)? != free {
1836                        continue;
1837                    }
1838                    // The foot on the line, then out along the normal until
1839                    // the exact side says it is no longer on the free side.
1840                    let d = q - p;
1841                    let t = (*v - p).dot(d) / d.dot(d);
1842                    let foot = Point2::new(p.x + d.x * t, p.y + d.y * t);
1843                    let out = if left {
1844                        Point2::new(d.y, -d.x)
1845                    } else {
1846                        Point2::new(-d.y, d.x)
1847                    };
1848                    let unit = Point2::new(out.x / d.length(), out.y / d.length());
1849                    let mut step = f64::EPSILON * foot.x.abs().max(foot.y.abs()).max(reach);
1850                    let mut moved = foot;
1851                    while side(p, q, moved)? == free {
1852                        moved = Point2::new(foot.x + unit.x * step, foot.y + unit.y * step);
1853                        step *= 2.0;
1854                    }
1855                    *v = moved;
1856                }
1857            }
1858        }
1859    }
1860    Ok(out)
1861}
1862
1863/// The corners of the convex hull of `points`, counter-clockwise, or
1864/// `None` when they are collinear. Decided exactly.
1865fn convex_hull(points: &[Point2]) -> Result<Option<Vec<Point2>>, RouteError> {
1866    let mut pts: Vec<Point2> = points.to_vec();
1867    pts.sort_by(|p, q| p.x.total_cmp(&q.x).then(p.y.total_cmp(&q.y)));
1868    pts.dedup();
1869    if pts.len() < 3 {
1870        return Ok(None);
1871    }
1872    // Andrew's monotone chain with exact turns.
1873    let mut hull: Vec<Point2> = Vec::new();
1874    for pass in 0..2 {
1875        let start = hull.len();
1876        let iter: Box<dyn Iterator<Item = &Point2>> = if pass == 0 {
1877            Box::new(pts.iter())
1878        } else {
1879            Box::new(pts.iter().rev())
1880        };
1881        for &p in iter {
1882            while hull.len() >= start + 2
1883                && side(hull[hull.len() - 2], hull[hull.len() - 1], p)? != Sign::Positive
1884            {
1885                hull.pop();
1886            }
1887            hull.push(p);
1888        }
1889        hull.pop();
1890    }
1891    Ok((hull.len() >= 3).then_some(hull))
1892}
1893
1894/// Whether the closed polygon holds the convex polygon `hull`
1895/// (counter-clockwise, with interior). No edge of the polygon may meet the
1896/// hull's interior, which then lies wholly inside the polygon or wholly
1897/// outside, so one point strictly inside the hull decides -- corners on
1898/// the polygon's boundary do not: a hull can span a notch of a polygon,
1899/// touching it only along two sides. A hull too thin to hold a point
1900/// provably inside it is not held.
1901fn holds(polygon: &Polygon, hull: &[Point2]) -> Result<bool, RouteError> {
1902    for &c in hull {
1903        if !in_polygon(polygon, c)? {
1904            return Ok(false);
1905        }
1906    }
1907    let n = hull.len();
1908    for ring in core::iter::once(&polygon.outer).chain(polygon.holes.iter()) {
1909        for (p, q) in ring_edges(ring) {
1910            if meets_open_convex(p, q, hull, n)? {
1911                return Ok(false);
1912            }
1913        }
1914    }
1915    let inv = 1.0 / n as f64;
1916    let centre = hull.iter().fold(Point2::ZERO, |sum, c| sum + *c * inv);
1917    for i in 0..n {
1918        if side(hull[i], hull[(i + 1) % n], centre)? != Sign::Positive {
1919            return Ok(false);
1920        }
1921    }
1922    let on_boundary = core::iter::once(&polygon.outer)
1923        .chain(polygon.holes.iter())
1924        .flat_map(ring_edges)
1925        .try_fold(false, |on, (p, q)| {
1926            Ok::<_, RouteError>(on || (within(p, q, centre) && side(p, q, centre)? == Sign::Zero))
1927        })?;
1928    Ok(!on_boundary && in_polygon(polygon, centre)?)
1929}
1930
1931/// Whether the closed segment `p`-`q` meets the open interior of the
1932/// counter-clockwise convex polygon: no edge line of the polygon, nor the
1933/// segment's line, separates them.
1934fn meets_open_convex(p: Point2, q: Point2, hull: &[Point2], n: usize) -> Result<bool, RouteError> {
1935    for i in 0..n {
1936        let (u, v) = (hull[i], hull[(i + 1) % n]);
1937        if side(u, v, p)? != Sign::Positive && side(u, v, q)? != Sign::Positive {
1938            return Ok(false);
1939        }
1940    }
1941    if p != q {
1942        let mut all_left = true;
1943        let mut all_right = true;
1944        for &c in hull {
1945            let s = side(p, q, c)?;
1946            all_left &= s != Sign::Negative;
1947            all_right &= s != Sign::Positive;
1948        }
1949        if all_left || all_right {
1950            return Ok(false);
1951        }
1952    }
1953    Ok(true)
1954}
1955
1956/// The greatest weighted distance from a subregion to the nearest target
1957/// over its points in the free space, bracketed to within `tolerance`
1958/// where the map's own bracket allows: the result is never narrower than
1959/// the gap between the map's bounds at the farthest point.
1960///
1961/// # Errors
1962///
1963/// As [`crate::farthest_point`].
1964pub fn weighted_farthest_point(
1965    map: &WeightedMap,
1966    subregion: &Polygon,
1967    tolerance: f64,
1968) -> Result<Farthest, FarthestError> {
1969    weighted_farthest_point_within(map, subregion, tolerance, MAX_CELLS)
1970}
1971
1972#[derive(Debug, Clone, Copy)]
1973struct Cell {
1974    corners: [Point2; 3],
1975    root: usize,
1976    anchor: (Point2, f64),
1977    upper: f64,
1978    order: usize,
1979    depth: u32,
1980}
1981
1982impl PartialEq for Cell {
1983    fn eq(&self, other: &Self) -> bool {
1984        self.cmp(other) == Ordering::Equal
1985    }
1986}
1987
1988impl Eq for Cell {}
1989
1990impl PartialOrd for Cell {
1991    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1992        Some(self.cmp(other))
1993    }
1994}
1995
1996impl Ord for Cell {
1997    fn cmp(&self, other: &Self) -> Ordering {
1998        self.upper
1999            .total_cmp(&other.upper)
2000            .then(other.order.cmp(&self.order))
2001    }
2002}
2003
2004/// [`weighted_farthest_point`] with a caller-chosen cell budget.
2005///
2006/// # Errors
2007///
2008/// As [`weighted_farthest_point`].
2009pub fn weighted_farthest_point_within(
2010    map: &WeightedMap,
2011    subregion: &Polygon,
2012    tolerance: f64,
2013    max_cells: usize,
2014) -> Result<Farthest, FarthestError> {
2015    if !(tolerance.is_finite() && tolerance >= 0.0) {
2016        return Err(FarthestError::InvalidTolerance);
2017    }
2018    validate_region(core::slice::from_ref(subregion), &[])?;
2019    let roots = free_triangles_in(&map.region, &map.obstacles)?;
2020    let steep: Vec<f64> = roots
2021        .iter()
2022        .map(|t| map.weights.steepest(t))
2023        .collect::<Result<_, _>>()?;
2024    let slack = |depth: u32, radius: f64| {
2025        f64::from(depth + 4) * 4.0 * f64::EPSILON * map.scale + 4.0 * f64::EPSILON * radius
2026    };
2027    let mut evaluated: HashMap<(u64, u64), Option<(f64, f64)>> = HashMap::new();
2028    let mut lower: Option<(f64, Point2)> = None;
2029    let mut heap = BinaryHeap::new();
2030    let mut cells = 0usize;
2031    let mut cell = |corners: [Point2; 3],
2032                    root: usize,
2033                    inherited: Option<(Point2, f64)>,
2034                    depth: u32,
2035                    order: usize,
2036                    lower: &mut Option<(f64, Point2)>|
2037     -> Result<Option<Cell>, FarthestError> {
2038        let centroid = Point2::new(
2039            (corners[0].x + corners[1].x + corners[2].x) / 3.0,
2040            (corners[0].y + corners[1].y + corners[2].y) / 3.0,
2041        );
2042        let radius = |a: Point2| {
2043            corners
2044                .iter()
2045                .map(|c| (*c - a).length())
2046                .fold(0.0, f64::max)
2047        };
2048        let k = steep[root];
2049        let mut best: Option<(f64, (Point2, f64))> =
2050            inherited.map(|(a, d)| (d + k * radius(a), (a, d)));
2051        for a in [corners[0], corners[1], corners[2], centroid] {
2052            if !in_triangle(&roots[root], a)? {
2053                continue;
2054            }
2055            if map.walls.iter().try_fold(false, |m, &(p, q)| {
2056                Ok::<_, RouteError>(m || (within(p, q, a) && side(p, q, a)? == Sign::Zero))
2057            })? {
2058                continue;
2059            }
2060            let key = (a.x.to_bits(), a.y.to_bits());
2061            let bracket = match evaluated.get(&key) {
2062                Some(b) => *b,
2063                None => {
2064                    let b = map.bracket(a)?;
2065                    evaluated.insert(key, b);
2066                    b
2067                }
2068            };
2069            let Some((lo, hi)) = bracket else {
2070                return Err(FarthestError::Unreachable {
2071                    triangle: roots[root],
2072                });
2073            };
2074            if in_polygon(subregion, a)? && lower.is_none_or(|(l, _)| lo > l) {
2075                *lower = Some((lo, a));
2076            }
2077            let bound = hi + k * radius(a);
2078            if best.is_none_or(|(b, _)| bound < b) {
2079                best = Some((bound, (a, hi)));
2080            }
2081        }
2082        Ok(best.map(|(bound, anchor)| Cell {
2083            corners,
2084            root,
2085            anchor,
2086            upper: bound + k * slack(depth, radius(anchor.0)),
2087            order,
2088            depth,
2089        }))
2090    };
2091    for (root, corners) in roots.iter().enumerate() {
2092        if outside(subregion, corners)? {
2093            continue;
2094        }
2095        cells += 1;
2096        let Some(c) = cell(*corners, root, None, 0, cells, &mut lower)? else {
2097            return Err(FarthestError::Triangulation);
2098        };
2099        heap.push(c);
2100    }
2101    if cells == 0 {
2102        return Err(FarthestError::Empty);
2103    }
2104    let best = |lower: &Option<(f64, Point2)>| lower.map_or(f64::NEG_INFINITY, |(d, _)| d);
2105    while let Some(top) = heap.peek() {
2106        if top.upper <= best(&lower) + tolerance || cells >= max_cells {
2107            break;
2108        }
2109        let parent = heap.pop().expect("peeked");
2110        let [a, b, c] = parent.corners;
2111        let mid = |p: Point2, q: Point2| Point2::new(0.5 * p.x + 0.5 * q.x, 0.5 * p.y + 0.5 * q.y);
2112        let (ab, bc, ca) = (mid(a, b), mid(b, c), mid(c, a));
2113        for corners in [[a, ab, ca], [ab, b, bc], [ca, bc, c], [ab, bc, ca]] {
2114            if outside(subregion, &corners)? {
2115                continue;
2116            }
2117            cells += 1;
2118            if let Some(child) = cell(
2119                corners,
2120                parent.root,
2121                Some(parent.anchor),
2122                parent.depth + 1,
2123                cells,
2124                &mut lower,
2125            )? {
2126                if child.upper > best(&lower) {
2127                    heap.push(child);
2128                }
2129            }
2130        }
2131    }
2132    let Some((lo, witness)) = lower else {
2133        if heap.is_empty() {
2134            return Err(FarthestError::Empty);
2135        }
2136        return Ok(Farthest {
2137            distance: LengthInterval {
2138                lower: 0.0,
2139                upper: heap.peek().map_or(0.0, |c| c.upper),
2140            },
2141            witness: None,
2142            converged: false,
2143            cells,
2144        });
2145    };
2146    let hi = heap.peek().map_or(lo, |c| c.upper.max(lo));
2147    Ok(Farthest {
2148        distance: LengthInterval {
2149            lower: lo,
2150            upper: hi,
2151        },
2152        witness: Some(witness),
2153        converged: hi - lo <= tolerance,
2154        cells,
2155    })
2156}
2157
2158#[cfg(test)]
2159mod tests {
2160    use super::*;
2161    use axiolid_overlay::Ring;
2162
2163    fn square() -> Weights {
2164        let p = |x: f64, y: f64| Point2::new(x, y);
2165        let polygon = Polygon {
2166            outer: Ring {
2167                points: vec![p(0.0, 0.0), p(1.0, 0.0), p(1.0, 1.0), p(0.0, 1.0)],
2168            },
2169            holes: Vec::new(),
2170        };
2171        Weights::new(&[CostRegion::new(polygon, 2.0)], &[], &[]).unwrap()
2172    }
2173
2174    /// A segment a rounding step off a cost edge cannot be told inside
2175    /// from outside by its middle: the stretch counts 1 below and the
2176    /// greatest factor above, whichever side it is on.
2177    #[test]
2178    fn a_stretch_within_rounding_of_a_cost_edge_is_bracketed_both_ways() {
2179        let weights = square();
2180        let tiny = 1e-17;
2181        // Just inside the square along its lower edge: 1 + 2 + 1 below.
2182        // Above, the walk along the edge itself, a rounding off the
2183        // segment, where the cheaper side counts: 3.
2184        let (lo, hi) = weights
2185            .segment(Point2::new(-1.0, tiny), Point2::new(2.0, tiny), 2.0)
2186            .unwrap();
2187        assert!(lo <= 3.0 && (hi - 3.0).abs() < 1e-12, "[{lo}, {hi}]");
2188        // Just outside it: 3 at factor 1.
2189        let (lo, hi) = weights
2190            .segment(Point2::new(-1.0, -tiny), Point2::new(2.0, -tiny), 2.0)
2191            .unwrap();
2192        assert!(lo <= 3.0 && hi >= 3.0, "[{lo}, {hi}]");
2193        // Inside a notched polygon, the notch's tip a rounding above the
2194        // middle: doubtful there, and hugging nothing, so the greatest
2195        // factor above, which is the true one.
2196        let p = |x: f64, y: f64| Point2::new(x, y);
2197        let notched = Polygon {
2198            outer: Ring {
2199                points: vec![
2200                    p(0.0, 0.0),
2201                    p(4.0, 0.0),
2202                    p(4.0, 2.0),
2203                    p(2.5, 2.0),
2204                    p(2.0, 1.0 + f64::EPSILON),
2205                    p(1.5, 2.0),
2206                    p(0.0, 2.0),
2207                ],
2208            },
2209            holes: Vec::new(),
2210        };
2211        let weights = Weights::new(&[CostRegion::new(notched, 2.0)], &[], &[]).unwrap();
2212        let (lo, hi) = weights.segment(p(0.5, 1.0), p(3.5, 1.0), 4.0).unwrap();
2213        assert!(lo <= 6.0 && hi >= 6.0, "[{lo}, {hi}]");
2214        let weights = square();
2215        // Crossing the edge within rounding of it, not along it: the
2216        // greatest factor above.
2217        let (lo, hi) = weights
2218            .segment(Point2::new(0.5, -tiny), Point2::new(0.5, tiny), 2.0)
2219            .unwrap();
2220        assert!(lo <= 2.0 * tiny && hi >= 2.0 * 2.0 * tiny, "[{lo}, {hi}]");
2221        // Clear of every edge the stretches are decided.
2222        let (lo, hi) = weights
2223            .segment(Point2::new(-1.0, 0.5), Point2::new(2.0, 0.5), 2.0)
2224            .unwrap();
2225        assert!(
2226            (lo - 4.0).abs() < 1e-12 && (hi - 4.0).abs() < 1e-12,
2227            "[{lo}, {hi}]"
2228        );
2229    }
2230
2231    fn p(x: f64, y: f64) -> Point2 {
2232        Point2::new(x, y)
2233    }
2234
2235    fn rect(x0: f64, y0: f64, x1: f64, y1: f64) -> Polygon {
2236        Polygon {
2237            outer: Ring {
2238                points: vec![p(x0, y0), p(x1, y0), p(x1, y1), p(x0, y1)],
2239            },
2240            holes: Vec::new(),
2241        }
2242    }
2243
2244    /// The interval of `map` whose span has `end` as one end and runs
2245    /// toward `toward`.
2246    fn interval(map: &WeightedMap, end: Point2, toward: Point2) -> usize {
2247        (0..map.nodes.len())
2248            .find(|&i| match map.kinds[i] {
2249                Kind::Interval { a, b, .. } => {
2250                    (a == end && (b - a).dot(toward - a) > 0.0)
2251                        || (b == end && (a - b).dot(toward - b) > 0.0)
2252                }
2253                Kind::Vertex => false,
2254            })
2255            .expect("an interval")
2256    }
2257
2258    /// A barrier's foot on the segment from an interval's inner end: the
2259    /// pieces from just beside that end pass under the foot, so the hop
2260    /// stands. From an end that is a vertex, the same touch is left to the
2261    /// vertex's own node, and the hop is blocked when every other piece
2262    /// crosses the barrier.
2263    #[test]
2264    fn a_touch_blocks_a_hop_only_at_a_vertex_end() {
2265        let room = [rect(0.0, 0.0, 10.0, 4.0)];
2266        let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2267        // The stair's east edge x = 4 is cut at y = 1 (spacing 1).
2268        let barrier = vec![vec![p(5.0, 1.0), p(5.0, 3.0)]];
2269        let map = weighted_distance_map(&room, &barrier, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2270        let above = interval(&map, p(4.0, 1.0), p(4.0, 2.0));
2271        let q = p(6.0, 1.0);
2272        let hop = map
2273            .hop(map.span(above), (q, q), map.kinds[above], Kind::Vertex)
2274            .unwrap();
2275        assert!(hop.is_some(), "the inner end's pieces graze the foot");
2276        // The same geometry with the foot's end a vertex: the stair's
2277        // corner (4, 4) and a barrier foot on the line to the query.
2278        // An end that is a vertex: the stair's corner (4, 4), with a
2279        // barrier's foot on the line from it to the query.
2280        let walled = vec![vec![p(5.0, 3.0), p(5.0, 1.0)]];
2281        let stair = CostRegion::new(rect(2.0, 0.0, 4.0, 4.0), 2.0);
2282        let map = weighted_distance_map(&room, &walled, &[p(0.5, 2.0)], &[stair], 1.0).unwrap();
2283        let corner = interval(&map, p(4.0, 4.0), p(4.0, 3.0));
2284        let q = p(6.0, 2.0);
2285        // Segments from (4, y), y just under 4, to (6, 2) cross x = 5 just
2286        // under y = 3: through the barrier; from the corner itself, at its
2287        // end (5, 3).
2288        let hop = map
2289            .hop(map.span(corner), (q, q), map.kinds[corner], Kind::Vertex)
2290            .unwrap();
2291        assert!(hop.is_none(), "{hop:?}");
2292    }
2293
2294    /// A notch poking into a hull through its side: every corner and the
2295    /// centre of the hull are inside the polygon, but part of the hull is
2296    /// not, so it is not held.
2297    #[test]
2298    fn a_hull_is_held_only_whole() {
2299        let square = rect(0.0, 0.0, 4.0, 4.0);
2300        let hull = [p(1.0, 1.0), p(3.0, 1.0), p(3.0, 3.0)];
2301        assert!(holds(&square, &hull).unwrap());
2302        // A notch cut in from the west side, its tip at (2.2, 1.6), inside
2303        // the hull, below its diagonal.
2304        let notched = Polygon {
2305            outer: Ring {
2306                points: vec![
2307                    p(0.0, 0.0),
2308                    p(4.0, 0.0),
2309                    p(4.0, 4.0),
2310                    p(0.0, 4.0),
2311                    p(0.0, 1.9),
2312                    p(2.2, 1.6),
2313                    p(0.0, 1.5),
2314                ],
2315            },
2316            holes: Vec::new(),
2317        };
2318        for c in hull {
2319            assert!(in_polygon(&notched, c).unwrap());
2320        }
2321        assert!(in_polygon(&notched, p(7.0 / 3.0, 5.0 / 3.0)).unwrap());
2322        assert!(!holds(&notched, &hull).unwrap());
2323    }
2324
2325    /// A segment along a blocker's own line, through its end, runs along
2326    /// it: no touch that separates anything.
2327    #[test]
2328    fn a_segment_along_a_blocker_does_not_pass_through_its_end() {
2329        assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(3.0, 0.0)).unwrap());
2330        assert!(!through_end(p(0.0, 0.0), p(4.0, 0.0), p(2.0, 0.0), p(1.0, 0.0)).unwrap());
2331        // Across the blocker's line, through its end.
2332        assert!(through_end(p(0.0, -1.0), p(0.0, 1.0), p(0.0, 0.0), p(1.0, 0.0)).unwrap());
2333    }
2334}