agt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.md +7 -0
- data/README.md +5 -0
- data/app/assets/javascripts/agt/config.coffee +9 -0
- data/app/assets/javascripts/agt/dom.coffee +12 -0
- data/app/assets/javascripts/agt/function.coffee +317 -0
- data/app/assets/javascripts/agt/geom/circle.coffee +338 -0
- data/app/assets/javascripts/agt/geom/cubic_bezier.coffee +37 -0
- data/app/assets/javascripts/agt/geom/diamond.coffee +241 -0
- data/app/assets/javascripts/agt/geom/ellipsis.coffee +141 -0
- data/app/assets/javascripts/agt/geom/linear_spline.coffee +56 -0
- data/app/assets/javascripts/agt/geom/matrix.coffee +240 -0
- data/app/assets/javascripts/agt/geom/mixins/geometry.coffee +171 -0
- data/app/assets/javascripts/agt/geom/mixins/intersections.coffee +150 -0
- data/app/assets/javascripts/agt/geom/mixins/path.coffee +145 -0
- data/app/assets/javascripts/agt/geom/mixins/proxyable.coffee +9 -0
- data/app/assets/javascripts/agt/geom/mixins/spline.coffee +329 -0
- data/app/assets/javascripts/agt/geom/mixins/surface.coffee +55 -0
- data/app/assets/javascripts/agt/geom/mixins/triangulable.coffee +112 -0
- data/app/assets/javascripts/agt/geom/point.coffee +454 -0
- data/app/assets/javascripts/agt/geom/polygon.coffee +119 -0
- data/app/assets/javascripts/agt/geom/quad_bezier.coffee +43 -0
- data/app/assets/javascripts/agt/geom/quint_bezier.coffee +42 -0
- data/app/assets/javascripts/agt/geom/rectangle.coffee +599 -0
- data/app/assets/javascripts/agt/geom/spiral.coffee +90 -0
- data/app/assets/javascripts/agt/geom/transformation_proxy.coffee +43 -0
- data/app/assets/javascripts/agt/geom/triangle.coffee +442 -0
- data/app/assets/javascripts/agt/impulse.coffee +105 -0
- data/app/assets/javascripts/agt/index.coffee +56 -0
- data/app/assets/javascripts/agt/inflector/inflection.coffee +21 -0
- data/app/assets/javascripts/agt/inflector/inflections.coffee +75 -0
- data/app/assets/javascripts/agt/inflector/inflector.coffee +235 -0
- data/app/assets/javascripts/agt/inheritance.coffee +132 -0
- data/app/assets/javascripts/agt/math.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/activable.coffee +31 -0
- data/app/assets/javascripts/agt/mixins/aliasable.coffee +25 -0
- data/app/assets/javascripts/agt/mixins/alternate_case.coffee +72 -0
- data/app/assets/javascripts/agt/mixins/cloneable.coffee +71 -0
- data/app/assets/javascripts/agt/mixins/delegation.coffee +90 -0
- data/app/assets/javascripts/agt/mixins/disposable.coffee +7 -0
- data/app/assets/javascripts/agt/mixins/equatable.coffee +37 -0
- data/app/assets/javascripts/agt/mixins/formattable.coffee +52 -0
- data/app/assets/javascripts/agt/mixins/globalizable.coffee +175 -0
- data/app/assets/javascripts/agt/mixins/has_ancestors.coffee +47 -0
- data/app/assets/javascripts/agt/mixins/has_collection.coffee +107 -0
- data/app/assets/javascripts/agt/mixins/has_nested_collection.coffee +51 -0
- data/app/assets/javascripts/agt/mixins/memoizable.coffee +64 -0
- data/app/assets/javascripts/agt/mixins/parameterizable.coffee +101 -0
- data/app/assets/javascripts/agt/mixins/poolable.coffee +62 -0
- data/app/assets/javascripts/agt/mixins/sourcable.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/state_machine.coffee +47 -0
- data/app/assets/javascripts/agt/net/router.coffee +165 -0
- data/app/assets/javascripts/agt/object.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/base_action.coffee +7 -0
- data/app/assets/javascripts/agt/particles/actions/die_on_surface.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/force.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/friction.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/live.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/macro_action.coffee +13 -0
- data/app/assets/javascripts/agt/particles/actions/move.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/null_action.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/by_rate.coffee +17 -0
- data/app/assets/javascripts/agt/particles/counters/fixed.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/null_counter.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emission.coffee +35 -0
- data/app/assets/javascripts/agt/particles/emitters/null_emitter.coffee +7 -0
- data/app/assets/javascripts/agt/particles/emitters/path.coffee +10 -0
- data/app/assets/javascripts/agt/particles/emitters/ponctual.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emitters/surface.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/explosion.coffee +19 -0
- data/app/assets/javascripts/agt/particles/initializers/life.coffee +16 -0
- data/app/assets/javascripts/agt/particles/initializers/macro_initializer.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/null_initializer.coffee +7 -0
- data/app/assets/javascripts/agt/particles/initializers/particle_sub_system.coffee +12 -0
- data/app/assets/javascripts/agt/particles/initializers/stream.coffee +20 -0
- data/app/assets/javascripts/agt/particles/mixins/randomizable.coffee +10 -0
- data/app/assets/javascripts/agt/particles/particle.coffee +32 -0
- data/app/assets/javascripts/agt/particles/sub_system.coffee +11 -0
- data/app/assets/javascripts/agt/particles/system.coffee +103 -0
- data/app/assets/javascripts/agt/particles/timers/instant.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/limited.coffee +19 -0
- data/app/assets/javascripts/agt/particles/timers/null_timer.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/unlimited.coffee +9 -0
- data/app/assets/javascripts/agt/particles/timers/until_death.coffee +11 -0
- data/app/assets/javascripts/agt/promise.coffee +214 -0
- data/app/assets/javascripts/agt/random/random.coffee +100 -0
- data/app/assets/javascripts/agt/random/seeds/lagged_fibonnacci.coffee +53 -0
- data/app/assets/javascripts/agt/random/seeds/linear.coffee +16 -0
- data/app/assets/javascripts/agt/random/seeds/linear_congruential.coffee +23 -0
- data/app/assets/javascripts/agt/random/seeds/math_random.coffee +9 -0
- data/app/assets/javascripts/agt/random/seeds/mersenne_twister.coffee +42 -0
- data/app/assets/javascripts/agt/random/seeds/no_random.coffee +12 -0
- data/app/assets/javascripts/agt/random/seeds/paul_houle.coffee +19 -0
- data/app/assets/javascripts/agt/signal.coffee +272 -0
- data/app/assets/javascripts/agt/sprites/animation.coffee +13 -0
- data/app/assets/javascripts/agt/sprites/sprite.coffee +30 -0
- data/app/assets/javascripts/agt/widgets/hash.coffee +36 -0
- data/app/assets/javascripts/agt/widgets/widgets.coffee +194 -0
- data/app/assets/javascripts/agt/widgets/widgets/checked_input.coffee +7 -0
- data/app/assets/javascripts/agt/widgets/widgets/focus_bubbling.coffee +15 -0
- data/lib/agt.rb +8 -0
- data/lib/agt/version.rb +5 -0
- metadata +173 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
|
|
3
|
+
# Public: The Matrix class represents a transformation matrix that
|
|
4
|
+
# determines how to map points from one coordinate space to another.
|
|
5
|
+
# These transformation functions include translation (x and
|
|
6
|
+
# y repositioning), rotation, scaling, and skewing.
|
|
7
|
+
#
|
|
8
|
+
# A matrix can created with the following signature:
|
|
9
|
+
#
|
|
10
|
+
# ```coffeescript
|
|
11
|
+
# matrix = new Matrix
|
|
12
|
+
#
|
|
13
|
+
# matrix = new Matrix a, b, c, d, tx, ty
|
|
14
|
+
#
|
|
15
|
+
# matrix = new Matrix {a, b, c, d, tx, ty}
|
|
16
|
+
# ```
|
|
17
|
+
#
|
|
18
|
+
# An identity matrix is created if no arguments is provided.
|
|
19
|
+
class agt.geom.Matrix
|
|
20
|
+
|
|
21
|
+
# A list of the proprties to be checked to consider an object as a matrix.
|
|
22
|
+
properties = ['a', 'b', 'c', 'd', 'tx', 'ty']
|
|
23
|
+
|
|
24
|
+
@include agt.mixins.Equatable(properties...)
|
|
25
|
+
@include agt.mixins.Formattable(['Matrix'].concat(properties)...)
|
|
26
|
+
@include agt.mixins.Sourcable(['agt.geom.Matrix'].concat(properties)...)
|
|
27
|
+
@include agt.mixins.Parameterizable('matrixFrom', {
|
|
28
|
+
a: 1
|
|
29
|
+
b: 0
|
|
30
|
+
c: 0
|
|
31
|
+
d: 1
|
|
32
|
+
tx: 0
|
|
33
|
+
ty: 0
|
|
34
|
+
})
|
|
35
|
+
@include agt.mixins.Cloneable()
|
|
36
|
+
|
|
37
|
+
### Public ###
|
|
38
|
+
|
|
39
|
+
# Returns `true` if the passed-in object `m` is a valid matrix.
|
|
40
|
+
# A valid matrix is an object with properties `a`, `b`, `c`, `d`,
|
|
41
|
+
# `tx` and `ty` and that properties must contains numbers.
|
|
42
|
+
#
|
|
43
|
+
# ```coffeescript
|
|
44
|
+
# Matrix.isMatrix new Matrix # true
|
|
45
|
+
#
|
|
46
|
+
# matrix2 = {
|
|
47
|
+
# a: 1, b: 0, tx: 0,
|
|
48
|
+
# c: 0, d: 1, ty: 0
|
|
49
|
+
# }
|
|
50
|
+
# Matrix.isMatrix matrix # true
|
|
51
|
+
#
|
|
52
|
+
# matrix2 = {
|
|
53
|
+
# a: '1', b: '0', tx: '0',
|
|
54
|
+
# c: '0', d: '1', ty: '0'
|
|
55
|
+
# }
|
|
56
|
+
# Matrix.isMatrix matrix # true
|
|
57
|
+
#
|
|
58
|
+
# Matris.isMatrix {} # false
|
|
59
|
+
# ```
|
|
60
|
+
@isMatrix: (m) ->
|
|
61
|
+
return false unless m?
|
|
62
|
+
return false for k in PROPERTIES when not Math.isFloat m[k]
|
|
63
|
+
true
|
|
64
|
+
|
|
65
|
+
# Creates a new Matrix instance.
|
|
66
|
+
#
|
|
67
|
+
# a - The {Number} of the `a` property of the matrix
|
|
68
|
+
# or a Matrix-like {Object}.
|
|
69
|
+
# b - The {Number} of the `b` property of the matrix.
|
|
70
|
+
# c - The {Number} of the `c` property of the matrix.
|
|
71
|
+
# d - The {Number} of the `d` property of the matrix.
|
|
72
|
+
# tx - The {Number} of the `tx` property of the matrix.
|
|
73
|
+
# ty - The {Number} of the `ty` property of the matrix.
|
|
74
|
+
constructor: (a=1, b=0, c=0, d=1, tx=0, ty=0) ->
|
|
75
|
+
{@a, @b, @c, @d, @tx, @ty} = @matrixFrom a, b, c, d, tx, ty, true
|
|
76
|
+
|
|
77
|
+
# Returns a new point corresponding to the transformation
|
|
78
|
+
# of the passed-in point by the current matrix.
|
|
79
|
+
#
|
|
80
|
+
# ```coffeescript
|
|
81
|
+
# point = new Point 10, 0
|
|
82
|
+
#
|
|
83
|
+
# matrix = new Matrix().scale(2,2).rotate(90)
|
|
84
|
+
#
|
|
85
|
+
# projected = matrix.transformPoint point
|
|
86
|
+
# # projected = [object Point(0,20)]
|
|
87
|
+
# ```
|
|
88
|
+
#
|
|
89
|
+
# x - A {Number} for the x coordinate or a point-like {Object}.
|
|
90
|
+
# y - A {Number} for the y coordinate if the first argument
|
|
91
|
+
# was also a number.
|
|
92
|
+
#
|
|
93
|
+
# Returns a new transformed [Point]{agt.geom.Point}.
|
|
94
|
+
transformPoint: (x, y) ->
|
|
95
|
+
if not x? and not y?
|
|
96
|
+
throw new Error "transformPoint was called without arguments"
|
|
97
|
+
|
|
98
|
+
{x,y} = agt.geom.Point.pointFrom x, y, true
|
|
99
|
+
new agt.geom.Point x*@a + y*@c + @tx,
|
|
100
|
+
x*@b + y*@d + @ty
|
|
101
|
+
|
|
102
|
+
# Translates the matrix by the amount of the passed-in point.
|
|
103
|
+
#
|
|
104
|
+
# x - A {Number} for the x translation or a point-like {Object}.
|
|
105
|
+
# y - A {Number} for the y translation if the first argument
|
|
106
|
+
# was also a number.
|
|
107
|
+
#
|
|
108
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
109
|
+
translate: (x=0, y=0) ->
|
|
110
|
+
{x,y} = agt.geom.Point.pointFrom x, y
|
|
111
|
+
|
|
112
|
+
@tx += x
|
|
113
|
+
@ty += y
|
|
114
|
+
this
|
|
115
|
+
|
|
116
|
+
# Scales the matrix by the amount of the passed-in point.
|
|
117
|
+
#
|
|
118
|
+
# x - A {Number} for the x scale or a point-like {Object}.
|
|
119
|
+
# y - A {Number} for the y scale if the first argument
|
|
120
|
+
# was also a number.
|
|
121
|
+
#
|
|
122
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
123
|
+
scale: (x=1, y=1) ->
|
|
124
|
+
{x,y} = agt.geom.Point.pointFrom x, y
|
|
125
|
+
|
|
126
|
+
@a *= x
|
|
127
|
+
@d *= y
|
|
128
|
+
@tx *= x
|
|
129
|
+
@ty *= y
|
|
130
|
+
this
|
|
131
|
+
|
|
132
|
+
# Rotates the matrix by the amount of the passed-in angle in degrees.
|
|
133
|
+
#
|
|
134
|
+
# angle - The angle {Number} in radians.
|
|
135
|
+
#
|
|
136
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
137
|
+
rotate: (angle=0) ->
|
|
138
|
+
cos = Math.cos angle
|
|
139
|
+
sin = Math.sin angle
|
|
140
|
+
[@a, @b, @c, @d, @tx, @ty] = [
|
|
141
|
+
@a*cos - @b*sin
|
|
142
|
+
@a*sin + @b*cos
|
|
143
|
+
@c*cos - @d*sin
|
|
144
|
+
@c*sin + @d*cos
|
|
145
|
+
@tx*cos - @ty*sin
|
|
146
|
+
@tx*sin + @ty*cos
|
|
147
|
+
]
|
|
148
|
+
this
|
|
149
|
+
|
|
150
|
+
# Skews the matrix by the amount of the passed-in point.
|
|
151
|
+
#
|
|
152
|
+
# x - A {Number} for the x skew or a point-like {Object}.
|
|
153
|
+
# y - A {Number} for the y skew if the first argument
|
|
154
|
+
# was also a number.
|
|
155
|
+
#
|
|
156
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
157
|
+
skew: (x, y) ->
|
|
158
|
+
pt = agt.geom.Point.pointFrom(x, y, 0)
|
|
159
|
+
@append Math.cos(pt.y),
|
|
160
|
+
Math.sin(pt.y),
|
|
161
|
+
-Math.sin(pt.x),
|
|
162
|
+
Math.cos(pt.x)
|
|
163
|
+
this
|
|
164
|
+
|
|
165
|
+
# Append the passed-in matrix to this matrix.
|
|
166
|
+
#
|
|
167
|
+
# a - The {Number} of the `a` property of the matrix to append
|
|
168
|
+
# or a Matrix-like {Object}.
|
|
169
|
+
# b - The {Number} of the `b` property of the matrix to append.
|
|
170
|
+
# c - The {Number} of the `c` property of the matrix to append.
|
|
171
|
+
# d - The {Number} of the `d` property of the matrix to append.
|
|
172
|
+
# tx - The {Number} of the `tx` property of the matrix to append.
|
|
173
|
+
# ty - The {Number} of the `ty` property of the matrix to append.
|
|
174
|
+
#
|
|
175
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
176
|
+
append: (a=1, b=0, c=0, d=1, tx=0, ty=0) ->
|
|
177
|
+
{a, b, c, d, tx, ty} = @matrixFrom a, b, c, d, tx, ty, true
|
|
178
|
+
[@a, @b, @c, @d, @tx, @ty] = [
|
|
179
|
+
a*@a + b*@c
|
|
180
|
+
a*@b + b*@d
|
|
181
|
+
c*@a + d*@c
|
|
182
|
+
c*@b + d*@d
|
|
183
|
+
tx*@a + ty*@c + @tx
|
|
184
|
+
tx*@b + ty*@d + @ty
|
|
185
|
+
]
|
|
186
|
+
this
|
|
187
|
+
|
|
188
|
+
# Prepend the passed-in matrix with this matrix.
|
|
189
|
+
#
|
|
190
|
+
# a - The {Number} of the `a` property of the matrix to prepend
|
|
191
|
+
# or a Matrix-like {Object}.
|
|
192
|
+
# b - The {Number} of the `b` property of the matrix to prepend.
|
|
193
|
+
# c - The {Number} of the `c` property of the matrix to prepend.
|
|
194
|
+
# d - The {Number} of the `d` property of the matrix to prepend.
|
|
195
|
+
# tx - The {Number} of the `tx` property of the matrix to prepend.
|
|
196
|
+
# ty - The {Number} of the `ty` property of the matrix to prepend.
|
|
197
|
+
#
|
|
198
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
199
|
+
prepend: (a=1, b=0, c=0, d=1, tx=0, ty=0) ->
|
|
200
|
+
{a, b, c, d, tx, ty} = @matrixFrom a, b, c, d, tx, ty, true
|
|
201
|
+
if a isnt 1 or b isnt 0 or c isnt 0 or d isnt 1
|
|
202
|
+
[@a, @b, @c, @d] = [
|
|
203
|
+
@a*a + @b*c
|
|
204
|
+
@a*b + @b*d
|
|
205
|
+
@c*a + @d*c
|
|
206
|
+
@c*b + @d*d
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
[@tx, @ty] = [
|
|
210
|
+
@tx*a + @ty*c + tx
|
|
211
|
+
@tx*b + @ty*d + ty
|
|
212
|
+
]
|
|
213
|
+
this
|
|
214
|
+
|
|
215
|
+
# Converts this matrix into an identity matrix.
|
|
216
|
+
#
|
|
217
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
218
|
+
identity: -> [@a, @b, @c, @d, @tx, @ty] = [1, 0, 0, 1, 0, 0]; this
|
|
219
|
+
|
|
220
|
+
# Converts this matrix into its inverse.
|
|
221
|
+
#
|
|
222
|
+
# Returns this [Matrix]{agt.geom.Matrix}.
|
|
223
|
+
inverse: ->
|
|
224
|
+
n = @a * @d - @b * @c
|
|
225
|
+
[@a, @b, @c, @d, @tx, @ty] = [
|
|
226
|
+
@d / n
|
|
227
|
+
-@b / n
|
|
228
|
+
-@c / n
|
|
229
|
+
@a / n
|
|
230
|
+
(@c*@ty - @d*@tx) / n
|
|
231
|
+
-(@a*@ty - @b*@tx) / n
|
|
232
|
+
]
|
|
233
|
+
this
|
|
234
|
+
|
|
235
|
+
# Alias the `Matrix.isMatrix` method in instances.
|
|
236
|
+
#
|
|
237
|
+
# m - An {Object} to test for matrix properties.
|
|
238
|
+
#
|
|
239
|
+
# Returns a {Boolean}.
|
|
240
|
+
isMatrix: (m) -> Matrix.isMatrix m
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
# Public: The `Geometry` mixin describes the most basic interface a geometry
|
|
3
|
+
# should implements.
|
|
4
|
+
#
|
|
5
|
+
# ```coffeescript
|
|
6
|
+
# class DummyGeometry
|
|
7
|
+
# @include agt.geom.Geometry
|
|
8
|
+
#
|
|
9
|
+
# # Your geometry implementation...
|
|
10
|
+
# ```
|
|
11
|
+
#
|
|
12
|
+
# ### Shape
|
|
13
|
+
#
|
|
14
|
+
# <script>window.exampleKey = 'geometry'</script>
|
|
15
|
+
# <script>drawGeometryPoints(exampleKey, 'points')</script>
|
|
16
|
+
#
|
|
17
|
+
# A geometry has a shape constituted by an {Array} of [Points]{agt.geom.Point}.
|
|
18
|
+
# These points can be accessed using the {::points} method
|
|
19
|
+
#
|
|
20
|
+
# A geometry can be either opened or closed. It can be retrieved using
|
|
21
|
+
# the {::closedGeometry} method.
|
|
22
|
+
#
|
|
23
|
+
# ### Bounds
|
|
24
|
+
#
|
|
25
|
+
# <script>drawGeometry(exampleKey, {bounds: true})</script>
|
|
26
|
+
#
|
|
27
|
+
# A geometry has bounds, represented by the {::left}, {::right}, {::top}
|
|
28
|
+
# and {::bottom} methods. An object with the bounds value can be retrieved
|
|
29
|
+
# using the {::bounds} method, and the [Rectangle]{agt.geom.Rectangle} formed
|
|
30
|
+
# by the geometry bounds can be retrieved using the {::boundingBox} method.
|
|
31
|
+
#
|
|
32
|
+
# The default bounds computation consist in looking for every points
|
|
33
|
+
# of the geometry shape and detect the minimal and maximal values
|
|
34
|
+
# on each axis.
|
|
35
|
+
# Generally the concrete geometry classes override the bounds methods
|
|
36
|
+
# to provide a faster implementation.
|
|
37
|
+
#
|
|
38
|
+
# ### Drawing API
|
|
39
|
+
#
|
|
40
|
+
# <script>drawGeometry(exampleKey, {highlight: true})</script>
|
|
41
|
+
#
|
|
42
|
+
# Every geometry provides basic methods to draw themselves on a canvas,
|
|
43
|
+
# {::stroke} and {::fill}, the latter renders a line when the former
|
|
44
|
+
# renders a plain shape.
|
|
45
|
+
#
|
|
46
|
+
# Behind the hood, these two methods relies on the {::drawPath} method that
|
|
47
|
+
# actually draw the shape in the canvas context. You can just override this
|
|
48
|
+
# method when you need to draw the shape in a specific way.
|
|
49
|
+
class agt.geom.Geometry
|
|
50
|
+
|
|
51
|
+
### Public ###
|
|
52
|
+
|
|
53
|
+
# Abstract: Returns an {Array} of [Points]{agt.geom.Point}
|
|
54
|
+
# constituting the geometry.
|
|
55
|
+
#
|
|
56
|
+
# Closed geometry should always returns an array where the start and end
|
|
57
|
+
# values are equals.
|
|
58
|
+
#
|
|
59
|
+
# <script>drawGeometryPoints(exampleKey, 'points')</script>
|
|
60
|
+
#
|
|
61
|
+
# Returns an {Array} of [Points]{agt.geom.Point}.
|
|
62
|
+
points: ->
|
|
63
|
+
|
|
64
|
+
# Abstract: Returns a {Boolean} of whether the points forms
|
|
65
|
+
# a closed geometry.
|
|
66
|
+
#
|
|
67
|
+
# Returns a {Boolean}.
|
|
68
|
+
closedGeometry: -> false
|
|
69
|
+
|
|
70
|
+
# Internal: The `pointsBounds` private utility is meant to provide
|
|
71
|
+
# the default bounds computation for a geometry, subclasses should
|
|
72
|
+
# implements their own bounds methods if a faster implementation exist.
|
|
73
|
+
pointsBounds = (points, mode, axis) ->
|
|
74
|
+
Math[mode].apply Math, points.map (pt) -> pt[axis]
|
|
75
|
+
|
|
76
|
+
# Returns the top-most coordinate of the geometry shape.
|
|
77
|
+
#
|
|
78
|
+
# <script>drawGeometryBound(exampleKey, 'top')</script>
|
|
79
|
+
#
|
|
80
|
+
# Returns a {Number}.
|
|
81
|
+
top: -> pointsBounds @points(), 'min', 'y'
|
|
82
|
+
|
|
83
|
+
# Returns the bottom-most coordinate of the geometry shape.
|
|
84
|
+
#
|
|
85
|
+
# <script>drawGeometryBound(exampleKey, 'bottom')</script>
|
|
86
|
+
#
|
|
87
|
+
# Returns a {Number}.
|
|
88
|
+
bottom: -> pointsBounds @points(), 'max', 'y'
|
|
89
|
+
|
|
90
|
+
# Returns the left-most coordinate of the geometry shape.
|
|
91
|
+
#
|
|
92
|
+
# <script>drawGeometryBound(exampleKey, 'left')</script>
|
|
93
|
+
#
|
|
94
|
+
# Returns a {Number}.
|
|
95
|
+
left: -> pointsBounds @points(), 'min', 'x'
|
|
96
|
+
|
|
97
|
+
# Returns the right-most coordinate of the geometry shape.
|
|
98
|
+
#
|
|
99
|
+
# <script>drawGeometryBound(exampleKey, 'right')</script>
|
|
100
|
+
#
|
|
101
|
+
# Returns a {Number}.
|
|
102
|
+
right: -> pointsBounds @points(), 'max', 'x'
|
|
103
|
+
|
|
104
|
+
# Returns an {Object} containing the bounds of the object.
|
|
105
|
+
#
|
|
106
|
+
# <script>drawGeometry(exampleKey, {bounds: true})</script>
|
|
107
|
+
#
|
|
108
|
+
# Returns an {Object} with the following properties:
|
|
109
|
+
# :top - The {Number} for the shape upper bound.
|
|
110
|
+
# :bottom - The {Number} for the shape lower bound.
|
|
111
|
+
# :left - The {Number} for the shape left bound.
|
|
112
|
+
# :right - The {Number} for the shape right bound.
|
|
113
|
+
bounds: ->
|
|
114
|
+
top: @top()
|
|
115
|
+
left: @left()
|
|
116
|
+
right: @right()
|
|
117
|
+
bottom: @bottom()
|
|
118
|
+
|
|
119
|
+
# Returns a [Rectangle]{agt.geom.Rectangle} corresponding to the bounds
|
|
120
|
+
# of the current geometry.
|
|
121
|
+
#
|
|
122
|
+
# <script>drawGeometry(exampleKey, {bounds: true})</script>
|
|
123
|
+
#
|
|
124
|
+
# Returns a [Rectangle]{agt.geom.Rectangle}.
|
|
125
|
+
boundingBox: ->
|
|
126
|
+
new agt.geom.Rectangle(
|
|
127
|
+
@left(),
|
|
128
|
+
@top(),
|
|
129
|
+
@right() - @left(),
|
|
130
|
+
@bottom() - @top()
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Paints the geometry shape in the specified canvas `context` as a line.
|
|
134
|
+
#
|
|
135
|
+
# <script>drawGeometry(exampleKey, {stroke: 'highlight'})</script>
|
|
136
|
+
#
|
|
137
|
+
# context - The canvas context into which draw the geometry.
|
|
138
|
+
# color - The {String} color of the stroke.
|
|
139
|
+
stroke: (context, color=agt.COLORS.STROKE) ->
|
|
140
|
+
return unless context?
|
|
141
|
+
|
|
142
|
+
context.strokeStyle = color
|
|
143
|
+
@drawPath context
|
|
144
|
+
context.stroke()
|
|
145
|
+
|
|
146
|
+
# Paints the geometry shape in the specified canvas `context`
|
|
147
|
+
# as a plain shape.
|
|
148
|
+
#
|
|
149
|
+
# <script>drawGeometry(exampleKey, {fill: 'highlight'})</script>
|
|
150
|
+
#
|
|
151
|
+
# context - The canvas context into which draw the geometry.
|
|
152
|
+
# color - The {String} color of the fill.
|
|
153
|
+
fill: (context, color=agt.COLORS.FILL) ->
|
|
154
|
+
return unless context?
|
|
155
|
+
|
|
156
|
+
context.fillStyle = color
|
|
157
|
+
@drawPath context
|
|
158
|
+
context.fill()
|
|
159
|
+
|
|
160
|
+
# Draws the current geometry into the passed-in canvas `context`.
|
|
161
|
+
# That method only implements creating the geometry path using
|
|
162
|
+
# canvas methods.
|
|
163
|
+
#
|
|
164
|
+
# context - The canvas context into which draw the geometry.
|
|
165
|
+
drawPath: (context) ->
|
|
166
|
+
points = @points()
|
|
167
|
+
start = points.shift()
|
|
168
|
+
context.beginPath()
|
|
169
|
+
context.moveTo(start.x,start.y)
|
|
170
|
+
context.lineTo(p.x,p.y) for p in points
|
|
171
|
+
context.closePath()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
namespace('agt.geom')
|
|
2
|
+
# Public: The `Intersections` mixin provides methods to geometries that
|
|
3
|
+
# allow to compute intersections with other geometries.
|
|
4
|
+
#
|
|
5
|
+
# The default intersections computation implies testing all segments formed
|
|
6
|
+
# by the `points` array returned by both geometries.
|
|
7
|
+
#
|
|
8
|
+
# The mixin also provides methods to register faster routines when one
|
|
9
|
+
# is available for a given kind of geometry. For instance, when computing
|
|
10
|
+
# intersections between a [Circle]{agt.geom.Circle} and another geometry
|
|
11
|
+
# the {agt.geom.Circle::eachIntersections} method wil be used instead.
|
|
12
|
+
# <script>window.exampleKey = 'geometry'</script>
|
|
13
|
+
class agt.geom.Intersections
|
|
14
|
+
@iterators: {}
|
|
15
|
+
|
|
16
|
+
### Public ###
|
|
17
|
+
|
|
18
|
+
# Returns `true` if the passed-in geometry intersects the current geometry.
|
|
19
|
+
#
|
|
20
|
+
# <script>drawIntersectsGeometry(exampleKey)</script>
|
|
21
|
+
#
|
|
22
|
+
# geometry - The [Geometry]{agt.geom.Geometry} to test.
|
|
23
|
+
#
|
|
24
|
+
# Returns a {Boolean} of whether the two geometries intersects.
|
|
25
|
+
intersects: (geometry) ->
|
|
26
|
+
return false if geometry.bounds? and not @boundsCollide geometry
|
|
27
|
+
output = false
|
|
28
|
+
iterator = @intersectionsIterator this, geometry
|
|
29
|
+
iterator.call this, this, geometry, -> output = true
|
|
30
|
+
|
|
31
|
+
output
|
|
32
|
+
|
|
33
|
+
# Returns an {Array} of all the intersections [Points]{agt.geom.Point} with
|
|
34
|
+
# the passed-in geometry. If there's no intersections the function returns
|
|
35
|
+
# `null`.
|
|
36
|
+
#
|
|
37
|
+
# <script>drawShapeIntersections(exampleKey, exampleKey)</script>
|
|
38
|
+
#
|
|
39
|
+
# geometry - The [Geometry]{agt.geom.Geometry} to test.
|
|
40
|
+
#
|
|
41
|
+
# Returns an {Array} of [Points]{agt.geom.Point}.
|
|
42
|
+
intersections: (geometry) ->
|
|
43
|
+
return null if geometry.bounds? and not @boundsCollide geometry
|
|
44
|
+
output = []
|
|
45
|
+
iterator = @intersectionsIterator this, geometry
|
|
46
|
+
iterator.call this, this, geometry, (intersection) ->
|
|
47
|
+
output.push intersection
|
|
48
|
+
false
|
|
49
|
+
|
|
50
|
+
if output.length > 0 then output else null
|
|
51
|
+
|
|
52
|
+
# Returns `true` if the bounds of the passed-in geometry intersects
|
|
53
|
+
# the current geometry bounds.
|
|
54
|
+
#
|
|
55
|
+
# geometry - The [Geometry]{agt.geom.Geometry} to test.
|
|
56
|
+
#
|
|
57
|
+
# Returns a {Boolean}.
|
|
58
|
+
boundsCollide: (geometry) ->
|
|
59
|
+
bounds1 = @bounds()
|
|
60
|
+
bounds2 = geometry.bounds()
|
|
61
|
+
|
|
62
|
+
not (
|
|
63
|
+
bounds1.top > bounds2.bottom or
|
|
64
|
+
bounds1.left > bounds2.right or
|
|
65
|
+
bounds1.bottom < bounds2.top or
|
|
66
|
+
bounds1.right < bounds2.left
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
### Internal ###
|
|
70
|
+
|
|
71
|
+
intersectionsIterator: (geom1, geom2) ->
|
|
72
|
+
c1 = if geom1.classname then geom1.classname() else ''
|
|
73
|
+
c2 = if geom2.classname then geom2.classname() else ''
|
|
74
|
+
iterator = null
|
|
75
|
+
iterator = Intersections.iterators[c1 + c2]
|
|
76
|
+
iterator ||= Intersections.iterators[c1]
|
|
77
|
+
iterator ||= Intersections.iterators[c2]
|
|
78
|
+
iterator || @eachIntersections
|
|
79
|
+
|
|
80
|
+
eachIntersections: (geom1, geom2, block, providesDataInCallback=false) ->
|
|
81
|
+
points1 = geom1.points()
|
|
82
|
+
points2 = geom2.points()
|
|
83
|
+
length1 = points1.length
|
|
84
|
+
length2 = points2.length
|
|
85
|
+
lastIntersection = null
|
|
86
|
+
|
|
87
|
+
for i in [0..length1-2]
|
|
88
|
+
sv1 = points1[i]
|
|
89
|
+
ev1 = points1[i+1]
|
|
90
|
+
dif1x = ev1.x - sv1.x
|
|
91
|
+
dif1y = ev1.y - sv1.y
|
|
92
|
+
dif1l = dif1x * dif1x + dif1y * dif1y
|
|
93
|
+
|
|
94
|
+
for j in [0..length2-2]
|
|
95
|
+
sv2 = points2[j]
|
|
96
|
+
ev2 = points2[j+1]
|
|
97
|
+
dif2x = ev2.x - sv2.x
|
|
98
|
+
dif2y = ev2.y - sv2.y
|
|
99
|
+
dif2l = dif2x * dif2x + dif2y * dif2y
|
|
100
|
+
|
|
101
|
+
cross = @perCrossing sv1, {x:dif1x,y:dif1y}, sv2, {x:dif2x,y:dif2y}
|
|
102
|
+
d1x = cross.x - ev1.x
|
|
103
|
+
d1y = cross.y - ev1.y
|
|
104
|
+
d2x = cross.x - sv1.x
|
|
105
|
+
d2y = cross.y - sv1.y
|
|
106
|
+
d3x = cross.x - ev2.x
|
|
107
|
+
d3y = cross.y - ev2.y
|
|
108
|
+
d4x = cross.x - sv2.x
|
|
109
|
+
d4y = cross.y - sv2.y
|
|
110
|
+
|
|
111
|
+
d1l = d1x * d1x + d1y * d1y
|
|
112
|
+
d2l = d2x * d2x + d2y * d2y
|
|
113
|
+
d3l = d3x * d3x + d3y * d3y
|
|
114
|
+
d4l = d4x * d4x + d4y * d4y
|
|
115
|
+
|
|
116
|
+
if d1l <= dif1l and
|
|
117
|
+
d2l <= dif1l and
|
|
118
|
+
d3l <= dif2l and
|
|
119
|
+
d4l <= dif2l
|
|
120
|
+
|
|
121
|
+
if cross.equals lastIntersection
|
|
122
|
+
lastIntersection = cross
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
if providesDataInCallback
|
|
126
|
+
context =
|
|
127
|
+
segment1: new agt.geom.Point dif1x, dif1y
|
|
128
|
+
segmentIndex1: i
|
|
129
|
+
segmentStart1: sv1
|
|
130
|
+
segmentEnd1: ev1
|
|
131
|
+
segment2: new agt.geom.Point dif2x, dif2y
|
|
132
|
+
segmentIndex2: j
|
|
133
|
+
segmentStart2: sv2
|
|
134
|
+
segmentEnd2: ev2
|
|
135
|
+
|
|
136
|
+
return if block.call this, cross, context
|
|
137
|
+
lastIntersection = cross
|
|
138
|
+
|
|
139
|
+
perCrossing: (start1, dir1, start2, dir2) ->
|
|
140
|
+
v3bx = start2.x - start1.x
|
|
141
|
+
v3by = start2.y - start1.y
|
|
142
|
+
perP1 = v3bx*dir2.y - v3by*dir2.x
|
|
143
|
+
perP2 = dir1.x*dir2.y - dir1.y*dir2.x
|
|
144
|
+
|
|
145
|
+
t = perP1 / perP2
|
|
146
|
+
|
|
147
|
+
cx = start1.x + dir1.x*t
|
|
148
|
+
cy = start1.y + dir1.y*t
|
|
149
|
+
|
|
150
|
+
new agt.geom.Point cx, cy
|