Switch from springs to constraints

This commit is contained in:
mars 2022-11-04 19:28:55 -06:00
parent a530600e54
commit 3a04fe95b6
1 changed files with 45 additions and 51 deletions

View File

@ -30,6 +30,10 @@ impl Body {
self.acceleration += force / self.mass; // F = ma
}
pub fn apply_velocity(&mut self, vel: Vec2) {
self.velocity += vel;
}
pub fn update(&mut self, dt: f32) {
self.velocity += self.acceleration * dt;
self.velocity *= self.drag.powf(dt);
@ -46,6 +50,7 @@ pub struct NodeInfo {
pub label_offset: Vec2,
pub text_size: f32,
pub hidden: bool,
pub parent: Option<usize>,
pub unhide: Vec<usize>,
}
@ -61,6 +66,7 @@ impl NodeInfo {
label_offset,
text_size,
hidden: false,
parent: None,
unhide: Vec::new(),
}
}
@ -180,25 +186,24 @@ impl MenuNode {
},
info: NodeInfo {
hidden: true,
parent: Some(parent),
..NodeInfo::new(&child.label)
},
});
graph.springs.push(Spring {
graph.springs.push(Constraint {
from: parent,
to: id,
length: 30.0,
stiffness: 15.0,
margin: 10.0,
strength: 1.0,
});
for sibling in siblings.iter() {
graph.springs.push(Spring {
graph.springs.push(Constraint {
from: *sibling,
to: id,
length: 60.0,
stiffness: 15.0,
margin: 10.0,
strength: 0.1,
});
}
@ -219,47 +224,20 @@ impl MenuNode {
}
#[derive(Clone)]
pub struct Spring {
pub struct Constraint {
pub from: usize,
pub to: usize,
pub length: f32,
pub stiffness: f32,
pub margin: f32,
pub strength: f32,
}
impl Spring {
pub fn hookes_law(&self, delta: Vec2) -> Vec2 {
const MAX_DISPLACE: f32 = 20.0;
let distance = delta.length();
let mut displacement = distance - self.length;
/*if displacement.abs() < self.margin {
return Vec2::ZERO;
} else if displacement.is_sign_positive() {
displacement -= self.margin;
} else {
displacement += self.margin;
}*/
/*if displacement.abs() < self.margin {
displacement *= displacement.abs() / self.margin;
}*/
let displacement = displacement.clamp(-MAX_DISPLACE, MAX_DISPLACE);
let force = displacement * self.stiffness * delta.normalize_or_zero();
force
}
pub fn d3_spring(&self, delta: Vec2) -> Vec2 {
impl Constraint {
pub fn apply(&self, mut delta: Vec2) -> Vec2 {
const ALPHA: f32 = 0.1;
let distance = delta.length();
let displacement = distance - self.length;
if displacement.abs() < self.margin {
return Vec2::ZERO;
}
self.stiffness.copysign(displacement) * delta.normalize()
delta *= displacement / distance * self.strength * ALPHA;
delta
}
}
@ -274,13 +252,13 @@ pub struct Connection {
#[derive(Default)]
pub struct ForceGraph {
pub nodes: Vec<Node>,
pub springs: Vec<Spring>,
pub springs: Vec<Constraint>,
pub connections: Vec<Connection>,
}
impl ForceGraph {
pub fn update(&mut self, dt: f32) {
self.apply_springs();
self.apply_springs(dt);
self.apply_repulsion();
for node in self.nodes.iter_mut() {
@ -306,16 +284,18 @@ impl ForceGraph {
}
}
pub fn apply_springs(&mut self) {
pub fn apply_springs(&mut self, dt: f32) {
for spring in self.springs.iter() {
let force = self.with_unhidden(spring.from, spring.to, |from, to| {
let delta = to.body.position - from.body.position;
spring.d3_spring(delta)
let vel = self.with_unhidden(spring.from, spring.to, |from, to| {
let from = from.body.position + from.body.velocity * dt;
let to = to.body.position + to.body.velocity * dt;
let delta = to - from;
spring.apply(delta) // * dt
});
if let Some(force) = force {
self.nodes[spring.from].body.apply_force(force);
self.nodes[spring.to].body.apply_force(-force);
if let Some(vel) = vel {
self.nodes[spring.from].body.apply_velocity(vel);
self.nodes[spring.to].body.apply_velocity(-vel);
}
}
}
@ -324,7 +304,7 @@ impl ForceGraph {
for i in 1..self.nodes.len() {
for j in i..self.nodes.len() {
let force = self.with_unhidden(i - 1, j, |from, to| {
const REPULSION_CONSTANT: f32 = 300.0;
const REPULSION_CONSTANT: f32 = 1000.0;
let delta = to.body.position - from.body.position;
let proximity = delta.length().max(0.1);
let force = -(REPULSION_CONSTANT / (proximity * proximity));
@ -345,8 +325,22 @@ impl ForceGraph {
let position = node.body.position;
let children = node.info.unhide.to_owned();
// Avoid divide-by-zero
if children.is_empty() {
return;
}
let mut neighbor_num = children.len();
if node.info.parent.is_some() {
neighbor_num += 1;
}
let theta = std::f32::consts::TAU / neighbor_num as f32;
for child in children {
let angle = Vec2::from_angle(child as f32);
let angle = Vec2::from_angle((child + 1) as f32 * theta);
let child = &mut self.nodes[child];
child.info.hidden = false;
child.body.fixed = false;