mirror of https://codeberg.org/topola/topola.git
Add methods to locate primitive and component infringements
This commit is contained in:
parent
ae211b302e
commit
ce7db181a7
|
|
@ -49,15 +49,6 @@ impl<T: Copy + PartialOrd + Signed + Zero> CompassDirection<T> 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 {
|
fn turn_counterclockwise(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::East => Self::North,
|
Self::East => Self::North,
|
||||||
|
|
@ -66,6 +57,15 @@ impl<T: Copy + PartialOrd + Signed + Zero> CompassDirection<T> for CardinalDirec
|
||||||
Self::South => Self::East,
|
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)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -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<I = PrimitiveId, E = PrimitiveId> {
|
||||||
|
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<PrimitiveId> {}
|
||||||
|
|
||||||
|
impl IntoPrimitiveId for JointId {}
|
||||||
|
impl IntoPrimitiveId for SegmentId {}
|
||||||
|
impl IntoPrimitiveId for ViaId {}
|
||||||
|
impl IntoPrimitiveId for PolygonId {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: sealed::IntoPrimitiveId, E: sealed::IntoPrimitiveId> From<Infringement<I, E>>
|
||||||
|
for Infringement<I, PrimitiveId>
|
||||||
|
where
|
||||||
|
E: sealed::IntoPrimitiveId,
|
||||||
|
{
|
||||||
|
fn from(from: Infringement<I, E>) -> Self {
|
||||||
|
Infringement {
|
||||||
|
infringer: from.infringer,
|
||||||
|
infringee: from.infringee.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: sealed::IntoPrimitiveId, E: sealed::IntoPrimitiveId> From<Infringement<I, E>>
|
||||||
|
for Infringement
|
||||||
|
{
|
||||||
|
fn from(from: Infringement<I, E>) -> Self {
|
||||||
|
Infringement {
|
||||||
|
infringer: from.infringer.into(),
|
||||||
|
infringee: from.infringee.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: sealed::IntoPrimitiveId> From<Infringement<I, PrimitiveId>> for Infringement {
|
||||||
|
fn from(from: Infringement<I, PrimitiveId>) -> Self {
|
||||||
|
Infringement {
|
||||||
|
infringer: from.infringer.into(),
|
||||||
|
infringee: from.infringee,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
pub fn locate_component_component_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: ComponentId,
|
||||||
|
) -> impl Iterator<Item = Infringement<ComponentId, ComponentId>> + '_ {
|
||||||
|
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<Item = Infringement> + '_ {
|
||||||
|
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<Item = Infringement<JointId>> + '_ {
|
||||||
|
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<Item = Infringement<JointId, JointId>> + '_ {
|
||||||
|
self.locate_same_infringements(infringer, self.layout().joints_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_joint_segment_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: JointId,
|
||||||
|
) -> impl Iterator<Item = Infringement<JointId, SegmentId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_joint_via_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: JointId,
|
||||||
|
) -> impl Iterator<Item = Infringement<JointId, ViaId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_joint_polygon_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: JointId,
|
||||||
|
) -> impl Iterator<Item = Infringement<JointId, PolygonId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_segment_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: SegmentId,
|
||||||
|
) -> impl Iterator<Item = Infringement<SegmentId>> + '_ {
|
||||||
|
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<Item = Infringement<SegmentId, JointId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_segment_segment_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: SegmentId,
|
||||||
|
) -> impl Iterator<Item = Infringement<SegmentId, SegmentId>> + '_ {
|
||||||
|
self.locate_same_infringements(infringer, self.layout().segments_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_segment_via_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: SegmentId,
|
||||||
|
) -> impl Iterator<Item = Infringement<SegmentId, ViaId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_segment_polygon_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: SegmentId,
|
||||||
|
) -> impl Iterator<Item = Infringement<SegmentId, PolygonId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_via_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: ViaId,
|
||||||
|
) -> impl Iterator<Item = Infringement<ViaId>> + '_ {
|
||||||
|
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<Item = Infringement<ViaId, JointId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_via_segment_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: ViaId,
|
||||||
|
) -> impl Iterator<Item = Infringement<ViaId, SegmentId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_via_via_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: ViaId,
|
||||||
|
) -> impl Iterator<Item = Infringement<ViaId, ViaId>> + '_ {
|
||||||
|
self.locate_same_infringements(infringer, self.layout().vias_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_via_polygon_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: ViaId,
|
||||||
|
) -> impl Iterator<Item = Infringement<ViaId, PolygonId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().polygons_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_polygon_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: PolygonId,
|
||||||
|
) -> impl Iterator<Item = Infringement<PolygonId>> + '_ {
|
||||||
|
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<Item = Infringement<PolygonId, JointId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().joints_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_polygon_segment_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: PolygonId,
|
||||||
|
) -> impl Iterator<Item = Infringement<PolygonId, SegmentId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().segments_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_polygon_via_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: PolygonId,
|
||||||
|
) -> impl Iterator<Item = Infringement<PolygonId, ViaId>> + '_ {
|
||||||
|
self.locate_cross_infringements(infringer, self.layout().vias_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn locate_polygon_polygon_infringements(
|
||||||
|
&self,
|
||||||
|
infringer: PolygonId,
|
||||||
|
) -> impl Iterator<Item = Infringement<PolygonId, PolygonId>> + '_ {
|
||||||
|
self.locate_same_infringements(infringer, self.layout().polygons_rtree().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locate_cross_infringements<
|
||||||
|
'a,
|
||||||
|
I: Copy + Into<PrimitiveId> + 'a,
|
||||||
|
E: Copy + Into<PrimitiveId>,
|
||||||
|
>(
|
||||||
|
&'a self,
|
||||||
|
infringer: I,
|
||||||
|
infringee_tree: &'a RTree<GeomWithData<Rectangle<[i64; 3]>, E>>,
|
||||||
|
) -> impl Iterator<Item = Infringement<I, E>> + '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<PrimitiveId> + 'a>(
|
||||||
|
&'a self,
|
||||||
|
infringer: I,
|
||||||
|
rtree: &'a RTree<GeomWithData<Rectangle<[i64; 3]>, I>>,
|
||||||
|
) -> impl Iterator<Item = Infringement<I, I>> + '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<ComponentId> {
|
||||||
|
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<NetId> {
|
||||||
|
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<NetId>, b: Option<NetId>) -> bool {
|
||||||
|
matches!((a, b), (Some(net_a), Some(net_b)) if net_a == net_b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
mod bbox;
|
mod bbox;
|
||||||
pub mod compounds;
|
pub mod compounds;
|
||||||
mod delete;
|
mod delete;
|
||||||
|
mod infringement;
|
||||||
mod insert;
|
mod insert;
|
||||||
mod locate;
|
mod locate;
|
||||||
mod modify;
|
mod modify;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use derive_more::From;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod joint;
|
mod joint;
|
||||||
|
|
@ -14,7 +15,7 @@ pub use polygon::*;
|
||||||
pub use segment::*;
|
pub use segment::*;
|
||||||
pub use via::*;
|
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 {
|
pub enum PrimitiveId {
|
||||||
Joint(JointId),
|
Joint(JointId),
|
||||||
Segment(SegmentId),
|
Segment(SegmentId),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue