Switch from springs to constraints
This commit is contained in:
parent
a530600e54
commit
3a04fe95b6
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue