Add WIP autoplacer implementation (not yet used)

This commit is contained in:
Mikolaj Wielgus 2026-06-04 20:53:00 +02:00
parent 299afde271
commit 0cd4b005fd
3 changed files with 138 additions and 0 deletions

View File

@ -16,6 +16,8 @@ derive_more.workspace = true
i_triangle = "0.40"
num-traits.workspace = true
polygon_unionfind = "0.7"
rand = "0.10"
rand_distr = "0.6"
rstar = "0.12"
serde.workspace = true
spade = "2.15"

135
topola/src/autoplacer.rs Normal file
View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use rand::RngExt;
use rand_distr::{Distribution, Normal};
use undoredo::{FlushDelta, ResetDelta};
use crate::{
Board, Vector2, board::BoardDelta, layout::compounds::ComponentId, orientation::Orientation,
selections::ComponentSelection,
};
pub struct AutoplacerSchedule {
initial_temperature: f64,
temperature_common_ratio: f64,
initial_std_dev: f64,
std_dev_common_ratio: f64,
}
#[derive(Clone, Copy)]
pub struct AutoplacerStepParams {
temperature: f64,
std_dev: f64,
}
pub struct Autoplacer {
components: Vec<ComponentId>,
schedule: AutoplacerSchedule,
step_counter: u32,
origin_delta: BoardDelta, //rng: ThreadRng,
}
impl Autoplacer {
pub fn new(
board: &mut Board,
selection: ComponentSelection,
schedule: AutoplacerSchedule,
) -> Self {
Self {
components: board.resolve_components(selection).collect(),
schedule,
step_counter: 0,
origin_delta: board.flush_delta(),
}
}
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),
},
)
}
// TODO.
/*pub fn reject(&mut self) {
}*/
fn step_with_params(&mut self, board: &mut Board, params: AutoplacerStepParams) -> bool {
for &component in self.components.iter() {
//self.step_component_with_params(component, params);
let last_cost = self.cost(board, params);
let dx_gaussian = Normal::new(0.0, params.std_dev).unwrap();
let dy_gaussian = Normal::new(0.0, params.std_dev).unwrap();
//let dx = dx_gaussian.sample(&mut self.rng);
//let dy = dy_gaussian.sample(&mut self.rng);
let dx = dx_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));
let new_cost = self.cost(board, params);
let delta_cost = new_cost - last_cost;
if delta_cost <= 0.0
|| f64::exp(-delta_cost / params.temperature) <= rand::rng().random()
{
self.origin_delta = self.origin_delta.clone().merge_delta(board.flush_delta());
} else {
board.reset_delta();
}
}
true
}
fn cost(&self, board: &Board, params: AutoplacerStepParams) -> f64 {
self.components
.iter()
.map(|&component| self.component_cost(board, component, params))
.sum()
}
fn component_cost(
&self,
board: &Board,
component: ComponentId,
_params: AutoplacerStepParams,
) -> f64 {
let layout = board.layout();
let repulsion_cost: i64 = layout
.locate_component_repulsions(component, Orientation::Oblique)
.map(|vector| vector.x.abs() + vector.y.abs())
.sum();
let attraction_cost: f64 = layout
.component_attractions(component)
.map(|vector| 1.0 / (1.0 + (vector.x.abs() + vector.y.abs()) as f64))
.sum();
repulsion_cost as f64 + attraction_cost
}
/*fn step_component_with_params(
&mut self,
component: ComponentId,
params: AutoplacerStepParams,
) -> bool {
//
}*/
}

View File

@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
mod autoplacer;
mod autorouter;
mod board;
mod compass;