Skip to content
Permalink
Browse files

Forgot to add these

  • Loading branch information...
Koen Bok
Koen Bok committed Mar 12, 2013
1 parent a379acc commit 250cb04cc9b94fe9c525e0116bee2e39441871a1
Showing with 341 additions and 0 deletions.
  1. +94 −0 src/curves/bezier.coffee
  2. +123 −0 src/curves/spring.coffee
  3. +124 −0 src/primitives/matrix.coffee
@@ -0,0 +1,94 @@
# WebKit implementation found on http://stackoverflow.com/a/11697909

class UnitBezier

epsilon: 1e-6 # Precision

constructor: (p1x, p1y, p2x, p2y) ->

# pre-calculate the polynomial coefficients
# First and last control points are implied to be (0,0) and (1.0, 1.0)
@cx = 3.0 * p1x
@bx = 3.0 * (p2x - p1x) - @cx
@ax = 1.0 - @cx - @bx
@cy = 3.0 * p1y
@by = 3.0 * (p2y - p1y) - @cy
@ay = 1.0 - @cy - @by

sampleCurveX: (t) ->
((@ax * t + @bx) * t + @cx) * t

sampleCurveY: (t) ->
((@ay * t + @by) * t + @cy) * t

sampleCurveDerivativeX: (t) ->
(3.0 * @ax * t + 2.0 * @bx) * t + @cx

solveCurveX: (x) ->

# First try a few iterations of Newton's method -- normally very fast.
t2 = x
i = 0

while i < 8
x2 = @sampleCurveX(t2) - x
return t2 if Math.abs(x2) < @epsilon
d2 = @sampleCurveDerivativeX(t2)
break if Math.abs(d2) < @epsilon
t2 = t2 - x2 / d2
i++

# No solution found - use bi-section
t0 = 0.0
t1 = 1.0
t2 = x
return t0 if t2 < t0
return t1 if t2 > t1
while t0 < t1
x2 = @sampleCurveX(t2)
return t2 if Math.abs(x2 - x) < @epsilon
if x > x2
t0 = t2
else
t1 = t2
t2 = (t1 - t0) * .5 + t0

# Give up
t2

solve: (x) ->
@sampleCurveY @solveCurveX(x)

BezierCurve = (a, b, c, d, time, fps) ->

# console.log "bezier.BezierCurve", a, b, c, d, time, fps

curve = new UnitBezier a, b, c, d

values = []
steps = (time / 1000) * fps

if steps > 1000
throw Error "Bezier: too many values"

for step in [0..steps]
values.push curve.solve(step/steps) * 100

values

defaults = {}

defaults.Linear = (time, fps) ->
BezierCurve 0, 0, 1, 1, time, fps
defaults.Ease = (time, fps) ->
BezierCurve .25, .1, .25, 1, time, fps
defaults.EaseIn = (time, fps) ->
BezierCurve .42, 0, 1, 1, time, fps
defaults.EaseOut = (time, fps) ->
BezierCurve 0, 0, .58, 1, time, fps
defaults.EaseInOut = (time, fps) ->
BezierCurve .42, 0, .58, 1, time, fps

exports.defaults = defaults
exports.BezierCurve = BezierCurve

@@ -0,0 +1,123 @@
defaults =
tension: 80
friction: 8
velocity: 0
speed: 1/60.0
tolerance: 0.01


springAccelerationForState = (state) ->
return - state.tension * state.x - state.friction * state.v

springEvaluateState = (initialState) ->

output = {}
output.dx = initialState.v
output.dv = springAccelerationForState initialState

return output

springEvaluateStateWithDerivative = (initialState, dt, derivative) ->

state = {}
state.x = initialState.x + derivative.dx * dt
state.v = initialState.v + derivative.dv * dt
state.tension = initialState.tension
state.friction = initialState.friction

output = {}
output.dx = state.v
output.dv = springAccelerationForState state

return output

springIntegrateState = (state, speed) ->

a = springEvaluateState state
b = springEvaluateStateWithDerivative state, speed * 0.5, a
c = springEvaluateStateWithDerivative state, speed * 0.5, b
d = springEvaluateStateWithDerivative state, speed, c

dxdt = 1.0/6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx)
dvdt = 1.0/6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv)

state.x = state.x + dxdt * speed
state.v = state.v + dvdt * speed

return state

class Spring

constructor: (args) ->

args = args or {}

@velocity = args.velocity or defaults.velocity
@tension = args.tension or defaults.tension
@friction = args.friction or defaults.friction

@speed = args.speed or defaults.speed
@tolerance = args.tolerance or defaults.tolerance

@reset()

reset: =>
@startValue = 0
@currentValue = @startValue
@endValue = 100
@moving = true

