diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index b7ef7d3..538d684 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -35,6 +35,7 @@ tr-menu-route-planar-autoroute = Planar Autoroute tr-menu-route-topo-autoroute = Topological planar Autoroute tr-menu-route-routed-band-width = Routed Band Width tr-menu-route-fanout-clearance = Fanout Clearance +tr-menu-route-autoplace = Autoplace tr-menu-debug = Debug tr-menu-debug-highlight-obstacles = Highlight Obstacles diff --git a/topola-egui/src/actions.rs b/topola-egui/src/actions.rs index 702e054..d0b177c 100644 --- a/topola-egui/src/actions.rs +++ b/topola-egui/src/actions.rs @@ -12,6 +12,7 @@ use crate::{ pub struct Actions { pub file: FileActions, + pub run: RunActions, pub debug: DebugActions, } @@ -19,6 +20,7 @@ impl Actions { pub fn new(tr: &Translator) -> Self { Self { file: FileActions::new(tr), + run: RunActions::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 fix_step_rate: Switch, } diff --git a/topola-egui/src/app.rs b/topola-egui/src/app.rs index 079f5f6..03c9999 100644 --- a/topola-egui/src/app.rs +++ b/topola-egui/src/app.rs @@ -125,8 +125,12 @@ impl eframe::App for App { /// Called each time the UI has to be repainted. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - self.menu_bar - .update(ctx, &mut self.translator, self.content_channel.0.clone()); + self.menu_bar.update( + ctx, + &mut self.translator, + self.content_channel.0.clone(), + self.controller.as_mut(), + ); self.update_state(); diff --git a/topola-egui/src/controller.rs b/topola-egui/src/controller.rs index 9fa2206..4ad1510 100644 --- a/topola-egui/src/controller.rs +++ b/topola-egui/src/controller.rs @@ -11,18 +11,19 @@ use crate::{layers_panel::LayersPanel, translator::Translator}; pub struct Controller { pub workspace: Workspace, pub appearance_panel: LayersPanel, - pub master_interactor: Option, - pub dt_accum: f64, + pub master_interactor: MasterInteractor, + dt_accum: f64, } impl Controller { pub fn new(board: Board, tr: &Translator) -> Self { let appearance_panel = LayersPanel::new(&board); + let workspace = Workspace::new_board(board); Self { - workspace: Workspace::new_board(board), + master_interactor: MasterInteractor::new(workspace.selection().clone()), + workspace, appearance_panel, - master_interactor: None, dt_accum: 0.0, } } @@ -59,11 +60,8 @@ impl Controller { true } - pub fn step(&mut self, tr: &Translator) -> ControlFlow<()> { - self.master_interactor - .as_mut() - .map(|master_interactor| master_interactor.step(self.workspace.board_mut())); - ControlFlow::Continue(()) + pub fn step(&mut self, _tr: &Translator) -> ControlFlow<()> { + self.master_interactor.step(self.workspace.board_mut()) } pub fn update_appearance_panel(&mut self, ctx: &egui::Context) { @@ -87,13 +85,22 @@ impl Controller { ui: &mut egui::Ui, ) { if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { - if let (Some(interactor), Workspace::Board(workspace)) = - (&mut self.master_interactor, &mut self.workspace) - { - interactor.abort(&mut workspace.board); - *self.workspace.selection_mut() = interactor.selection().clone(); + let board_master = + if let MasterInteractor::Autoplacer(interactor) = &self.master_interactor { + Some(interactor.board_master().clone()) + } else { + 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)); @@ -110,21 +117,20 @@ impl Controller { maybe_pointer_on_scene = Some(pointer_on_scene); if primary_pressed && scene_hovered { - self.master_interactor = - Some(MasterInteractor::new(self.workspace.selection().clone())); + self.master_interactor = MasterInteractor::new(self.workspace.selection().clone()); } - if let (Some(interactor), Workspace::Board(workspace)) = - (&mut self.master_interactor, &mut self.workspace) - { + if let Workspace::Board(workspace) = &mut self.workspace { if primary_down { - interactor.hold( + self.master_interactor.hold( &mut workspace.board, self.appearance_panel.active, 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 drag_rect_scene = egui::Rect::from_min_max( egui::pos2( @@ -160,27 +166,25 @@ impl Controller { } if primary_released { - if let Some(mut interactor) = self.master_interactor.take() { - let active = self.appearance_panel.active; - let pointer_for_scene = maybe_pointer_on_scene.unwrap_or_else(|| { - interactor - .select_interactor() - .as_ref() - .map(|select_interactor| *select_interactor.origin()) - .unwrap_or(Vector2::new(0, 0)) - }); - if let Workspace::Board(workspace) = &mut self.workspace { - interactor.release(&mut workspace.board, active, pointer_for_scene); - *self.workspace.selection_mut() = interactor.selection().clone(); - } + let active = self.appearance_panel.active; + let pointer_for_scene = maybe_pointer_on_scene.unwrap_or_else(|| { + self.master_interactor + .select_interactor() + .as_ref() + .map(|select_interactor| *select_interactor.origin()) + .unwrap_or(Vector2::new(0, 0)) + }); + if let Workspace::Board(workspace) = &mut self.workspace { + self.master_interactor + .release(&mut workspace.board, active, pointer_for_scene); + *self.workspace.selection_mut() = self.master_interactor.selection().clone(); } } if delete_pressed { - let mut interactor = MasterInteractor::new(self.workspace.selection().clone()); if let Workspace::Board(workspace) = &mut self.workspace { - interactor.delete(&mut workspace.board); - *self.workspace.selection_mut() = interactor.selection().clone(); + self.master_interactor.delete(&mut workspace.board); + *self.workspace.selection_mut() = self.master_interactor.selection().clone(); } } } diff --git a/topola-egui/src/menu_bar.rs b/topola-egui/src/menu_bar.rs index 28d9c8f..a0b1cfc 100644 --- a/topola-egui/src/menu_bar.rs +++ b/topola-egui/src/menu_bar.rs @@ -14,10 +14,12 @@ use specctra::{ read::ListTokenizer, structure::DsnFile, }; +use topola::AutoplacerSchedule; use crate::{ actions::Actions, app::{execute, handle_file}, + controller::Controller, translator::Translator, }; @@ -40,17 +42,22 @@ impl MenuBar { ctx: &egui::Context, tr: &mut Translator, content_sender: Sender>, + controller: Option<&mut Controller>, ) { let mut actions = Actions::new(tr); egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { egui::MenuBar::new().ui(ui, |ui| { ui.menu_button("File", |ui| { - actions.file.render_menu(ctx, ui, false); + actions.file.render_menu(ctx, ui, controller.is_some()); }); ui.separator(); + actions.run.render_menu(ctx, ui, controller.is_some()); + + ui.separator(); + MenuButton::new(tr.text("tr-menu-debug")) .config( 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.99, + initial_std_dev: 1000.0, + std_dev_common_ratio: 0.99, + max_steps: 2000, + }, + ); + } + } }); } } diff --git a/topola/src/autoplacer/interactors/master.rs b/topola/src/autoplacer/interactors/master.rs index 5154153..9974dfa 100644 --- a/topola/src/autoplacer/interactors/master.rs +++ b/topola/src/autoplacer/interactors/master.rs @@ -2,11 +2,15 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use std::ops::ControlFlow; + +use derive_getters::Getters; + use crate::{ autoplacer::{Autoplacer, AutoplacerSchedule}, board::{ Board, - interactors::{MasterInteractor as BoardMasterInteractor, SelectInteractor}, + interactors::{BoardMasterInteractor, SelectInteractor}, selections::PersistableSelection, }, interactor::Interactor, @@ -14,20 +18,23 @@ use crate::{ vector::Vector2, }; -pub struct MasterInteractor { +#[derive(Getters)] +pub struct AutoplacerMasterInteractor { board_master: BoardMasterInteractor, autoplacer: Autoplacer, } -impl MasterInteractor { +impl AutoplacerMasterInteractor { pub fn new( board: &mut Board, - selection: PersistableSelection, + board_master: BoardMasterInteractor, schedule: AutoplacerSchedule, ) -> Self { + let selection = board_master.selection().components.clone(); + Self { - board_master: BoardMasterInteractor::new(selection.clone()), - autoplacer: Autoplacer::new(board, selection.components.clone(), schedule), + board_master, + autoplacer: Autoplacer::new(board, selection, schedule), } } @@ -40,9 +47,9 @@ impl MasterInteractor { } } -impl Interactor for MasterInteractor { - fn step(&mut self, board: &mut Board) { - self.autoplacer.step(board); +impl Interactor for AutoplacerMasterInteractor { + fn step(&mut self, board: &mut Board) -> ControlFlow<()> { + self.autoplacer.step(board) } fn hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2) { diff --git a/topola/src/autoplacer/interactors/mod.rs b/topola/src/autoplacer/interactors/mod.rs index c29522b..1b26d5e 100644 --- a/topola/src/autoplacer/interactors/mod.rs +++ b/topola/src/autoplacer/interactors/mod.rs @@ -4,4 +4,4 @@ mod master; -pub use master::MasterInteractor; +pub use master::AutoplacerMasterInteractor; diff --git a/topola/src/autoplacer/mod.rs b/topola/src/autoplacer/mod.rs index 89abcf0..0062d6e 100644 --- a/topola/src/autoplacer/mod.rs +++ b/topola/src/autoplacer/mod.rs @@ -4,6 +4,8 @@ pub mod interactors; +use std::ops::ControlFlow; + use rand::RngExt; use rand_distr::{Distribution, Normal}; use undoredo::{FlushDelta, ResetDelta}; @@ -17,10 +19,11 @@ use crate::{ }; pub struct AutoplacerSchedule { - initial_temperature: f64, - temperature_common_ratio: f64, - initial_std_dev: f64, - std_dev_common_ratio: f64, + pub initial_temperature: f64, + pub temperature_common_ratio: f64, + pub initial_std_dev: f64, + pub std_dev_common_ratio: f64, + pub max_steps: u64, } #[derive(Clone, Copy)] @@ -32,7 +35,7 @@ pub struct AutoplacerStepParams { pub struct Autoplacer { components: Vec, schedule: AutoplacerSchedule, - step_counter: u32, + step_counter: u64, origin_delta: BoardDelta, //rng: ThreadRng, } @@ -50,22 +53,29 @@ impl Autoplacer { } } - pub fn step(&mut self, board: &mut Board) -> bool { - self.step_with_params( - board, - AutoplacerStepParams { - temperature: self.schedule.initial_temperature - * self - .schedule - .temperature_common_ratio - .powf(self.step_counter as f64), - std_dev: self.schedule.initial_std_dev - * self - .schedule - .std_dev_common_ratio - .powf(self.step_counter as f64), - }, - ) + pub fn step(&mut self, board: &mut Board) -> ControlFlow<()> { + if self.step_counter < self.schedule.max_steps { + let control_flow = self.step_with_params( + board, + AutoplacerStepParams { + temperature: self.schedule.initial_temperature + * self + .schedule + .temperature_common_ratio + .powf(self.step_counter as f64), + std_dev: self.schedule.initial_std_dev + * self + .schedule + .std_dev_common_ratio + .powf(self.step_counter as f64), + }, + ); + + self.step_counter += 1; + control_flow + } else { + ControlFlow::Break(()) + } } // TODO. @@ -73,7 +83,11 @@ 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() { //self.step_component_with_params(component, params); let last_cost = self.cost(board, params); @@ -100,7 +114,7 @@ impl Autoplacer { } } - true + ControlFlow::Continue(()) } fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 { diff --git a/topola/src/board/interactors/master.rs b/topola/src/board/interactors/master.rs index d83928f..f509ecf 100644 --- a/topola/src/board/interactors/master.rs +++ b/topola/src/board/interactors/master.rs @@ -16,13 +16,13 @@ use crate::{ }; #[derive(Clone, Debug, Eq, Getters, PartialEq)] -pub struct MasterInteractor { +pub struct BoardMasterInteractor { select_interactor: Option, drag_move_interactor: Option, selection: PersistableSelection, } -impl MasterInteractor { +impl BoardMasterInteractor { pub fn new(selection: PersistableSelection) -> Self { Self { select_interactor: None, @@ -32,7 +32,7 @@ impl MasterInteractor { } } -impl Interactor for MasterInteractor { +impl Interactor for BoardMasterInteractor { fn delete(&mut self, board: &mut Board) { board.delete_net_free_primitives(self.selection.nets.clone()); } diff --git a/topola/src/board/interactors/mod.rs b/topola/src/board/interactors/mod.rs index 0b8705b..3d6cfd3 100644 --- a/topola/src/board/interactors/mod.rs +++ b/topola/src/board/interactors/mod.rs @@ -9,7 +9,7 @@ mod select; pub use drag_move::DragMoveInteractor; pub use drag_select::{DragSelectInteractor, DragSelectOptions}; -pub use master::MasterInteractor; +pub use master::BoardMasterInteractor; pub use select::SelectInteractor; use serde::{Deserialize, Serialize}; diff --git a/topola/src/interactor.rs b/topola/src/interactor.rs index c10acab..178f55e 100644 --- a/topola/src/interactor.rs +++ b/topola/src/interactor.rs @@ -2,14 +2,23 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 +use std::ops::ControlFlow; + use crate::{ - board::{Board, interactors::SelectInteractor, selections::PersistableSelection}, + autoplacer::{AutoplacerSchedule, interactors::AutoplacerMasterInteractor}, + board::{ + Board, + interactors::{BoardMasterInteractor, SelectInteractor}, + selections::PersistableSelection, + }, layout::LayerId, vector::Vector2, }; 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 hold(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2) {} fn release(&mut self, board: &mut Board, layer: LayerId, pointer: Vector2) {} @@ -17,35 +26,57 @@ pub trait Interactor { } pub enum MasterInteractor { - Board(crate::board::interactors::MasterInteractor), - Autoplacer(crate::autoplacer::interactors::MasterInteractor), + Board(BoardMasterInteractor), + Autoplacer(AutoplacerMasterInteractor), } impl MasterInteractor { 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 { match self { - Self::Board(interactor) => interactor.selection(), - Self::Autoplacer(interactor) => interactor.selection(), + Self::Board(board_master) => board_master.selection(), + Self::Autoplacer(autoplacer_master) => autoplacer_master.selection(), } } pub fn select_interactor(&self) -> &Option { match self { - Self::Board(interactor) => interactor.select_interactor(), - Self::Autoplacer(interactor) => interactor.select_interactor(), + Self::Board(board_master) => board_master.select_interactor(), + Self::Autoplacer(autoplacer_master) => autoplacer_master.select_interactor(), } } } impl Interactor for MasterInteractor { - fn step(&mut self, board: &mut Board) { + fn step(&mut self, board: &mut Board) -> ControlFlow<()> { match self { 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(()) + } } } diff --git a/topola/src/lib.rs b/topola/src/lib.rs index 4182b2a..050e948 100644 --- a/topola/src/lib.rs +++ b/topola/src/lib.rs @@ -20,6 +20,7 @@ mod specctra; mod vector; mod workspace; +pub use crate::autoplacer::AutoplacerSchedule; pub use crate::autorouter::Autorouter; pub use crate::board::selections; pub use crate::interactor::{Interactor, MasterInteractor};