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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7bcb67eb9415c02fa8086e62407819b7aface85f
4
+ data.tar.gz: f96ea9a25186d7ea3010a892816cce8758fb9661
5
+ SHA512:
6
+ metadata.gz: 888e8ce59fda8602529542eb5d2cc57f3bd2d2509cc14176135de918c0d01178de50d0d384b6f77e2251d5d19e252616719f5fd1202bab897e6e0171bff4bfe8
7
+ data.tar.gz: 9e6cac65330d02b561882454a7cd4f48a6606d9d1b6e3194787e0c08c4b09b76b524389f05590bc1e8eda932416359fd34406ae4765909eee60ce82ed0eee55e
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2014 Cédric Néhémie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ ## Game Tools
2
+
3
+ [![Travis Status](https://travis-ci.org/abe33/agt.png)](https://travis-ci.org/abe33/agt)
4
+
5
+ [![Browser Testing CI Status](https://ci.testling.com/abe33/agt.png)](https://ci.testling.com/abe33/agt)
@@ -0,0 +1,9 @@
1
+
2
+ agt.CAMEL_CASE = 'camel'
3
+ agt.SNAKE_CASE = 'snake'
4
+ agt.COLORS =
5
+ STROKE: '#ff0000'
6
+ FILL: 'rgba(255,0,0,0.5)'
7
+ VERTICES: '#0077ff'
8
+ VERTICES_CONNECTIONS: 'rgba(0,127,255,0.5)'
9
+ BOUNDING_BOX: '#69af23'
@@ -0,0 +1,12 @@
1
+ agt.domEvent = (type, data={}, options={bubbles, cancelable}={}) ->
2
+ try
3
+ event = new Event type, {
4
+ bubbles: bubbles ? true
5
+ cancelable: cancelable ? true
6
+ }
7
+ catch e
8
+ event = document.createEvent 'Event'
9
+ event.initEvent type, bubbles ? true, cancelable ? true
10
+
11
+ event.data = data
12
+ event
@@ -0,0 +1,317 @@
1
+ ### Public ###
2
+
3
+ # Creates a new non-enumerable method on the current class prototype.
4
+ #
5
+ # ```coffeescript
6
+ # class Dummy
7
+ # @def 'method', ->
8
+ # ```
9
+ #
10
+ # name - The {String} for the method name.
11
+ # block - The {Function} body.
12
+ Function::def = (name, block) ->
13
+ Object.defineProperty @prototype, name, {
14
+ value: block
15
+ configurable: true
16
+ enumerable: false
17
+ }
18
+ this
19
+
20
+ # Creates a virtual property on the current class's prototype.
21
+ #
22
+ # ```coffeescript
23
+ # class Dummy
24
+ # @accessor 'foo', {
25
+ # get: -> @fooValue * 2
26
+ # set: (value) -> @fooValue = value / 2
27
+ # }
28
+ #
29
+ # dummy = new Dummy
30
+ # dummy.foo = 10
31
+ # dummy.fooValue # 5
32
+ # dummy.foo # 10
33
+ # ```
34
+ #
35
+ # name - The {String} for the accessor name.
36
+ # options - A descriptor {Object} for the accessor. It can contains
37
+ # the following properties:
38
+ # get - A {Function} to read the property's value.
39
+ # set - A {Function} to write the property's value.
40
+ Function::accessor = (name, options) ->
41
+ oldDescriptor = Object.getPropertyDescriptor @prototype, name
42
+
43
+ options.get ||= oldDescriptor.get if oldDescriptor?
44
+ options.set ||= oldDescriptor.set if oldDescriptor?
45
+
46
+ Object.defineProperty @prototype, name, {
47
+ get: options.get
48
+ set: options.set
49
+ configurable: true
50
+ enumerable: true
51
+ }
52
+ this
53
+
54
+ # Creates a getter on the given class prototype.
55
+ #
56
+ # ```coffeescript
57
+ # class Dummy
58
+ # @getter 'foo', -> 'bar'
59
+ # ```
60
+ #
61
+ # name - The {String} name of the property accessor to create.
62
+ # block - The {Function} to read the property value.
63
+ Function::getter = (name, block) -> @accessor name, get: block
64
+
65
+ # Creates a setter on the given class prototype.
66
+ #
67
+ # ```coffeescript
68
+ # class Dummy
69
+ # @setter 'foo', (value) -> @fooValue = value / 2
70
+ # ```
71
+ #
72
+ # name - The {String} name of the property accessor to create.
73
+ # block - The {Function} to write the property value.
74
+ Function::setter = (name, block) -> @accessor name, set: block
75
+
76
+ # Internal: Registers a method as the super method for another method
77
+ # for the given class. The super methods are stored in a map structure
78
+ # where the `__included__` array stores the keys and the `__super__`
79
+ # array stores the values. A meaningful name is added to the function
80
+ # to know its origin.
81
+ #
82
+ # key - The {String} name of the field that is being manipulated.
83
+ # value - A {Function} that will be set as the new value of the field.
84
+ # klass - The {Function} that is or has its prototype being manipulated.
85
+ # sup - The {Function} that is actually stored in the manipulated field
86
+ # and that is going to become the super method of the passed-in `value`.
87
+ # mixin - The {Function} mixin that is currently decorating the target class.
88
+ registerSuper = (key, value, klass, sup, mixin) ->
89
+ return if value.__included__? and klass in value.__included__
90
+
91
+ value.__super__ ||= []
92
+ value.__super__.push sup
93
+
94
+ value.__included__ ||= []
95
+ value.__included__.push klass
96
+
97
+ value.__name__ = "#{mixin.name}::#{key}"
98
+
99
+ # Injects the properties from the mixin in the `mixins` {Array}
100
+ # into the target prototype.
101
+ #
102
+ #
103
+ # ```coffeescript
104
+ # class Mixin
105
+ # instanceMethod: -> 'in the instance'
106
+ #
107
+ # class Dummy
108
+ # @include Mixin
109
+ #
110
+ # dummy = new Dummy
111
+ # dummy.instanceMethod() # 'in the instance'
112
+ # ```
113
+ #
114
+ # ```coffeescript
115
+ # class Dummy
116
+ # @include MixinA, MixinB, MixinC
117
+ # ```
118
+ #
119
+ # mixins - A list of `Mixin` to include in the class.
120
+ Function::include = (mixins...) ->
121
+
122
+ # The mixins prototype constructor and excluded properties
123
+ # are always excluded.
124
+ excluded = ['constructor', 'excluded', 'super']
125
+
126
+ # Internal: Stores the mixins included in the current class.
127
+ @__mixins__ ||= []
128
+
129
+ # Internal: Stores the parent class prototype when the `extend`
130
+ # keyword is used. It also stores mixins methods when the class doesn't
131
+ # extend another class.
132
+ @__super__ ||= {}
133
+
134
+ # We create a new `__super__` using the previous one as prototype.
135
+ # It allow to have mixins overrides some properties already defined
136
+ # by a parent prototype without actually modifying this prototype.
137
+ @__super__ = Object.create @__super__
138
+
139
+ # For each mixin passed to the `include` class method:
140
+ # We'll store the mixin in the `__mixins__` array to keep track of
141
+ # its inclusion.
142
+ for mixin in mixins
143
+ @__mixins__.push mixin
144
+
145
+ # A new Array is created to store the exclusion list of the current
146
+ # mixin. It is based on the default exclusion array.
147
+ excl = excluded.concat()
148
+ excl = excl.concat mixin::excluded if mixin::excluded?
149
+
150
+ # We loop through all the enumerable properties of the mixin's
151
+ # prototype that is not marked for exclusion.
152
+ keys = Object.keys mixin.prototype
153
+ for k in keys
154
+ if k not in excl
155
+
156
+ # We prefer working with property descriptors rather than with
157
+ # the plain values.
158
+ oldDescriptor = Object.getPropertyDescriptor @prototype, k
159
+ newDescriptor = Object.getPropertyDescriptor mixin.prototype, k
160
+
161
+ # If the two descriptors are available we'll have to go deeper.
162
+ if oldDescriptor? and newDescriptor?
163
+ oldHasAccessor = oldDescriptor.get? or oldDescriptor.set?
164
+ newHasAccessor = newDescriptor.get? or newDescriptor.set?
165
+ bothHaveGet = oldDescriptor.get? and newDescriptor.get?
166
+ bothHaveSet = oldDescriptor.set? and newDescriptor.set?
167
+ bothHaveValue = oldDescriptor.value? and newDescriptor.value?
168
+
169
+ # When both properties are accessors we'll be able to follow
170
+ # the super accross them.
171
+ #
172
+ # Super methods are registered if both are there for getters
173
+ # and setters.
174
+ if oldHasAccessor and newHasAccessor
175
+ registerSuper k, newDescriptor.get, @, oldDescriptor.get, mixin if bothHaveGet
176
+ registerSuper k, newDescriptor.set, @, oldDescriptor.set, mixin if bothHaveSet
177
+
178
+ # If there was a getter or a setter and the new accessor
179
+ # doesn't define one them, the previous value is used.
180
+ newDescriptor.get ||= oldDescriptor.get
181
+ newDescriptor.set ||= oldDescriptor.set
182
+
183
+ # When both have a value, the super is also available.
184
+ else if bothHaveValue
185
+ registerSuper k, newDescriptor.value, @, oldDescriptor.value, mixin
186
+
187
+ else
188
+ throw new Error "Can't mix accessors and plain values inheritance"
189
+
190
+ # We also have to create the property on the class `__super__`
191
+ # property. It'll allow the method defined on the class itself
192
+ # and overriding the property to have access to its super property
193
+ # through the `super` keyword or with `this.super` method.
194
+ Object.defineProperty @__super__, k, newDescriptor
195
+
196
+ # We only have a descriptor for the new property, the previous
197
+ # one is just added to the class `__super__` property.
198
+ else if newDescriptor?
199
+ @__super__[k] = mixin[k]
200
+
201
+ # We only have a descriptor for the previous property, we'll
202
+ # create it on the class `__super__` property.
203
+ else if oldDescriptor?
204
+ Object.defineProperty @__super__, k, newDescriptor
205
+
206
+ # No descriptors at all. The super property is attached directly
207
+ # to the value.
208
+ else if @::[k]?
209
+ registerSuper k, mixin[k], @, @::[k], mixin
210
+ @__super__[k] = mixin[k]
211
+
212
+ # With a descriptor the new property is created using
213
+ # `Object.defineProperty` or by affecting the value
214
+ # to the prototype.
215
+ if newDescriptor?
216
+ Object.defineProperty @prototype, k, newDescriptor
217
+ else
218
+ @::[k] = mixin::[k]
219
+
220
+ # The `included` hook is triggered on the mixin.
221
+ mixin.included? this
222
+
223
+ this
224
+
225
+ # Extends the current class with the properties of the passed-in
226
+ # `mixins`.
227
+ #
228
+ # ```coffeescript
229
+ # class Mixin
230
+ # @classMethod: -> 'in the class'
231
+ #
232
+ # class Dummy
233
+ # @extend Mixin
234
+ #
235
+ # Dummy.classMethod() # 'in the class'
236
+ # ```
237
+ #
238
+ # mixins - A list of `Mixin` to extend this class.
239
+ Function::extend = (mixins...) ->
240
+ excluded = ['extended', 'excluded', 'included']
241
+
242
+ # The `__mixins__` class property will stores the mixins included
243
+ # in the current class.
244
+ @__mixins__ ||= []
245
+
246
+ for mixin in mixins
247
+ @__mixins__.push mixin
248
+
249
+ excl = excluded.concat()
250
+ excl = excl.concat mixin.excluded if mixin.excluded?
251
+
252
+ keys = Object.keys mixin
253
+ for k in keys
254
+ if k not in excl
255
+ oldDescriptor = Object.getPropertyDescriptor this, k
256
+ newDescriptor = Object.getPropertyDescriptor mixin, k
257
+
258
+ if oldDescriptor? and newDescriptor?
259
+ oldHasAccessor = oldDescriptor.get? or oldDescriptor.set?
260
+ newHasAccessor = newDescriptor.get? or newDescriptor.set?
261
+ bothHaveGet = oldDescriptor.get? and newDescriptor.get?
262
+ bothHaveSet = oldDescriptor.set? and newDescriptor.set?
263
+ bothHaveValue = oldDescriptor.value? and newDescriptor.value?
264
+
265
+ # When both properties are accessors we'll be able to follow
266
+ # the super accross them
267
+ #
268
+ # Super methods are registered if both are there for getters
269
+ # and setters.
270
+ if oldHasAccessor and newHasAccessor
271
+ registerSuper k, newDescriptor.get, @, oldDescriptor.get, mixin if bothHaveGet
272
+ registerSuper k, newDescriptor.set, @, oldDescriptor.set, mixin if bothHaveSet
273
+
274
+ # If there was a getter or a setter and the new accessor
275
+ # doesn't define one them, the previous value is used.
276
+ newDescriptor.get ||= oldDescriptor.get
277
+ newDescriptor.set ||= oldDescriptor.set
278
+
279
+ # When both have a value, the super is also available.
280
+ else if bothHaveValue
281
+ registerSuper k, newDescriptor.value, @, oldDescriptor.value, mixin
282
+
283
+ else
284
+ throw new Error "Can't mix accessors and plain values inheritance"
285
+
286
+ # With a descriptor the new property is created using
287
+ # `Object.defineProperty` or by affecting the value
288
+ # to the prototype.
289
+ if newDescriptor?
290
+ Object.defineProperty this, k, newDescriptor
291
+ else
292
+ @[k] = mixin[k]
293
+
294
+ mixin.extended? this
295
+
296
+ this
297
+
298
+ # Combinates `Function::include` and `Function::extend` into
299
+ # one function.
300
+ #
301
+ # ```coffeescript
302
+ # class Mixin
303
+ # @classMethod: -> 'in class method'
304
+ # instanceMethod: -> 'in instance method'
305
+ #
306
+ # class Dummy
307
+ # @concern Mixin
308
+ #
309
+ # Dummy.classMethod() # 'in class method'
310
+ # dummy = new Dummy
311
+ # dummy.instanceMethod() # 'in instance method'
312
+ # ```
313
+ #
314
+ # mixins - A list of `Mixin` that concern the class.
315
+ Function::concern = (mixins...) ->
316
+ @include.apply(this, mixins)
317
+ @extend.apply(this, mixins)
@@ -0,0 +1,338 @@
1
+ namespace('agt.geom')
2
+
3
+ # Public: A `Circle` object is defined by a radius, a position
4
+ # and a rotation.
5
+ #
6
+ # ```coffeescript
7
+ # circle = new Circle 80, 100, 100, 0
8
+ # circle = new Circle radius: 80, x: 100, y: 100, rotation: 0
9
+ # ```
10
+ #
11
+ # <script>window.exampleKey = 'circle'</script>
12
+ # <script>drawGeometry(exampleKey, {highlight: true})</script>
13
+ #
14
+ # When conforming to the [Geometry]{agt.geom.Geometry} interface such as the
15
+ # [points]{agt.geom.Geometry::points} method the `Circle` class
16
+ # use approximations based on the number of ` segments` defined on the circle.
17
+ #
18
+ # ### Included Mixins
19
+ #
20
+ # - {agt.geom.Geometry}
21
+ # - {agt.geom.Intersections}
22
+ # - {agt.geom.Path}
23
+ # - {agt.geom.Surface}
24
+ # - [agt.mixins.Cloneable](../../../files/mixins/cloneable.coffee.html)
25
+ # - [agt.mixins.Equatable](../../../files/mixins/equatable.coffee.html)
26
+ # - [agt.mixins.Formattable](../../../files/mixins/formattable.coffee.html)
27
+ # - [agt.mixins.Memoizable](../../../files/mixins/memoizable.coffee.html)
28
+ # - [agt.mixins.Parameterizable](../../../files/mixins/parameterizable.coffee.html)
29
+ # - [agt.mixins.Sourcable](../../../files/mixins/sourcable.coffee.html)
30
+ class agt.geom.Circle
31
+ @include agt.mixins.Equatable('x','y','radius','rotation')
32
+ @include agt.mixins.Formattable('Circle','x','y','radius','rotation')
33
+ @include agt.mixins.Parameterizable('circleFrom', radius: 1, x: 0, y: 0, rotation: 0, segments: 36)
34
+ @include agt.mixins.Sourcable('agt.geom.Circle', 'radius', 'x', 'y', 'rotation', 'segments')
35
+ @include agt.mixins.Cloneable()
36
+ @include agt.mixins.Memoizable
37
+ @include agt.geom.Geometry
38
+ @include agt.geom.Surface
39
+ @include agt.geom.Path
40
+ @include agt.geom.Intersections
41
+
42
+ ### Public ###
43
+
44
+ # The general intersections algorithm for circles with non-circle
45
+ # geometries.
46
+ #
47
+ # <script>drawShapeIntersections(exampleKey, 'rectangle')</script>
48
+ #
49
+ # geom1 - The first [Geometry]{agt.geom.Geometry}.
50
+ # geom2 - The second [Geometry]{agt.geom.Geometry}.
51
+ # block - The iterator {Function} to call with the found intersections.
52
+ @eachIntersections: (geom1, geom2, block) ->
53
+ [geom1, geom2] = [geom2, geom1] if geom2.classname?() is 'Circle'
54
+ points = geom2.points()
55
+ length = points.length
56
+ output = []
57
+
58
+ for i in [0..length-2]
59
+ sv = points[i]
60
+ ev = points[i+1]
61
+
62
+ return if geom1.eachLineIntersections sv, ev, block
63
+
64
+ # The specific algorithm for circle with circle intersections.
65
+ #
66
+ # <script>drawShapeIntersections(exampleKey, exampleKey)</script>
67
+ #
68
+ # geom1 - The first `Circle`.
69
+ # geom2 - The second `Circle`.
70
+ # block - The iterator {Function} to call with the found intersections.
71
+ @eachCircleCircleIntersections: (geom1, geom2, block) ->
72
+ if geom1.equals geom2
73
+ for p in geom1.points()
74
+ return if block.call this, p
75
+ else
76
+ r1 = geom1.radius
77
+ r2 = geom2.radius
78
+ p1 = geom1.center()
79
+ p2 = geom2.center()
80
+ d = p1.distance(p2)
81
+ dv = p2.subtract(p1)
82
+ radii = r1 + r2
83
+
84
+ return if d > radii
85
+ return block.call this, p1.add(dv.normalize(r1)) if d is radii
86
+
87
+ a = (r1*r1 - r2*r2 + d*d) / (2*d)
88
+ h = Math.sqrt(r1*r1 - a*a)
89
+ hv = new agt.geom.Point h * (p2.y - p1.y) / d,
90
+ -h * (p2.x - p1.x) / d
91
+
92
+ p = p1.add(dv.normalize(a)).add(hv)
93
+ block.call this, p
94
+
95
+ p = p1.add(dv.normalize(a)).add(hv.scale(-1))
96
+ block.call this, p
97
+
98
+ # Registers the fast intersections iterators for the Circle class
99
+ iterators = agt.geom.Intersections.iterators
100
+ iterators['Circle'] = Circle.eachIntersections
101
+ iterators['CircleCircle'] = Circle.eachCircleCircleIntersections
102
+
103
+ # Creates a new circle instance.
104
+ #
105
+ # radius - The radius {Number} or a circle-like {Object}.
106
+ # x - The {Number} position of the circle center on the x axis.
107
+ # u - The {Number} position of the circle center on the y axis.
108
+ # rotation - The rotation {Number} of the circle in radians.
109
+ # segments - The {Number} of segments when drawing the circle.
110
+ constructor: (radius, x, y, rotation, segments) ->
111
+ {@radius,@x,@y,@rotation,@segments} = @circleFrom radius, x, y, rotation, segments
112
+
113
+ # Returns the center of the circle.
114
+ #
115
+ # <script>drawGeometry(exampleKey, {center: true})</script>
116
+ #
117
+ # Returns a [Point]{agt.geom.Point}.
118
+ center: -> new agt.geom.Point @x, @y
119
+
120
+ # Returns the top-most coordinate of the circle shape.
121
+ #
122
+ # <script>drawGeometryBound(exampleKey, 'top')</script>
123
+ #
124
+ # Returns a {Number}.
125
+ top: -> @y - @radius
126
+
127
+ # Returns the bottom-most coordinate of the circle shape.
128
+ #
129
+ # <script>drawGeometryBound(exampleKey, 'bottom')</script>
130
+ #
131
+ # Returns a {Number}.
132
+ bottom: -> @y + @radius
133
+
134
+ # Returns the left-most coordinate of the circle shape.
135
+ #
136
+ # <script>drawGeometryBound(exampleKey, 'left')</script>
137
+ #
138
+ # Returns a {Number}.
139
+ left: -> @x - @radius
140
+
141
+ # Returns the right-most coordinate of the circle shape.
142
+ #
143
+ # <script>drawGeometryBound(exampleKey, 'right')</script>
144
+ #
145
+ # Returns a {Number}.
146
+ right: -> @x + @radius
147
+
148
+ # Adds the passed-in [Point]{agt.geom.Point} to the position
149
+ # of this circle.
150
+ #
151
+ # <script>drawTransform(exampleKey, {type: 'translate', args: [50, 0], width: 150})</script>
152
+ #
153
+ # x - A {Number} for the x coordinate or a point-like {Object}.
154
+ # y - A {Number} for the y coordinate if the first argument
155
+ # was also a number.
156
+ #
157
+ # Returns this [Circle]{agt.geomCircle}.
158
+ translate: (x, y) ->
159
+ {x,y} = agt.geom.Point.pointFrom x, y
160
+
161
+ @x += x
162
+ @y += y
163
+ this
164
+
165
+ # Adds the passed-in rotation to the current circle rotation.
166
+ #
167
+ # The rotation of a circle defines at which angle the circle perimeter
168
+ # starts. In consequences it affects its triangulation, its path properties
169
+ # and in general every methods that deals with position on the perimeter.
170
+ #
171
+ # <script>drawTransform(exampleKey, {type: 'rotate', args: [Math.PI/ 3]})</script>
172
+ #
173
+ # rotation - The rotation {Number}.
174
+ #
175
+ # Returns this [Circle]{agt.geomCircle}.
176
+ rotate: (rotation) ->
177
+ @rotation += rotation
178
+ this
179
+
180
+ # Scales the circle by multiplying its radius by the passed-in `scale`.
181
+ #
182
+ # <script>drawTransform(exampleKey, {type: 'scale', args: [0.6]})</script>
183
+ #
184
+ # scale - The scale {Number} to apply to the circle.
185
+ #
186
+ # Returns this [Circle]{agt.geomCircle}.
187
+ scale: (scale) ->
188
+ @radius *= scale
189
+ this
190
+
191
+ # Returns the circle points as approximated using the current `segments`
192
+ # of the circle.
193
+ #
194
+ # <script>drawGeometryPoints(exampleKey, 'points')</script>
195
+ #
196
+ # Returns an {Array}.
197
+ points: ->
198
+ step = Math.PI * 2 / @segments
199
+ @pointAtAngle(n * step) for n in [0..@segments]
200
+
201
+ # Returns the triangles forming the circle as approximated using
202
+ # the current `segments` of the circle.
203
+ #
204
+ # <script>drawGeometry(exampleKey, {triangles: true})</script>
205
+ #
206
+ # Returns an {Array} of [Triangles]{agt.geom.Triangle}.
207
+ triangles: ->
208
+ return @memoFor 'triangles' if @memoized 'triangles'
209
+
210
+ triangles = []
211
+ points = @points()
212
+ center = @center()
213
+ for i in [1..points.length-1]
214
+ triangles.push new agt.geom.Triangle center, points[i-1], points[i]
215
+
216
+ @memoize 'triangles', triangles
217
+
218
+ # Always returns `true`.
219
+ #
220
+ # Returns a {Boolean}.
221
+ closedGeometry: -> true
222
+
223
+ # Iterates over all intersections between the vector formed by the `a` and `b`
224
+ # [Points]{agt.geom.Point} and the current circle.
225
+ #
226
+ # <script>drawLineIntersections(exampleKey)</script>
227
+ #
228
+ # a - The starting [Points]{agt.geom.Point} of the vector.
229
+ # b - The ending [Points]{agt.geom.Point} of the vector.
230
+ # block - The iterator {Function} to call with the found intersections.
231
+ eachLineIntersections: (a, b, block) ->
232
+ c = @center()
233
+
234
+ _a = (b.x - a.x) * (b.x - a.x) +
235
+ (b.y - a.y) * (b.y - a.y)
236
+ _b = 2 * ((b.x - a.x) * (a.x - c.x) +
237
+ (b.y - a.y) * (a.y - c.y))
238
+ cc = c.x * c.x +
239
+ c.y * c.y +
240
+ a.x * a.x +
241
+ a.y * a.y -
242
+ 2 * (c.x * a.x + c.y * a.y) - @radius * @radius
243
+ deter = _b * _b - 4 * _a * cc
244
+
245
+ if deter > 0
246
+ e = Math.sqrt deter
247
+ u1 = ( - _b + e ) / (2 * _a )
248
+ u2 = ( - _b - e ) / (2 * _a )
249
+ unless ((u1 < 0 || u1 > 1) && (u2 < 0 || u2 > 1))
250
+ if 0 <= u2 and u2 <= 1
251
+ return if block.call this, agt.geom.Point.interpolate a, b, u2
252
+
253
+ if 0 <= u1 and u1 <= 1
254
+ return if block.call this, agt.geom.Point.interpolate a, b, u1
255
+
256
+ # Returns the [Point]{agt.geom.Point} on the perimeter of the circle
257
+ # at the given `angle`.
258
+ #
259
+ # angle - The angle {Number}.
260
+ #
261
+ # <script>drawGeometry(exampleKey, {angle: true})</script>
262
+ #
263
+ # Returns a [Point]{agt.geom.Point}.
264
+ pointAtAngle: (angle) ->
265
+ new agt.geom.Point @x + Math.cos(@rotation + angle) * @radius,
266
+ @y + Math.sin(@rotation + angle) * @radius
267
+
268
+ # Returns the surface {Number} of this circle.
269
+ #
270
+ # Returns a {Number}.
271
+ acreage: -> @radius * @radius * Math.PI
272
+
273
+ # Returns `true` when the given point is contained in the circle.
274
+ #
275
+ # In the example below all the green points on the screen represents
276
+ # coordinates that are contained in the circle.
277
+ #
278
+ # <script>drawGeometry(exampleKey, {contains: true})</script>
279
+ #
280
+ # x - A {Number} for the x coordinate or a point-like {Object}.
281
+ # y - A {Number} for the y coordinate if the first argument
282
+ # was also a number.
283
+ #
284
+ # Returns a {Boolean}.
285
+ contains: (x, y) ->
286
+ pt = agt.geom.Point.pointFrom x, y, true
287
+
288
+ @center().subtract(pt).length() <= @radius
289
+
290
+ # Returns a randomly generated point within the circle perimeter.
291
+ #
292
+ # <script>drawGeometry(exampleKey, {surface: true})</script>
293
+ #
294
+ # random - An optional [Random]{agt.random.Random} instance to use instead
295
+ # of the default `Math` random method.
296
+ #
297
+ # Returns a [Point]{agt.geom.Point}.
298
+ randomPointInSurface: (random) ->
299
+ unless random?
300
+ random = new agt.random.Random new agt.random.MathRandom
301
+
302
+ pt = @pointAtAngle random.random(Math.PI * 2)
303
+ center = @center()
304
+ dif = pt.subtract center
305
+ center.add dif.scale Math.sqrt random.random()
306
+
307
+ # Returns the length {Number} of the circle perimeter.
308
+ #
309
+ # Returns a {Number}.
310
+ length: -> @radius * Math.PI * 2
311
+
312
+ # Returns a [Point]{agt.geom.Point} on the circle perimeter using
313
+ # a {Number} between `0` and `1`.
314
+ #
315
+ # <script>drawGeometry(exampleKey, {paths: [0, 1/3, 2/3]})</script>
316
+ #
317
+ # n - A {Number} between `0` and `1`a {Number} between `0` and `1`.
318
+ #
319
+ # Returns a [Point]{agt.geom.Point}.
320
+ pathPointAt: (n) -> @pointAtAngle n * Math.PI * 2
321
+
322
+ # {Delegates to: agt.geom.Geometry.drawPath}
323
+ drawPath: (context) ->
324
+ context.beginPath()
325
+ context.arc @x, @y, @radius, 0, Math.PI * 2
326
+
327
+ # Generates the memoization key for this instance's state.
328
+ #
329
+ # For a circle, a memoized value will be invalidated whenever one of the
330
+ # following properties changes:
331
+ # - x
332
+ # - y
333
+ # - radius
334
+ # - rotation
335
+ # - segments
336
+ #
337
+ # Returns a {String}.
338
+ memoizationKey: -> "#{@radius};#{@x};#{@y};#{@rotation};#{@segments}"