next: =>

targetValue = @currentValue

stateBefore = {}
stateAfter = {}

# Calculate previous state
stateBefore.x = targetValue - @endValue
stateBefore.v = @velocity
stateBefore.tension = @tension
stateBefore.friction = @friction

# Calculate new state
stateAfter = springIntegrateState stateBefore, @speed
@currentValue = @endValue + stateAfter.x
finalVelocity = stateAfter.v
netFloat = stateAfter.x
net1DVelocity = stateAfter.v

# See if we reached the end state
netValueIsLow = Math.abs(netFloat) < @tolerance
netVelocityIsLow = Math.abs(net1DVelocity) < @tolerance

stopSpring = netValueIsLow and netVelocityIsLow

@moving = !stopSpring

if stopSpring
finalVelocity = 0
@currentValue = @endValue

@velocity = finalVelocity

return @currentValue

all: ->
@reset()
count = 0
while @moving
if count > 1000
throw Error "Spring: too many values"
count++
@next()

time: ->
@all().length * @speed

SpringCurve = (tension, friction, velocity, fps) ->
# console.log "spring.SpringCurve", tension, friction, velocity, fps
spring = new Spring(tension:tension, friction:friction, velocity:velocity, speed:1/fps)
spring.all()

exports.SpringCurve = SpringCurve
@@ -0,0 +1,124 @@
_ = require "underscore"



WebKitCSSMatrix::cssValues = ->

r = (v) -> v.toFixed 5

values = "
matrix3d(
#{r @m11}, #{r @m12}, #{r @m13}, #{r @m14},
#{r @m21}, #{r @m22}, #{r @m23}, #{r @m24},
#{r @m31}, #{r @m32}, #{r @m33}, #{r @m34},
#{r @m41}, #{r @m42}, #{r @m43}, #{r @m44})"

class Matrix

constructor: (matrix) ->
if matrix instanceof WebKitCSSMatrix
@from matrix

@define "x",
get: -> @_x or 0
set: (value) -> @_x = value

@define "y",
get: -> @_y or 0
set: (value) -> @_y = value

@define "z",
get: -> @_z or 0
set: (value) -> @_z = value


@define "scaleX",
get: -> @_scaleX or 1
set: (value) -> @_scaleX = value

@define "scaleY",
get: -> @_scaleY or 1
set: (value) -> @_scaleY = value

@define "scaleZ",
get: -> @_scaleZ or 1
set: (value) -> @_scaleZ = value

@define "scale",
get: -> (@_scaleX + @_scaleY) / 2.0
set: (value) ->
@_scaleX = value
@_scaleY = value


@define "rotateX",
get: -> @_rotateX or 0
set: (value) -> @_rotateX = value

@define "rotateY",
get: -> @_rotateY or 0
set: (value) -> @_rotateY = value

@define "rotateZ",
get: -> @_rotateZ or 0
set: (value) -> @_rotateZ = value

@define "rotate",
get: -> @rotateZ
set: (value) -> @rotateZ = value


decompose: (m) ->

result = {}

result.translation =
x: m.m41
y: m.m42
z: m.m43

result.scale =
x: Math.sqrt(m.m11*m.m11 + m.m12*m.m12 + m.m13*m.m13)
y: Math.sqrt(m.m21*m.m21 + m.m22*m.m22 + m.m23*m.m23)
z: Math.sqrt(m.m31*m.m31 + m.m32*m.m32 + m.m33*m.m33)

# http://blog.bwhiting.co.uk/?p=26
# Todo: There is still a bug here, where it sometimes rotates in reverse
result.rotation =
x: -Math.atan2(m.m32/result.scale.z, m.m33/result.scale.z)
y: Math.asin(m.m31/result.scale.z)
z: -Math.atan2(m.m21/result.scale.y, m.m11/result.scale.x)

return result

from: (matrix) ->

v = @decompose matrix

@x = v.translation.x
@y = v.translation.y

@scaleX = v.scale.x
@scaleY = v.scale.y
@scaleZ = v.scale.z

@rotateX = v.rotation.x / Math.PI * 180
@rotateY = v.rotation.y / Math.PI * 180
@rotateZ = v.rotation.z / Math.PI * 180


matrix: ->
m = new WebKitCSSMatrix()
m = m.translate @_x, @_y, @_z
m = m.rotate @_rotateX, 0, 0
m = m.rotate 0, @_rotateY, 0
m = m.rotate 0, 0, @_rotateZ
m = m.scale @scaleX, @scaleY, @scaleZ

return m

set: (view) ->
view._matrix = @


exports.Matrix = Matrix

0 comments on commit 250cb04

Please sign in to comment.
You can’t perform that action at this time.