agt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.md +7 -0
- data/README.md +5 -0
- data/app/assets/javascripts/agt/config.coffee +9 -0
- data/app/assets/javascripts/agt/dom.coffee +12 -0
- data/app/assets/javascripts/agt/function.coffee +317 -0
- data/app/assets/javascripts/agt/geom/circle.coffee +338 -0
- data/app/assets/javascripts/agt/geom/cubic_bezier.coffee +37 -0
- data/app/assets/javascripts/agt/geom/diamond.coffee +241 -0
- data/app/assets/javascripts/agt/geom/ellipsis.coffee +141 -0
- data/app/assets/javascripts/agt/geom/linear_spline.coffee +56 -0
- data/app/assets/javascripts/agt/geom/matrix.coffee +240 -0
- data/app/assets/javascripts/agt/geom/mixins/geometry.coffee +171 -0
- data/app/assets/javascripts/agt/geom/mixins/intersections.coffee +150 -0
- data/app/assets/javascripts/agt/geom/mixins/path.coffee +145 -0
- data/app/assets/javascripts/agt/geom/mixins/proxyable.coffee +9 -0
- data/app/assets/javascripts/agt/geom/mixins/spline.coffee +329 -0
- data/app/assets/javascripts/agt/geom/mixins/surface.coffee +55 -0
- data/app/assets/javascripts/agt/geom/mixins/triangulable.coffee +112 -0
- data/app/assets/javascripts/agt/geom/point.coffee +454 -0
- data/app/assets/javascripts/agt/geom/polygon.coffee +119 -0
- data/app/assets/javascripts/agt/geom/quad_bezier.coffee +43 -0
- data/app/assets/javascripts/agt/geom/quint_bezier.coffee +42 -0
- data/app/assets/javascripts/agt/geom/rectangle.coffee +599 -0
- data/app/assets/javascripts/agt/geom/spiral.coffee +90 -0
- data/app/assets/javascripts/agt/geom/transformation_proxy.coffee +43 -0
- data/app/assets/javascripts/agt/geom/triangle.coffee +442 -0
- data/app/assets/javascripts/agt/impulse.coffee +105 -0
- data/app/assets/javascripts/agt/index.coffee +56 -0
- data/app/assets/javascripts/agt/inflector/inflection.coffee +21 -0
- data/app/assets/javascripts/agt/inflector/inflections.coffee +75 -0
- data/app/assets/javascripts/agt/inflector/inflector.coffee +235 -0
- data/app/assets/javascripts/agt/inheritance.coffee +132 -0
- data/app/assets/javascripts/agt/math.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/activable.coffee +31 -0
- data/app/assets/javascripts/agt/mixins/aliasable.coffee +25 -0
- data/app/assets/javascripts/agt/mixins/alternate_case.coffee +72 -0
- data/app/assets/javascripts/agt/mixins/cloneable.coffee +71 -0
- data/app/assets/javascripts/agt/mixins/delegation.coffee +90 -0
- data/app/assets/javascripts/agt/mixins/disposable.coffee +7 -0
- data/app/assets/javascripts/agt/mixins/equatable.coffee +37 -0
- data/app/assets/javascripts/agt/mixins/formattable.coffee +52 -0
- data/app/assets/javascripts/agt/mixins/globalizable.coffee +175 -0
- data/app/assets/javascripts/agt/mixins/has_ancestors.coffee +47 -0
- data/app/assets/javascripts/agt/mixins/has_collection.coffee +107 -0
- data/app/assets/javascripts/agt/mixins/has_nested_collection.coffee +51 -0
- data/app/assets/javascripts/agt/mixins/memoizable.coffee +64 -0
- data/app/assets/javascripts/agt/mixins/parameterizable.coffee +101 -0
- data/app/assets/javascripts/agt/mixins/poolable.coffee +62 -0
- data/app/assets/javascripts/agt/mixins/sourcable.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/state_machine.coffee +47 -0
- data/app/assets/javascripts/agt/net/router.coffee +165 -0
- data/app/assets/javascripts/agt/object.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/base_action.coffee +7 -0
- data/app/assets/javascripts/agt/particles/actions/die_on_surface.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/force.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/friction.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/live.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/macro_action.coffee +13 -0
- data/app/assets/javascripts/agt/particles/actions/move.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/null_action.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/by_rate.coffee +17 -0
- data/app/assets/javascripts/agt/particles/counters/fixed.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/null_counter.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emission.coffee +35 -0
- data/app/assets/javascripts/agt/particles/emitters/null_emitter.coffee +7 -0
- data/app/assets/javascripts/agt/particles/emitters/path.coffee +10 -0
- data/app/assets/javascripts/agt/particles/emitters/ponctual.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emitters/surface.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/explosion.coffee +19 -0
- data/app/assets/javascripts/agt/particles/initializers/life.coffee +16 -0
- data/app/assets/javascripts/agt/particles/initializers/macro_initializer.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/null_initializer.coffee +7 -0
- data/app/assets/javascripts/agt/particles/initializers/particle_sub_system.coffee +12 -0
- data/app/assets/javascripts/agt/particles/initializers/stream.coffee +20 -0
- data/app/assets/javascripts/agt/particles/mixins/randomizable.coffee +10 -0
- data/app/assets/javascripts/agt/particles/particle.coffee +32 -0
- data/app/assets/javascripts/agt/particles/sub_system.coffee +11 -0
- data/app/assets/javascripts/agt/particles/system.coffee +103 -0
- data/app/assets/javascripts/agt/particles/timers/instant.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/limited.coffee +19 -0
- data/app/assets/javascripts/agt/particles/timers/null_timer.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/unlimited.coffee +9 -0
- data/app/assets/javascripts/agt/particles/timers/until_death.coffee +11 -0
- data/app/assets/javascripts/agt/promise.coffee +214 -0
- data/app/assets/javascripts/agt/random/random.coffee +100 -0
- data/app/assets/javascripts/agt/random/seeds/lagged_fibonnacci.coffee +53 -0
- data/app/assets/javascripts/agt/random/seeds/linear.coffee +16 -0
- data/app/assets/javascripts/agt/random/seeds/linear_congruential.coffee +23 -0
- data/app/assets/javascripts/agt/random/seeds/math_random.coffee +9 -0
- data/app/assets/javascripts/agt/random/seeds/mersenne_twister.coffee +42 -0
- data/app/assets/javascripts/agt/random/seeds/no_random.coffee +12 -0
- data/app/assets/javascripts/agt/random/seeds/paul_houle.coffee +19 -0
- data/app/assets/javascripts/agt/signal.coffee +272 -0
- data/app/assets/javascripts/agt/sprites/animation.coffee +13 -0
- data/app/assets/javascripts/agt/sprites/sprite.coffee +30 -0
- data/app/assets/javascripts/agt/widgets/hash.coffee +36 -0
- data/app/assets/javascripts/agt/widgets/widgets.coffee +194 -0
- data/app/assets/javascripts/agt/widgets/widgets/checked_input.coffee +7 -0
- data/app/assets/javascripts/agt/widgets/widgets/focus_bubbling.coffee +15 -0
- data/lib/agt.rb +8 -0
- data/lib/agt/version.rb +5 -0
- metadata +173 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
# Public: Every closed geometry should have the properties of a surface.
|
|
3
|
+
#
|
|
4
|
+
# These properties are:
|
|
5
|
+
#
|
|
6
|
+
# - **Acreage** - A `Surface` object can express its surface in px<sup>2</sup>.
|
|
7
|
+
# - **Inclusiveness** - A `Surface` object can `contains` other geometries
|
|
8
|
+
# inside the bounds of its shape. It also can returns coordinates inside
|
|
9
|
+
# its shape randomly.
|
|
10
|
+
#
|
|
11
|
+
# ```coffeescript
|
|
12
|
+
# class DummySurface
|
|
13
|
+
# @include agt.geom.Surface
|
|
14
|
+
#
|
|
15
|
+
# # Your surface implementation
|
|
16
|
+
# ```
|
|
17
|
+
# <script>window.exampleKey = 'geometry'</script>
|
|
18
|
+
class agt.geom.Surface
|
|
19
|
+
|
|
20
|
+
### Public ###
|
|
21
|
+
|
|
22
|
+
# Abstract: Returns the surface of the geometry in px<sup>2</sup>.
|
|
23
|
+
#
|
|
24
|
+
# Returns a {Number}.
|
|
25
|
+
acreage: -> null
|
|
26
|
+
|
|
27
|
+
# Abstract: Returns a random [Point]{agt.geom.Point} with coordinates
|
|
28
|
+
# inside the geometry shape.
|
|
29
|
+
#
|
|
30
|
+
# <script>drawGeometry(exampleKey, {surface: true})</script>
|
|
31
|
+
#
|
|
32
|
+
# Returns a [Point]{agt.geom.Point}.
|
|
33
|
+
randomPointInSurface: -> null
|
|
34
|
+
|
|
35
|
+
# Abstract: Tests if the passed-in coordinates are inside the geometry shape.
|
|
36
|
+
#
|
|
37
|
+
# <script>drawGeometry(exampleKey, {contains: true})</script>
|
|
38
|
+
#
|
|
39
|
+
# x - Either a {Number} for the x coordinate or a [Point]{agt.geom.Point}.
|
|
40
|
+
# y - A {Number} for the y coordinate, used when a {Number} was passed for
|
|
41
|
+
# the x coordinate as well.
|
|
42
|
+
#
|
|
43
|
+
# Returns a {Boolean}.
|
|
44
|
+
contains: (x, y) -> null
|
|
45
|
+
|
|
46
|
+
# Tests if the passed-in geometry is completely contained inside the
|
|
47
|
+
# current geometry shape.
|
|
48
|
+
#
|
|
49
|
+
# <script>drawContainsGeometry(exampleKey)</script>
|
|
50
|
+
#
|
|
51
|
+
# geometry - The [Geometry]{agt.geom.Geometry} to test.
|
|
52
|
+
#
|
|
53
|
+
# Returns a {Boolean}.
|
|
54
|
+
containsGeometry: (geometry) ->
|
|
55
|
+
geometry.points().every (point) => @contains point
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
# Public: The `Triangulable` mixin provides the {::triangles} method, allowing
|
|
3
|
+
# closed geometries to returns the [Triangles]{agt.geom.Triangle} that compose
|
|
4
|
+
# it.
|
|
5
|
+
#
|
|
6
|
+
# <script>window.exampleKey = 'geometry'</script>
|
|
7
|
+
class agt.geom.Triangulable
|
|
8
|
+
@include agt.mixins.Memoizable
|
|
9
|
+
|
|
10
|
+
### Public ###
|
|
11
|
+
|
|
12
|
+
# Returns the triangles that forms the geometry surface.
|
|
13
|
+
#
|
|
14
|
+
# <script>drawGeometry(exampleKey, {triangles: true})</script>
|
|
15
|
+
#
|
|
16
|
+
# Returns an {Array} of [Triangles]{agt.geom.Triangle}.
|
|
17
|
+
triangles: ->
|
|
18
|
+
return @memoFor 'triangles' if @memoized 'triangles'
|
|
19
|
+
|
|
20
|
+
vertices = @points()
|
|
21
|
+
vertices.pop()
|
|
22
|
+
indices = triangulate vertices
|
|
23
|
+
triangles = []
|
|
24
|
+
for i in [0..indices.length / 3 -1]
|
|
25
|
+
index = i * 3
|
|
26
|
+
a = vertices[indices[index]]
|
|
27
|
+
b = vertices[indices[index+1]]
|
|
28
|
+
c = vertices[indices[index+2]]
|
|
29
|
+
triangles.push new agt.geom.Triangle a, b, c
|
|
30
|
+
|
|
31
|
+
@memoize 'triangles', triangles
|
|
32
|
+
|
|
33
|
+
arrayCopy = (arrayTo, arrayFrom) -> arrayTo[i] = n for n,i in arrayFrom
|
|
34
|
+
|
|
35
|
+
pointInTriangle = (pt, v1, v2, v3) ->
|
|
36
|
+
denom = (v1.y - v3.y) * (v2.x - v3.x) +
|
|
37
|
+
(v2.y - v3.y) * (v3.x - v1.x)
|
|
38
|
+
b1 = ((pt.y - v3.y) * (v2.x - v3.x) +
|
|
39
|
+
(v2.y - v3.y) * (v3.x - pt.x)) / denom
|
|
40
|
+
b2 = ((pt.y - v1.y) * (v3.x - v1.x) +
|
|
41
|
+
(v3.y - v1.y) * (v1.x - pt.x)) / denom
|
|
42
|
+
b3 = ((pt.y - v2.y) * (v1.x - v2.x) +
|
|
43
|
+
(v1.y - v2.y) * (v2.x - pt.x)) / denom
|
|
44
|
+
return false if b1 < 0 or b2 < 0 or b3 < 0
|
|
45
|
+
true
|
|
46
|
+
|
|
47
|
+
polyArea = (pts) ->
|
|
48
|
+
sum = 0
|
|
49
|
+
i = 0
|
|
50
|
+
l = pts.length
|
|
51
|
+
|
|
52
|
+
for i in [0..l-1]
|
|
53
|
+
sum += pts[i].x * pts[(i + 1) % l].y - pts[(i + 1) % l].x * pts[i].y
|
|
54
|
+
|
|
55
|
+
sum / 2
|
|
56
|
+
|
|
57
|
+
triangulate = (vertices) ->
|
|
58
|
+
return if vertices.length < 4
|
|
59
|
+
|
|
60
|
+
safeGuard = 0
|
|
61
|
+
maxSafeGuard = 100
|
|
62
|
+
|
|
63
|
+
pts = vertices
|
|
64
|
+
refs = (i for n,i in pts)
|
|
65
|
+
ptsArea = []
|
|
66
|
+
i = 0
|
|
67
|
+
l = refs.length
|
|
68
|
+
|
|
69
|
+
while i < l
|
|
70
|
+
ptsArea[i] = pts[refs[i]].clone()
|
|
71
|
+
++i
|
|
72
|
+
pArea = polyArea(ptsArea)
|
|
73
|
+
cr = []
|
|
74
|
+
nr = []
|
|
75
|
+
arrayCopy cr, refs
|
|
76
|
+
while cr.length > 3
|
|
77
|
+
i = 0
|
|
78
|
+
l = cr.length
|
|
79
|
+
|
|
80
|
+
while i < l
|
|
81
|
+
r1 = cr[i % l]
|
|
82
|
+
r2 = cr[(i + 1) % l]
|
|
83
|
+
r3 = cr[(i + 2) % l]
|
|
84
|
+
v1 = pts[r1]
|
|
85
|
+
v2 = pts[r2]
|
|
86
|
+
v3 = pts[r3]
|
|
87
|
+
ok = true
|
|
88
|
+
j = (i + 3) % l
|
|
89
|
+
|
|
90
|
+
while j isnt i
|
|
91
|
+
ptsArea = [v1, v2, v3]
|
|
92
|
+
tArea = polyArea(ptsArea)
|
|
93
|
+
if (pArea < 0 and tArea > 0) or
|
|
94
|
+
(pArea > 0 and tArea < 0) or
|
|
95
|
+
pointInTriangle(pts[cr[j]], v1, v2, v3)
|
|
96
|
+
ok = false
|
|
97
|
+
break
|
|
98
|
+
j = (j + 1) % l
|
|
99
|
+
|
|
100
|
+
safeGuard += 1
|
|
101
|
+
if safeGuard > maxSafeGuard
|
|
102
|
+
break
|
|
103
|
+
|
|
104
|
+
if ok
|
|
105
|
+
nr.push r1, r2, r3
|
|
106
|
+
cr.splice (i + 1) % l, 1
|
|
107
|
+
break
|
|
108
|
+
++i
|
|
109
|
+
nr.push.apply nr, cr[0..2]
|
|
110
|
+
triangulated = true
|
|
111
|
+
|
|
112
|
+
return nr
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
|
|
3
|
+
# Public: A `Point` represent a location in a two-dimensional space.
|
|
4
|
+
#
|
|
5
|
+
# A point with coordinates (0,0) can be constructed with:
|
|
6
|
+
#
|
|
7
|
+
# ```coffeescript
|
|
8
|
+
# new Point
|
|
9
|
+
# new Point 0, 0
|
|
10
|
+
# new Point x: 0, y: 0
|
|
11
|
+
# ```
|
|
12
|
+
#
|
|
13
|
+
# **Note:** Any functions in agt that accept a `Point` object also allow
|
|
14
|
+
# to use numbers instead, this is obviously also the case in the `Point`
|
|
15
|
+
# class. For more details about how to achieve the same behavior in your own
|
|
16
|
+
# functions please refer to the {.pointFrom} method.
|
|
17
|
+
#
|
|
18
|
+
# ### Included Mixins
|
|
19
|
+
#
|
|
20
|
+
# - [agt.mixins.Cloneable](../../../files/mixins/cloneable.coffee.html)
|
|
21
|
+
# - [agt.mixins.Equatable](../../../files/mixins/equatable.coffee.html)
|
|
22
|
+
# - [agt.mixins.Formattable](../../../files/mixins/formattable.coffee.html)
|
|
23
|
+
# - [agt.mixins.Sourcable](../../../files/mixins/sourcable.coffee.html)
|
|
24
|
+
class agt.geom.Point
|
|
25
|
+
@include agt.mixins.Equatable('x', 'y')
|
|
26
|
+
@include agt.mixins.Formattable('Point','x', 'y')
|
|
27
|
+
@include agt.mixins.Sourcable('agt.geom.Point', 'x', 'y')
|
|
28
|
+
@include agt.mixins.Cloneable()
|
|
29
|
+
|
|
30
|
+
### Public: Static methods ###
|
|
31
|
+
|
|
32
|
+
# Returns `true` if the passed-in object pass the requirments
|
|
33
|
+
# to be a point. Valid points are objects that possess a x and
|
|
34
|
+
# a y property.
|
|
35
|
+
#
|
|
36
|
+
# ```coffeescript
|
|
37
|
+
# Point.isPoint new Point # true
|
|
38
|
+
# Point.isPoint x: 0, y: 0 # true
|
|
39
|
+
# Point.isPoint x: 0 # false
|
|
40
|
+
# ```
|
|
41
|
+
#
|
|
42
|
+
# pt - The point {Object} that will be tested.
|
|
43
|
+
#
|
|
44
|
+
# Returns a {Boolean}.
|
|
45
|
+
@isPoint: (pt) -> pt? and pt.x? and pt.y?
|
|
46
|
+
|
|
47
|
+
# Returns a point according to the provided arguments:
|
|
48
|
+
#
|
|
49
|
+
# ```coffeescript
|
|
50
|
+
# translate = (xOrPt, y) ->
|
|
51
|
+
# point = Point.pointFrom xOrPt, y
|
|
52
|
+
# ```
|
|
53
|
+
#
|
|
54
|
+
# The first argument can be either an {Object} or a {Number}.
|
|
55
|
+
# In the case the argument is an object, the function will
|
|
56
|
+
# extract the x and y values from it. However, if the `strict`
|
|
57
|
+
# argument is `true`, the function will throw an error if
|
|
58
|
+
# the object does not have either x or y property:
|
|
59
|
+
#
|
|
60
|
+
# ```coffeescript
|
|
61
|
+
# Point.pointFrom x: 10 # will not throw
|
|
62
|
+
# Point.pointFrom x: 10, 0, true # will throw
|
|
63
|
+
# ```
|
|
64
|
+
#
|
|
65
|
+
# In the case the object is incomplete or empty, and with
|
|
66
|
+
# the strict mode disabled, the missing property will end
|
|
67
|
+
# being set to `0`.
|
|
68
|
+
#
|
|
69
|
+
# ```coffeescript
|
|
70
|
+
# point = Point.pointFrom x: 10 # {x: 10, y: NaN}
|
|
71
|
+
# ```
|
|
72
|
+
#
|
|
73
|
+
# For further examples, feel free to take a look at the
|
|
74
|
+
# methods of the `Point` class.
|
|
75
|
+
#
|
|
76
|
+
# xOrPt - The x {Number} value or a point-like {Object}.
|
|
77
|
+
# y - The y {Number} value
|
|
78
|
+
# strict - A {Boolean} of whether the method raises an exception
|
|
79
|
+
# on type mismatch.
|
|
80
|
+
#
|
|
81
|
+
# Returns a [Point]{agt.geom.Point} instance.
|
|
82
|
+
@pointFrom: (xOrPt, y, strict=false) ->
|
|
83
|
+
x = xOrPt
|
|
84
|
+
{x,y} = xOrPt if xOrPt? and typeof xOrPt is 'object'
|
|
85
|
+
@notAPoint [x,y] if strict and (isNaN(x) or isNaN(y))
|
|
86
|
+
new Point x, y
|
|
87
|
+
|
|
88
|
+
# Converts polar coordinates in cartesian coordinates.
|
|
89
|
+
#
|
|
90
|
+
# ```coffeescript
|
|
91
|
+
# Point.polar 90, 10 # [Point(x=0,y=10)]
|
|
92
|
+
# ```
|
|
93
|
+
#
|
|
94
|
+
# angle - A {Number} of the angle in radians.
|
|
95
|
+
# length - A {Number} of the vector length in pixels.
|
|
96
|
+
#
|
|
97
|
+
# Returns a [Point]{agt.geom.Point} instance.
|
|
98
|
+
@polar: (angle, length=1) -> new Point Math.sin(angle) * length,
|
|
99
|
+
Math.cos(angle) * length
|
|
100
|
+
|
|
101
|
+
# Returns a point between `pt1` and `pt2` at a ratio corresponding to `pos`.
|
|
102
|
+
#
|
|
103
|
+
# The `Point.interpolate` method supports all the following forms:
|
|
104
|
+
#
|
|
105
|
+
# ```coffeescript
|
|
106
|
+
# # Two points:
|
|
107
|
+
# Point.interpolate pt1, pt2, pos
|
|
108
|
+
#
|
|
109
|
+
# # Two numbers and a Point:
|
|
110
|
+
# Point.interpolate x1, y1, pt2, pos
|
|
111
|
+
#
|
|
112
|
+
# # A Point and then two numbers:
|
|
113
|
+
# Point.interpolate pt1, x2, y2, pos
|
|
114
|
+
#
|
|
115
|
+
# # Four numbers:
|
|
116
|
+
# Point.interpolate x1, x2, x2, y2, pos
|
|
117
|
+
# ```
|
|
118
|
+
#
|
|
119
|
+
# pt1 - The starting [Point]{agt.geom.Point} of the interpolation.
|
|
120
|
+
# pt2 - The ending [Point]{agt.geom.Point} of the interpolation.
|
|
121
|
+
# pos - The amount {Number} of the interpolation.
|
|
122
|
+
#
|
|
123
|
+
# Returns a [Point]{agt.geom.Point} instance.
|
|
124
|
+
@interpolate: (pt1, pt2, pos) ->
|
|
125
|
+
args = []; args[i] = v for v,i in arguments
|
|
126
|
+
|
|
127
|
+
# Utility function that extract a point from `args`
|
|
128
|
+
# and removes the values it used from it.
|
|
129
|
+
extract = (args, name) =>
|
|
130
|
+
pt = null
|
|
131
|
+
if @isPoint args[0] then pt = args.shift()
|
|
132
|
+
else if Math.isFloat(args[0]) and Math.isFloat(args[1])
|
|
133
|
+
pt = new Point args[0], args[1]
|
|
134
|
+
args.splice 0, 2
|
|
135
|
+
else @missingPoint args, name
|
|
136
|
+
pt
|
|
137
|
+
|
|
138
|
+
pt1 = extract args, 'first'
|
|
139
|
+
pt2 = extract args, 'second'
|
|
140
|
+
pos = parseFloat args.shift()
|
|
141
|
+
@missingPosition pos if isNaN pos
|
|
142
|
+
|
|
143
|
+
dif = pt2.subtract(pt1)
|
|
144
|
+
new Point pt1.x + dif.x * pos,
|
|
145
|
+
pt1.y + dif.y * pos
|
|
146
|
+
|
|
147
|
+
### Internal: Class error methods ###
|
|
148
|
+
|
|
149
|
+
# Throws an error for a missing position in `Point.interpolate`.
|
|
150
|
+
@missingPosition: (pos) ->
|
|
151
|
+
throw new Error "Point.interpolate require a position but #{pos} was given"
|
|
152
|
+
|
|
153
|
+
# Throws an error for a missing point in `Point.interpolate`.
|
|
154
|
+
@missingPoint: (args, pos) ->
|
|
155
|
+
msg = "Can't find the #{pos} point in Point.interpolate arguments #{args}"
|
|
156
|
+
throw new Error msg
|
|
157
|
+
|
|
158
|
+
# Throws an error for an invalid point in `Point.pointFrom`.
|
|
159
|
+
@notAPoint: (args) ->
|
|
160
|
+
throw new Error "#{args} is not a point"
|
|
161
|
+
|
|
162
|
+
### Public ###
|
|
163
|
+
|
|
164
|
+
# Whatever is passed to the `Point` constructor, a valid point
|
|
165
|
+
# is always returned. All invalid properties will be default to `0`.
|
|
166
|
+
#
|
|
167
|
+
# A point be constructed with the following forms:
|
|
168
|
+
#
|
|
169
|
+
# ```coffeescript
|
|
170
|
+
# new Point
|
|
171
|
+
# new Point 0, 0
|
|
172
|
+
# new Point x: 0, y: 0
|
|
173
|
+
# ```
|
|
174
|
+
#
|
|
175
|
+
# x - A {Number} for the x coordinate or a point-like {Object}
|
|
176
|
+
# to initialize the point.
|
|
177
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
178
|
+
# was also a number.
|
|
179
|
+
constructor: (x, y) ->
|
|
180
|
+
y = x.y or y if x?; y = 0 if isNaN y
|
|
181
|
+
x = x.x or x if x?; x = 0 if isNaN x
|
|
182
|
+
@x = x
|
|
183
|
+
@y = y
|
|
184
|
+
|
|
185
|
+
# Returns the length of the current vector represented by this point.
|
|
186
|
+
#
|
|
187
|
+
# ```coffeescript
|
|
188
|
+
# length = point.length()
|
|
189
|
+
# ```
|
|
190
|
+
#
|
|
191
|
+
# Returns a {Number}.
|
|
192
|
+
length: -> Math.sqrt (@x * @x) + (@y * @y)
|
|
193
|
+
|
|
194
|
+
# Returns the angle in degrees formed by the vector.
|
|
195
|
+
#
|
|
196
|
+
# ```coffeescript
|
|
197
|
+
# angle = point.angle()
|
|
198
|
+
# ```
|
|
199
|
+
#
|
|
200
|
+
# Returns a {Number}.
|
|
201
|
+
angle: -> Math.atan2 @y, @x
|
|
202
|
+
|
|
203
|
+
# Given a triangle formed by this point, the passed-in point
|
|
204
|
+
# and the origin (0,0), the `Point::angleWith` method will
|
|
205
|
+
# return the angle in degrees formed by the two vectors at
|
|
206
|
+
# the origin.
|
|
207
|
+
#
|
|
208
|
+
# ```coffeescript
|
|
209
|
+
# point1 = new Point 10, 0
|
|
210
|
+
# point2 = new Point 10, 10
|
|
211
|
+
# angle = point1.angleWith point2
|
|
212
|
+
# # angle = 45
|
|
213
|
+
# ```
|
|
214
|
+
#
|
|
215
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
216
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
217
|
+
# was also a number.
|
|
218
|
+
#
|
|
219
|
+
# Returns a {Number}.
|
|
220
|
+
angleWith: (x, y) ->
|
|
221
|
+
@noPoint 'dot' if not x? and not y?
|
|
222
|
+
isPoint = @isPoint x
|
|
223
|
+
y = if isPoint then x.y else y
|
|
224
|
+
x = if isPoint then x.x else x
|
|
225
|
+
Point.notAPoint [x,y] if (isNaN(x) or isNaN(y))
|
|
226
|
+
|
|
227
|
+
d = @normalize().dot new Point(x,y).normalize()
|
|
228
|
+
|
|
229
|
+
Math.acos(Math.abs(d)) * (if d < 0 then -1 else 1)
|
|
230
|
+
|
|
231
|
+
# Returns a new point of length `length`.
|
|
232
|
+
#
|
|
233
|
+
# ```coffeescript
|
|
234
|
+
# normalized = point.normalize()
|
|
235
|
+
# normalized.length() # 1
|
|
236
|
+
#
|
|
237
|
+
# normalized = point.normalize(6)
|
|
238
|
+
# normalized.length() # 6
|
|
239
|
+
# ```
|
|
240
|
+
#
|
|
241
|
+
# length - The {Number} for the new length.
|
|
242
|
+
#
|
|
243
|
+
# Returns a [Point]{agt.geom.Point}
|
|
244
|
+
normalize: (length=1) ->
|
|
245
|
+
@invalidLength length unless Math.isFloat length
|
|
246
|
+
l = @length()
|
|
247
|
+
new Point @x / l * length, @y / l * length
|
|
248
|
+
|
|
249
|
+
# Returns a new point resulting of the addition of the
|
|
250
|
+
# passed-in point to this point.
|
|
251
|
+
#
|
|
252
|
+
# ```coffeescript
|
|
253
|
+
# point = new Point 4, 4
|
|
254
|
+
# inc = point.add 1, 5
|
|
255
|
+
# inc = point.add x: 0.2
|
|
256
|
+
# inc = point.add new Point 1.8, 8
|
|
257
|
+
# # inc = [Point(x=7,y=17)]
|
|
258
|
+
# ```
|
|
259
|
+
#
|
|
260
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
261
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
262
|
+
# was also a number.
|
|
263
|
+
#
|
|
264
|
+
# Returns a new [Point]{agt.geom.Point}.
|
|
265
|
+
add: (x, y) ->
|
|
266
|
+
y = x.y or y if x?; y = 0 if isNaN y
|
|
267
|
+
x = x.x or x if x?; x = 0 if isNaN x
|
|
268
|
+
new Point @x + x, @y + y
|
|
269
|
+
|
|
270
|
+
# Returns a new point that results of the subtraction of the
|
|
271
|
+
# passed-in point to this point.
|
|
272
|
+
#
|
|
273
|
+
# ```coffeescript
|
|
274
|
+
# point = new Point 4, 4
|
|
275
|
+
# inc = point.subtract 1, 5
|
|
276
|
+
# inc = point.subtract x: 0.2
|
|
277
|
+
# inc = point.subtract new Point 1.8, 8
|
|
278
|
+
# # inc = [Point(x=2,y=-9)]
|
|
279
|
+
# ```
|
|
280
|
+
#
|
|
281
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
282
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
283
|
+
# was also a number.
|
|
284
|
+
#
|
|
285
|
+
# Returns a new [Point]{agt.geom.Point}.
|
|
286
|
+
subtract: (x, y) ->
|
|
287
|
+
y = x.y or y if x?; y = 0 if isNaN y
|
|
288
|
+
x = x.x or x if x?; x = 0 if isNaN x
|
|
289
|
+
new Point @x - x, @y - y
|
|
290
|
+
|
|
291
|
+
# Returns the dot product of this point and the passed-in point.
|
|
292
|
+
#
|
|
293
|
+
# ```coffeescript
|
|
294
|
+
# dot = new Point(5,6).dot(7,8)
|
|
295
|
+
# # dot = 83
|
|
296
|
+
# ```
|
|
297
|
+
#
|
|
298
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
299
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
300
|
+
# was also a number.
|
|
301
|
+
#
|
|
302
|
+
# Returns a {Number}.
|
|
303
|
+
dot: (x, y) ->
|
|
304
|
+
@noPoint 'dot' if not x? and not y?
|
|
305
|
+
isPoint = @isPoint x
|
|
306
|
+
y = if isPoint then x.y else y
|
|
307
|
+
x = if isPoint then x.x else x
|
|
308
|
+
Point.notAPoint [x,y] if (isNaN(x) or isNaN(y))
|
|
309
|
+
@x * x + @y * y
|
|
310
|
+
|
|
311
|
+
# Returns the distance between this point and the passed-in point.
|
|
312
|
+
#
|
|
313
|
+
# ```coffeescript
|
|
314
|
+
# distance = new Point(0,2).distance(2,2)
|
|
315
|
+
# # distance = 2
|
|
316
|
+
# ```
|
|
317
|
+
#
|
|
318
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
319
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
320
|
+
# was also a number.
|
|
321
|
+
#
|
|
322
|
+
# Returns a {Number}.
|
|
323
|
+
distance: (x, y) ->
|
|
324
|
+
@noPoint 'distance' if not x? and not y?
|
|
325
|
+
isPoint = @isPoint x
|
|
326
|
+
y = if isPoint then x.y else y
|
|
327
|
+
x = if isPoint then x.x else x
|
|
328
|
+
Point.notAPoint [x,y] if (isNaN(x) or isNaN(y))
|
|
329
|
+
@subtract(x,y).length()
|
|
330
|
+
|
|
331
|
+
# Returns a new point which is a scaled copy of the current point.
|
|
332
|
+
#
|
|
333
|
+
# ```coffeescript
|
|
334
|
+
# point = new Point 1, 1
|
|
335
|
+
# scaled = point.scale 2
|
|
336
|
+
# # scaled = [Point(x=2,y=2)]
|
|
337
|
+
# ```
|
|
338
|
+
#
|
|
339
|
+
# n - The {Number} scale factor for the transformation.
|
|
340
|
+
#
|
|
341
|
+
# Returns a new [Point]{agt.geom.Point}.
|
|
342
|
+
scale: (n) ->
|
|
343
|
+
@invalidScale n unless Math.isFloat n
|
|
344
|
+
new Point @x * n, @y * n
|
|
345
|
+
|
|
346
|
+
# Returns a new point which is the result of rotating the
|
|
347
|
+
# current point around the origin (0,0).
|
|
348
|
+
#
|
|
349
|
+
# ```coffeescript
|
|
350
|
+
# point = new Point 10, 0
|
|
351
|
+
# rotated = point.rotate 90
|
|
352
|
+
# # rotated = [Point(x=0,y=10)]
|
|
353
|
+
# ```
|
|
354
|
+
#
|
|
355
|
+
# n - The {Number} amount of rotation to apply to the point in radians.
|
|
356
|
+
#
|
|
357
|
+
# Returns a new [Point]{agt.geom.Point}.
|
|
358
|
+
rotate: (n) ->
|
|
359
|
+
@invalidRotation n unless Math.isFloat n
|
|
360
|
+
l = @length()
|
|
361
|
+
a = Math.atan2(@y, @x) + n
|
|
362
|
+
x = Math.cos(a) * l
|
|
363
|
+
y = Math.sin(a) * l
|
|
364
|
+
new Point x, y
|
|
365
|
+
|
|
366
|
+
# Returns a new point which is the result of rotating the
|
|
367
|
+
# current point around the passed-in point.
|
|
368
|
+
#
|
|
369
|
+
# ```coffeescript
|
|
370
|
+
# point = new Point 10, 0
|
|
371
|
+
# origin = new Point 20, 0
|
|
372
|
+
# rotated = point.rotateAround origin, 90
|
|
373
|
+
# # rotated = [Point(x=20,y=-10)]
|
|
374
|
+
# ```
|
|
375
|
+
#
|
|
376
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
377
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
378
|
+
# was also a number or the {Number} for the angle otherwise.
|
|
379
|
+
# a - A {Number} for the rotation angle in radians only if the `x` and `y`
|
|
380
|
+
# arguments are [Numbers]{Number}.
|
|
381
|
+
#
|
|
382
|
+
# Returns a new [Point]{agt.geom.Point}.
|
|
383
|
+
rotateAround: (x, y, a) ->
|
|
384
|
+
isPoint = @isPoint x
|
|
385
|
+
a = y if isPoint
|
|
386
|
+
y = if isPoint then x.y else y
|
|
387
|
+
x = if isPoint then x.x else x
|
|
388
|
+
|
|
389
|
+
@subtract(x,y).rotate(a).add(x,y)
|
|
390
|
+
|
|
391
|
+
# Alias the `Point.isPoint` method in instances.
|
|
392
|
+
isPoint: Point.isPoint
|
|
393
|
+
|
|
394
|
+
# Alias the `Point.pointFrom` method in instances.
|
|
395
|
+
pointFrom: Point.pointFrom
|
|
396
|
+
|
|
397
|
+
# Internal: Returns the two arguments x and y in an array where arguments
|
|
398
|
+
# that were `NaN` are replaced by `0`.
|
|
399
|
+
#
|
|
400
|
+
# x - A {Number} for the x coordinate.
|
|
401
|
+
# y - A {Number} for the y coordinate.
|
|
402
|
+
#
|
|
403
|
+
# Returns an {Array}.
|
|
404
|
+
defaultToZero: (x, y) ->
|
|
405
|
+
x = if isNaN x then 0 else x
|
|
406
|
+
y = if isNaN y then 0 else y
|
|
407
|
+
[x,y]
|
|
408
|
+
|
|
409
|
+
# Copies the values of the passed-in point into this point.
|
|
410
|
+
#
|
|
411
|
+
# ```coffeescript
|
|
412
|
+
# point = new Point
|
|
413
|
+
# point.paste 1, 7
|
|
414
|
+
# # point = [Point(x=5,y=7)]
|
|
415
|
+
#
|
|
416
|
+
# point.paste new Point 4, 4
|
|
417
|
+
# # point = [Point(x=4,y=4)]
|
|
418
|
+
# ```
|
|
419
|
+
#
|
|
420
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
421
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
422
|
+
# was also a number.
|
|
423
|
+
#
|
|
424
|
+
# Returns the current [Point]{agt.geom.Point}
|
|
425
|
+
paste: (x, y) ->
|
|
426
|
+
return this if not x? and not y?
|
|
427
|
+
isObject = x? and typeof x is 'object'
|
|
428
|
+
y = if isObject then x.y else y
|
|
429
|
+
x = if isObject then x.x else x
|
|
430
|
+
@x = x unless isNaN x
|
|
431
|
+
@y = y unless isNaN y
|
|
432
|
+
this
|
|
433
|
+
|
|
434
|
+
### Internal: Instances error methods ###
|
|
435
|
+
|
|
436
|
+
# A generic error helper used by methods that require a point argument
|
|
437
|
+
# and was called without it.
|
|
438
|
+
noPoint: (method) ->
|
|
439
|
+
throw new Error "#{method} was called without arguments"
|
|
440
|
+
|
|
441
|
+
# Throws an error for an invalid length in `Point::normalize` method.
|
|
442
|
+
invalidLength: (l) ->
|
|
443
|
+
throw new Error "Invalid length #{l} provided"
|
|
444
|
+
|
|
445
|
+
# Throws an error for an invalid scale in `Point::scale` method.
|
|
446
|
+
invalidScale: (s) ->
|
|
447
|
+
throw new Error "Invalid scale #{s} provided"
|
|
448
|
+
|
|
449
|
+
##### Point::invalidRotation
|
|
450
|
+
#
|
|
451
|
+
# Throws an error for an invalid rotation in `Point::rotate`
|
|
452
|
+
# and `Point::rotateAround` method.
|
|
453
|
+
invalidRotation: (a) ->
|
|
454
|
+
throw new Error "Invalid rotation #{a} provided"
|