diff --git a/topola/src/compass.rs b/topola/src/compass.rs index 3025ef2..e5d5b96 100644 --- a/topola/src/compass.rs +++ b/topola/src/compass.rs @@ -49,15 +49,6 @@ impl CompassDirection for CardinalDirec } } - fn turn_clockwise(self) -> Self { - match self { - Self::East => Self::South, - Self::North => Self::East, - Self::West => Self::North, - Self::South => Self::West, - } - } - fn turn_counterclockwise(self) -> Self { match self { Self::East => Self::North, @@ -66,6 +57,15 @@ impl CompassDirection for CardinalDirec Self::South => Self::East, } } + + fn turn_clockwise(self) -> Self { + match self { + Self::East => Self::South, + Self::North => Self::East, + Self::West => Self::North, + Self::South => Self::West, + } + } } #[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] diff --git a/topola/src/layout/infringement.rs b/topola/src/layout/infringement.rs new file mode 100644 index 0000000..1df5d37 --- /dev/null +++ b/topola/src/layout/infringement.rs @@ -0,0 +1,394 @@ +// SPDX-FileCopyrightText: 2026 Topola contributors +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use std::collections::BTreeSet; + +use derive_getters::Getters; +use derive_more::Constructor; +use rstar::{ + AABB, RTree, + primitives::{GeomWithData, Rectangle}, +}; +use serde::{Deserialize, Serialize}; + +use crate::board::Board; +use crate::primitives::PrimitiveId; + +use super::compounds::{ComponentId, NetId}; +use super::primitives::{JointId, PolygonId, SegmentId, ViaId}; + +#[derive( + Clone, Copy, Constructor, Debug, Deserialize, Eq, Getters, Ord, PartialEq, PartialOrd, Serialize, +)] +pub struct Infringement { + infringer: I, + infringee: E, +} + +mod sealed { + use super::*; + + // Sealed trait to prevent having our `From` implementations below collide + // with an existing blanket implementation. + pub trait IntoPrimitiveId: Into {} + + impl IntoPrimitiveId for JointId {} + impl IntoPrimitiveId for SegmentId {} + impl IntoPrimitiveId for ViaId {} + impl IntoPrimitiveId for PolygonId {} +} + +impl From> + for Infringement +where + E: sealed::IntoPrimitiveId, +{ + fn from(from: Infringement) -> Self { + Infringement { + infringer: from.infringer, + infringee: from.infringee.into(), + } + } +} + +impl From> + for Infringement +{ + fn from(from: Infringement) -> Self { + Infringement { + infringer: from.infringer.into(), + infringee: from.infringee.into(), + } + } +} + +impl From> for Infringement { + fn from(from: Infringement) -> Self { + Infringement { + infringer: from.infringer.into(), + infringee: from.infringee, + } + } +} + +impl Board { + pub fn locate_component_component_infringements( + &self, + infringer: ComponentId, + ) -> impl Iterator> + '_ { + let mut infringee_components = BTreeSet::new(); + + for infringement in self.locate_component_primitive_infringements(infringer) { + let Some(infringee_component) = self.primitive_component(*infringement.infringee()) else { + continue; + }; + + if infringee_component == infringer { + continue; + } + + infringee_components.insert(infringee_component); + } + + infringee_components + .into_iter() + .map(move |infringee| Infringement::new(infringer, infringee)) + } + + pub fn locate_component_primitive_infringements( + &self, + infringer: ComponentId, + ) -> impl Iterator + '_ { + let component = self.layout().component(infringer); + + let joint_infringements = component + .joints + .iter() + .copied() + .flat_map(|joint_id| self.locate_joint_infringements(joint_id).map(Into::into)); + let segment_infringements = component.segments.iter().copied().flat_map(|segment_id| { + self.locate_segment_infringements(segment_id) + .map(Into::into) + }); + let via_infringements = component + .vias + .iter() + .copied() + .flat_map(|via_id| self.locate_via_infringements(via_id).map(Into::into)); + let polygon_infringements = component.polygons.iter().copied().flat_map(|polygon_id| { + self.locate_polygon_infringements(polygon_id) + .map(Into::into) + }); + + joint_infringements + .chain(segment_infringements) + .chain(via_infringements) + .chain(polygon_infringements) + } + + pub fn locate_joint_infringements( + &self, + infringer: JointId, + ) -> impl Iterator> + '_ { + self.locate_joint_joint_infringements(infringer) + .map(Into::into) + .chain( + self.locate_joint_segment_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_joint_via_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_joint_polygon_infringements(infringer) + .map(Into::into), + ) + } + + pub fn locate_joint_joint_infringements( + &self, + infringer: JointId, + ) -> impl Iterator> + '_ { + self.locate_same_infringements(infringer, self.layout().joints_rtree().as_ref()) + } + + pub fn locate_joint_segment_infringements( + &self, + infringer: JointId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref()) + } + + pub fn locate_joint_via_infringements( + &self, + infringer: JointId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref()) + } + + pub fn locate_joint_polygon_infringements( + &self, + infringer: JointId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref()) + } + + pub fn locate_segment_infringements( + &self, + infringer: SegmentId, + ) -> impl Iterator> + '_ { + self.locate_segment_joint_infringements(infringer) + .map(Into::into) + .chain( + self.locate_segment_segment_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_segment_via_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_segment_polygon_infringements(infringer) + .map(Into::into), + ) + } + + pub fn locate_segment_joint_infringements( + &self, + infringer: SegmentId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref()) + } + + pub fn locate_segment_segment_infringements( + &self, + infringer: SegmentId, + ) -> impl Iterator> + '_ { + self.locate_same_infringements(infringer, self.layout().segments_rtree().as_ref()) + } + + pub fn locate_segment_via_infringements( + &self, + infringer: SegmentId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref()) + } + + pub fn locate_segment_polygon_infringements( + &self, + infringer: SegmentId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref()) + } + + pub fn locate_via_infringements( + &self, + infringer: ViaId, + ) -> impl Iterator> + '_ { + self.locate_via_joint_infringements(infringer) + .map(Into::into) + .chain( + self.locate_via_segment_infringements(infringer) + .map(Into::into), + ) + .chain(self.locate_via_via_infringements(infringer).map(Into::into)) + .chain( + self.locate_via_polygon_infringements(infringer) + .map(Into::into), + ) + } + + pub fn locate_via_joint_infringements( + &self, + infringer: ViaId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref()) + } + + pub fn locate_via_segment_infringements( + &self, + infringer: ViaId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref()) + } + + pub fn locate_via_via_infringements( + &self, + infringer: ViaId, + ) -> impl Iterator> + '_ { + self.locate_same_infringements(infringer, self.layout().vias_rtree().as_ref()) + } + + pub fn locate_via_polygon_infringements( + &self, + infringer: ViaId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref()) + } + + pub fn locate_polygon_infringements( + &self, + infringer: PolygonId, + ) -> impl Iterator> + '_ { + self.locate_polygon_joint_infringements(infringer) + .map(Into::into) + .chain( + self.locate_polygon_segment_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_polygon_via_infringements(infringer) + .map(Into::into), + ) + .chain( + self.locate_polygon_polygon_infringements(infringer) + .map(Into::into), + ) + } + + pub fn locate_polygon_joint_infringements( + &self, + infringer: PolygonId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref()) + } + + pub fn locate_polygon_segment_infringements( + &self, + infringer: PolygonId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref()) + } + + pub fn locate_polygon_via_infringements( + &self, + infringer: PolygonId, + ) -> impl Iterator> + '_ { + self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref()) + } + + pub fn locate_polygon_polygon_infringements( + &self, + infringer: PolygonId, + ) -> impl Iterator> + '_ { + self.locate_same_infringements(infringer, self.layout().polygons_rtree().as_ref()) + } + + fn locate_cross_infringements< + 'a, + I: Copy + Into + 'a, + E: Copy + Into, + >( + &'a self, + infringer: I, + infringee_tree: &'a RTree, E>>, + ) -> impl Iterator> + 'a { + infringee_tree + .locate_in_envelope_intersecting(&self.primitive_bbox_envelope(infringer.into())) + .map(|infringee_geom| infringee_geom.data) + .filter(move |&infringee| { + !Self::nets_match( + self.primitive_net(infringer.into()), + self.primitive_net(infringee.into()), + ) + }) + .map(move |infringee| Infringement { + infringer, + infringee, + }) + } + + fn locate_same_infringements<'a, I: Copy + PartialEq + Into + 'a>( + &'a self, + infringer: I, + rtree: &'a RTree, I>>, + ) -> impl Iterator> + 'a { + rtree + .locate_in_envelope_intersecting(&self.primitive_bbox_envelope(infringer.into())) + .map(|infringee_geom| infringee_geom.data) + .filter(move |&infringee| infringee != infringer) + .filter(move |&infringee| { + !Self::nets_match( + self.primitive_net(infringer.into()), + self.primitive_net(infringee.into()), + ) + }) + .map(move |infringee| Infringement { + infringer, + infringee, + }) + } + + fn primitive_component(&self, primitive: PrimitiveId) -> Option { + match primitive { + PrimitiveId::Joint(joint_id) => self.layout().joint(joint_id).spec.component, + PrimitiveId::Segment(segment_id) => self.layout().segment(segment_id).spec.component, + PrimitiveId::Via(via_id) => self.layout().via(via_id).spec.component, + PrimitiveId::Polygon(polygon_id) => self.layout().polygon(polygon_id).component, + } + } + + fn primitive_bbox_envelope(&self, primitive: PrimitiveId) -> AABB<[i64; 3]> { + match primitive { + PrimitiveId::Joint(joint_id) => self.layout().joint(joint_id).bbox().aabb(), + PrimitiveId::Segment(segment_id) => self.layout().segment(segment_id).bbox().aabb(), + PrimitiveId::Via(via_id) => self.layout().via(via_id).bbox().aabb(), + PrimitiveId::Polygon(polygon_id) => self.layout().polygon(polygon_id).bbox().aabb(), + } + } + + fn primitive_net(&self, primitive: PrimitiveId) -> Option { + match primitive { + PrimitiveId::Joint(joint_id) => self.layout().joint(joint_id).spec.net, + PrimitiveId::Segment(segment_id) => self.layout().segment(segment_id).net, + PrimitiveId::Via(via_id) => self.layout().via(via_id).net, + PrimitiveId::Polygon(polygon_id) => self.layout().polygon(polygon_id).net, + } + } + + fn nets_match(a: Option, b: Option) -> bool { + matches!((a, b), (Some(net_a), Some(net_b)) if net_a == net_b) + } +} diff --git a/topola/src/layout/mod.rs b/topola/src/layout/mod.rs index 371734f..a9b3cc8 100644 --- a/topola/src/layout/mod.rs +++ b/topola/src/layout/mod.rs @@ -5,6 +5,7 @@ mod bbox; pub mod compounds; mod delete; +mod infringement; mod insert; mod locate; mod modify; diff --git a/topola/src/layout/primitives/mod.rs b/topola/src/layout/primitives/mod.rs index 3846b98..e9f7d72 100644 --- a/topola/src/layout/primitives/mod.rs +++ b/topola/src/layout/primitives/mod.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use derive_more::From; use serde::{Deserialize, Serialize}; mod joint; @@ -14,7 +15,7 @@ pub use polygon::*; pub use segment::*; pub use via::*; -#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, From, Ord, PartialEq, PartialOrd, Serialize)] pub enum PrimitiveId { Joint(JointId), Segment(SegmentId),