use crate::state::{GraphData, Vec2};
pub struct LayoutConfig {
pub area: f32,
pub cooling: f32,
}
impl Default for LayoutConfig {
fn default() -> Self {
Self {
area: 200_000.0, cooling: 0.95,
}
}
}
pub fn layout_step(graph: &mut GraphData, temperature: &mut f32, config: &LayoutConfig) -> bool {
let n = graph.nodes.len();
if n == 0 {
return false;
}
let k = (config.area / n as f32).sqrt();
let k_sq = k * k;
let mut disp: Vec<Vec2> = vec![Vec2::default(); n];
for i in 0..n {
for j in (i + 1)..n {
let delta = graph.nodes[i].pos - graph.nodes[j].pos;
let dist_sq = delta.x * delta.x + delta.y * delta.y;
let dist = dist_sq.sqrt().max(0.01);
let force = k_sq / dist;
let dir = delta * (force / dist);
disp[i] += dir;
disp[j] = disp[j] - dir;
}
}
for edge in &graph.edges {
let delta = graph.nodes[edge.src].pos - graph.nodes[edge.dst].pos;
let dist = delta.length().max(0.01);
let force = dist * dist / k;
let dir = delta * (force / dist);
disp[edge.src] = disp[edge.src] - dir;
disp[edge.dst] += dir;
}
for (i, d) in disp.iter().enumerate() {
if graph.nodes[i].pinned {
continue;
}
let mag = d.length().max(0.01);
let capped = mag.min(*temperature);
graph.nodes[i].pos += *d * (capped / mag);
}
*temperature *= config.cooling;
*temperature > 0.1
}
pub fn reset_temperature(node_count: usize) -> f32 {
(200_000.0_f32 / node_count.max(1) as f32).sqrt() * 0.5
}
pub fn layout_batch(graph: &mut GraphData, temperature: &mut f32, steps: usize) -> bool {
let config = LayoutConfig::default();
let mut running = true;
for _ in 0..steps {
running = layout_step(graph, temperature, &config);
if !running {
break;
}
}
running
}
pub fn center_graph(graph: &mut GraphData) {
if graph.nodes.is_empty() {
return;
}
let n = graph.nodes.len() as f32;
let cx: f32 = graph.nodes.iter().map(|n| n.pos.x).sum::<f32>() / n;
let cy: f32 = graph.nodes.iter().map(|n| n.pos.y).sum::<f32>() / n;
for node in &mut graph.nodes {
node.pos.x -= cx;
node.pos.y -= cy;
}
}