Hylograph.ForceEngine.Setup
- Package
- purescript-hylograph-simulation
- Repository
- afcondon/purescript-hylograph-simulation
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
applySetupthat 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
#valueToNumber Source
valueToNumber :: forall node. Value node Number -> NumberExtract a number from a Value (for static values only, dynamic returns default) Used internally; prefer the accessor pattern for dynamic values
#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 -> StringGet the name of a force config
#manyBody Source
manyBody :: forall node. String -> ForceConfig nodeCreate a many-body (charge) force Default: repulsive with strength -30
#collide Source
collide :: forall node. String -> ForceConfig nodeCreate a collision force Default: radius 1, strength 1
#link Source
link :: forall node. String -> ForceConfig nodeCreate a link (spring) force Default: distance 30, strength 1
#center Source
center :: forall node. String -> ForceConfig nodeCreate a centering force Default: center at (0, 0), strength 1
#positionX Source
positionX :: forall node. String -> ForceConfig nodeCreate an X-positioning force Default: x = 0, strength 0.1
#positionY Source
positionY :: forall node. String -> ForceConfig nodeCreate a Y-positioning force Default: y = 0, strength 0.1
#radial Source
radial :: forall node. String -> ForceConfig nodeCreate a radial force Default: radius 100, center (0,0), strength 0.1
#withStrength Source
withStrength :: forall node. Value node Number -> ForceConfig node -> ForceConfig nodeSet strength (works for all force types)
#withRadius Source
withRadius :: forall node. Value node Number -> ForceConfig node -> ForceConfig nodeSet radius (for Collide, Radial)
#withX Source
withX :: forall node. Value node Number -> ForceConfig node -> ForceConfig nodeSet X position/target (for Center, PositionX, Radial)
#withY Source
withY :: forall node. Value node Number -> ForceConfig node -> ForceConfig nodeSet Y position/target (for Center, PositionY, Radial)
#withDistance Source
withDistance :: forall node. Value node Number -> ForceConfig node -> ForceConfig nodeSet distance (for Link)
#withTheta Source
withTheta :: forall node. Number -> ForceConfig node -> ForceConfig nodeSet theta (Barnes-Hut approximation, for ManyBody)
#withIterations Source
withIterations :: forall node. Int -> ForceConfig node -> ForceConfig nodeSet iterations (for Collide, Link)
#withFilter Source
withFilter :: forall node. (node -> Boolean) -> ForceConfig node -> ForceConfig nodeAdd a filter predicate (force only applies to matching nodes)
#withDistanceMin Source
withDistanceMin :: forall node. Number -> ForceConfig node -> ForceConfig nodeSet distance min (for ManyBody)
#withDistanceMax Source
withDistanceMax :: forall node. Number -> ForceConfig node -> ForceConfig nodeSet distance max (for ManyBody)
#Setup Source
type Setup node = { forces :: Array (ForceConfig node), name :: String, params :: SetupParams }Complete setup: forces + simulation params
#defaultParams Source
defaultParams :: SetupParamsDefault simulation parameters
#setup Source
setup :: forall node. String -> Array (ForceConfig node) -> Setup nodeCreate a setup with default params
#setupWithParams Source
setupWithParams :: forall node. String -> Array (ForceConfig node) -> SetupParams -> Setup nodeCreate 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 -> SetupParamsGet params from setup
#addForce Source
addForce :: forall node. ForceConfig node -> Setup node -> Setup nodeAdd a force to the setup
#removeForce Source
removeForce :: forall node. String -> Setup node -> Setup nodeRemove a force by name
#updateForce Source
updateForce :: forall node. ForceConfig node -> Setup node -> Setup nodeUpdate a force by name (replace if exists)
#withAlphaDecay Source
withAlphaDecay :: forall node. Number -> Setup node -> Setup nodeSet alpha decay
#withAlphaTarget Source
withAlphaTarget :: forall node. Number -> Setup node -> Setup nodeSet alpha target
#withVelocityDecay Source
withVelocityDecay :: forall node. Number -> Setup node -> Setup nodeSet velocity decay
#applySetup Source
applySetup :: forall row linkRow. Setup (SimulationNode row) -> Simulation row linkRow -> Effect UnitApply 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.
Example:
-- Initial setup
applySetup mySetup sim
-- Later: update setup (just call again)
applySetup (mySetup # removeForce "charge") sim
#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
#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 (General Update Pattern) semantics.
This function handles three concerns:
- Force configuration (same as applySetup)
- Node GUP: diff desired nodes vs current, merge simulation state for updates
- Link GUP: diff desired links vs current
Simulation state preservation (for updated nodes):
- x, y: Current position (preserved)
- vx, vy: Current velocity (preserved)
- fx, fy: Pinned position (preserved)
- All other fields: Taken from desired nodes
Example usage:
-- Stop simulation before transition
Sim.stop sim
-- Apply new configuration and data
result <- applySetupWithData mySetup newNodes newLinks sim
-- Use result for animations
animateEnter result.nodes.entered
animateExit result.nodes.exited
-- Restart simulation
Sim.reheat sim
#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
#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 linkRowCompute link GUP: which links are entering, updating, exiting Links are keyed by (source, target) pair
#mergeSimulationState Source
mergeSimulationState :: forall row. SimulationNode row -> SimulationNode row -> SimulationNode rowMerge simulation-managed state from current node into desired node
Preserves: x, y, vx, vy, fx, fy (simulation-managed) Takes from desired: everything else (application data)
#addForceToSim Source
addForceToSim :: forall row linkRow. ForceConfig (SimulationNode row) -> Simulation row linkRow -> Effect UnitAdd a single force to simulation (without full setup)
#removeForceFromSim Source
removeForceFromSim :: forall row linkRow. String -> Simulation row linkRow -> Effect UnitRemove a single force from simulation by name
- Modules
- Hylograph.
Config. Apply - Hylograph.
Config. Force - Hylograph.
Config. Scene - Hylograph.
ForceEngine - Hylograph.
ForceEngine. Core - Hylograph.
ForceEngine. Demo - Hylograph.
ForceEngine. Events - Hylograph.
ForceEngine. Links - Hylograph.
ForceEngine. Registry - Hylograph.
ForceEngine. Render - Hylograph.
ForceEngine. Setup - Hylograph.
ForceEngine. Setup. WASM - Hylograph.
ForceEngine. Simulation - Hylograph.
ForceEngine. Types - Hylograph.
ForceEngine. WASM - Hylograph.
ForceEngine. WASMEngine - Hylograph.
Scene. Engine - Hylograph.
Scene. Handle - Hylograph.
Scene. Rules - Hylograph.
Scene. Types - Hylograph.
Simulation - Hylograph.
Simulation. Emitter - Hylograph.
Simulation. HATS - Hylograph.
Simulation. Scene - Hylograph.
Transition. Consumers - Hylograph.
Transition. Example