Display repulsions and attractions of selected pins and components

This commit is contained in:
Mikolaj Wielgus 2026-06-03 02:20:45 +02:00
parent 4c6f5b4c43
commit 05108e964d
5 changed files with 225 additions and 5 deletions

View File

@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{viewport::Viewport, workspace::GuiWorkspace};
use topola::Workspace;
use topola::primitives::{Joint, Polygon, Segment, Via};
use topola::{Orientation, Vector2, Workspace};
pub struct Display {}
@ -22,6 +22,8 @@ impl Display {
workspace: &GuiWorkspace,
) {
self.display_layout(ctx, ui, /*menu_bar,*/ viewport, workspace);
self.display_repulsions(ui, viewport, workspace);
self.display_attractions(ui, viewport, workspace);
self.display_bboxes(ctx, ui, viewport, workspace);
self.display_navmeshes(ctx, ui, viewport, workspace);
self.display_ratsnest(ctx, ui, viewport, workspace);
@ -156,6 +158,109 @@ impl Display {
);
}
fn display_repulsions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &GuiWorkspace) {
let board = workspace.workspace.board();
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::YELLOW);
for selector in &workspace.workspace.selection().components.0 {
let Some(component_id) = board.component_id(&selector.component) else {
continue;
};
let Some(bbox) = board.layout().component_bbox2(component_id) else {
continue;
};
let origin = Vector2::new((bbox.min.x + bbox.max.x) / 2, (bbox.min.y + bbox.max.y) / 2);
Self::paint_arrows(
ui,
origin,
board
.layout()
.locate_component_repulsions(component_id, Orientation::Oblique),
stroke,
);
}
for selector in &workspace.workspace.selection().pins.0 {
let Some(pin_id) = board.pin_id(&selector.pin) else {
continue;
};
Self::paint_arrows(
ui,
board.layout().pin_centroid(pin_id),
board
.layout()
.locate_pin_repulsions(pin_id, Orientation::Oblique),
stroke,
);
}
}
fn display_attractions(
&mut self,
ui: &egui::Ui,
viewport: &Viewport,
workspace: &GuiWorkspace,
) {
let board = workspace.workspace.board();
let layout = board.layout();
let stroke = egui::Stroke::new(150.0 / viewport.scale_factor(), egui::Color32::BLUE);
for selector in &workspace.workspace.selection().components.0 {
let Some(component_id) = board.component_id(&selector.component) else {
continue;
};
let Some(bbox) = layout.component_bbox2(component_id) else {
continue;
};
let origin = Vector2::new((bbox.min.x + bbox.max.x) / 2, (bbox.min.y + bbox.max.y) / 2);
Self::paint_arrows(
ui,
origin,
layout.component_attractions(component_id),
stroke,
);
}
for selector in &workspace.workspace.selection().pins.0 {
let Some(pin_id) = board.pin_id(&selector.pin) else {
continue;
};
Self::paint_arrows(
ui,
layout.pin_centroid(pin_id),
layout.pin_attractions(pin_id),
stroke,
);
}
}
fn paint_arrows(
ui: &egui::Ui,
origin: Vector2<i64>,
repulsions: impl IntoIterator<Item = Vector2<i64>>,
stroke: egui::Stroke,
) {
for repulsion in repulsions {
if repulsion.x == 0 && repulsion.y == 0 {
continue;
}
ui.painter().arrow(
egui::pos2(origin.x as f32, origin.y as f32),
egui::vec2(repulsion.x as f32, repulsion.y as f32),
stroke,
);
}
}
fn paint_joint(
&mut self,
ctx: &egui::Context,

View File

@ -11,6 +11,7 @@ use crate::{
compounds::{ComponentId, NetId},
primitives::{JointId, PolygonId, SegmentId, ViaId},
},
primitives::PrimitiveId,
};
#[derive(
@ -61,6 +62,23 @@ impl Pin {
polygons: Vec::new(),
}
}
pub fn primitives(&self) -> impl Iterator<Item = PrimitiveId> + '_ {
self.joints
.iter()
.map(|&joint_id| PrimitiveId::Joint(joint_id))
.chain(
self.segments
.iter()
.map(|&segment_id| PrimitiveId::Segment(segment_id)),
)
.chain(self.vias.iter().map(|&via_id| PrimitiveId::Via(via_id)))
.chain(
self.polygons
.iter()
.map(|&polygon_id| PrimitiveId::Polygon(polygon_id)),
)
}
}
impl Layout {

View File

@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
use crate::primitives::PrimitiveId;
use super::Layout;
use super::compounds::{ComponentId, NetId};
use super::compounds::{ComponentId, NetId, PinId};
use super::primitives::{JointId, PolygonId, SegmentId, ViaId};
#[derive(
@ -140,6 +140,60 @@ impl Layout {
.chain(polygon_infringements)
}
pub fn locate_pin_infringements(
&self,
infringer: PinId,
) -> impl Iterator<Item = Infringement<PinId, PinId>> + '_ {
let mut infringee_pins = BTreeSet::new();
for infringement in self.locate_pin_primitive_infringements(infringer) {
let Some(infringee_pin) = self.primitive_pin(infringement.infringee()) else {
continue;
};
if infringee_pin == infringer {
continue;
}
infringee_pins.insert(infringee_pin);
}
infringee_pins
.into_iter()
.map(move |infringee| Infringement {
infringer,
infringee,
})
}
pub fn locate_pin_primitive_infringements(
&self,
infringer: PinId,
) -> impl Iterator<Item = Infringement> + '_ {
let pin = self.pin(infringer);
pin.joints
.iter()
.copied()
.flat_map(|joint_id| self.locate_joint_infringements(joint_id).map(Into::into))
.chain(
pin.segments.iter().copied().flat_map(|segment_id| {
self.locate_segment_infringements(segment_id)
.map(Into::into)
}),
)
.chain(
pin.vias
.iter()
.copied()
.flat_map(|via_id| self.locate_via_infringements(via_id).map(Into::into)),
)
.chain(pin.polygons.iter().copied().flat_map(|polygon_id| {
self.locate_polygon_infringements(polygon_id)
.map(Into::into)
}))
}
pub fn locate_joint_infringements(
&self,
infringer: JointId,

View File

@ -5,7 +5,10 @@
use crate::{
Rect2, Vector2,
compass::CompassDirection,
layout::{Layout, compounds::ComponentId},
layout::{
Layout,
compounds::{ComponentId, PinId},
},
orientation::Orientation,
primitives::{JointId, PolygonId, PrimitiveId, SegmentId, ViaId},
};
@ -35,8 +38,47 @@ impl Layout {
let mut max_repulsion = Vector2::new(0, 0);
let mut max_repulsion_magnitude = 0;
for infringer_primitive in self.component(infringer).primitives() {
for infringee_primitive in self.component(infringee).primitives() {
for &infringer_pin in self.component(infringer).pins.iter() {
for &infringee_pin in self.component(infringee).pins.iter() {
let repulsion = self.pin_pin_repulsion(infringer_pin, infringee_pin, orientation);
let repulsion_magnitude = repulsion.x.abs() + repulsion.y.abs();
if repulsion_magnitude > max_repulsion_magnitude {
max_repulsion = repulsion;
max_repulsion_magnitude = repulsion_magnitude;
}
}
}
max_repulsion
}
pub fn locate_pin_repulsions(
&self,
infringer: PinId,
orientation: Orientation,
) -> impl Iterator<Item = Vector2<i64>> + '_ {
self.locate_pin_infringements(infringer)
.map(move |infringement| {
self.pin_pin_repulsion(
infringement.infringer(),
infringement.infringee(),
orientation,
)
})
}
pub fn pin_pin_repulsion(
&self,
infringer: PinId,
infringee: PinId,
orientation: Orientation,
) -> Vector2<i64> {
let mut max_repulsion = Vector2::new(0, 0);
let mut max_repulsion_magnitude = 0;
for infringer_primitive in self.pin(infringer).primitives() {
for infringee_primitive in self.pin(infringee).primitives() {
let repulsion = self.primitive_primitive_repulsion(
infringer_primitive,
infringee_primitive,

View File

@ -32,6 +32,7 @@ pub use crate::layout::LayerId;
pub use crate::layout::Layout;
pub use crate::layout::compounds::{Pin, PinId};
pub use crate::layout::primitives;
pub use crate::orientation::Orientation;
pub use crate::ratsnest::{Ratline, Ratsnest};
pub use crate::rect::{Rect2, Rect3};
pub use crate::vector::{Vector2, Vector3};