use std::{cmp::Ordering, collections::HashMap}; use bimap::BiHashMap; use derive_getters::Getters; use serde::{Deserialize, Serialize}; use crate::{ board::mesadata::AccessMesadata, drawing::{ band::BandUid, bend::{BendIndex, BendWeight}, dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight}, graph::{GetLayer, GetMaybeNet, PrimitiveIndex, PrimitiveWeight}, seg::{FixedSegIndex, FixedSegWeight, SegIndex, SegWeight}, }, geometry::{edit::ApplyGeometryEdit, shape::AccessShape, GenericNode}, graph::GenericIndex, layout::{ poly::{GetMaybeApex, MakePolyShape, PolyWeight}, CompoundWeight, Layout, LayoutEdit, NodeIndex, }, math::Circle, }; /// Represents a band between two pins. #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BandName(String, String); impl BandName { /// Creates a new [`BandName`] and manages their order. /// /// This function ensures that the two pin names are sorted in lexicographical order, so that the smaller name always comes first. pub fn new(pinname1: String, pinname2: String) -> Self { if pinname1.cmp(&pinname2) == Ordering::Greater { BandName(pinname2, pinname1) } else { BandName(pinname1, pinname2) } } } /// Represents a board layout and its associated metadata. /// /// The struct manages the relationships between board's layout, /// and its compounds, as well as provides methods to manipulate them. #[derive(Debug, Getters)] pub struct Board { layout: Layout, // TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed. #[getter(skip)] node_to_pinname: HashMap, #[getter(skip)] band_bandname: BiHashMap, } impl Board { /// Initializes the board with given [`Layout`] pub fn new(layout: Layout) -> Self { Self { layout, node_to_pinname: HashMap::new(), band_bandname: BiHashMap::new(), } } /// Adds a new fixed dot with an optional pin name. /// /// Inserts the dot into the layout and, if a pin name is provided, maps it to the created dot's node. pub fn add_fixed_dot_infringably( &mut self, recorder: &mut LayoutEdit, weight: FixedDotWeight, maybe_pin: Option, ) -> FixedDotIndex { let dot = self.layout.add_fixed_dot_infringably(recorder, weight); if let Some(ref pin) = maybe_pin { self.node_to_pinname .insert(GenericNode::Primitive(dot.into()), pin.clone()); } dot } /// Adds a fixed segment between two dots with an optional pin name. /// /// Adds the segment to the layout and maps the pin name to the created segment if provided. pub fn add_poly_fixed_dot_infringably( &mut self, recorder: &mut LayoutEdit, weight: FixedDotWeight, poly: GenericIndex, ) -> FixedDotIndex { let dot = self .layout .add_poly_fixed_dot_infringably(recorder, weight, poly); if let Some(pin) = self.node_pinname(&GenericNode::Compound(poly.into())) { self.node_to_pinname .insert(GenericNode::Primitive(dot.into()), pin.to_string()); } dot } /// Adds a fixed segment associated with a polygon in the layout. /// /// Adds the segment to the layout and updates the internal mapping if necessary. pub fn add_fixed_seg_infringably( &mut self, recorder: &mut LayoutEdit, from: FixedDotIndex, to: FixedDotIndex, weight: FixedSegWeight, maybe_pin: Option, ) -> FixedSegIndex { let seg = self .layout .add_fixed_seg_infringably(recorder, from, to, weight); if let Some(pin) = maybe_pin { self.node_to_pinname .insert(GenericNode::Primitive(seg.into()), pin.to_string()); } seg } /// Adds a fixed segment associated with a polygon in the layout. /// /// Adds the segment to the layout and updates the internal mapping if necessary. pub fn add_poly_fixed_seg_infringably( &mut self, recorder: &mut LayoutEdit, from: FixedDotIndex, to: FixedDotIndex, weight: FixedSegWeight, poly: GenericIndex, ) -> FixedSegIndex { let seg = self .layout .add_poly_fixed_seg_infringably(recorder, from, to, weight, poly); if let Some(pin) = self.node_pinname(&GenericNode::Compound(poly.into())) { self.node_to_pinname .insert(GenericNode::Primitive(seg.into()), pin.to_string()); } seg } /// Adds a new polygon to the layout with an optional pin name. /// /// Inserts the polygon into the layout and, if a pin name is provided, maps it to the created polygon's node. pub fn add_poly( &mut self, recorder: &mut LayoutEdit, weight: PolyWeight, maybe_pin: Option, ) -> GenericIndex { let poly = self.layout.add_poly(recorder, weight); if let Some(pin) = maybe_pin { self.node_to_pinname .insert(GenericNode::Compound(poly.into()), pin.to_string()); } poly } /// Retrieves or creates the apex (center point) of a polygon in the layout. /// /// If the polygon already has an apex, returns it. Otherwise, creates and returns a new fixed dot as the apex. pub fn poly_apex( &mut self, recorder: &mut LayoutEdit, poly: GenericIndex, ) -> FixedDotIndex { if let Some(apex) = self.layout.poly(poly).maybe_apex() { apex } else { self.add_poly_fixed_dot_infringably( recorder, FixedDotWeight { circle: Circle { pos: self.layout.poly(poly).shape().center(), r: 100.0, }, layer: self.layout.poly(poly).layer(), maybe_net: self.layout.poly(poly).maybe_net(), }, poly, ) } } /// Returns the pin name associated with a given node. pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> { self.node_to_pinname.get(node) } /// Returns the band name associated with a given band. pub fn band_bandname(&self, band: &BandUid) -> Option<&BandName> { self.band_bandname.get_by_left(band) } /// Returns the unique id associated with a given band name. pub fn bandname_band(&self, bandname: &BandName) -> Option<&BandUid> { self.band_bandname.get_by_right(bandname) } /// Creates band between the two nodes pub fn try_set_band_between_nodes( &mut self, source: FixedDotIndex, target: FixedDotIndex, band: BandUid, ) { let source_pinname = self .node_pinname(&GenericNode::Primitive(source.into())) .unwrap() .to_string(); let target_pinname = self .node_pinname(&GenericNode::Primitive(target.into())) .unwrap() .to_string(); self.band_bandname .insert(band, BandName::new(source_pinname, target_pinname)); } /// Finds a band between two pin names. pub fn band_between_pins(&self, pinname1: &str, pinname2: &str) -> Option { if let Some(band) = self .band_bandname .get_by_right(&BandName::new(pinname1.to_string(), pinname2.to_string())) { Some(*band) } else if let Some(band) = self .band_bandname .get_by_right(&BandName::new(pinname2.to_string(), pinname1.to_string())) { Some(*band) } else { None } } /// Returns the mesadata associated with the layout's drawing rules. pub fn mesadata(&self) -> &M { self.layout.drawing().rules() } /// Returns a mutable reference to the layout, allowing modifications. pub fn layout_mut(&mut self) -> &mut Layout { &mut self.layout } } impl ApplyGeometryEdit< PrimitiveWeight, DotWeight, SegWeight, BendWeight, CompoundWeight, PrimitiveIndex, DotIndex, SegIndex, BendIndex, > for Board { fn apply(&mut self, edit: LayoutEdit) { self.layout.apply(edit); } }