agt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +7 -0
  3. data/README.md +5 -0
  4. data/app/assets/javascripts/agt/config.coffee +9 -0
  5. data/app/assets/javascripts/agt/dom.coffee +12 -0
  6. data/app/assets/javascripts/agt/function.coffee +317 -0
  7. data/app/assets/javascripts/agt/geom/circle.coffee +338 -0
  8. data/app/assets/javascripts/agt/geom/cubic_bezier.coffee +37 -0
  9. data/app/assets/javascripts/agt/geom/diamond.coffee +241 -0
  10. data/app/assets/javascripts/agt/geom/ellipsis.coffee +141 -0
  11. data/app/assets/javascripts/agt/geom/linear_spline.coffee +56 -0
  12. data/app/assets/javascripts/agt/geom/matrix.coffee +240 -0
  13. data/app/assets/javascripts/agt/geom/mixins/geometry.coffee +171 -0
  14. data/app/assets/javascripts/agt/geom/mixins/intersections.coffee +150 -0
  15. data/app/assets/javascripts/agt/geom/mixins/path.coffee +145 -0
  16. data/app/assets/javascripts/agt/geom/mixins/proxyable.coffee +9 -0
  17. data/app/assets/javascripts/agt/geom/mixins/spline.coffee +329 -0
  18. data/app/assets/javascripts/agt/geom/mixins/surface.coffee +55 -0
  19. data/app/assets/javascripts/agt/geom/mixins/triangulable.coffee +112 -0
  20. data/app/assets/javascripts/agt/geom/point.coffee +454 -0
  21. data/app/assets/javascripts/agt/geom/polygon.coffee +119 -0
  22. data/app/assets/javascripts/agt/geom/quad_bezier.coffee +43 -0
  23. data/app/assets/javascripts/agt/geom/quint_bezier.coffee +42 -0
  24. data/app/assets/javascripts/agt/geom/rectangle.coffee +599 -0
  25. data/app/assets/javascripts/agt/geom/spiral.coffee +90 -0
  26. data/app/assets/javascripts/agt/geom/transformation_proxy.coffee +43 -0
  27. data/app/assets/javascripts/agt/geom/triangle.coffee +442 -0
  28. data/app/assets/javascripts/agt/impulse.coffee +105 -0
  29. data/app/assets/javascripts/agt/index.coffee +56 -0
  30. data/app/assets/javascripts/agt/inflector/inflection.coffee +21 -0
  31. data/app/assets/javascripts/agt/inflector/inflections.coffee +75 -0
  32. data/app/assets/javascripts/agt/inflector/inflector.coffee +235 -0
  33. data/app/assets/javascripts/agt/inheritance.coffee +132 -0
  34. data/app/assets/javascripts/agt/math.coffee +45 -0
  35. data/app/assets/javascripts/agt/mixins/activable.coffee +31 -0
  36. data/app/assets/javascripts/agt/mixins/aliasable.coffee +25 -0
  37. data/app/assets/javascripts/agt/mixins/alternate_case.coffee +72 -0
  38. data/app/assets/javascripts/agt/mixins/cloneable.coffee +71 -0
  39. data/app/assets/javascripts/agt/mixins/delegation.coffee +90 -0
  40. data/app/assets/javascripts/agt/mixins/disposable.coffee +7 -0
  41. data/app/assets/javascripts/agt/mixins/equatable.coffee +37 -0
  42. data/app/assets/javascripts/agt/mixins/formattable.coffee +52 -0
  43. data/app/assets/javascripts/agt/mixins/globalizable.coffee +175 -0
  44. data/app/assets/javascripts/agt/mixins/has_ancestors.coffee +47 -0
  45. data/app/assets/javascripts/agt/mixins/has_collection.coffee +107 -0
  46. data/app/assets/javascripts/agt/mixins/has_nested_collection.coffee +51 -0
  47. data/app/assets/javascripts/agt/mixins/memoizable.coffee +64 -0
  48. data/app/assets/javascripts/agt/mixins/parameterizable.coffee +101 -0
  49. data/app/assets/javascripts/agt/mixins/poolable.coffee +62 -0
  50. data/app/assets/javascripts/agt/mixins/sourcable.coffee +45 -0
  51. data/app/assets/javascripts/agt/mixins/state_machine.coffee +47 -0
  52. data/app/assets/javascripts/agt/net/router.coffee +165 -0
  53. data/app/assets/javascripts/agt/object.coffee +9 -0
  54. data/app/assets/javascripts/agt/particles/actions/base_action.coffee +7 -0
  55. data/app/assets/javascripts/agt/particles/actions/die_on_surface.coffee +14 -0
  56. data/app/assets/javascripts/agt/particles/actions/force.coffee +11 -0
  57. data/app/assets/javascripts/agt/particles/actions/friction.coffee +14 -0
  58. data/app/assets/javascripts/agt/particles/actions/live.coffee +9 -0
  59. data/app/assets/javascripts/agt/particles/actions/macro_action.coffee +13 -0
  60. data/app/assets/javascripts/agt/particles/actions/move.coffee +11 -0
  61. data/app/assets/javascripts/agt/particles/actions/null_action.coffee +9 -0
  62. data/app/assets/javascripts/agt/particles/counters/by_rate.coffee +17 -0
  63. data/app/assets/javascripts/agt/particles/counters/fixed.coffee +9 -0
  64. data/app/assets/javascripts/agt/particles/counters/null_counter.coffee +9 -0
  65. data/app/assets/javascripts/agt/particles/emission.coffee +35 -0
  66. data/app/assets/javascripts/agt/particles/emitters/null_emitter.coffee +7 -0
  67. data/app/assets/javascripts/agt/particles/emitters/path.coffee +10 -0
  68. data/app/assets/javascripts/agt/particles/emitters/ponctual.coffee +9 -0
  69. data/app/assets/javascripts/agt/particles/emitters/surface.coffee +10 -0
  70. data/app/assets/javascripts/agt/particles/initializers/explosion.coffee +19 -0
  71. data/app/assets/javascripts/agt/particles/initializers/life.coffee +16 -0
  72. data/app/assets/javascripts/agt/particles/initializers/macro_initializer.coffee +10 -0
  73. data/app/assets/javascripts/agt/particles/initializers/null_initializer.coffee +7 -0
  74. data/app/assets/javascripts/agt/particles/initializers/particle_sub_system.coffee +12 -0
  75. data/app/assets/javascripts/agt/particles/initializers/stream.coffee +20 -0
  76. data/app/assets/javascripts/agt/particles/mixins/randomizable.coffee +10 -0
  77. data/app/assets/javascripts/agt/particles/particle.coffee +32 -0
  78. data/app/assets/javascripts/agt/particles/sub_system.coffee +11 -0
  79. data/app/assets/javascripts/agt/particles/system.coffee +103 -0
  80. data/app/assets/javascripts/agt/particles/timers/instant.coffee +11 -0
  81. data/app/assets/javascripts/agt/particles/timers/limited.coffee +19 -0
  82. data/app/assets/javascripts/agt/particles/timers/null_timer.coffee +11 -0
  83. data/app/assets/javascripts/agt/particles/timers/unlimited.coffee +9 -0
  84. data/app/assets/javascripts/agt/particles/timers/until_death.coffee +11 -0
  85. data/app/assets/javascripts/agt/promise.coffee +214 -0
  86. data/app/assets/javascripts/agt/random/random.coffee +100 -0
  87. data/app/assets/javascripts/agt/random/seeds/lagged_fibonnacci.coffee +53 -0
  88. data/app/assets/javascripts/agt/random/seeds/linear.coffee +16 -0
  89. data/app/assets/javascripts/agt/random/seeds/linear_congruential.coffee +23 -0
  90. data/app/assets/javascripts/agt/random/seeds/math_random.coffee +9 -0
  91. data/app/assets/javascripts/agt/random/seeds/mersenne_twister.coffee +42 -0
  92. data/app/assets/javascripts/agt/random/seeds/no_random.coffee +12 -0
  93. data/app/assets/javascripts/agt/random/seeds/paul_houle.coffee +19 -0
  94. data/app/assets/javascripts/agt/signal.coffee +272 -0
  95. data/app/assets/javascripts/agt/sprites/animation.coffee +13 -0
  96. data/app/assets/javascripts/agt/sprites/sprite.coffee +30 -0
  97. data/app/assets/javascripts/agt/widgets/hash.coffee +36 -0
  98. data/app/assets/javascripts/agt/widgets/widgets.coffee +194 -0
  99. data/app/assets/javascripts/agt/widgets/widgets/checked_input.coffee +7 -0
  100. data/app/assets/javascripts/agt/widgets/widgets/focus_bubbling.coffee +15 -0
  101. data/lib/agt.rb +8 -0
  102. data/lib/agt/version.rb +5 -0
  103. 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"