UNPKG

fieldkit

Version:

Basic building blocks for computational design projects. Written in CoffeeScript for browser and server environments.

257 lines (181 loc) 5.07 kB
util = require '../util' particleModule = require './particle' Particle = particleModule.Particle ### A Particle Physics Simulation ### class Physics constructor: -> # List of all particles in the simulation @particles = [] # List of all spring constraints in the simulation @springs = [] # List of all behaviours @behaviours = [] @constraints = [] # The particle emitter @emitter = null # The spatial optimisation manager to use @space = null # Settings @constraintIterations = 1 @springIterations = 1 @space = new Space() @emitter = new Emitter(this) @clear() clear: -> @particles = [] @behaviours = [] @constraints = [] # polymorphic add add: -> return if arguments.length == 0 arg = arguments[0] if arg instanceof Particle @addParticle arg else if arg instanceof Behaviour console.log "adding behaviour #{arg}" state = arguments[1] if arguments.length > 1 @addBehaviour arg, state else if arg instanceof Spring console.log "adding spring #{arg}" @addSpring arg else "Cannot add #{arg}" arg addParticle: (particle) -> @particles.push particle particle addSpring: (spring) -> @springs.push spring spring # Add a behaviour or constraint to the simulation addBehaviour: (effector, state=particleModule.State.ALIVE) -> list = if effector instanceof Constraint then @constraints else @behaviours list[state] = [] unless list[state] list[state].push effector update: -> @emitter.update() @space.update(this) particles = @particles # apply behaviours @applyEffectors @behaviours, particles # apply constraints for j in [0..@constraintIterations] @applyEffectors @constraints, particles for i in [0..@springIterations] # update springs for spring in @springs spring.update() # update all particles dead = [] stateDead = particleModule.State.DEAD for particle in particles particle.update() dead.push particle if particle.state is stateDead undefined # remove dead particles i = dead.length while i-- particle = dead[i] util.removeElement particle, particles undefined undefined # --- utilities --- # applies all effectors to the given particle list when their states match applyEffectors: (effectors, particles) -> # go through all states in effectors list state = effectors.length while state-- stateEffectors = effectors[state] # apply all behaviours for this state if particle is in this state for effector in stateEffectors effector.prepare this for particle in particles if particle.state is state and not particle.isLocked effector.apply particle undefined undefined undefined undefined # returns the number of particles size: -> @particles.length ### Spatial Search Simple brute force spatial searches. Subclasses may override this to organise particles so they can be found quicker later ### class Space physics = null constructor: -> update: (physics_) -> physics = physics_ search: (center, radius) -> result = [] radiusSq = radius * radius for particle in physics.particles if center.distanceSquared(particle.position) < radiusSq result.push particle result ### Particle Emitter ### class Emitter # local vars timer = -1 id = 0 # created with a reference to the original physics particle array constructor: (@physics) -> @rate = 1 @interval = 1 @max = 100 # @override to create your own particle type @type = particleModule.Particle3 update: -> if timer is -1 or timer >= @interval timer = 0 i = 0 while i < @rate and @physics.size() < @max p = @create() @init p i++ timer++ create: -> p = new @type(id++) @physics.addParticle p p # @overridable initialiser function run on all newly created particles init: (particle) -> ### Base class for all physical forces, behaviours & constraints ### class Behaviour prepare: -> apply: (particle) -> class Constraint extends Behaviour prepare: -> apply: (particle) -> ### Verlet Spring ### class Spring constructor: (@a, @b, @strength = 0.5) -> @restLength = @a.position.distance @b.position update: -> delta = @b.position.sub_ @a.position dist = delta.length() + Number.MIN_VALUE normDistStrength = (dist - @restLength) / dist * @strength return if normDistStrength == 0 delta.scale normDistStrength if not @a.isLocked # @a.position.add delta.scale_ normDistStrength @a.position.add delta if not @b.isLocked @b.position.sub delta toString: -> "Spring(#{a}, #{b})" module.exports = Physics: Physics Emitter: Emitter Space: Space Behaviour: Behaviour Constraint: Constraint Spring: Spring