diff --git a/committed.toml b/committed.toml index f6b6cc7..b9f2c05 100644 --- a/committed.toml +++ b/committed.toml @@ -70,6 +70,7 @@ allowed_scopes = [ "layout/collect_bands", "layout/layout", "layout/poly", + "layout/query", "layout/via", "math/circle", "math/cyclic_search", diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index 8bfe03d..e9b38a6 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -16,9 +16,9 @@ use crate::{ permutator::PlanarAutorouteExecutionPermutator, planner::Planner, }, board::{AccessMesadata, Board}, - drawing::{band::BandTermsegIndex, Infringement}, + drawing::band::BandTermsegIndex, graph::MakeRef, - layout::{via::ViaWeight, LayoutEdit}, + layout::{via::ViaWeight, LayoutEdit, LayoutException}, router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions}, triangulation::GetTrianvertexNodeIndex, }; @@ -59,7 +59,7 @@ pub enum AutorouterError { #[error("TopoNavmesh generation failed: {0}")] TopoNavmeshGeneration(#[from] ng::NavmeshCalculationError), #[error("could not place via")] - CouldNotPlaceVia(#[from] Infringement), + CouldNotPlaceVia(#[from] LayoutException), #[error("could not remove band")] CouldNotRemoveBand(BandTermsegIndex), #[error("need exactly two ratlines")] diff --git a/src/board/mod.rs b/src/board/mod.rs index 8a52543..980ab89 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -21,11 +21,13 @@ use crate::{ dot::{FixedDotIndex, FixedDotWeight}, graph::PrimitiveIndex, seg::{FixedSegIndex, FixedSegWeight}, - DrawingException, Infringement, + DrawingException, }, geometry::{edit::ApplyGeometryEdit, GenericNode, GetLayer}, graph::{GenericIndex, MakeRef}, - layout::{poly::PolyWeight, via::ViaWeight, CompoundWeight, Layout, NodeIndex}, + layout::{ + poly::PolyWeight, via::ViaWeight, CompoundWeight, Layout, LayoutException, NodeIndex, + }, router::ng::EtchedPath, }; @@ -110,7 +112,7 @@ impl Board { recorder: &mut BoardEdit, weight: ViaWeight, maybe_pin: Option, - ) -> Result<(GenericIndex, Vec), Infringement> { + ) -> Result<(GenericIndex, Vec), LayoutException> { let (weight, dots) = self.layout.add_via(&mut recorder.layout_edit, weight)?; if let Some(pin) = maybe_pin { diff --git a/src/layout/layout.rs b/src/layout/layout.rs index f6a6f01..df55326 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -5,8 +5,9 @@ use contracts_try::debug_ensures; use derive_getters::Getters; use enum_dispatch::enum_dispatch; -use geo::Point; +use geo::{Point, Polygon}; use rstar::AABB; +use thiserror::Error; use crate::{ drawing::{ @@ -40,13 +41,23 @@ use crate::{ math::RotationSense, }; -/// Represents a weight for various compounds +#[derive(Clone, Debug, Error)] +pub enum LayoutException { + #[error(transparent)] + HasPointInPoly(#[from] HasPointInPoly), + #[error(transparent)] + Infringement(#[from] Infringement), +} + +#[derive(Clone, Debug, Error)] +#[error("(0:?) has point in {1:?}")] +pub struct HasPointInPoly(pub Polygon, pub Point); + +/// Represents a weight for various compounds. #[derive(Clone, Copy, Debug)] #[enum_dispatch(GetMaybeNet, IsInLayer)] pub enum CompoundWeight { - /// Represents the weight of a polygon compound, includes its basic [`Layout`] information Poly(PolyWeight), - /// Represents Via weight properties, containing its [`Layout`] properties Via(ViaWeight), } @@ -57,12 +68,12 @@ pub enum CompoundEntryLabel { Fillet, } -/// The alias to differ node types +/// The alias to differentiate node types. pub type NodeIndex = GenericNode>; pub type LayoutEdit = DrawingEdit; #[derive(Clone, Debug, Getters)] -/// Structure for managing the Layout design +/// Structure for managing the Layout design. pub struct Layout { pub(super) drawing: Drawing, } @@ -144,7 +155,7 @@ impl Layout { &mut self, recorder: &mut LayoutEdit, weight: ViaWeight, - ) -> Result<(GenericIndex, Vec), Infringement> { + ) -> Result<(GenericIndex, Vec), LayoutException> { let compound = self.drawing.add_compound(recorder, weight.into()); let mut dots = vec![]; @@ -158,23 +169,34 @@ impl Layout { }), ) { Ok(dot) => { + dots.push(dot); + + let maybe_enclosing_poly = self + .polys_enclosing_point_on_layers(weight.circle.pos, layer) + .next(); + + if let Some(enclosing_poly) = maybe_enclosing_poly { + // If a via is inside poly, it may not necessarily + // trigger an infringement on its primitives. To take + // this situation into account, we also check if the + // via's center is inside the poly's polygon. + self.remove_failed_via(recorder, compound, dots); + return Err(LayoutException::HasPointInPoly(HasPointInPoly( + enclosing_poly.ref_(self).shape(), + weight.circle.pos, + ))); + } + self.drawing.add_to_compound( recorder, dot, CompoundEntryLabel::Normal, compound, ); - dots.push(dot); } Err(err) => { - // Remove inserted dots. - self.drawing.remove_compound(recorder, compound); - - for dot in dots.iter().rev() { - self.drawing.remove_fixed_dot(recorder, *dot); - } - - return Err(err); + self.remove_failed_via(recorder, compound, dots); + return Err(err.into()); } } } @@ -182,6 +204,20 @@ impl Layout { Ok((GenericIndex::::new(compound.index()), dots)) } + fn remove_failed_via( + &mut self, + recorder: &mut LayoutEdit, + compound: GenericIndex, + dots: Vec, + ) { + self.drawing.remove_compound(recorder, compound); + + // Remove inserted dots. + for dot in dots.iter().rev() { + self.drawing.remove_fixed_dot(recorder, *dot); + } + } + pub fn add_fixed_dot( &mut self, recorder: &mut LayoutEdit, diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 3e619b1..0f931e3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -7,6 +7,7 @@ mod collect_bands; mod layout; pub mod poly; +pub mod query; pub mod via; pub use layout::*; diff --git a/src/layout/query.rs b/src/layout/query.rs new file mode 100644 index 0000000..d9990af --- /dev/null +++ b/src/layout/query.rs @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2025 Topola contributors +// +// SPDX-License-Identifier: MIT + +use geo::Point; +use rstar::AABB; +use specctra_core::rules::AccessRules; + +use crate::{ + drawing::graph::IsInLayer, + geometry::{shape::AccessShape, GenericNode}, + graph::{GenericIndex, GetIndex}, + layout::{poly::PolyWeight, CompoundWeight, Layout}, +}; + +impl Layout { + pub fn polys_enclosing_point_on_layers( + &self, + point: Point, + layer: usize, + ) -> impl Iterator> + '_ { + self.drawing() + .rtree() + .locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners( + [point.x(), point.y(), -f64::INFINITY], + [point.x(), point.y(), f64::INFINITY], + )) + .filter_map(move |geom| { + let node = geom.data; + + let GenericNode::Compound(compound) = node else { + return None; + }; + + let CompoundWeight::Poly(_) = self.drawing.compound_weight(compound) else { + return None; + }; + + if !self.drawing.compound_weight(compound).is_in_layer(layer) + || !self.node_shape(node).contains_point(point) + { + return None; + } + + Some(GenericIndex::::new(compound.index())) + }) + } +}