mirror of https://codeberg.org/topola/topola.git
feat(board/mod): Make it possible to iterate over nodes under pin name
This commit is contained in:
parent
d708c7177a
commit
da37fbe2ec
|
|
@ -35,6 +35,8 @@ allowed_scopes = [
|
||||||
"autorouter/ratline",
|
"autorouter/ratline",
|
||||||
"autorouter/remove_bands",
|
"autorouter/remove_bands",
|
||||||
"autorouter/selection",
|
"autorouter/selection",
|
||||||
|
"board/mod",
|
||||||
|
"board/edit",
|
||||||
"drawing/band",
|
"drawing/band",
|
||||||
"drawing/bend",
|
"drawing/bend",
|
||||||
"drawing/cane",
|
"drawing/cane",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
collections::{BTreeMap, BTreeSet},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A bidirectional map between keys and sets of values.
|
||||||
|
///
|
||||||
|
/// - Each key can have multiple associated values (`BTreeSet<V>`).
|
||||||
|
/// - Each value maps to exactly one key (i.e., it's unique across keys).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BiBTreeMapSet<K, V> {
|
||||||
|
key_to_values: BTreeMap<K, BTreeSet<V>>, // Forward mapping: key -> set of values.
|
||||||
|
value_to_key: BTreeMap<V, K>, // Reverse mapping: value -> key.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Eq + Ord + Clone, V: Eq + Ord + Clone> BiBTreeMapSet<K, V> {
|
||||||
|
/// Creates a new, empty `BiBTreeMapSet`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
key_to_values: BTreeMap::new(),
|
||||||
|
value_to_key: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a (key, value) pair.
|
||||||
|
///
|
||||||
|
/// If the value was previously associated with a different key,
|
||||||
|
/// it is removed from the old key and reassigned to the new one.
|
||||||
|
pub fn insert(&mut self, key: K, value: V) {
|
||||||
|
// Insert the new value-to-key mapping, capturing the old key if it existed.
|
||||||
|
if let Some(old_key) = self.value_to_key.insert(value.clone(), key.clone()) {
|
||||||
|
// Remove value from the old key's set of values.
|
||||||
|
if let Some(set) = self.key_to_values.get_mut(&old_key) {
|
||||||
|
set.remove(&value);
|
||||||
|
// Clean up the old key if its set becomes empty.
|
||||||
|
if set.is_empty() {
|
||||||
|
self.key_to_values.remove(&old_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert value into the new key's set of values.
|
||||||
|
self.key_to_values
|
||||||
|
.entry(key)
|
||||||
|
.or_insert_with(BTreeSet::new)
|
||||||
|
.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the set of values associated with the given key.
|
||||||
|
pub fn get_values<Q: ?Sized + Ord>(&self, key: &Q) -> Option<&BTreeSet<V>>
|
||||||
|
where
|
||||||
|
K: Borrow<Q>,
|
||||||
|
{
|
||||||
|
self.key_to_values.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the key associated with the given value.
|
||||||
|
pub fn get_key(&self, value: &V) -> Option<&K> {
|
||||||
|
self.value_to_key.get(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a key from the forward map and all its associated values from
|
||||||
|
/// the reverse map.
|
||||||
|
pub fn remove_by_key<Q: ?Sized + Ord>(&mut self, key: &Q) -> Option<BTreeSet<V>>
|
||||||
|
where
|
||||||
|
K: Borrow<Q>,
|
||||||
|
{
|
||||||
|
if let Some(values) = self.key_to_values.remove(key) {
|
||||||
|
// Remove each value from the reverse map.
|
||||||
|
for v in &values {
|
||||||
|
self.value_to_key.remove(v);
|
||||||
|
}
|
||||||
|
Some(values)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a value from the reverse map and the key from the forward map if
|
||||||
|
/// it no longer has any values.
|
||||||
|
pub fn remove_by_value(&mut self, value: &V) -> Option<K> {
|
||||||
|
if let Some(k) = self.value_to_key.remove(value) {
|
||||||
|
// Remove the value from the key's value set.
|
||||||
|
if let Some(set) = self.key_to_values.get_mut(&k) {
|
||||||
|
set.remove(value);
|
||||||
|
// Remove the key if it no longer has any values.
|
||||||
|
if set.is_empty() {
|
||||||
|
self.key_to_values.remove(&k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(k)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,9 +13,9 @@ pub use specctra_core::mesadata::AccessMesadata;
|
||||||
use bimap::BiBTreeMap;
|
use bimap::BiBTreeMap;
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bimapset::BiBTreeMapSet,
|
||||||
drawing::{
|
drawing::{
|
||||||
band::BandUid,
|
band::BandUid,
|
||||||
dot::{FixedDotIndex, FixedDotWeight},
|
dot::{FixedDotIndex, FixedDotWeight},
|
||||||
|
|
@ -82,7 +82,7 @@ pub struct Board<M> {
|
||||||
bands_by_id: BiBTreeMap<EtchedPath, BandUid>,
|
bands_by_id: BiBTreeMap<EtchedPath, BandUid>,
|
||||||
// TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed.
|
// TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed.
|
||||||
#[getter(skip)]
|
#[getter(skip)]
|
||||||
node_to_pinname: BTreeMap<NodeIndex, String>,
|
pinname_nodes: BiBTreeMapSet<String, NodeIndex>,
|
||||||
#[getter(skip)]
|
#[getter(skip)]
|
||||||
band_bandname: BiBTreeMap<BandUid, BandName>,
|
band_bandname: BiBTreeMap<BandUid, BandName>,
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ impl<M> Board<M> {
|
||||||
Self {
|
Self {
|
||||||
layout,
|
layout,
|
||||||
bands_by_id: BiBTreeMap::new(),
|
bands_by_id: BiBTreeMap::new(),
|
||||||
node_to_pinname: BTreeMap::new(),
|
pinname_nodes: BiBTreeMapSet::new(),
|
||||||
band_bandname: BiBTreeMap::new(),
|
band_bandname: BiBTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,8 +119,8 @@ impl<M: AccessMesadata> Board<M> {
|
||||||
.add_fixed_dot_infringably(&mut recorder.layout_edit, weight);
|
.add_fixed_dot_infringably(&mut recorder.layout_edit, weight);
|
||||||
|
|
||||||
if let Some(pin) = maybe_pin {
|
if let Some(pin) = maybe_pin {
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.insert(GenericNode::Primitive(dot.into()), pin);
|
.insert(pin, GenericNode::Primitive(dot.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
dot
|
dot
|
||||||
|
|
@ -142,8 +142,8 @@ impl<M: AccessMesadata> Board<M> {
|
||||||
.add_fixed_seg_infringably(&mut recorder.layout_edit, from, to, weight);
|
.add_fixed_seg_infringably(&mut recorder.layout_edit, from, to, weight);
|
||||||
|
|
||||||
if let Some(pin) = maybe_pin {
|
if let Some(pin) = maybe_pin {
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.insert(GenericNode::Primitive(seg.into()), pin);
|
.insert(pin, GenericNode::Primitive(seg.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
seg
|
seg
|
||||||
|
|
@ -166,34 +166,43 @@ impl<M: AccessMesadata> Board<M> {
|
||||||
|
|
||||||
if let Some(pin) = maybe_pin {
|
if let Some(pin) = maybe_pin {
|
||||||
for i in nodes {
|
for i in nodes {
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.insert(GenericNode::Primitive(*i), pin.clone());
|
.insert(pin.clone(), GenericNode::Primitive(*i));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.insert(GenericNode::Primitive(apex.into()), pin.clone());
|
.insert(pin.clone(), GenericNode::Primitive(apex.into()));
|
||||||
|
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.insert(GenericNode::Compound(poly.into()), pin);
|
.insert(pin, GenericNode::Compound(poly.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
poly
|
poly
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pin name associated with a given node.
|
/// Returns an iterator over the set of all nodes associated with a given
|
||||||
pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> {
|
/// pin name.
|
||||||
self.node_to_pinname.get(node)
|
pub fn pinname_nodes(&self, pinname: &str) -> impl Iterator<Item = NodeIndex> + '_ {
|
||||||
|
self.pinname_nodes
|
||||||
|
.get_values(pinname)
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|set| set.iter().map(|node| *node))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the apex belonging to a given pin, if any
|
/// Returns the pin name associated with a given node.
|
||||||
///
|
pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> {
|
||||||
/// Warning: this is very slow.
|
self.pinname_nodes.get_key(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the apex belonging to a given pin, if there is any.
|
||||||
pub fn pin_apex(&self, pin: &str, layer: usize) -> Option<(FixedDotIndex, Point)> {
|
pub fn pin_apex(&self, pin: &str, layer: usize) -> Option<(FixedDotIndex, Point)> {
|
||||||
self.node_to_pinname
|
self.pinname_nodes
|
||||||
.iter()
|
.get_values(pin)
|
||||||
.filter(|(_, node_pin)| *node_pin == pin)
|
.map(|node| {
|
||||||
// this should only ever return one result
|
node.iter()
|
||||||
.find_map(|(node, _)| self.layout().apex_of_compoundless_node(*node, layer))
|
.find_map(|node| self.layout().apex_of_compoundless_node(*node, layer))
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the band name associated with a given band.
|
/// Returns the band name associated with a given band.
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ use crate::{
|
||||||
primitive::PrimitiveShape,
|
primitive::PrimitiveShape,
|
||||||
recording_with_rtree::RecordingGeometryWithRtree,
|
recording_with_rtree::RecordingGeometryWithRtree,
|
||||||
with_rtree::BboxedIndex,
|
with_rtree::BboxedIndex,
|
||||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer,
|
||||||
GetLayer, GetOffset, GetSetPos, GetWidth,
|
GetOffset, GetSetPos, GetWidth,
|
||||||
},
|
},
|
||||||
graph::{GenericIndex, GetIndex, MakeRef},
|
graph::{GenericIndex, MakeRef},
|
||||||
math::{NoBitangents, RotationSense},
|
math::{NoBitangents, RotationSense},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
use std::collections::btree_map::Entry as BTreeMapEntry;
|
use std::collections::btree_map::Entry as BTreeMapEntry;
|
||||||
|
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
use petgraph::stable_graph::StableDiGraph;
|
|
||||||
use rstar::RTree;
|
use rstar::RTree;
|
||||||
|
|
||||||
use crate::graph::{GenericIndex, GetIndex};
|
use crate::graph::{GenericIndex, GetIndex};
|
||||||
|
|
@ -14,8 +13,8 @@ use super::{
|
||||||
compound::ManageCompounds,
|
compound::ManageCompounds,
|
||||||
edit::{ApplyGeometryEdit, GeometryEdit},
|
edit::{ApplyGeometryEdit, GeometryEdit},
|
||||||
with_rtree::{BboxedIndex, GeometryWithRtree},
|
with_rtree::{BboxedIndex, GeometryWithRtree},
|
||||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer, GetWidth,
|
||||||
GetLayer, GetWidth, Retag,
|
Retag,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
use contracts_try::debug_invariant;
|
use contracts_try::debug_invariant;
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
use petgraph::{stable_graph::StableDiGraph, visit::Walker};
|
use petgraph::visit::Walker;
|
||||||
use rstar::{primitives::GeomWithData, Envelope, RTree, RTreeObject, AABB};
|
use rstar::{primitives::GeomWithData, Envelope, RTree, RTreeObject, AABB};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{
|
geometry::{
|
||||||
compound::ManageCompounds,
|
compound::ManageCompounds,
|
||||||
primitive::{AccessPrimitiveShape, PrimitiveShape},
|
primitive::{AccessPrimitiveShape, PrimitiveShape},
|
||||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer,
|
||||||
GetLayer, GetWidth, Retag,
|
GetWidth, Retag,
|
||||||
},
|
},
|
||||||
graph::{GenericIndex, GetIndex},
|
graph::{GenericIndex, GetIndex},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ pub mod graph;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod drawing;
|
pub mod drawing;
|
||||||
pub mod autorouter;
|
pub mod autorouter;
|
||||||
|
pub mod bimapset;
|
||||||
pub mod board;
|
pub mod board;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod interactor;
|
pub mod interactor;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue