Module

Hylograph.ForceEngine.Setup.WASM

Package
purescript-hylograph-simulation
Repository
afcondon/purescript-hylograph-simulation

WASM Force Setup

Declarative force configuration for WASM simulation engine. Uses the same Setup types as D3, providing functional parity.

Key differences from D3:

  • WASM has fixed force types (one of each, not multiple named)
  • Dynamic (per-node) values use static fallback
  • Filters are not supported

Example:

import Hylograph.ForceEngine.Setup (setup, manyBody, center, withStrength, static)
import Hylograph.ForceEngine.Setup.WASM as WASM

mySetup = setup "physics"
  [ manyBody "charge" # withStrength (static (-30.0))
  , center "center" # withX (static 0.0) # withY (static 0.0)
  ]

main = do
  WASM.initWasm "./pkg/force_kernel.js"
  sim <- WASM.create initialNodes initialLinks
  result <- WASM.applySetupWithData mySetup newNodes newLinks sim
  -- Use result.nodes.entered, result.nodes.exited for animations

#WASMSim Source

type WASMSim :: Row Type -> Typetype WASMSim r = { linksRef :: Ref (Array { source :: Int, target :: Int }), nodesRef :: Ref (Array (SimulationNode r)), wasmSim :: WASMSimulation }

WASM Simulation handle. Wraps the low-level WASM handle with PureScript state management.

#initWasm Source

initWasm :: String -> Aff Unit

Initialize the WASM module. Call once at application startup.

#isWasmReady Source

isWasmReady :: Effect Boolean

Check if WASM is initialized

#create Source

create :: forall r. Array (SimulationNode r) -> Array { source :: Int, target :: Int } -> Effect (WASMSim r)

Create a WASM simulation with initial nodes and links.

#createEmpty Source

createEmpty :: forall r. Effect (WASMSim r)

Create an empty WASM simulation (for use with applySetupWithData)

#free Source

free :: forall r. WASMSim r -> Effect Unit

Free WASM simulation memory

#applySetup Source

applySetup :: forall r. Setup (SimulationNode r) -> WASMSim r -> Effect Unit

Apply a Setup to a WASM simulation.

Maps declarative force configuration to WASM kernel calls. This is idempotent - can be called repeatedly with different setups.

Limitations vs D3:

  • Only one force of each type (first wins if multiple specified)
  • Dynamic values fall back to static (with warning)
  • Filters are ignored (with warning)
  • ForceRadial is not supported (warning logged)

#applySetupWithData Source

applySetupWithData :: forall r. Setup (SimulationNode r) -> Array (SimulationNode r) -> Array { source :: Int, target :: Int } -> WASMSim r -> Effect { links :: GUPLinkResult (), nodes :: GUPResult (SimulationNode r) }

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

This provides functional parity with D3's applySetupWithData:

  1. Force configuration (via applySetup)
  2. Node GUP: diff desired vs current, preserve simulation state
  3. Link GUP: diff desired vs current

Simulation state preserved for updated nodes:

  • x, y: Current position
  • vx, vy: Current velocity
  • fx, fy: Pinned position

Example:

result <- applySetupWithData mySetup newNodes newLinks sim
animateEnter result.nodes.entered
animateExit result.nodes.exited
reheat sim

#tick Source

tick :: forall r. WASMSim r -> Effect Number

Run a single simulation tick. Returns new alpha value.

#tickN Source

tickN :: forall r. Int -> WASMSim r -> Effect Number

Run multiple ticks. Returns final alpha.

#getNodes Source

getNodes :: forall r. WASMSim r -> Effect (Array (SimulationNode r))

Get current nodes with positions

#getAlpha Source

getAlpha :: forall r. WASMSim r -> Effect Number

Get current alpha value

#reheat Source

reheat :: forall r. WASMSim r -> Effect Unit

Reheat simulation (set alpha to 1.0)

#isRunning Source

isRunning :: forall r. WASMSim r -> Effect Boolean

Check if simulation is still running

Re-exports from Hylograph.ForceEngine.Setup

#Value Source

data Value node a

A value that can be static or computed per-node

Constructors

#SetupParams Source

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

Simulation parameters

#Setup Source

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

Complete setup: forces + simulation params

#NodeGUPInternal Source

type NodeGUPInternal node = { entered :: Array node, exited :: Array node, merged :: Array node, updated :: Array node }

Internal result type that includes the merged array

#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

  • entered: Nodes newly added to simulation (use for enter animations)
  • updated: Existing nodes that were modified (simulation state preserved)
  • exited: Nodes removed from simulation (use for exit animations)

#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 Links are keyed by (source, target) pair

#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

#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

#getParams Source

getParams :: forall node. Setup node -> SetupParams

Get params from setup

#getForces Source

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

Get forces from setup

#computeNodeGUP Source

computeNodeGUP :: forall row. Array (SimulationNode row) -> Array (SimulationNode row) -> NodeGUPInternal (SimulationNode row)

Compute node GUP: which nodes are entering, updating, exiting For updates, merge simulation state (x, y, vx, vy, fx, fy) from current into desired

#computeLinkGUP Source

computeLinkGUP :: forall linkRow. Array { source :: NodeID, target :: NodeID | linkRow } -> Array { source :: NodeID, target :: NodeID | linkRow } -> GUPLinkResult linkRow

Compute link GUP: which links are entering, updating, exiting Links are keyed by (source, target) pair

Re-exports from Hylograph.ForceEngine.Simulation

#SimulationNode Source

type SimulationNode :: Row Type -> Typetype SimulationNode r = Record (D3_ID + D3_XY + D3_VxyFxy + r)

A simulation node with all required fields for force simulation and transitions. Extends user data row with id, position (x/y), velocity (vx/vy), and fixed position (fx/fy).

Example:

type MyNode = SimulationNode (name :: String, group :: Int)
-- Expands to: { id :: Int, x, y, vx, vy, fx, fy, name :: String, group :: Int }