use super::super::Graph;
use super::super::GraphNode;
use crate::graphics::*;
#[derive(Clone, Copy, Debug)]
struct Vector2D {
x: f64,
y: f64,
}
impl Vector2D {
fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
fn zero() -> Self {
Self { x: 0.0, y: 0.0 }
}
fn length(&self) -> f64 {
(self.x * self.x + self.y * self.y).sqrt()
}
fn normalized(&self) -> Self {
let len = self.length();
if len > 0.0 {
Self { x: self.x / len, y: self.y / len }
} else {
Self::zero()
}
}
}
impl std::ops::Add for Vector2D {
type Output = Self;
fn add(self, other: Self) -> Self {
Self { x: self.x + other.x, y: self.y + other.y }
}
}
impl std::ops::AddAssign for Vector2D {
fn add_assign(&mut self, other: Self) {
self.x += other.x;
self.y += other.y;
}
}
impl std::ops::Sub for Vector2D {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self { x: self.x - other.x, y: self.y - other.y }
}
}
impl std::ops::Mul<f64> for Vector2D {
type Output = Self;
fn mul(self, scalar: f64) -> Self {
Self { x: self.x * scalar, y: self.y * scalar }
}
}
pub(in super::super) fn rearange<T: GraphNode>(graph: &mut Graph<T>) {
if graph.nodes.is_empty() {
return;
}
let mut max_node_size = Size::new(1, 1);
for node in &graph.nodes {
let sz = node.rect.size();
max_node_size.width = max_node_size.width.max(sz.width);
max_node_size.height = max_node_size.height.max(sz.height);
}
let node_count = graph.nodes.len();
let area_size = ((node_count as f64).sqrt() * (max_node_size.width as f64 + max_node_size.height as f64) * 3.0) as i32;
let mut positions = Vec::with_capacity(node_count);
let mut velocities = Vec::with_capacity(node_count);
for i in 0..node_count {
let angle = 2.0 * std::f64::consts::PI * i as f64 / node_count as f64;
let radius = area_size as f64 * 0.3;
positions.push(Vector2D::new(
radius * angle.cos(),
radius * angle.sin(),
));
velocities.push(Vector2D::zero());
}
run_force_simulation(graph, &mut positions, &mut velocities, area_size);
apply_positions_to_nodes(graph, &positions, &max_node_size);
}
fn run_force_simulation<T: GraphNode>(
graph: &Graph<T>,
positions: &mut [Vector2D], velocities: &mut [Vector2D], area_size: i32,
) {
let node_count = positions.len();
if node_count <= 1 {
return;
}
let iterations = 50.min(node_count * 2); let area = (area_size as f64).powi(2);
let k = (area / node_count as f64).sqrt();
let repulsion_strength = k * k;
let attraction_strength = k;
let damping = 0.9; let max_displacement = area_size as f64 * 0.1;
for iteration in 0..iterations {
let mut forces = vec![Vector2D::zero(); node_count];
let cooling = 1.0 - (iteration as f64 / iterations as f64);
let temperature = max_displacement * cooling;
for i in 0..node_count {
for j in (i + 1)..node_count {
let delta = positions[i] - positions[j];
let distance = delta.length().max(0.1);
let repulsion_force = repulsion_strength / distance;
let force_direction = delta.normalized();
let force = force_direction * repulsion_force;
forces[i] += force;
forces[j] += force * -1.0;
}
}
for edge in &graph.edges {
let from_idx = edge.from_node_id as usize;
let to_idx = edge.to_node_id as usize;
if from_idx < node_count && to_idx < node_count {
let delta = positions[to_idx] - positions[from_idx];
let distance = delta.length().max(0.1);
let attraction_force = (distance * distance) / attraction_strength;
let force_direction = delta.normalized();
let force = force_direction * attraction_force;
forces[from_idx] += force;
forces[to_idx] += force * -1.0;
}
}
for i in 0..node_count {
velocities[i] = (velocities[i] + forces[i]) * damping;
let displacement = velocities[i] * temperature;
let displacement_length = displacement.length();
if displacement_length > temperature {
velocities[i] = displacement.normalized() * temperature;
}
positions[i] += velocities[i];
let boundary = area_size as f64 * 0.4;
if positions[i].x.abs() > boundary {
positions[i].x = positions[i].x.signum() * boundary;
velocities[i].x *= -0.5;
}
if positions[i].y.abs() > boundary {
positions[i].y = positions[i].y.signum() * boundary;
velocities[i].y *= -0.5;
}
}
}
}
fn apply_positions_to_nodes<T: GraphNode>(
graph: &mut Graph<T>,
positions: &[Vector2D],
max_node_size: &Size,
) {
if positions.is_empty() {
return;
}
let mut min_x = positions[0].x;
let mut max_x = positions[0].x;
let mut min_y = positions[0].y;
let mut max_y = positions[0].y;
for pos in positions {
min_x = min_x.min(pos.x);
max_x = max_x.max(pos.x);
min_y = min_y.min(pos.y);
max_y = max_y.max(pos.y);
}
let padding = (max_node_size.width.max(max_node_size.height) as f64) * 2.0;
let offset_x = -min_x + padding;
let offset_y = -min_y + padding;
for (i, node) in graph.nodes.iter_mut().enumerate() {
if i < positions.len() {
let final_x = (positions[i].x + offset_x) as i32;
let final_y = ((positions[i].y + offset_y) / 2.0) as i32;
node.rect.set_left(final_x - (node.rect.width() as i32 / 2), true);
node.rect.set_top(final_y - (node.rect.height() as i32 / 2), true);
}
}
}