mirror of https://codeberg.org/topola/topola.git
First crude attempt at autoplacing using simulated annealing
This commit is contained in:
parent
65f96f1fc2
commit
8df8d760c3
|
|
@ -35,6 +35,7 @@ tr-menu-route-planar-autoroute = Planar Autoroute
|
||||||
tr-menu-route-topo-autoroute = Topological planar Autoroute
|
tr-menu-route-topo-autoroute = Topological planar Autoroute
|
||||||
tr-menu-route-routed-band-width = Routed Band Width
|
tr-menu-route-routed-band-width = Routed Band Width
|
||||||
tr-menu-route-fanout-clearance = Fanout Clearance
|
tr-menu-route-fanout-clearance = Fanout Clearance
|
||||||
|
tr-menu-route-autoplace = Autoplace
|
||||||
|
|
||||||
tr-menu-debug = Debug
|
tr-menu-debug = Debug
|
||||||
tr-menu-debug-highlight-obstacles = Highlight Obstacles
|
tr-menu-debug-highlight-obstacles = Highlight Obstacles
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
|
|
||||||
pub struct Actions {
|
pub struct Actions {
|
||||||
pub file: FileActions,
|
pub file: FileActions,
|
||||||
|
pub run: RunActions,
|
||||||
pub debug: DebugActions,
|
pub debug: DebugActions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ impl Actions {
|
||||||
pub fn new(tr: &Translator) -> Self {
|
pub fn new(tr: &Translator) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file: FileActions::new(tr),
|
file: FileActions::new(tr),
|
||||||
|
run: RunActions::new(tr),
|
||||||
debug: DebugActions::new(tr),
|
debug: DebugActions::new(tr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +76,29 @@ impl FileActions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RunActions {
|
||||||
|
pub autoplace: Trigger,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunActions {
|
||||||
|
pub fn new(tr: &Translator) -> Self {
|
||||||
|
Self {
|
||||||
|
autoplace: Action::new(
|
||||||
|
tr.text("tr-menu-route-autoplace"),
|
||||||
|
egui::Modifiers::NONE,
|
||||||
|
egui::Key::Space,
|
||||||
|
)
|
||||||
|
.into_trigger(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_menu(&mut self, ctx: &egui::Context, ui: &mut egui::Ui, have_workspace: bool) {
|
||||||
|
ui.add_enabled_ui(have_workspace, |ui| {
|
||||||
|
self.autoplace.button(ctx, ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DebugActions {
|
pub struct DebugActions {
|
||||||
pub fix_step_rate: Switch,
|
pub fix_step_rate: Switch,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,12 @@ impl eframe::App for App {
|
||||||
|
|
||||||
/// Called each time the UI has to be repainted.
|
/// Called each time the UI has to be repainted.
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
self.menu_bar
|
self.menu_bar.update(
|
||||||
.update(ctx, &mut self.translator, self.content_channel.0.clone());
|
ctx,
|
||||||
|
&mut self.translator,
|
||||||
|
self.content_channel.0.clone(),
|
||||||
|
self.controller.as_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
self.update_state();
|
self.update_state();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,19 @@ use crate::{layers_panel::LayersPanel, translator::Translator};
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
pub workspace: Workspace,
|
pub workspace: Workspace,
|
||||||
pub appearance_panel: LayersPanel,
|
pub appearance_panel: LayersPanel,
|
||||||
pub master_interactor: Option<MasterInteractor>,
|
pub master_interactor: MasterInteractor,
|
||||||
pub dt_accum: f64,
|
dt_accum: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Controller {
|
impl Controller {
|
||||||
pub fn new(board: Board, tr: &Translator) -> Self {
|
pub fn new(board: Board, tr: &Translator) -> Self {
|
||||||
let appearance_panel = LayersPanel::new(&board);
|
let appearance_panel = LayersPanel::new(&board);
|
||||||
|
let workspace = Workspace::new_board(board);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
workspace: Workspace::new_board(board),
|
master_interactor: MasterInteractor::new(workspace.selection().clone()),
|
||||||
|
workspace,
|
||||||
appearance_panel,
|
appearance_panel,
|
||||||
master_interactor: None,
|
|
||||||
dt_accum: 0.0,
|
dt_accum: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,11 +60,8 @@ impl Controller {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, tr: &Translator) -> ControlFlow<()> {
|
pub fn step(&mut self, _tr: &Translator) -> ControlFlow<()> {
|
||||||
self.master_interactor
|
self.master_interactor.step(self.workspace.board_mut())
|
||||||
.as_mut()
|
|
||||||
.map(|master_interactor| master_interactor.step(self.workspace.board_mut()));
|
|
||||||
ControlFlow::Continue(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
pub fn update_appearance_panel(&mut self, ctx: &egui::Context) {
|
||||||
|
|
@ -87,13 +85,22 @@ impl Controller {
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
) {
|
) {
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||||
if let (Some(interactor), Workspace::Board(workspace)) =
|
let board_master =
|
||||||
(&mut self.master_interactor, &mut self.workspace)
|
if let MasterInteractor::Autoplacer(interactor) = &self.master_interactor {
|
||||||
{
|
Some(interactor.board_master().clone())
|
||||||
interactor.abort(&mut workspace.board);
|
} else {
|
||||||
*self.workspace.selection_mut() = interactor.selection().clone();
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Workspace::Board(workspace) = &mut self.workspace {
|
||||||
|
self.master_interactor.abort(&mut workspace.board);
|
||||||
|
|
||||||
|
if let Some(board_master) = board_master {
|
||||||
|
self.master_interactor = MasterInteractor::Board(board_master);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.workspace.selection_mut() = self.master_interactor.selection().clone();
|
||||||
}
|
}
|
||||||
self.master_interactor = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let primary_pressed = ctx.input(|i| i.pointer.button_pressed(egui::PointerButton::Primary));
|
let primary_pressed = ctx.input(|i| i.pointer.button_pressed(egui::PointerButton::Primary));
|
||||||
|
|
@ -110,21 +117,20 @@ impl Controller {
|
||||||
maybe_pointer_on_scene = Some(pointer_on_scene);
|
maybe_pointer_on_scene = Some(pointer_on_scene);
|
||||||
|
|
||||||
if primary_pressed && scene_hovered {
|
if primary_pressed && scene_hovered {
|
||||||
self.master_interactor =
|
self.master_interactor = MasterInteractor::new(self.workspace.selection().clone());
|
||||||
Some(MasterInteractor::new(self.workspace.selection().clone()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(interactor), Workspace::Board(workspace)) =
|
if let Workspace::Board(workspace) = &mut self.workspace {
|
||||||
(&mut self.master_interactor, &mut self.workspace)
|
|
||||||
{
|
|
||||||
if primary_down {
|
if primary_down {
|
||||||
interactor.hold(
|
self.master_interactor.hold(
|
||||||
&mut workspace.board,
|
&mut workspace.board,
|
||||||
self.appearance_panel.active,
|
self.appearance_panel.active,
|
||||||
pointer_on_scene,
|
pointer_on_scene,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(select_interactor) = interactor.select_interactor().as_ref() {
|
if let Some(select_interactor) =
|
||||||
|
self.master_interactor.select_interactor().as_ref()
|
||||||
|
{
|
||||||
let origin = *select_interactor.origin();
|
let origin = *select_interactor.origin();
|
||||||
let drag_rect_scene = egui::Rect::from_min_max(
|
let drag_rect_scene = egui::Rect::from_min_max(
|
||||||
egui::pos2(
|
egui::pos2(
|
||||||
|
|
@ -160,27 +166,25 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
if primary_released {
|
if primary_released {
|
||||||
if let Some(mut interactor) = self.master_interactor.take() {
|
let active = self.appearance_panel.active;
|
||||||
let active = self.appearance_panel.active;
|
let pointer_for_scene = maybe_pointer_on_scene.unwrap_or_else(|| {
|
||||||
let pointer_for_scene = maybe_pointer_on_scene.unwrap_or_else(|| {
|
self.master_interactor
|
||||||
interactor
|
.select_interactor()
|
||||||
.select_interactor()
|
.as_ref()
|
||||||
.as_ref()
|
.map(|select_interactor| *select_interactor.origin())
|
||||||
.map(|select_interactor| *select_interactor.origin())
|
.unwrap_or(Vector2::new(0, 0))
|
||||||
.unwrap_or(Vector2::new(0, 0))
|
});
|
||||||
});
|
if let Workspace::Board(workspace) = &mut self.workspace {
|
||||||
if let Workspace::Board(workspace) = &mut self.workspace {
|
self.master_interactor
|
||||||
interactor.release(&mut workspace.board, active, pointer_for_scene);
|
.release(&mut workspace.board, active, pointer_for_scene);
|
||||||
*self.workspace.selection_mut() = interactor.selection().clone();
|
*self.workspace.selection_mut() = self.master_interactor.selection().clone();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if delete_pressed {
|
if delete_pressed {
|
||||||
let mut interactor = MasterInteractor::new(self.workspace.selection().clone());
|
|
||||||
if let Workspace::Board(workspace) = &mut self.workspace {
|
if let Workspace::Board(workspace) = &mut self.workspace {
|
||||||
interactor.delete(&mut workspace.board);
|
self.master_interactor.delete(&mut workspace.board);
|
||||||
*self.workspace.selection_mut() = interactor.selection().clone();
|
*self.workspace.selection_mut() = self.master_interactor.selection().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ impl Display {
|
||||||
self.display_layout(ctx, ui, /*menu_bar,*/ viewport, workspace);
|
self.display_layout(ctx, ui, /*menu_bar,*/ viewport, workspace);
|
||||||
self.display_repulsions(ui, viewport, workspace);
|
self.display_repulsions(ui, viewport, workspace);
|
||||||
self.display_attractions(ui, viewport, workspace);
|
self.display_attractions(ui, viewport, workspace);
|
||||||
|
self.display_retentions(ui, viewport, workspace);
|
||||||
self.display_bboxes(ctx, ui, viewport, workspace);
|
self.display_bboxes(ctx, ui, viewport, workspace);
|
||||||
self.display_navmeshes(ctx, ui, viewport, workspace);
|
self.display_navmeshes(ctx, ui, viewport, workspace);
|
||||||
self.display_ratsnest(ctx, ui, viewport, workspace);
|
self.display_ratsnest(ctx, ui, viewport, workspace);
|
||||||
|
|
@ -152,8 +153,8 @@ impl Display {
|
||||||
.boundary()
|
.boundary()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| egui::Pos2 {
|
.map(|p| egui::Pos2 {
|
||||||
x: p[0] as f32,
|
x: p.x as f32,
|
||||||
y: p[1] as f32,
|
y: p.y as f32,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
egui::Stroke::new(5.0 / viewport.scale_factor(), egui::Color32::WHITE),
|
egui::Stroke::new(5.0 / viewport.scale_factor(), egui::Color32::WHITE),
|
||||||
|
|
@ -201,6 +202,47 @@ impl Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_retentions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
||||||
|
let board = workspace.workspace.board();
|
||||||
|
let layout = board.layout();
|
||||||
|
let stroke = egui::Stroke::new(
|
||||||
|
150.0 / viewport.scale_factor(),
|
||||||
|
egui::Color32::from_rgb(192, 64, 255),
|
||||||
|
);
|
||||||
|
|
||||||
|
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_retentions(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_retentions(pin_id),
|
||||||
|
stroke,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn display_attractions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
fn display_attractions(&mut self, ui: &egui::Ui, viewport: &Viewport, workspace: &Controller) {
|
||||||
let board = workspace.workspace.board();
|
let board = workspace.workspace.board();
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@ use specctra::{
|
||||||
read::ListTokenizer,
|
read::ListTokenizer,
|
||||||
structure::DsnFile,
|
structure::DsnFile,
|
||||||
};
|
};
|
||||||
|
use topola::AutoplacerSchedule;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::Actions,
|
actions::Actions,
|
||||||
app::{execute, handle_file},
|
app::{execute, handle_file},
|
||||||
|
controller::Controller,
|
||||||
translator::Translator,
|
translator::Translator,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,17 +42,22 @@ impl MenuBar {
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
tr: &mut Translator,
|
tr: &mut Translator,
|
||||||
content_sender: Sender<Result<DsnFile, ParseErrorContext>>,
|
content_sender: Sender<Result<DsnFile, ParseErrorContext>>,
|
||||||
|
controller: Option<&mut Controller>,
|
||||||
) {
|
) {
|
||||||
let mut actions = Actions::new(tr);
|
let mut actions = Actions::new(tr);
|
||||||
|
|
||||||
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
||||||
egui::MenuBar::new().ui(ui, |ui| {
|
egui::MenuBar::new().ui(ui, |ui| {
|
||||||
ui.menu_button("File", |ui| {
|
ui.menu_button("File", |ui| {
|
||||||
actions.file.render_menu(ctx, ui, false);
|
actions.file.render_menu(ctx, ui, controller.is_some());
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
actions.run.render_menu(ctx, ui, controller.is_some());
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
MenuButton::new(tr.text("tr-menu-debug"))
|
MenuButton::new(tr.text("tr-menu-debug"))
|
||||||
.config(
|
.config(
|
||||||
MenuConfig::default()
|
MenuConfig::default()
|
||||||
|
|
@ -95,6 +102,21 @@ impl MenuBar {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if actions.run.autoplace.consume_key_triggered(ctx, ui) {
|
||||||
|
if let Some(controller) = controller {
|
||||||
|
controller.master_interactor.autoplace(
|
||||||
|
controller.workspace.board_mut(),
|
||||||
|
AutoplacerSchedule {
|
||||||
|
initial_temperature: 1000.0,
|
||||||
|
temperature_common_ratio: 0.95,
|
||||||
|
initial_std_dev: 1000.0,
|
||||||
|
std_dev_common_ratio: 0.995,
|
||||||
|
max_steps: 200,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,23 +124,23 @@ impl Viewport {
|
||||||
fn boundary_bounding_box(controller: &Controller) -> egui::Rect {
|
fn boundary_bounding_box(controller: &Controller) -> egui::Rect {
|
||||||
let first = controller.workspace.board().layout().boundary()[0];
|
let first = controller.workspace.board().layout().boundary()[0];
|
||||||
|
|
||||||
let mut min_x = first[0];
|
let mut min_x = first.x;
|
||||||
let mut max_x = first[0];
|
let mut max_x = first.x;
|
||||||
let mut min_y = first[1];
|
let mut min_y = first.y;
|
||||||
let mut max_y = first[1];
|
let mut max_y = first.y;
|
||||||
|
|
||||||
for point in controller.workspace.board().layout().boundary()[1..].iter() {
|
for point in controller.workspace.board().layout().boundary()[1..].iter() {
|
||||||
if point[0] < min_x {
|
if point.x < min_x {
|
||||||
min_x = point[0];
|
min_x = point.x;
|
||||||
}
|
}
|
||||||
if point[0] > max_x {
|
if point.x > max_x {
|
||||||
max_x = point[0];
|
max_x = point.x;
|
||||||
}
|
}
|
||||||
if point[1] < min_y {
|
if point.y < min_y {
|
||||||
min_y = point[1];
|
min_y = point.y;
|
||||||
}
|
}
|
||||||
if point[1] > max_y {
|
if point.y > max_y {
|
||||||
max_y = point[1];
|
max_y = point.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,15 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use derive_getters::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoplacer::{Autoplacer, AutoplacerSchedule},
|
autoplacer::{Autoplacer, AutoplacerSchedule},
|
||||||
board::{
|
board::{
|
||||||
Board,
|
Board,
|
||||||
interactors::{MasterInteractor as BoardMasterInteractor, SelectInteractor},
|
interactors::{BoardMasterInteractor, SelectInteractor},
|
||||||
selections::PersistableSelection,
|
selections::PersistableSelection,
|
||||||
},
|
},
|
||||||
interactor::Interactor,
|
interactor::Interactor,
|
||||||
|
|
@ -14,20 +18,23 @@ use crate::{
|
||||||
vector::Vector2,
|
vector::Vector2,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct MasterInteractor {
|
#[derive(Getters)]
|
||||||
|
pub struct AutoplacerMasterInteractor {
|
||||||
board_master: BoardMasterInteractor,
|
board_master: BoardMasterInteractor,
|
||||||
autoplacer: Autoplacer,
|
autoplacer: Autoplacer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MasterInteractor {
|
impl AutoplacerMasterInteractor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
board: &mut Board,
|
board: &mut Board,
|
||||||
selection: PersistableSelection,
|
board_master: BoardMasterInteractor,
|
||||||
schedule: AutoplacerSchedule,
|
schedule: AutoplacerSchedule,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let selection = board_master.selection().components.clone();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
board_master: BoardMasterInteractor::new(selection.clone()),
|
board_master,
|
||||||
autoplacer: Autoplacer::new(board, selection.components.clone(), schedule),
|
autoplacer: Autoplacer::new(board, selection, schedule),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,9 +47,9 @@ impl MasterInteractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interactor for MasterInteractor {
|
impl Interactor for AutoplacerMasterInteractor {
|
||||||
fn step(&mut self, board: &mut Board) {
|
fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
||||||
self.autoplacer.step(board);
|
self.autoplacer.step(board)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {
|
fn hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
mod master;
|
mod master;
|
||||||
|
|
||||||
pub use master::MasterInteractor;
|
pub use master::AutoplacerMasterInteractor;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
pub mod interactors;
|
pub mod interactors;
|
||||||
|
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use rand_distr::{Distribution, Normal};
|
use rand_distr::{Distribution, Normal};
|
||||||
use undoredo::{FlushDelta, ResetDelta};
|
use undoredo::{FlushDelta, ResetDelta};
|
||||||
|
|
@ -17,10 +19,11 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct AutoplacerSchedule {
|
pub struct AutoplacerSchedule {
|
||||||
initial_temperature: f64,
|
pub initial_temperature: f64,
|
||||||
temperature_common_ratio: f64,
|
pub temperature_common_ratio: f64,
|
||||||
initial_std_dev: f64,
|
pub initial_std_dev: f64,
|
||||||
std_dev_common_ratio: f64,
|
pub std_dev_common_ratio: f64,
|
||||||
|
pub max_steps: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -32,7 +35,7 @@ pub struct AutoplacerStepParams {
|
||||||
pub struct Autoplacer {
|
pub struct Autoplacer {
|
||||||
components: Vec<ComponentId>,
|
components: Vec<ComponentId>,
|
||||||
schedule: AutoplacerSchedule,
|
schedule: AutoplacerSchedule,
|
||||||
step_counter: u32,
|
step_counter: u64,
|
||||||
origin_delta: BoardDelta, //rng: ThreadRng,
|
origin_delta: BoardDelta, //rng: ThreadRng,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,22 +53,29 @@ impl Autoplacer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, board: &mut Board) -> bool {
|
pub fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
||||||
self.step_with_params(
|
if self.step_counter < self.schedule.max_steps {
|
||||||
board,
|
let control_flow = self.step_with_params(
|
||||||
AutoplacerStepParams {
|
board,
|
||||||
temperature: self.schedule.initial_temperature
|
AutoplacerStepParams {
|
||||||
* self
|
temperature: self.schedule.initial_temperature
|
||||||
.schedule
|
* self
|
||||||
.temperature_common_ratio
|
.schedule
|
||||||
.powf(self.step_counter as f64),
|
.temperature_common_ratio
|
||||||
std_dev: self.schedule.initial_std_dev
|
.powf(self.step_counter as f64),
|
||||||
* self
|
std_dev: self.schedule.initial_std_dev
|
||||||
.schedule
|
* self
|
||||||
.std_dev_common_ratio
|
.schedule
|
||||||
.powf(self.step_counter as f64),
|
.std_dev_common_ratio
|
||||||
},
|
.powf(self.step_counter as f64),
|
||||||
)
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
self.step_counter += 1;
|
||||||
|
control_flow
|
||||||
|
} else {
|
||||||
|
ControlFlow::Break(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO.
|
// TODO.
|
||||||
|
|
@ -73,10 +83,15 @@ impl Autoplacer {
|
||||||
|
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
fn step_with_params(&mut self, board: &mut Board, params: AutoplacerStepParams) -> bool {
|
fn step_with_params(
|
||||||
|
&mut self,
|
||||||
|
board: &mut Board,
|
||||||
|
params: AutoplacerStepParams,
|
||||||
|
) -> ControlFlow<()> {
|
||||||
for &component in self.components.iter() {
|
for &component in self.components.iter() {
|
||||||
//self.step_component_with_params(component, params);
|
//self.step_component_with_params(component, params);
|
||||||
let last_cost = self.cost(board, params);
|
//let last_cost = self.cost(board, params);
|
||||||
|
let last_cost = self.component_cost(board, component, params);
|
||||||
|
|
||||||
let dx_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
let dx_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
||||||
let dy_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
let dy_gaussian = Normal::new(0.0, params.std_dev).unwrap();
|
||||||
|
|
@ -86,13 +101,14 @@ impl Autoplacer {
|
||||||
let dx = dx_gaussian.sample(&mut rand::rng());
|
let dx = dx_gaussian.sample(&mut rand::rng());
|
||||||
let dy = dy_gaussian.sample(&mut rand::rng());
|
let dy = dy_gaussian.sample(&mut rand::rng());
|
||||||
|
|
||||||
board.move_resolved_components_by(&self.components, Vector2::new(dx as i64, dy as i64));
|
board.move_resolved_components_by(&[component], Vector2::new(dx as i64, dy as i64));
|
||||||
|
|
||||||
let new_cost = self.cost(board, params);
|
//let new_cost = self.cost(board, params);
|
||||||
|
let new_cost = self.component_cost(board, component, params);
|
||||||
let delta_cost = new_cost - last_cost;
|
let delta_cost = new_cost - last_cost;
|
||||||
|
|
||||||
if delta_cost <= 0.0
|
if delta_cost < 0.0
|
||||||
|| f64::exp(-delta_cost / params.temperature) <= rand::rng().random()
|
|| rand::rng().random::<f64>() < f64::exp(-delta_cost / params.temperature)
|
||||||
{
|
{
|
||||||
self.origin_delta = self.origin_delta.clone().merge_delta(board.flush_delta());
|
self.origin_delta = self.origin_delta.clone().merge_delta(board.flush_delta());
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -100,21 +116,21 @@ impl Autoplacer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 {
|
/*fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 {
|
||||||
self.components
|
self.components
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&component| self.component_cost(board, component, params))
|
.map(|&component| self.component_cost(board, component, params))
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fn component_cost(
|
fn component_cost(
|
||||||
&self,
|
&self,
|
||||||
board: &Board,
|
board: &Board,
|
||||||
component: ComponentId,
|
component: ComponentId,
|
||||||
_params: AutoplacerStepParams,
|
params: AutoplacerStepParams,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
let layout = board.layout();
|
let layout = board.layout();
|
||||||
|
|
||||||
|
|
@ -126,8 +142,12 @@ impl Autoplacer {
|
||||||
.component_attractions(component)
|
.component_attractions(component)
|
||||||
.map(|vector| 1.0 / (1.0 + (vector.x.abs() + vector.y.abs()) as f64))
|
.map(|vector| 1.0 / (1.0 + (vector.x.abs() + vector.y.abs()) as f64))
|
||||||
.sum();
|
.sum();
|
||||||
|
let retention_cost: i64 = layout
|
||||||
|
.component_retentions(component)
|
||||||
|
.map(|vector| 100 * (vector.x.abs() + vector.y.abs()))
|
||||||
|
.sum();
|
||||||
|
|
||||||
repulsion_cost as f64 + attraction_cost
|
repulsion_cost as f64 + attraction_cost + retention_cost as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn step_component_with_params(
|
/*fn step_component_with_params(
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Getters, PartialEq)]
|
#[derive(Clone, Debug, Eq, Getters, PartialEq)]
|
||||||
pub struct MasterInteractor {
|
pub struct BoardMasterInteractor {
|
||||||
select_interactor: Option<SelectInteractor>,
|
select_interactor: Option<SelectInteractor>,
|
||||||
drag_move_interactor: Option<DragMoveInteractor>,
|
drag_move_interactor: Option<DragMoveInteractor>,
|
||||||
selection: PersistableSelection,
|
selection: PersistableSelection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MasterInteractor {
|
impl BoardMasterInteractor {
|
||||||
pub fn new(selection: PersistableSelection) -> Self {
|
pub fn new(selection: PersistableSelection) -> Self {
|
||||||
Self {
|
Self {
|
||||||
select_interactor: None,
|
select_interactor: None,
|
||||||
|
|
@ -32,7 +32,7 @@ impl MasterInteractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interactor for MasterInteractor {
|
impl Interactor for BoardMasterInteractor {
|
||||||
fn delete(&mut self, board: &mut Board) {
|
fn delete(&mut self, board: &mut Board) {
|
||||||
board.delete_net_free_primitives(self.selection.nets.clone());
|
board.delete_net_free_primitives(self.selection.nets.clone());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ mod select;
|
||||||
|
|
||||||
pub use drag_move::DragMoveInteractor;
|
pub use drag_move::DragMoveInteractor;
|
||||||
pub use drag_select::{DragSelectInteractor, DragSelectOptions};
|
pub use drag_select::{DragSelectInteractor, DragSelectOptions};
|
||||||
pub use master::MasterInteractor;
|
pub use master::BoardMasterInteractor;
|
||||||
pub use select::SelectInteractor;
|
pub use select::SelectInteractor;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,7 @@ impl Board {
|
||||||
net_names: BiBTreeMap<NetId, String>,
|
net_names: BiBTreeMap<NetId, String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
layout: Layout::new(
|
layout: Layout::new(boundary, layer_descs.len(), net_names.len()),
|
||||||
boundary.into_iter().map(Into::into).collect(),
|
|
||||||
layer_descs.len(),
|
|
||||||
net_names.len(),
|
|
||||||
),
|
|
||||||
component_names: Recorder::new(BiBTreeMap::new()),
|
component_names: Recorder::new(BiBTreeMap::new()),
|
||||||
pin_names: Recorder::new(BiBTreeMap::new()),
|
pin_names: Recorder::new(BiBTreeMap::new()),
|
||||||
layer_descs: Recorder::new(layer_descs),
|
layer_descs: Recorder::new(layer_descs),
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,23 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{Board, interactors::SelectInteractor, selections::PersistableSelection},
|
autoplacer::{AutoplacerSchedule, interactors::AutoplacerMasterInteractor},
|
||||||
|
board::{
|
||||||
|
Board,
|
||||||
|
interactors::{BoardMasterInteractor, SelectInteractor},
|
||||||
|
selections::PersistableSelection,
|
||||||
|
},
|
||||||
layout::LayerId,
|
layout::LayerId,
|
||||||
vector::Vector2,
|
vector::Vector2,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Interactor {
|
pub trait Interactor {
|
||||||
fn step(&mut self, board: &mut Board) {}
|
fn step(&mut self, _board: &mut Board) -> ControlFlow<()> {
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
fn delete(&mut self, board: &mut Board) {}
|
fn delete(&mut self, board: &mut Board) {}
|
||||||
fn hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {}
|
fn hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {}
|
||||||
fn release(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {}
|
fn release(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2<i64>) {}
|
||||||
|
|
@ -17,35 +26,57 @@ pub trait Interactor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MasterInteractor {
|
pub enum MasterInteractor {
|
||||||
Board(crate::board::interactors::MasterInteractor),
|
Board(BoardMasterInteractor),
|
||||||
Autoplacer(crate::autoplacer::interactors::MasterInteractor),
|
Autoplacer(AutoplacerMasterInteractor),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MasterInteractor {
|
impl MasterInteractor {
|
||||||
pub fn new(selection: PersistableSelection) -> Self {
|
pub fn new(selection: PersistableSelection) -> Self {
|
||||||
Self::Board(crate::board::interactors::MasterInteractor::new(selection))
|
Self::Board(crate::board::interactors::BoardMasterInteractor::new(
|
||||||
|
selection,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn autoplace(&mut self, board: &mut Board, schedule: AutoplacerSchedule) {
|
||||||
|
match self {
|
||||||
|
Self::Board(board_master) => {
|
||||||
|
*self = Self::Autoplacer(AutoplacerMasterInteractor::new(
|
||||||
|
board,
|
||||||
|
board_master.clone(),
|
||||||
|
schedule,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
//_ => panic!("autoplacement can be only started from board at rest"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selection(&self) -> &PersistableSelection {
|
pub fn selection(&self) -> &PersistableSelection {
|
||||||
match self {
|
match self {
|
||||||
Self::Board(interactor) => interactor.selection(),
|
Self::Board(board_master) => board_master.selection(),
|
||||||
Self::Autoplacer(interactor) => interactor.selection(),
|
Self::Autoplacer(autoplacer_master) => autoplacer_master.selection(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_interactor(&self) -> &Option<SelectInteractor> {
|
pub fn select_interactor(&self) -> &Option<SelectInteractor> {
|
||||||
match self {
|
match self {
|
||||||
Self::Board(interactor) => interactor.select_interactor(),
|
Self::Board(board_master) => board_master.select_interactor(),
|
||||||
Self::Autoplacer(interactor) => interactor.select_interactor(),
|
Self::Autoplacer(autoplacer_master) => autoplacer_master.select_interactor(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interactor for MasterInteractor {
|
impl Interactor for MasterInteractor {
|
||||||
fn step(&mut self, board: &mut Board) {
|
fn step(&mut self, board: &mut Board) -> ControlFlow<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Board(interactor) => interactor.step(board),
|
Self::Board(interactor) => interactor.step(board),
|
||||||
Self::Autoplacer(interactor) => interactor.step(board),
|
Self::Autoplacer(interactor) => {
|
||||||
|
if interactor.step(board).is_break() {
|
||||||
|
*self = Self::Board(interactor.board_master().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ mod modify;
|
||||||
mod overlap;
|
mod overlap;
|
||||||
pub mod primitives;
|
pub mod primitives;
|
||||||
mod repulsion;
|
mod repulsion;
|
||||||
|
mod retention;
|
||||||
mod transforms;
|
mod transforms;
|
||||||
|
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
|
|
@ -26,9 +27,12 @@ use stable_vec::StableVec;
|
||||||
use undoredo::aliases::RTreeHalfDelta;
|
use undoredo::aliases::RTreeHalfDelta;
|
||||||
use undoredo::{Delta, Recorder};
|
use undoredo::{Delta, Recorder};
|
||||||
|
|
||||||
use crate::layout::{
|
use crate::{
|
||||||
compounds::{Component, ComponentId, Net, Pin, PinId},
|
layout::{
|
||||||
primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId},
|
compounds::{Component, ComponentId, Net, Pin, PinId},
|
||||||
|
primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId},
|
||||||
|
},
|
||||||
|
vector::Vector2,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -57,9 +61,9 @@ impl LayerId {
|
||||||
#[derive(Clone, Debug, Delta, Getters)]
|
#[derive(Clone, Debug, Delta, Getters)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
#[undoredo(skip)]
|
#[undoredo(skip)]
|
||||||
boundary: Vec<[i64; 2]>,
|
boundary: Vec<Vector2<i64>>,
|
||||||
#[undoredo(skip)]
|
#[undoredo(skip)]
|
||||||
place_boundary: Vec<[i64; 2]>,
|
place_boundary: Vec<Vector2<i64>>,
|
||||||
#[undoredo(skip)]
|
#[undoredo(skip)]
|
||||||
layer_count: usize,
|
layer_count: usize,
|
||||||
|
|
||||||
|
|
@ -91,7 +95,7 @@ pub struct Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub fn new(boundary: Vec<[i64; 2]>, layer_count: usize, net_count: usize) -> Self {
|
pub fn new(boundary: Vec<Vector2<i64>>, layer_count: usize, net_count: usize) -> Self {
|
||||||
let mut nets = StableVec::new();
|
let mut nets = StableVec::new();
|
||||||
for _ in 0..net_count {
|
for _ in 0..net_count {
|
||||||
nets.push(Net::default());
|
nets.push(Net::default());
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@ pub use polygon::*;
|
||||||
pub use segment::*;
|
pub use segment::*;
|
||||||
pub use via::*;
|
pub use via::*;
|
||||||
|
|
||||||
use crate::layout::{Layout, compounds::PinId};
|
use crate::{
|
||||||
|
Rect2,
|
||||||
|
layout::{Layout, compounds::PinId},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, From, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, From, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub enum PrimitiveId {
|
pub enum PrimitiveId {
|
||||||
|
|
@ -34,4 +37,13 @@ impl Layout {
|
||||||
PrimitiveId::Polygon(polygon_id) => self.polygon(polygon_id).pin,
|
PrimitiveId::Polygon(polygon_id) => self.polygon(polygon_id).pin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn primitive_bbox2(&self, primitive: PrimitiveId) -> Rect2<i64> {
|
||||||
|
match primitive {
|
||||||
|
PrimitiveId::Joint(joint_id) => self.joint(joint_id).bbox().xy(),
|
||||||
|
PrimitiveId::Segment(segment_id) => self.segment(segment_id).bbox().xy(),
|
||||||
|
PrimitiveId::Via(via_id) => self.via(via_id).bbox().xy(),
|
||||||
|
PrimitiveId::Polygon(polygon_id) => self.polygon(polygon_id).bbox().xy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ impl Layout {
|
||||||
&self,
|
&self,
|
||||||
infringer: ComponentId,
|
infringer: ComponentId,
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
) -> impl Iterator<Item = Vector2<i64>> {
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
self.locate_component_infringements(infringer)
|
self.locate_component_infringements(infringer)
|
||||||
.map(move |infringement| {
|
.flat_map(move |infringement| {
|
||||||
self.component_component_repulsion(
|
self.component_component_repulsions(
|
||||||
infringement.infringer(),
|
infringement.infringer(),
|
||||||
infringement.infringee(),
|
infringement.infringee(),
|
||||||
orientation,
|
orientation,
|
||||||
|
|
@ -30,28 +30,25 @@ impl Layout {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn component_component_repulsion(
|
pub fn component_component_repulsions(
|
||||||
&self,
|
&self,
|
||||||
infringer: ComponentId,
|
infringer: ComponentId,
|
||||||
infringee: ComponentId,
|
infringee: ComponentId,
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
) -> Vector2<i64> {
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
let mut max_repulsion = Vector2::new(0, 0);
|
self.component(infringer)
|
||||||
let mut max_repulsion_magnitude = 0;
|
.pins
|
||||||
|
.iter()
|
||||||
for &infringer_pin in self.component(infringer).pins.iter() {
|
.copied()
|
||||||
for &infringee_pin in self.component(infringee).pins.iter() {
|
.flat_map(move |infringer_pin| {
|
||||||
let repulsion = self.pin_pin_repulsion(infringer_pin, infringee_pin, orientation);
|
self.component(infringee)
|
||||||
let repulsion_magnitude = repulsion.x.abs() + repulsion.y.abs();
|
.pins
|
||||||
|
.iter()
|
||||||
if repulsion_magnitude > max_repulsion_magnitude {
|
.copied()
|
||||||
max_repulsion = repulsion;
|
.flat_map(move |infringee_pin| {
|
||||||
max_repulsion_magnitude = repulsion_magnitude;
|
self.pin_pin_repulsions(infringer_pin, infringee_pin, orientation)
|
||||||
}
|
})
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
max_repulsion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn locate_pin_repulsions(
|
pub fn locate_pin_repulsions(
|
||||||
|
|
@ -60,8 +57,8 @@ impl Layout {
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
self.locate_pin_infringements(infringer)
|
self.locate_pin_infringements(infringer)
|
||||||
.map(move |infringement| {
|
.flat_map(move |infringement| {
|
||||||
self.pin_pin_repulsion(
|
self.pin_pin_repulsions(
|
||||||
infringement.infringer(),
|
infringement.infringer(),
|
||||||
infringement.infringee(),
|
infringement.infringee(),
|
||||||
orientation,
|
orientation,
|
||||||
|
|
@ -69,32 +66,25 @@ impl Layout {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pin_pin_repulsion(
|
pub fn pin_pin_repulsions(
|
||||||
&self,
|
&self,
|
||||||
infringer: PinId,
|
infringer: PinId,
|
||||||
infringee: PinId,
|
infringee: PinId,
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
) -> Vector2<i64> {
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
let mut max_repulsion = Vector2::new(0, 0);
|
self.pin(infringer)
|
||||||
let mut max_repulsion_magnitude = 0;
|
.primitives()
|
||||||
|
.flat_map(move |infringer_primitive| {
|
||||||
for infringer_primitive in self.pin(infringer).primitives() {
|
self.pin(infringee)
|
||||||
for infringee_primitive in self.pin(infringee).primitives() {
|
.primitives()
|
||||||
let repulsion = self.primitive_primitive_repulsion(
|
.map(move |infringee_primitive| {
|
||||||
infringer_primitive,
|
self.primitive_primitive_repulsion(
|
||||||
infringee_primitive,
|
infringer_primitive,
|
||||||
orientation,
|
infringee_primitive,
|
||||||
);
|
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 primitive_primitive_repulsion(
|
pub fn primitive_primitive_repulsion(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Rect2,
|
||||||
|
layout::{
|
||||||
|
Layout,
|
||||||
|
compounds::{ComponentId, PinId},
|
||||||
|
primitives::PrimitiveId,
|
||||||
|
},
|
||||||
|
vector::Vector2,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
pub fn component_retentions(
|
||||||
|
&self,
|
||||||
|
violator: ComponentId,
|
||||||
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
|
self.component(violator)
|
||||||
|
.pins
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.flat_map(move |pin_id| self.pin_retentions(pin_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pin_retentions(&self, violator: PinId) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
|
self.pin(violator)
|
||||||
|
.primitives()
|
||||||
|
.flat_map(move |primitive| self.primitive_retentions(primitive))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn primitive_retentions(
|
||||||
|
&self,
|
||||||
|
violator: PrimitiveId,
|
||||||
|
) -> impl Iterator<Item = Vector2<i64>> + '_ {
|
||||||
|
let boundary = self.place_boundary();
|
||||||
|
std::array::IntoIter::new(self.primitive_bbox2(violator).corners())
|
||||||
|
.map(move |corner| Self::point_retention(corner, boundary))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn point_retention(violator: Vector2<i64>, boundary: &[Vector2<i64>]) -> Vector2<i64> {
|
||||||
|
if boundary.len() < 3 || violator.inside_polygon(boundary) {
|
||||||
|
return Vector2::new(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
violator.closest_point_on_polygon_boundary(boundary) - violator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ mod specctra;
|
||||||
mod vector;
|
mod vector;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
|
||||||
|
pub use crate::autoplacer::AutoplacerSchedule;
|
||||||
pub use crate::autorouter::Autorouter;
|
pub use crate::autorouter::Autorouter;
|
||||||
pub use crate::board::selections;
|
pub use crate::board::selections;
|
||||||
pub use crate::interactor::{Interactor, MasterInteractor};
|
pub use crate::interactor::{Interactor, MasterInteractor};
|
||||||
|
|
|
||||||
|
|
@ -206,11 +206,7 @@ impl NavmesherBoard {
|
||||||
pub fn new(board: Board) -> Self {
|
pub fn new(board: Board) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
navmesher: Navmesher::new(
|
navmesher: Navmesher::new(
|
||||||
board
|
board.layout().boundary().iter().copied(),
|
||||||
.layout()
|
|
||||||
.boundary()
|
|
||||||
.iter()
|
|
||||||
.map(|p| Vector2::new(p[0], p[1])),
|
|
||||||
*board.layout().layer_count(),
|
*board.layout().layer_count(),
|
||||||
),
|
),
|
||||||
board,
|
board,
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,72 @@ impl_vector2_inside_polygon!(u128);
|
||||||
impl_vector2_inside_polygon!(f32);
|
impl_vector2_inside_polygon!(f32);
|
||||||
impl_vector2_inside_polygon!(f64);
|
impl_vector2_inside_polygon!(f64);
|
||||||
|
|
||||||
|
macro_rules! impl_vector2_closest_point_on_boundary {
|
||||||
|
($type:ty) => {
|
||||||
|
impl Vector2<$type> {
|
||||||
|
pub fn closest_point_on_segment(
|
||||||
|
self,
|
||||||
|
segment_start: Vector2<$type>,
|
||||||
|
segment_end: Vector2<$type>,
|
||||||
|
) -> Vector2<$type> {
|
||||||
|
let abx = segment_end.x - segment_start.x;
|
||||||
|
let aby = segment_end.y - segment_start.y;
|
||||||
|
let apx = self.x - segment_start.x;
|
||||||
|
let apy = self.y - segment_start.y;
|
||||||
|
let ab_len_sq = abx * abx + aby * aby;
|
||||||
|
|
||||||
|
if ab_len_sq == 0 as $type {
|
||||||
|
return segment_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = (apx * abx + apy * aby).clamp(0 as $type, ab_len_sq);
|
||||||
|
|
||||||
|
Vector2::new(
|
||||||
|
segment_start.x + abx * t / ab_len_sq,
|
||||||
|
segment_start.y + aby * t / ab_len_sq,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn closest_point_on_polygon_boundary(
|
||||||
|
self,
|
||||||
|
polygon: &[Vector2<$type>],
|
||||||
|
) -> Vector2<$type> {
|
||||||
|
let mut closest_point = polygon[0];
|
||||||
|
let mut best_distance_sq = <$type as Bounded>::max_value();
|
||||||
|
|
||||||
|
for i in 0..polygon.len() {
|
||||||
|
let segment_start = polygon[i];
|
||||||
|
let segment_end = polygon[(i + 1) % polygon.len()];
|
||||||
|
let candidate = self.closest_point_on_segment(segment_start, segment_end);
|
||||||
|
let dx = self.x - candidate.x;
|
||||||
|
let dy = self.y - candidate.y;
|
||||||
|
let distance_sq = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
if distance_sq < best_distance_sq {
|
||||||
|
best_distance_sq = distance_sq;
|
||||||
|
closest_point = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closest_point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_vector2_closest_point_on_boundary!(i8);
|
||||||
|
impl_vector2_closest_point_on_boundary!(i16);
|
||||||
|
impl_vector2_closest_point_on_boundary!(i32);
|
||||||
|
impl_vector2_closest_point_on_boundary!(i64);
|
||||||
|
impl_vector2_closest_point_on_boundary!(i128);
|
||||||
|
impl_vector2_closest_point_on_boundary!(u8);
|
||||||
|
impl_vector2_closest_point_on_boundary!(u16);
|
||||||
|
impl_vector2_closest_point_on_boundary!(u32);
|
||||||
|
impl_vector2_closest_point_on_boundary!(u64);
|
||||||
|
impl_vector2_closest_point_on_boundary!(u128);
|
||||||
|
impl_vector2_closest_point_on_boundary!(f32);
|
||||||
|
impl_vector2_closest_point_on_boundary!(f64);
|
||||||
|
|
||||||
macro_rules! impl_vector2_rotate_around_point {
|
macro_rules! impl_vector2_rotate_around_point {
|
||||||
($type:ty) => {
|
($type:ty) => {
|
||||||
impl Vector2<$type> {
|
impl Vector2<$type> {
|
||||||
|
|
@ -197,10 +263,12 @@ impl_polygon_centroid!(i8);
|
||||||
impl_polygon_centroid!(i16);
|
impl_polygon_centroid!(i16);
|
||||||
impl_polygon_centroid!(i32);
|
impl_polygon_centroid!(i32);
|
||||||
impl_polygon_centroid!(i64);
|
impl_polygon_centroid!(i64);
|
||||||
|
impl_polygon_centroid!(i128);
|
||||||
impl_polygon_centroid!(u8);
|
impl_polygon_centroid!(u8);
|
||||||
impl_polygon_centroid!(u16);
|
impl_polygon_centroid!(u16);
|
||||||
impl_polygon_centroid!(u32);
|
impl_polygon_centroid!(u32);
|
||||||
impl_polygon_centroid!(u64);
|
impl_polygon_centroid!(u64);
|
||||||
|
impl_polygon_centroid!(u128);
|
||||||
impl_polygon_centroid!(f32);
|
impl_polygon_centroid!(f32);
|
||||||
impl_polygon_centroid!(f64);
|
impl_polygon_centroid!(f64);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue