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/remove_bands",
|
||||
"autorouter/selection",
|
||||
"board/mod",
|
||||
"board/edit",
|
||||
"drawing/band",
|
||||
"drawing/bend",
|
||||
"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 derive_getters::Getters;
|
||||
use geo::Point;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{
|
||||
bimapset::BiBTreeMapSet,
|
||||
drawing::{
|
||||
band::BandUid,
|
||||
dot::{FixedDotIndex, FixedDotWeight},
|
||||
|
|
@ -82,7 +82,7 @@ pub struct Board<M> {
|
|||
bands_by_id: BiBTreeMap<EtchedPath, BandUid>,
|
||||
// TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed.
|
||||
#[getter(skip)]
|
||||
node_to_pinname: BTreeMap<NodeIndex, String>,
|
||||
pinname_nodes: BiBTreeMapSet<String, NodeIndex>,
|
||||
#[getter(skip)]
|
||||
band_bandname: BiBTreeMap<BandUid, BandName>,
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ impl<M> Board<M> {
|
|||
Self {
|
||||
layout,
|
||||
bands_by_id: BiBTreeMap::new(),
|
||||
node_to_pinname: BTreeMap::new(),
|
||||
pinname_nodes: BiBTreeMapSet::new(),
|
||||
band_bandname: BiBTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -119,8 +119,8 @@ impl<M: AccessMesadata> Board<M> {
|
|||
.add_fixed_dot_infringably(&mut recorder.layout_edit, weight);
|
||||
|
||||
if let Some(pin) = maybe_pin {
|
||||
self.node_to_pinname
|
||||
.insert(GenericNode::Primitive(dot.into()), pin);
|
||||
self.pinname_nodes
|
||||
.insert(pin, GenericNode::Primitive(dot.into()));
|
||||
}
|
||||
|
||||
dot
|
||||
|
|
@ -142,8 +142,8 @@ impl<M: AccessMesadata> Board<M> {
|
|||
.add_fixed_seg_infringably(&mut recorder.layout_edit, from, to, weight);
|
||||
|
||||
if let Some(pin) = maybe_pin {
|
||||
self.node_to_pinname
|
||||
.insert(GenericNode::Primitive(seg.into()), pin);
|
||||
self.pinname_nodes
|
||||
.insert(pin, GenericNode::Primitive(seg.into()));
|
||||
}
|
||||
|
||||
seg
|
||||
|
|
@ -166,34 +166,43 @@ impl<M: AccessMesadata> Board<M> {
|
|||
|
||||
if let Some(pin) = maybe_pin {
|
||||
for i in nodes {
|
||||
self.node_to_pinname
|
||||
.insert(GenericNode::Primitive(*i), pin.clone());
|
||||
self.pinname_nodes
|
||||
.insert(pin.clone(), GenericNode::Primitive(*i));
|
||||
}
|
||||
|
||||
self.node_to_pinname
|
||||
.insert(GenericNode::Primitive(apex.into()), pin.clone());
|
||||
self.pinname_nodes
|
||||
.insert(pin.clone(), GenericNode::Primitive(apex.into()));
|
||||
|
||||
self.node_to_pinname
|
||||
.insert(GenericNode::Compound(poly.into()), pin);
|
||||
self.pinname_nodes
|
||||
.insert(pin, GenericNode::Compound(poly.into()));
|
||||
}
|
||||
|
||||
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 an iterator over the set of all nodes associated with a given
|
||||
/// pin name.
|
||||
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
|
||||
///
|
||||
/// Warning: this is very slow.
|
||||
/// Returns the pin name associated with a given node.
|
||||
pub fn node_pinname(&self, node: &NodeIndex) -> Option<&String> {
|
||||
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)> {
|
||||
self.node_to_pinname
|
||||
.iter()
|
||||
.filter(|(_, node_pin)| *node_pin == pin)
|
||||
// this should only ever return one result
|
||||
.find_map(|(node, _)| self.layout().apex_of_compoundless_node(*node, layer))
|
||||
self.pinname_nodes
|
||||
.get_values(pin)
|
||||
.map(|node| {
|
||||
node.iter()
|
||||
.find_map(|node| self.layout().apex_of_compoundless_node(*node, layer))
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Returns the band name associated with a given band.
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ use crate::{
|
|||
primitive::PrimitiveShape,
|
||||
recording_with_rtree::RecordingGeometryWithRtree,
|
||||
with_rtree::BboxedIndex,
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
||||
GetLayer, GetOffset, GetSetPos, GetWidth,
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer,
|
||||
GetOffset, GetSetPos, GetWidth,
|
||||
},
|
||||
graph::{GenericIndex, GetIndex, MakeRef},
|
||||
graph::{GenericIndex, MakeRef},
|
||||
math::{NoBitangents, RotationSense},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
use std::collections::btree_map::Entry as BTreeMapEntry;
|
||||
|
||||
use geo::Point;
|
||||
use petgraph::stable_graph::StableDiGraph;
|
||||
use rstar::RTree;
|
||||
|
||||
use crate::graph::{GenericIndex, GetIndex};
|
||||
|
|
@ -14,8 +13,8 @@ use super::{
|
|||
compound::ManageCompounds,
|
||||
edit::{ApplyGeometryEdit, GeometryEdit},
|
||||
with_rtree::{BboxedIndex, GeometryWithRtree},
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
||||
GetLayer, GetWidth, Retag,
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer, GetWidth,
|
||||
Retag,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
use contracts_try::debug_invariant;
|
||||
use derive_getters::Getters;
|
||||
use geo::Point;
|
||||
use petgraph::{stable_graph::StableDiGraph, visit::Walker};
|
||||
use petgraph::visit::Walker;
|
||||
use rstar::{primitives::GeomWithData, Envelope, RTree, RTreeObject, AABB};
|
||||
|
||||
use crate::{
|
||||
geometry::{
|
||||
compound::ManageCompounds,
|
||||
primitive::{AccessPrimitiveShape, PrimitiveShape},
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GeometryLabel,
|
||||
GetLayer, GetWidth, Retag,
|
||||
AccessBendWeight, AccessDotWeight, AccessSegWeight, GenericNode, Geometry, GetLayer,
|
||||
GetWidth, Retag,
|
||||
},
|
||||
graph::{GenericIndex, GetIndex},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ pub mod graph;
|
|||
#[macro_use]
|
||||
pub mod drawing;
|
||||
pub mod autorouter;
|
||||
pub mod bimapset;
|
||||
pub mod board;
|
||||
pub mod geometry;
|
||||
pub mod interactor;
|
||||
|
|
|
|||
Loading…
Reference in New Issue