agt 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -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
|
data/LICENSE.md
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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}"
|