- Metrics display and inputs
The metrics panel across the top of the svg element gives a live display of the layout state. The inputs on the left allow for the number of nodes, the strength of the side constraint and the windup factor for un-mixing to be adjusted live. The currentalpha
value for the layout is displayed along with instantaneous and averaged tick time and the average frame rate of the layout. Changing the number of nodes re-starts the layout. - Custom gravity (layers)
The nodes have acy
member which is its target y value, thegravity
function, which is called from theforce.on("tick"...)
callback has behaviour to move the nodes toward theircy
position on each tick. - Collisions between nodes
Based on this example but enhanced to simulate mass, so that bigger nodes have higher inertia in collisions and momentum is roughly conserved.
The mass is calculated assuming the nodes are spheres, using r3, and the relative rebounds calculated according to relative "mass". - Bounding box constraints and boundary collisions
This behaviour is also included in thegravity
function and uses basic geometry to reflect the incident velocity in the plane of the boundary. It does this by manipulating thed.px
andd.py
values of the nodes. It is possible, however, for nodes to penetrate the boundaries due to limitations in the temporal resolution of the layout. - Recovering escaped nodes
If a node escapes the boundaries of the bounding box, the velocity is still reflected in the plane of the penetrated boundary. If the escaped node has no velocity (p.q.x/y == p.q.px/py
) then the node is moved back toward the boundary. After it is fully recovered, the velocity of the node will be naturally determined by the distance it was last moved by the recovery behaviour. - Quantisation of screen position
It is possible for nodes with different values to be stationary due to quantisation ofd.x
andd.px
by the rendering process. This means that decisions in the code based on position may not reflect the rendered state properly. This is fixed by adding a getter to each data point that returns a rounded version ofd.x/y
andd.px/y
:d.q.x/y
andd.q.px/y
. The quantised versions are used for the decision making in the boundary collisions and recovery behaviour. - Guaranteed un-mixing of nodes
It's possible for nodes to be blocked from reaching they'recy
positions by other, more massive nodes in adjacent layers, in order to overcome this, a frustration factor is applied to the isolated nodes which has the effect of increasing they're effective mass with each tick that they are outside they're designated band. The increase in mass factor per tick is the windup which defaults to 0.01. This is adjustable via an input in the metrics panel. Nodes with an anxiety greater than one are highlighted with a white stroke and as soon as they make it to they're layer they're anxiety is switched back to 1. - Weak constraints
The strength of the boundary constraints, in terms of the rate of recovery of escaped nodes, is regulated by implementing the amount by which the nodes are moved back as a uniformly distributed random variable. This is done usingMath.random()
. This is controlled, for side constraints, via the constraint input in the metrics panel. The value of the constraint is the probability that nodes escaped to the sides will be moved back toward the bounding box. - Extended cooling time
The cooling time for the force layout is determined by the evolution of itsalpha
value. This is set to 0.1 when the layout is started and is normally reduced by 1% each tick until it reaches 0.005, at which point the layout will stop updating. It is possible to manipulate the cooling rate so that it cools at x% by dividing the current alpha value by the standard factor of 0.99 and multiplying it by (1 - x%) at the end of the tick callback:
force.alpha(a/0.99*(1 - x))
forked from cool-Blue's block: Layered gravity with weak side constraints, mass and wind-up