Copyright | (c) 2011 Brent Yorgey |
---|---|
License | BSD-style (see LICENSE) |
Maintainer | byorgey@cis.upenn.edu |
Safe Haskell | None |
Language | Haskell2010 |
A simple, Haskell-native simulator for doing force-directed layout, e.g. of trees or graphs.
To use, just create an Ensemble
like so:
import Control.Lens ((&), (.~)) import Data.Default.Class (def) import qualified Data.Map as M import Linear.Affine import Linear.V2 import Physics.ForceLayout e :: Ensemble V2 Double e = Ensemble [ (edges, hookeForce 0.05 4) , (allPairs, coulombForce 1) ] particleMap where edges = [(1,2), (2,3), (2,5), (3,5), (3,4), (4,5)] allPairs = [(x,y) | x <- [1..4], y <- [x+1..5]] particleMap = M.fromList . zip [1..] . map (initParticle . P . uncurry V2) $ [ (2.0, 3.1), (6.3, 7.2) , (0.3, 4.2), (1.6, -1.1) , (4.8, 2.9) ]
Then run a simulation using either simulate
(to get the list of
all intermediate states) or forceLayout
(to get only the ending
state):
e' :: Ensemble V2 Double e' = forceLayout (def & damping .~ 0.8 & energyLimit .~ Just 0.001 & stepLimit .~ Nothing ) e
See the diagrams-contrib package (http://github.com/diagrams/diagrams-contrib/) for more examples.
- data Particle v n = Particle {}
- pos :: forall v n. Lens' (Particle v n) (Point v n)
- vel :: forall v n. Lens' (Particle v n) (v n)
- force :: forall v n. Lens' (Particle v n) (v n)
- initParticle :: (Additive v, Num n) => Point v n -> Particle v n
- type PID = Int
- type Edge = (PID, PID)
- data Ensemble v n = Ensemble {}
- forces :: forall v n. Lens' (Ensemble v n) [([Edge], Point v n -> Point v n -> v n)]
- particles :: forall v n. Lens' (Ensemble v n) (Map PID (Particle v n))
- hookeForce :: (Metric v, Floating n) => n -> n -> Point v n -> Point v n -> v n
- coulombForce :: (Metric v, Floating n) => n -> Point v n -> Point v n -> v n
- distForce :: (Metric v, Floating n) => (n -> n) -> Point v n -> Point v n -> v n
- data ForceLayoutOpts n = FLOpts {
- _damping :: n
- _energyLimit :: Maybe n
- _stepLimit :: Maybe Int
- damping :: forall n. Lens' (ForceLayoutOpts n) n
- energyLimit :: forall n. Lens' (ForceLayoutOpts n) (Maybe n)
- stepLimit :: forall n. Lens' (ForceLayoutOpts n) (Maybe Int)
- simulate :: (Metric v, Num n, Ord n) => ForceLayoutOpts n -> Ensemble v n -> [Ensemble v n]
- forceLayout :: (Metric v, Num n, Ord n) => ForceLayoutOpts n -> Ensemble v n -> Ensemble v n
- ensembleStep :: (Additive v, Num n) => n -> Ensemble v n -> Ensemble v n
- particleStep :: (Additive v, Num n) => n -> Particle v n -> Particle v n
- recalcForces :: (Additive v, Num n) => Ensemble v n -> Ensemble v n
- kineticEnergy :: (Metric v, Num n) => Ensemble v n -> n
Data structures
A particle has a current position, current velocity, and current force acting on it.
initParticle :: (Additive v, Num n) => Point v n -> Particle v n Source #
Create an initial particle at rest at a particular location.
An Ensemble
is a physical configuration of particles. It
consists of a mapping from particle IDs (unique integers) to
particles, and a list of forces that are operative. Each force
has a list of edges to which it applies, and is represented by a
function giving the force between any two points.
Pre-defined forces
hookeForce :: (Metric v, Floating n) => n -> n -> Point v n -> Point v n -> v n Source #
hookeForce k l p1 p2
computes the force on p1
, assuming that
p1
and p2
are connected by a spring with equilibrium length l
and spring constant k
.
coulombForce :: (Metric v, Floating n) => n -> Point v n -> Point v n -> v n Source #
coulombForce k
computes the electrostatic repulsive force
between two charged particles, with constant of proportionality
k
.
distForce :: (Metric v, Floating n) => (n -> n) -> Point v n -> Point v n -> v n Source #
distForce f p1 p2
computes the force between two points as a
multiple of the unit vector in the direction from p1
to p2
,
given a function f
which computes the force's magnitude as a
function of the distance between the points.
Running simulations
data ForceLayoutOpts n Source #
Options for customizing a simulation.
FLOpts | |
|
Fractional n => Default (ForceLayoutOpts n) Source # | |
damping :: forall n. Lens' (ForceLayoutOpts n) n Source #
energyLimit :: forall n. Lens' (ForceLayoutOpts n) (Maybe n) Source #
simulate :: (Metric v, Num n, Ord n) => ForceLayoutOpts n -> Ensemble v n -> [Ensemble v n] Source #
Simulate a starting ensemble according to the given options,
producing a list of all the intermediate ensembles. Useful for,
e.g., making an animation. Note that the resulting list could
be infinite, if a stepLimit
is not specified and either the
kinetic energy never falls below the specified threshold, or no
energy threshold is specified.
forceLayout :: (Metric v, Num n, Ord n) => ForceLayoutOpts n -> Ensemble v n -> Ensemble v n Source #
Run a simluation from a starting ensemble, yielding either the
first ensemble to have kinetic energy below the energyLimit
(if
given), or the ensemble that results after a number of steps
equal to the stepLimit
(if given), whichever comes first.
Otherwise forceLayout
will not terminate.
Internals
ensembleStep :: (Additive v, Num n) => n -> Ensemble v n -> Ensemble v n Source #
Simulate one time step for an entire ensemble, with the given damping factor.
particleStep :: (Additive v, Num n) => n -> Particle v n -> Particle v n Source #
Simulate one time step for a particle (assuming the force acting on it has already been computed), with the given damping factor.