mod geometry;
mod obstacle;
use std::collections::BTreeMap;
use rstar::{RTree, AABB};
use geometry::point_in_polygon;
pub use geometry::segment_distance;
pub use obstacle::{ObstacleKind, SpatialObstacle};
#[derive(Copy, Clone, Debug)]
pub struct Probe<'a> {
pub width: f64,
pub clearance: f64,
pub net_id: &'a str,
}
#[derive(Clone, Default)]
pub struct SpatialRoutingIndex {
layer_trees: BTreeMap<String, RTree<SpatialObstacle>>,
boundary: Option<Vec<[f64; 2]>>,
}
impl SpatialRoutingIndex {
pub fn new() -> Self {
Self::default()
}
pub fn set_boundary(&mut self, polygon: Vec<[f64; 2]>) {
self.boundary = Some(polygon);
}
pub fn insert_obstacle(&mut self, layer: &str, obstacle: SpatialObstacle) {
self.layer_trees
.entry(layer.to_string())
.or_default()
.insert(obstacle);
}
pub fn is_segment_clear(
&self,
layer: &str,
p1: [f64; 2],
p2: [f64; 2],
probe: Probe<'_>,
) -> bool {
let margin = probe.width / 2.0 + probe.clearance;
if !self.inside_boundary(p1, p2, margin) {
return false;
}
let Some(tree) = self.layer_trees.get(layer) else {
return true;
};
let query_env = AABB::from_corners(
[p1[0].min(p2[0]) - margin, p1[1].min(p2[1]) - margin],
[p1[0].max(p2[0]) + margin, p1[1].max(p2[1]) + margin],
);
tree.locate_in_envelope_intersecting(query_env)
.filter(|obs| obs.net_id.as_deref() != Some(probe.net_id))
.all(|obs| obs.distance_to_segment(p1, p2) >= margin)
}
fn inside_boundary(&self, p1: [f64; 2], p2: [f64; 2], margin: f64) -> bool {
let Some(outline) = &self.boundary else {
return true;
};
if !point_in_polygon(p1, outline) || !point_in_polygon(p2, outline) {
return false;
}
let n = outline.len();
!(0..n).any(|i| segment_distance(p1, p2, outline[i], outline[(i + 1) % n]) < margin)
}
}