Module

Hylograph.Kernel.D3.Setup

Package
purescript-hylograph-d3-kernel
Repository
afcondon/purescript-hylograph-d3-kernel

Declarative Force Setup

Unified system for declaratively configuring force simulations. Replaces the older ForceSpec (too simple) and Config.* modules (wrong abstraction layer).

Key features:

  • Static or dynamic (per-node) parameter values
  • Optional filters to apply forces selectively
  • Idempotent applySetup that works for both initial setup and updates

Example:

mySetup = setup "physics"
  [ manyBody "charge" # withStrength (-30.0)
  , collide "collision" # withRadius (dynamic _.r)
  , positionX "gridX" # withX (dynamic _.gridX) # withStrength 0.1
  , positionY "gridY" # withY (dynamic _.gridY) # withStrength 0.1
  ]

-- Apply to simulation (idempotent - can call repeatedly)
applySetup mySetup sim

-- Update: just call applySetup with new config
applySetup (mySetup # removeForce "charge") sim

#Value Source

data Value node a

A value that can be static or computed per-node

Constructors

#static Source

static :: forall node a. a -> Value node a

Create a static value

#dynamic Source

dynamic :: forall node a. (node -> a) -> Value node a

Create a dynamic (per-node) value

#valueToNumber Source

valueToNumber :: forall node. Value node Number -> Number

Extract a number from a Value (for static values only, dynamic returns default) Used internally; prefer the accessor pattern for dynamic values

#ForceType Source

data ForceType

Force type enumeration

Instances

#ForceConfig Source

type ForceConfig node = { distance :: Value node Number, distanceMax :: Number, distanceMin :: Number, filter :: Maybe (node -> Boolean), forceType :: ForceType, iterations :: Int, name :: String, radius :: Value node Number, strength :: Value node Number, theta :: Number, x :: Value node Number, y :: Value node Number }

Configuration for a single force Parameterized by node type for dynamic values and filters

#forceName Source

forceName :: forall node. ForceConfig node -> String

Get the name of a force config

#manyBody Source

manyBody :: forall node. String -> ForceConfig node

Create a many-body (charge) force Default: repulsive with strength -30

#collide Source

collide :: forall node. String -> ForceConfig node

Create a collision force Default: radius 1, strength 1

#center Source

center :: forall node. String -> ForceConfig node

Create a centering force Default: center at (0, 0), strength 1

#positionX Source

positionX :: forall node. String -> ForceConfig node

Create an X-positioning force Default: x = 0, strength 0.1

#positionY Source

positionY :: forall node. String -> ForceConfig node

Create a Y-positioning force Default: y = 0, strength 0.1

#radial Source

radial :: forall node. String -> ForceConfig node

Create a radial force Default: radius 100, center (0,0), strength 0.1

#withStrength Source

withStrength :: forall node. Value node Number -> ForceConfig node -> ForceConfig node

Set strength (works for all force types)

#withRadius Source

withRadius :: forall node. Value node Number -> ForceConfig node -> ForceConfig node

Set radius (for Collide, Radial)

#withX Source

withX :: forall node. Value node Number -> ForceConfig node -> ForceConfig node

Set X position/target (for Center, PositionX, Radial)

#withY Source

withY :: forall node. Value node Number -> ForceConfig node -> ForceConfig node

Set Y position/target (for Center, PositionY, Radial)

#withDistance Source

withDistance :: forall node. Value node Number -> ForceConfig node -> ForceConfig node

Set distance (for Link)

#withTheta Source

withTheta :: forall node. Number -> ForceConfig node -> ForceConfig node

Set theta (Barnes-Hut approximation, for ManyBody)

#withIterations Source

withIterations :: forall node. Int -> ForceConfig node -> ForceConfig node

Set iterations (for Collide, Link)

#withFilter Source

withFilter :: forall node. (node -> Boolean) -> ForceConfig node -> ForceConfig node

Add a filter predicate (force only applies to matching nodes)

#withDistanceMin Source

withDistanceMin :: forall node. Number -> ForceConfig node -> ForceConfig node

Set distance min (for ManyBody)

#withDistanceMax Source

withDistanceMax :: forall node. Number -> ForceConfig node -> ForceConfig node

Set distance max (for ManyBody)

#Setup Source

type Setup node = { forces :: Array (ForceConfig node), name :: String, params :: SetupParams }

Complete setup: forces + simulation params

#SetupParams Source

type SetupParams = { alpha :: Number, alphaDecay :: Number, alphaMin :: Number, alphaTarget :: Number, velocityDecay :: Number }

Simulation parameters

#defaultParams Source

defaultParams :: SetupParams

Default simulation parameters

#setup Source

setup :: forall node. String -> Array (ForceConfig node) -> Setup node

Create a setup with default params

#setupWithParams Source

setupWithParams :: forall node. String -> Array (ForceConfig node) -> SetupParams -> Setup node

Create a setup with custom params

#getForces Source

getForces :: forall node. Setup node -> Array (ForceConfig node)

Get forces from setup

#getParams Source

getParams :: forall node. Setup node -> SetupParams

Get params from setup

#addForce Source

addForce :: forall node. ForceConfig node -> Setup node -> Setup node

Add a force to the setup

#removeForce Source

removeForce :: forall node. String -> Setup node -> Setup node

Remove a force by name

#updateForce Source

updateForce :: forall node. ForceConfig node -> Setup node -> Setup node

Update a force by name (replace if exists)

#withAlpha Source

withAlpha :: forall node. Number -> Setup node -> Setup node

Set alpha

#withAlphaDecay Source

withAlphaDecay :: forall node. Number -> Setup node -> Setup node

Set alpha decay

#withAlphaTarget Source

withAlphaTarget :: forall node. Number -> Setup node -> Setup node

Set alpha target

#withAlphaMin Source

withAlphaMin :: forall node. Number -> Setup node -> Setup node

Set alpha min

#withVelocityDecay Source

withVelocityDecay :: forall node. Number -> Setup node -> Setup node

Set velocity decay

#applySetup Source

applySetup :: forall row linkRow. Setup (SimulationNode row) -> Simulation row linkRow -> Effect Unit

Apply a setup to a simulation.

This is idempotent - calling with the same setup does minimal work:

  • Forces in setup but not simulation: create and add
  • Forces in simulation but not setup: remove

After applying, forces are re-initialized with current nodes.

#GUPResult Source

type GUPResult node = { entered :: Array node, exited :: Array node, updated :: Array node }

Result of a node data update with enter/update/exit categorization

#GUPLinkResult Source

type GUPLinkResult :: Row Type -> Typetype GUPLinkResult linkRow = { entered :: Array { source :: NodeID, target :: NodeID | linkRow }, exited :: Array { source :: NodeID, target :: NodeID | linkRow }, updated :: Array { source :: NodeID, target :: NodeID | linkRow } }

Result of a link data update with enter/update/exit categorization

#applySetupWithData Source

applySetupWithData :: forall row linkRow. Setup (SimulationNode row) -> Array (SimulationNode row) -> Array { source :: NodeID, target :: NodeID | linkRow } -> Simulation row linkRow -> Effect { links :: GUPLinkResult linkRow, nodes :: GUPResult (SimulationNode row) }

Apply setup with new nodes and links, using GUP semantics.

#addForceToSim Source

addForceToSim :: forall row linkRow. ForceConfig (SimulationNode row) -> Simulation row linkRow -> Effect Unit

Add a single force to simulation (without full setup)

#removeForceFromSim Source

removeForceFromSim :: forall row linkRow. String -> Simulation row linkRow -> Effect Unit

Remove a single force from simulation by name