Back to Atlas map viewer
SOURCE / PINNED RELEASE

Made of little things.

Atlas map viewer

Release
1d2807e80b44…
Author-recorded commit
43810ed2b368…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-4757dbabc86

Archive hash verified: 8db6876d96c460ac…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

crates/routing/src/cells/bidirectional.rs
//! Exact two-frontier A*. Forward keys remember the incoming edge; reverse keys
//! remember the outgoing edge. A meeting is valid only when that turn is legal.
use super::*;

#[derive(Default)]
struct Frontier {
    queue: BinaryHeap<Entry>,
    records: HashMap<Key, Record>,
    at_node: HashMap<i64, Vec<Key>>,
    indexed: usize,
}
impl Frontier {
    fn top(&mut self) -> Option<Entry> {
        while let Some(e) = self.queue.peek() {
            if e.g > self.records[&e.key].g {
                self.queue.pop();
            } else {
                return Some(*e);
            }
        }
        None
    }
    fn insert(&mut self, key: Key, r: Record, potential: f64) {
        if (key.way != 0 || key.before != 0) && !self.records.contains_key(&key) {
            self.at_node.entry(key.node).or_default().push(key);
            self.indexed += 1;
        }
        self.queue.push(Entry {
            key,
            g: r.g,
            f: r.g + potential,
        });
        self.records.insert(key, r);
    }
    fn memory(&self) -> usize {
        // Includes per-node meeting index and heap/map allocation overhead.
        self.records.len() * 192 + self.indexed * 96 + self.queue.len() * 64
    }
}

