2014-01-03 13:32:13 -05:00
|
|
|
# https://github.com/hornairs/blog/blob/master/assets/coffeescripts/flocking/vector.coffee
|
|
|
|
class Vector
|
2014-06-30 22:16:26 -04:00
|
|
|
@className: 'Vector'
|
2014-01-03 13:32:13 -05:00
|
|
|
# Class methods for nondestructively operating
|
2014-07-13 20:25:17 -04:00
|
|
|
for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
|
2014-01-03 13:32:13 -05:00
|
|
|
do (name) ->
|
|
|
|
Vector[name] = (a, b, useZ) ->
|
2015-10-21 14:37:45 -04:00
|
|
|
a.copy()["#{name}Self"](b, useZ)
|
|
|
|
for name in ['magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared']
|
|
|
|
do (name) ->
|
|
|
|
Vector[name] = (a, b, useZ) ->
|
|
|
|
a[name](b, useZ)
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
isVector: true
|
2015-10-21 14:37:45 -04:00
|
|
|
apiProperties: ['x', 'y', 'z', 'magnitude', 'heading', 'distance', 'dot', 'equals', 'copy', 'distanceSquared', 'rotate', 'add', 'subtract', 'multiply', 'divide', 'limit', 'normalize', 'rotate']
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2015-03-16 20:56:28 -04:00
|
|
|
constructor: (x=0, y=0, z=0) ->
|
2015-03-16 22:49:56 -04:00
|
|
|
return new Vector x, y, z unless @ instanceof Vector
|
|
|
|
[@x, @y, @z] = [x, y, z]
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
copy: ->
|
|
|
|
new Vector(@x, @y, @z)
|
|
|
|
|
2015-10-21 14:37:45 -04:00
|
|
|
|
|
|
|
# Mutating methods:
|
|
|
|
|
|
|
|
normalizeSelf: (useZ) ->
|
|
|
|
m = @magnitude useZ
|
|
|
|
@divideSelf m, useZ if m > 0
|
|
|
|
@
|
|
|
|
|
|
|
|
normalize: (useZ) ->
|
|
|
|
# Hack to detect when we are in player code so we can avoid mutation
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).normalizeSelf(useZ)
|
|
|
|
|
|
|
|
limitSelf: (max) ->
|
|
|
|
if @magnitude() > max
|
|
|
|
@normalizeSelf()
|
|
|
|
@multiplySelf(max)
|
|
|
|
else
|
|
|
|
@
|
|
|
|
|
|
|
|
limit: (useZ) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).limitSelf(useZ)
|
|
|
|
|
|
|
|
subtractSelf: (other, useZ) ->
|
|
|
|
@x -= other.x
|
|
|
|
@y -= other.y
|
|
|
|
@z -= other.z if useZ
|
|
|
|
@
|
|
|
|
|
|
|
|
subtract: (other, useZ) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).subtractSelf(other, useZ)
|
|
|
|
|
|
|
|
addSelf: (other, useZ) ->
|
|
|
|
@x += other.x
|
|
|
|
@y += other.y
|
|
|
|
@z += other.z if useZ
|
|
|
|
@
|
|
|
|
|
|
|
|
add: (other, useZ) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).addSelf(other, useZ)
|
|
|
|
|
|
|
|
divideSelf: (n, useZ) ->
|
|
|
|
[@x, @y] = [@x / n, @y / n]
|
|
|
|
@z = @z / n if useZ
|
|
|
|
@
|
|
|
|
|
|
|
|
divide: (n, useZ) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).divideSelf(n, useZ)
|
|
|
|
|
|
|
|
multiplySelf: (n, useZ) ->
|
|
|
|
[@x, @y] = [@x * n, @y * n]
|
|
|
|
@z = @z * n if useZ
|
|
|
|
@
|
|
|
|
|
|
|
|
multiply: (n, useZ) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).multiplySelf(n, useZ)
|
|
|
|
|
|
|
|
# Rotate it around the origin
|
|
|
|
# If we ever want to make this also use z: https://en.wikipedia.org/wiki/Axes_conventions
|
|
|
|
rotateSelf: (theta) ->
|
|
|
|
return @ unless theta
|
|
|
|
[@x, @y] = [Math.cos(theta) * @x - Math.sin(theta) * @y, Math.sin(theta) * @x + Math.cos(theta) * @y]
|
|
|
|
@
|
|
|
|
|
|
|
|
rotate: (theta) ->
|
|
|
|
(if @__aetherAPIValue? then @copy() else @).rotateSelf(theta)
|
|
|
|
|
|
|
|
# Non-mutating methods:
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
magnitude: (useZ) ->
|
|
|
|
sum = @x * @x + @y * @y
|
|
|
|
sum += @z * @z if useZ
|
|
|
|
Math.sqrt sum
|
|
|
|
|
|
|
|
magnitudeSquared: (useZ) ->
|
|
|
|
sum = @x * @x + @y * @y
|
|
|
|
sum += @z * @z if useZ
|
|
|
|
sum
|
|
|
|
|
|
|
|
heading: ->
|
|
|
|
-1 * Math.atan2(-1 * @y, @x)
|
|
|
|
|
|
|
|
distance: (other, useZ) ->
|
|
|
|
dx = @x - other.x
|
|
|
|
dy = @y - other.y
|
|
|
|
sum = dx * dx + dy * dy
|
|
|
|
if useZ
|
|
|
|
dz = @z - other.z
|
|
|
|
sum += dz * dz
|
|
|
|
Math.sqrt sum
|
|
|
|
|
|
|
|
distanceSquared: (other, useZ) ->
|
|
|
|
dx = @x - other.x
|
|
|
|
dy = @y - other.y
|
|
|
|
sum = dx * dx + dy * dy
|
|
|
|
if useZ
|
|
|
|
dz = @z - other.z
|
|
|
|
sum += dz * dz
|
|
|
|
sum
|
|
|
|
|
|
|
|
dot: (other, useZ) ->
|
|
|
|
sum = @x * other.x + @y * other.y
|
2016-03-15 11:05:00 -04:00
|
|
|
sum += @z * other.z if useZ
|
2014-01-03 13:32:13 -05:00
|
|
|
sum
|
|
|
|
|
|
|
|
# Not the strict projection, the other isn't converted to a unit vector first.
|
|
|
|
projectOnto: (other, useZ) ->
|
2015-10-21 14:37:45 -04:00
|
|
|
other.copy().multiplySelf(@dot(other, useZ), useZ)
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
isZero: (useZ) ->
|
|
|
|
result = @x is 0 and @y is 0
|
|
|
|
result = result and @z is 0 if useZ
|
|
|
|
result
|
|
|
|
|
|
|
|
equals: (other, useZ) ->
|
|
|
|
result = other and @x is other.x and @y is other.y
|
|
|
|
result = result and @z is other.z if useZ
|
|
|
|
result
|
|
|
|
|
|
|
|
invalid: () ->
|
|
|
|
return (@x is Infinity) || isNaN(@x) || @y is Infinity || isNaN(@y) || @z is Infinity || isNaN(@z)
|
|
|
|
|
2015-04-11 02:44:28 -04:00
|
|
|
toString: (precision = 2) ->
|
|
|
|
return "{x: #{@x.toFixed(precision)}, y: #{@y.toFixed(precision)}, z: #{@z.toFixed(precision)}}"
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
serialize: ->
|
|
|
|
{CN: @constructor.className, x: @x, y: @y, z: @z}
|
|
|
|
|
|
|
|
@deserialize: (o, world, classMap) ->
|
|
|
|
new Vector o.x, o.y, o.z
|
|
|
|
|
2014-01-24 16:03:04 -05:00
|
|
|
serializeForAether: -> @serialize()
|
|
|
|
@deserializeFromAether: (o) -> @deserialize o
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
module.exports = Vector
|