fieldkit
Version:
Basic building blocks for computational design projects. Written in CoffeeScript for browser and server environments.
257 lines (181 loc) • 5.07 kB
text/coffeescript
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