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
@@ -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
|