open-standards/map/map-rs/crates/map-index/src/edge.rs
Transaction Science ba1212361f Add aac, map, sandbox standards; catalog the standards family
Commit the previously-untracked standard directories and catalog them in the
root README alongside proof:

- aac/    — communication cargo class: portable boards + a ten-level
            offline-first cascade (Apache-2.0 code / CC-BY-4.0 spec).
- map/    — geospatial cargo class: range-requestable signed tilesets +
            an F0–F4 fidelity cascade (Apache-2.0 code / CC-BY-4.0 spec).
- sandbox/— isolated, metered, attested execution across the heterogeneous
            compute fabric (Apache-2.0 code / CC-BY-4.0 spec).
- jouleclaw-otel-authz, jouleclaw-trust — new jouleclaw reference crates.

Root README now lists aac, map, sandbox, and proof in the catalog, the tree,
and the contribution guide.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-07 17:04:36 -04:00

176 lines
5.7 KiB
Rust

// Edge utilities — crossing tests, interpolation, distance.
use crate::point::S2Point;
/// Test whether two edges (a0->a1) and (b0->b1) cross each other.
/// Returns true if the edges properly cross (interiors intersect).
/// Does not count shared endpoints as crossings.
pub fn crosses(a0: &S2Point, a1: &S2Point, b0: &S2Point, b1: &S2Point) -> bool {
// Two great-circle arcs cross if and only if:
// 1) a0 and a1 are on opposite sides of the plane through (origin, b0, b1), AND
// 2) b0 and b1 are on opposite sides of the plane through (origin, a0, a1).
//
// The normal to the plane through b0 and b1 is b0 x b1.
// A point p is on the positive side if dot(p, b0 x b1) > 0.
let n_a = a0.cross(*a1); // normal to edge A's great circle
let n_b = b0.cross(*b1); // normal to edge B's great circle
// Check if b0 and b1 are on opposite sides of edge A's plane.
let d_b0 = b0.dot(n_a);
let d_b1 = b1.dot(n_a);
if d_b0 * d_b1 >= 0.0 {
return false; // b0, b1 on same side (or on the plane)
}
// Check if a0 and a1 are on opposite sides of edge B's plane.
let d_a0 = a0.dot(n_b);
let d_a1 = a1.dot(n_b);
if d_a0 * d_a1 >= 0.0 {
return false; // a0, a1 on same side
}
true
}
/// Spherical linear interpolation (slerp) between two points.
/// t=0 returns a, t=1 returns b.
pub fn interpolate(a: &S2Point, b: &S2Point, t: f64) -> S2Point {
let angle = a.angle(*b);
if angle.abs() < 1e-15 {
return *a;
}
let sin_angle = angle.sin();
let sa = ((1.0 - t) * angle).sin() / sin_angle;
let sb = (t * angle).sin() / sin_angle;
S2Point::new(
sa * a.x + sb * b.x,
sa * a.y + sb * b.y,
sa * a.z + sb * b.z,
)
}
/// Angular distance from a point to the nearest point on a great-circle edge (a->b).
/// Returns the distance in radians.
pub fn distance_to_edge(point: &S2Point, a: &S2Point, b: &S2Point) -> f64 {
// Normal to the great circle containing the edge.
let n = a.cross(*b);
let n_norm = n.norm();
if n_norm < 1e-15 {
// Degenerate edge: a and b are nearly identical (or antipodal).
return point.angle(*a);
}
// Signed distance from point to the great circle plane.
let sin_dist = point.dot(n) / n_norm;
// The closest point on the great circle is at distance asin(|sin_dist|).
// But we need to check if the projection falls within the arc.
// Project point onto the great circle.
let proj = point.add(n.scale(-sin_dist / n_norm)).normalize();
// Check if the projection falls within the arc a->b.
// It does if the cross products a x proj and proj x b have the same sign
// as a x b (all relative to the normal).
let ab_sign = a.cross(*b).dot(n);
let ap_sign = a.cross(proj).dot(n);
let pb_sign = proj.cross(*b).dot(n);
if ab_sign > 0.0 && ap_sign >= 0.0 && pb_sign >= 0.0 {
// Projection is within the arc.
sin_dist.abs().asin()
} else if ab_sign < 0.0 && ap_sign <= 0.0 && pb_sign <= 0.0 {
sin_dist.abs().asin()
} else {
// Projection is outside the arc. Distance is to the nearest endpoint.
point.angle(*a).min(point.angle(*b))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::f64::consts::PI;
#[test]
fn crossing_edges() {
// Two edges that cross on the equator.
let a0 = S2Point::from_lat_lng(0.1, -0.1);
let a1 = S2Point::from_lat_lng(-0.1, 0.1);
let b0 = S2Point::from_lat_lng(-0.1, -0.1);
let b1 = S2Point::from_lat_lng(0.1, 0.1);
assert!(crosses(&a0, &a1, &b0, &b1));
}
#[test]
fn non_crossing_edges() {
// Two parallel-ish edges that don't cross.
let a0 = S2Point::from_lat_lng(0.1, 0.0);
let a1 = S2Point::from_lat_lng(0.2, 0.0);
let b0 = S2Point::from_lat_lng(0.1, 0.5);
let b1 = S2Point::from_lat_lng(0.2, 0.5);
assert!(!crosses(&a0, &a1, &b0, &b1));
}
#[test]
fn interpolate_endpoints() {
let a = S2Point::new(1.0, 0.0, 0.0);
let b = S2Point::new(0.0, 1.0, 0.0);
let p0 = interpolate(&a, &b, 0.0);
assert!((p0.x - a.x).abs() < 1e-14);
assert!((p0.y - a.y).abs() < 1e-14);
let p1 = interpolate(&a, &b, 1.0);
assert!((p1.x - b.x).abs() < 1e-14);
assert!((p1.y - b.y).abs() < 1e-14);
}
#[test]
fn interpolate_midpoint() {
let a = S2Point::new(1.0, 0.0, 0.0);
let b = S2Point::new(0.0, 1.0, 0.0);
let mid = interpolate(&a, &b, 0.5);
// Midpoint should be equidistant from both.
let da = mid.angle(a);
let db = mid.angle(b);
assert!((da - db).abs() < 1e-14);
}
#[test]
fn distance_to_edge_on_edge() {
let a = S2Point::new(1.0, 0.0, 0.0);
let b = S2Point::new(0.0, 1.0, 0.0);
let mid = interpolate(&a, &b, 0.5).normalize();
let dist = distance_to_edge(&mid, &a, &b);
assert!(dist < 1e-12, "point on edge should have distance ~0, got {dist}");
}
#[test]
fn distance_to_edge_at_pole() {
// Edge along equator, point at north pole.
let a = S2Point::new(1.0, 0.0, 0.0);
let b = S2Point::new(0.0, 1.0, 0.0);
let pole = S2Point::new(0.0, 0.0, 1.0);
let dist = distance_to_edge(&pole, &a, &b);
assert!(
(dist - PI / 2.0).abs() < 1e-12,
"north pole to equatorial edge should be PI/2, got {dist}"
);
}
#[test]
fn distance_to_degenerate_edge() {
let a = S2Point::new(1.0, 0.0, 0.0);
let p = S2Point::new(0.0, 1.0, 0.0);
let dist = distance_to_edge(&p, &a, &a);
assert!((dist - PI / 2.0).abs() < 1e-12);
}
}