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