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.
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"