pub(super) struct Bidirectional {
    start: Endpoint,
    end: Endpoint,
    fronts: [Frontier; 2],
    speed: f64,
    best: f64,
    best_meters: f64,
    meeting: Option<[Key; 2]>,
    pub(super) visited: usize,
    tie_reverse: bool,
}
impl Bidirectional {
    pub(super) fn new(start: Endpoint, end: Endpoint, speed: f64) -> Self {
        let mut s = Self {
            start,
            end,
            speed,
            fronts: Default::default(),
            best: f64::INFINITY,
            best_meters: 0.,
            meeting: None,
            visited: 0,
            tie_reverse: false,
        };
        let delta = s.end.snap.t - s.start.snap.t;
        if s.start.id == s.end.id
            && ((delta >= 0. && s.start.directions & 1 != 0)
                || (delta <= 0. && s.start.directions & 2 != 0)
                || delta.abs() < 1e-12)
        {
            s.best = delta.abs() * s.start.costs[usize::from(delta < 0.)];
            s.best_meters = delta.abs() * s.start.length;
        }
        for (key, p, g, meters) in s.start.connections() {
            let potential = s.potential(p);
            s.fronts[0].insert(
                key,
                Record {
                    p,
                    g,
                    meters,
                    previous: None,
                    way: key.way,
                },
                potential,
            );
        }
        for (node, p, g, meters, next) in s.end.goals() {
            let key = Key {
                node,
                way: if g <= 1e-12 { 0 } else { s.end.snap_way() },
                before: next,
            };
            let potential = -s.potential(p);
            s.fronts[1].insert(
                key,
                Record {
                    p,
                    g,
                    meters,
                    previous: None,
                    way: key.way,
                },
                potential,
            );
        }
        s
    }
    fn potential(&self, p: [f64; 2]) -> f64 {
        // Balanced, feasible potentials: each edge costs at least distance/speed.
        // Reverse uses -p(v), so the two frontier minima sum to a route lower
        // bound. This is exact A*, not a weighted heuristic or corridor cutoff.
        (distance(p, self.end.snap.coordinates) - distance(self.start.snap.coordinates, p))
            / (2. * self.speed)
    }
    pub(super) fn memory(&self) -> usize {
        self.fronts.iter().map(Frontier::memory).sum()
    }
    fn join(&mut self, side: usize, key: Key, c: &Cell) {
        let other_front = &self.fronts[1 - side];
        let canonical = Key {
            node: key.node,
            way: 0,
            before: 0,
        };
        let others = other_front
            .records
            .contains_key(&canonical)
            .then_some(canonical)
            .into_iter()
            .chain(
                other_front
                    .at_node
                    .get(&key.node)
                    .into_iter()
                    .flat_map(|keys| keys.iter().copied()),
            );
        for other in others {
            let keys = if side == 0 {
                [key, other]
            } else {
                [other, key]
            };
            let a = &self.fronts[0].records[&keys[0]];
            let b = &self.fronts[1].records[&keys[1]];
            if a.g + b.g < self.best
                && (keys[1].way == 0 || c.allowed(keys[0], keys[1].way, keys[1].before))
            {
                self.best = a.g + b.g;
                self.best_meters = a.meters + b.meters;
                self.meeting = Some(keys);
            }
        }
    }
    pub(super) fn step(
        &mut self,
        cells: &mut CellCache,
        touched: &mut HashSet<(u32, u32)>,
        limit: usize,
        search_limit: usize,
    ) -> Result<Step, String> {
        for _ in 0..limit.clamp(1, 4096) {
            let (Some(a), Some(b)) = (self.fronts[0].top(), self.fronts[1].top()) else {
                return self.finish();
            };
            if self.best.is_finite() && a.f + b.f >= self.best - 1e-7 {
                return self.finish();
            }
            let side = usize::from(a.f > b.f || (a.f == b.f && self.tie_reverse));
            let entry = if side == 0 { a } else { b };
            let record = &self.fronts[side].records[&entry.key];
            let (pos, meters) = (record.p, record.meters);
            let address = cell(pos);
            if !cells.contains(&address) {
                if cells.is_empty(&address) {
                    return Err("Missing owner cell for a known routing node".into());
                }
                return Ok(Step::NeedBlocks {
                    keys: vec![key(address)],
                    visited: self.visited,
                });
            }
            self.tie_reverse = !self.tie_reverse;
            self.fronts[side].queue.pop();
            touched.insert(address);
            let c = cells.get_mut(&address).unwrap();
            let i = *c
                .by_id
                .get(&entry.key.node)
                .ok_or("Routing cell is missing a referenced node")?;
            if c.graph.nodes[i].position != pos {
                return Err("Conflicting routing node coordinates".into());
            }
            self.visited += 1;
            self.join(side, entry.key, c);
            let arcs = if side == 0 {
                &c.graph.adjacency[i]
            } else {
                &c.incoming[i]
            };
            for arc in arcs {
                let n = &c.graph.nodes[arc.to];
                let seg = &c.graph.segments[arc.segment];
                let allowed = if side == 0 {
                    c.allowed(entry.key, seg.way, n.id)
                } else {
                    entry.key.way == 0
                        || c.allowed(
                            Key {
                                node: entry.key.node,
                                way: seg.way,
                                before: n.id,
                            },
                            entry.key.way,
                            entry.key.before,
                        )
                };
                if !allowed {
                    continue;
                }
                let g = entry.g + arc.seconds;
                // Arrival/departure history only changes legal moves at a node
                // carrying turn restrictions. Else all histories are equivalent.
                // Keep the traversed way on the record for route reconstruction.
                let restricted = c.turns_by_node.contains_key(&n.id);
                let key = Key {
                    node: n.id,
                    way: if restricted { seg.way } else { 0 },
                    before: if restricted { entry.key.node } else { 0 },
                };
                if g > self.best
                    || self.fronts[side]
                        .records
                        .get(&key)
                        .is_some_and(|r| r.g <= g)
                {
                    continue;
                }
                let count: usize = self.fronts.iter().map(|f| f.records.len()).sum();
                let queued: usize = self.fronts.iter().map(|f| f.queue.len()).sum();
                if queued >= search_limit * 4
                    || (count >= search_limit && !self.fronts[side].records.contains_key(&key))
                {
                    return Ok(Step::BudgetExceeded {
                        visited: self.visited,
                    });
                }
                let potential = self.potential(n.position) * if side == 0 { 1. } else { -1. };
                self.fronts[side].insert(
                    key,
                    Record {
                        p: n.position,
                        g,
                        meters: meters + arc.meters,
                        previous: Some(entry.key),
                        way: seg.way,
                    },
                    potential,
                );
                // Each cell retains all turn rules at its segment endpoints,
                // including boundary nodes. Check every improved tentative label:
                // the first meeting is only an upper bound, never a stopping rule.
                self.join(side, key, c);
            }
        }
        Ok(Step::Running {
            visited: self.visited,
        })
    }
    fn finish(&self) -> Result<Step, String> {
        if !self.best.is_finite() {
            return Ok(Step::Complete {
                result: Route::NoRoute {
                    start: self.start.snap.clone(),
                    end: self.end.snap.clone(),
                    visited: self.visited,
                },
            });
        }
        let mut coordinates = vec![self.start.snap.coordinates];
        let mut way_ids = vec![self.start.snap.way_id.clone()];
        if let Some(keys) = self.meeting {
            for (side, mut key) in keys.into_iter().enumerate() {
                let mut points = Vec::new();
                let mut ways = Vec::new();
                loop {
                    let r = &self.fronts[side].records[&key];
                    points.push(r.p);
                    if let Some(prev) = r.previous {
                        ways.push(format!("way/{}", r.way));
                        key = prev;
                    } else {
                        break;
                    }
                }
                if side == 0 {
                    points.reverse();
                    ways.reverse();
                }
                coordinates.extend(points);
                way_ids.extend(ways);
            }
        }
        coordinates.push(self.end.snap.coordinates);
        coordinates.dedup();
        way_ids.push(self.end.snap.way_id.clone());
        way_ids.dedup();
        Ok(Step::Complete {
            result: Route::Found {
                coordinates,
                way_ids,
                meters: self.best_meters,
                seconds: self.best,
                visited: self.visited,
                start: self.start.snap.clone(),
                end: self.end.snap.clone(),
            },
        })
    }
}