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