topola/src/board/board.rs

276 lines
8.6 KiB
Rust

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<M: AccessMesadata> {
layout: Layout<M>,
// TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed.
#[getter(skip)]
node_to_pinname: HashMap<NodeIndex, String>,
#[getter(skip)]
band_bandname: BiHashMap<BandUid, BandName>,
}
impl<M: AccessMesadata> Board<M> {
/// Initializes the board with given [`Layout`]
pub fn new(layout: Layout<M>) -> 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<String>,
) -> 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<PolyWeight>,
) -> 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<String>,
) -> 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<PolyWeight>,
) -> 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<String>,
) -> GenericIndex<PolyWeight> {
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<PolyWeight>,
) -> 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<BandUid> {
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<M> {
&mut self.layout
}
}
impl<M: AccessMesadata>
ApplyGeometryEdit<
PrimitiveWeight,
DotWeight,
SegWeight,
BendWeight,
CompoundWeight,
PrimitiveIndex,
DotIndex,
SegIndex,
BendIndex,
> for Board<M>
{
fn apply(&mut self, edit: LayoutEdit) {
self.layout.apply(edit);
}
}