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,90 @@
1
+
2
+ namespace('agt.geom')
3
+
4
+ # Public:
5
+ class agt.geom.Spiral
6
+ properties = ['radius1', 'radius2', 'twirl', 'x', 'y', 'rotation','segments']
7
+
8
+ @include agt.mixins.Equatable.apply(null, properties)
9
+ @include agt.mixins.Formattable.apply(null, ['Spiral'].concat properties)
10
+ @include agt.mixins.Parameterizable('spiralFrom', {
11
+ radius1: 1
12
+ radius2: 1
13
+ twirl: 1
14
+ x: 0
15
+ y: 0
16
+ rotation: 0
17
+ segments: 36
18
+ })
19
+ @include agt.mixins.Sourcable.apply(null, ['agt.geom.Spiral'].concat properties)
20
+ @include agt.mixins.Cloneable()
21
+ @include agt.mixins.Memoizable
22
+ @include agt.geom.Geometry
23
+ @include agt.geom.Path
24
+ @include agt.geom.Intersections
25
+
26
+ ### Public ###
27
+
28
+ constructor: (r1, r2, twirl, x, y, rot, segments) ->
29
+ {
30
+ @radius1
31
+ @radius2
32
+ @twirl
33
+ @x
34
+ @y
35
+ @rotation
36
+ @segments
37
+ } = @spiralFrom r1, r2, twirl, x, y, rot, segments
38
+
39
+ center: -> new agt.geom.Point @x, @y
40
+
41
+ ellipsis: ->
42
+ return @memoFor 'ellipsis' if @memoized 'ellipsis'
43
+ @memoize 'ellipsis', new agt.geom.Ellipsis this
44
+
45
+ translate: (x,y) ->
46
+ {x,y} = agt.geom.Point.pointFrom x, y
47
+ @x += x
48
+ @y += y
49
+ this
50
+
51
+ rotate: (rotation) ->
52
+ @rotation += rotation
53
+ this
54
+
55
+ scale: (scale) ->
56
+ @radius1 *= scale
57
+ @radius2 *= scale
58
+ this
59
+
60
+ points: ->
61
+ return @memoFor('points').concat() if @memoized 'points'
62
+ points = []
63
+ center = @center()
64
+ ellipsis = @ellipsis()
65
+
66
+ for i in [0..@segments]
67
+ p = i / @segments
68
+ points.push @pathPointAt p
69
+
70
+ @memoize 'points', points
71
+
72
+ pathPointAt: (pos, posBasedOnLength=true) ->
73
+ center = @center()
74
+ ellipsis = @ellipsis()
75
+ PI2 = Math.PI * 2
76
+ angle = @rotation + pos * PI2 * @twirl % PI2
77
+ pt = ellipsis.pointAtAngle(angle)?.subtract(center).scale(pos)
78
+ center.add pt
79
+
80
+ fill: ->
81
+
82
+ drawPath: (context) ->
83
+ points = @points()
84
+ start = points.shift()
85
+ context.beginPath()
86
+ context.moveTo(start.x,start.y)
87
+ context.lineTo(p.x,p.y) for p in points
88
+
89
+ memoizationKey = ->
90
+ "#{@radius1};#{@radius2};#{@twirl};#{@x};#{@y};#{@rotation};#{@segments}"
@@ -0,0 +1,43 @@
1
+
2
+ namespace('agt.geom')
3
+
4
+ # Public:
5
+ class agt.geom.TransformationProxy
6
+ ### Public ###
7
+
8
+ @defineProxy: (key, type) ->
9
+ switch type
10
+ when 'PointList'
11
+ @::[key] = ->
12
+ points = @geometry[key].apply(@geometry, arguments)
13
+ if @matrix?
14
+ points.map (pt) => @matrix.transformPoint pt
15
+ else points
16
+
17
+ when 'Point'
18
+ @::[key] = ->
19
+ point = @geometry[key].apply(@geometry, arguments)
20
+ if @matrix? then @matrix.transformPoint point else point
21
+
22
+ when 'Angle'
23
+ @::[key] = ->
24
+ angle = @geometry[key].apply(@geometry, arguments)
25
+ if @matrix?
26
+ vec = new agt.geom.Point Math.cos(angle),
27
+ Math.sin(angle)
28
+ @matrix.transformPoint(vec).angle()
29
+ else angle
30
+
31
+
32
+ constructor: (@geometry, @matrix) ->
33
+ @proxiedMethods = @detectProxyableMethods @geometry
34
+
35
+ proxied: -> k for k,v of @proxiedMethods
36
+
37
+ detectProxyableMethods: (geometry) ->
38
+ proxiedMethods = {}
39
+ for k,v of geometry.constructor.prototype
40
+ if v.proxyable
41
+ proxiedMethods[k] = v.proxyable
42
+ TransformationProxy.defineProxy k, v.proxyable
43
+ proxiedMethods
@@ -0,0 +1,442 @@
1
+
2
+ namespace('agt.geom')
3
+
4
+ # Public: A `Triangle` is only defined using three [Points]{agt.geom.Point}.
5
+ # It is the simplest geometry you can find in the `geom` package, every other
6
+ # surface geometries can be reduced to a set of triangles.
7
+ #
8
+ # <script>window.exampleKey = 'triangle'</script>
9
+ # <script>drawGeometry(exampleKey, {highlight: true})</script>
10
+ #
11
+ # ### Included Mixins
12
+ #
13
+ # - {agt.geom.Geometry}
14
+ # - {agt.geom.Intersections}
15
+ # - {agt.geom.Path}
16
+ # - {agt.geom.Surface}
17
+ # - [agt.mixins.Aliasable](../../../classes/agt/mixins/aliasable.coffee.html)
18
+ # - [agt.mixins.Cloneable](../../../files/mixins/cloneable.coffee.html)
19
+ # - [agt.mixins.Equatable](../../../files/mixins/equatable.coffee.html)
20
+ # - [agt.mixins.Formattable](../../../files/mixins/formattable.coffee.html)
21
+ # - [agt.mixins.Memoizable](../../../files/mixins/memoizable.coffee.html)
22
+ # - [agt.mixins.Sourcable](../../../files/mixins/sourcable.coffee.html)
23
+ class agt.geom.Triangle
24
+ @extend agt.mixins.Aliasable
25
+
26
+ @include agt.mixins.Equatable('a','b','c')
27
+ @include agt.mixins.Formattable('Triangle','a','b','c')
28
+ @include agt.mixins.Sourcable('agt.geom.Triangle','a','b','c')
29
+ @include agt.mixins.Cloneable()
30
+ @include agt.mixins.Memoizable
31
+ @include agt.geom.Geometry
32
+ @include agt.geom.Surface
33
+ @include agt.geom.Path
34
+ @include agt.geom.Intersections
35
+
36
+ ### Public ###
37
+
38
+ # Returns a triangle-like {Object} using the given arguments.
39
+ #
40
+ # a - Either a [Point]{agt.geom.Point} or a triangle-like {Object}.
41
+ # b - A [Point]{agt.geom.Point} when the first parameter is also a point.
42
+ # c - A [Point]{agt.geom.Point} when the first parameter is also a point.
43
+ #
44
+ # Returns an {Object} with the following properties:
45
+ # :a - A [Point]{agt.geom.Point} for the first vertex of the triangle.
46
+ # :b - A [Point]{agt.geom.Point} for the second vertex of the triangle.
47
+ # :c - A [Point]{agt.geom.Point} for the mast vertex of the triangle.
48
+ @triangleFrom: (a, b, c) ->
49
+ {a,b,c} = a if a? and typeof a is 'object' and not agt.geom.Point.isPoint a
50
+
51
+ @invalidPoint 'a', a unless agt.geom.Point.isPoint a
52
+ @invalidPoint 'b', b unless agt.geom.Point.isPoint b
53
+ @invalidPoint 'c', c unless agt.geom.Point.isPoint c
54
+
55
+ {
56
+ a: new agt.geom.Point(a)
57
+ b: new agt.geom.Point(b)
58
+ c: new agt.geom.Point(c)
59
+ }
60
+
61
+ # Creates a new `Triangle` instance.
62
+ #
63
+ # a - Either a [Point]{agt.geom.Point} or a triangle-like {Object}.
64
+ # b - A [Point]{agt.geom.Point} when the first parameter is also a point.
65
+ # c - A [Point]{agt.geom.Point} when the first parameter is also a point.
66
+ constructor: (a, b, c) ->
67
+ {@a,@b,@c} = @triangleFrom a, b, c
68
+
69
+ # Returns the center of the triangle.
70
+ #
71
+ # <script>drawGeometry(exampleKey, {center: true})</script>
72
+ #
73
+ # Returns a [Point]{agt.geom.Point}.
74
+ center: -> new agt.geom.Point (@a.x + @b.x + @c.x) / 3,
75
+ (@a.y + @b.y + @c.y) / 3
76
+
77
+ # Returns the center of the `ab` edge of the triangle.
78
+ #
79
+ # <script>drawGeometryPoints(exampleKey, 'abCenter')</script>
80
+ #
81
+ # Returns a [Point]{agt.geom.Point}.
82
+ abCenter: -> @a.add @ab().scale(0.5)
83
+
84
+ # Returns the center of the `ac` edge of the triangle.
85
+ #
86
+ # <script>drawGeometryPoints(exampleKey, 'acCenter')</script>
87
+ #
88
+ # Returns a [Point]{agt.geom.Point}.
89
+ acCenter: -> @a.add @ac().scale(0.5)
90
+
91
+ # Returns the center of the `bc` edge of the triangle.
92
+ #
93
+ # <script>drawGeometryPoints(exampleKey, 'bcCenter')</script>
94
+ #
95
+ # Returns a [Point]{agt.geom.Point}.
96
+ bcCenter: -> @b.add @bc().scale(0.5)
97
+
98
+ # Returns an {Array} with the triangle's edges vectors.
99
+ #
100
+ # Returns an {Array}.
101
+ edges: -> [@ab(), @bc(), @ca()]
102
+
103
+ # Returns the triangle's `ab` edge vector.
104
+ #
105
+ # <script>drawGeometryEdge(exampleKey, 'a', 'ab')</script>
106
+ #
107
+ # Returns a [Point]{agt.geom.Point}.
108
+ ab: -> @b.subtract @a
109
+
110
+ # Returns the triangle's `ab` edge vector.
111
+ #
112
+ # <script>drawGeometryEdge(exampleKey, 'a', 'ac')</script>
113
+ #
114
+ # Returns a [Point]{agt.geom.Point}.
115
+ ac: -> @c.subtract @a
116
+
117
+ # Returns the triangle's `ba` edge vector.
118
+ #
119
+ # <script>drawGeometryEdge(exampleKey, 'b', 'ba')</script>
120
+ #
121
+ # Returns a [Point]{agt.geom.Point}.
122
+ ba: -> @a.subtract @b
123
+
124
+ # Returns the triangle's `bc` edge vector.
125
+ #
126
+ # <script>drawGeometryEdge(exampleKey, 'b', 'bc')</script>
127
+ #
128
+ # Returns a [Point]{agt.geom.Point}.
129
+ bc: -> @c.subtract @b
130
+
131
+ # Returns the triangle's `ca` edge vector.
132
+ #
133
+ # <script>drawGeometryEdge(exampleKey, 'c', 'ca')</script>
134
+ #
135
+ # Returns a [Point]{agt.geom.Point}.
136
+ ca: -> @a.subtract @c
137
+
138
+ # Returns the triangle's `cb` edge vector.
139
+ #
140
+ # <script>drawGeometryEdge(exampleKey, 'c', 'cb')</script>
141
+ #
142
+ # Returns a [Point]{agt.geom.Point}.
143
+ cb: -> @b.subtract @c
144
+
145
+ # Returns the angle formed by the {::ba} and {::bc} vectors.
146
+ #
147
+ # Returns a {Number}.
148
+ abc: -> @ba().angleWith @bc()
149
+
150
+ # Returns the angle formed by the {::ab} and {::ac} vectors.
151
+ #
152
+ # Returns a {Number}.
153
+ bac: -> @ab().angleWith @ac()
154
+
155
+ # Returns the angle formed by the {::ca} and {::cb} vectors.
156
+ #
157
+ # Returns a {Number}.
158
+ acb: -> @ca().angleWith @cb()
159
+
160
+ # Returns the top-most coordinate of the triangle shape.
161
+ #
162
+ # <script>drawGeometryBound(exampleKey, 'top')</script>
163
+ #
164
+ # Returns a {Number}.
165
+ top: -> Math.min @a.y, @b.y, @c.y
166
+
167
+ # Returns the bottom-most coordinate of the triangle shape.
168
+ #
169
+ # <script>drawGeometryBound(exampleKey, 'bottom')</script>
170
+ #
171
+ # Returns a {Number}.
172
+ bottom: -> Math.max @a.y, @b.y, @c.y
173
+
174
+ # Returns the left-most coordinate of the triangle shape.
175
+ #
176
+ # <script>drawGeometryBound(exampleKey, 'left')</script>
177
+ #
178
+ # Returns a {Number}.
179
+ left: -> Math.min @a.x, @b.x, @c.x
180
+
181
+ # Returns the right-most coordinate of the triangle shape.
182
+ #
183
+ # <script>drawGeometryBound(exampleKey, 'right')</script>
184
+ #
185
+ # Returns a {Number}.
186
+ right: -> Math.max @a.x, @b.x, @c.x
187
+
188
+ # Returns `true` if the triangle's edge have the same length.
189
+ #
190
+ # Returns a {Boolean}.
191
+ equilateral: ->
192
+ Math.deltaBelowRatio(@ab().length(), @bc().length()) and
193
+ Math.deltaBelowRatio(@ab().length(), @ac().length())
194
+
195
+ # Returns `true` if two edges of the triangle have the same length.
196
+ #
197
+ # Returns a {Boolean}.
198
+ isosceles: ->
199
+ Math.deltaBelowRatio(@ab().length(), @bc().length()) or
200
+ Math.deltaBelowRatio(@ab().length(), @ac().length()) or
201
+ Math.deltaBelowRatio(@bc().length(), @ac().length())
202
+
203
+ # Returns `true` if one angle of the triangle has a value of `Math.PI / 2`
204
+ # radians (90 degrees).
205
+ #
206
+ # Returns a {Boolean}.
207
+ rectangle: ->
208
+ sqr = Math.PI / 2
209
+ Math.deltaBelowRatio(Math.abs(@abc()), sqr) or
210
+ Math.deltaBelowRatio(Math.abs(@bac()), sqr) or
211
+ Math.deltaBelowRatio(Math.abs(@acb()), sqr)
212
+
213
+ # Adds the passed-in [Point]{agt.geom.Point} to the position
214
+ # of this triangle.
215
+ #
216
+ # <script>drawTransform(exampleKey, {type: 'translate', args: [50, 0], width: 150})</script>
217
+ #
218
+ # x - A {Number} for the x coordinate or a point-like {Object}.
219
+ # y - A {Number} for the y coordinate if the first argument
220
+ # was also a number.
221
+ #
222
+ # Returns this [Triangle]{agt.geom.Triangle}.
223
+ translate: (x,y) ->
224
+ pt = agt.geom.Point.pointFrom x,y
225
+ @a.x += pt.x; @a.y += pt.y
226
+ @b.x += pt.x; @b.y += pt.y
227
+ @c.x += pt.x; @c.y += pt.y
228
+ this
229
+
230
+ # Adds the passed-in rotation to the current triangle rotation.
231
+ #
232
+ # <script>drawTransform(exampleKey, {type: 'rotate', args: [Math.PI/ 3]})</script>
233
+ #
234
+ # rotation - The rotation {Number}.
235
+ #
236
+ # Returns this [Triangle]{agt.geom.Triangle}.
237
+ rotate: (rotation) ->
238
+ center = @center()
239
+ @a = @a.rotateAround center, rotation
240
+ @b = @b.rotateAround center, rotation
241
+ @c = @c.rotateAround center, rotation
242
+ this
243
+
244
+ @alias 'rotate', 'rotateAroundCenter'
245
+
246
+ # Scales the triangle around its center.
247
+ #
248
+ # <script>drawTransform(exampleKey, {type: 'scale', args: [0.6]})</script>
249
+ #
250
+ # scale - The scale {Number} to apply to the triangle.
251
+ #
252
+ # Returns this [Triangle]{agt.geom.Triangle}.
253
+ scale: (scale) ->
254
+ center = @center()
255
+ @a = center.add @a.subtract(center).scale(scale)
256
+ @b = center.add @b.subtract(center).scale(scale)
257
+ @c = center.add @c.subtract(center).scale(scale)
258
+ this
259
+
260
+ @alias 'scale', 'scaleAroundCenter'
261
+
262
+ # Always returns `true`.
263
+ #
264
+ # Returns a {Boolean}.
265
+ closedGeometry: -> true
266
+
267
+ # Returns the triangle points.
268
+ #
269
+ # <script>drawGeometryPoints(exampleKey, 'points')</script>
270
+ #
271
+ # Returns an {Array}.
272
+ points: -> [@a.clone(), @b.clone(), @c.clone(), @a.clone()]
273
+
274
+ # Returns the [Point]{agt.geom.Point} on the perimeter of the triangle
275
+ # at the given `angle`.
276
+ #
277
+ # angle - The angle {Number}.
278
+ #
279
+ # <script>drawGeometry(exampleKey, {angle: true})</script>
280
+ #
281
+ # Returns a [Point]{agt.geom.Point}.
282
+ pointAtAngle: (angle) ->
283
+ center = @center()
284
+ vec = center.add Math.cos(angle)*10000,
285
+ Math.sin(angle)*10000
286
+ @intersections(points: -> [center, vec])?[0]
287
+
288
+ # Returns the surface {Number} of this triangle.
289
+ #
290
+ # Returns a {Number}.
291
+ acreage: ->
292
+ return @memoFor 'acreage' if @memoized 'acreage'
293
+ @memoize 'acreage', @ab().length() *
294
+ @bc().length() *
295
+ Math.abs(Math.sin(@abc())) / 2
296
+
297
+ # Returns `true` when the given point is contained in the triangle.
298
+ #
299
+ # In the example below all the green points on the screen represents
300
+ # coordinates that are contained in the triangle.
301
+ #
302
+ # <script>drawGeometry(exampleKey, {contains: true})</script>
303
+ #
304
+ # x - A {Number} for the x coordinate or a point-like {Object}.
305
+ # y - A {Number} for the y coordinate if the first argument
306
+ # was also a number.
307
+ #
308
+ # Returns a {Boolean}.
309
+ contains: (x, y) ->
310
+ p = new agt.geom.Point x, y
311
+
312
+ v0 = @ac()
313
+ v1 = @ab()
314
+ v2 = p.subtract(@a)
315
+
316
+ dot00 = v0.dot v0
317
+ dot01 = v0.dot v1
318
+ dot02 = v0.dot v2
319
+ dot11 = v1.dot v1
320
+ dot12 = v1.dot v2
321
+
322
+ invDenom = 1 / (dot00 * dot11 - dot01 * dot01)
323
+ u = (dot11 * dot02 - dot01 * dot12) * invDenom
324
+ v = (dot00 * dot12 - dot01 * dot02) * invDenom
325
+
326
+ u > 0 and v > 0 and u + v < 1
327
+
328
+ # Returns a randomly generated point within the triangle perimeter.
329
+ #
330
+ # <script>drawGeometry(exampleKey, {surface: true})</script>
331
+ #
332
+ # random - An optional [Random]{agt.random.Random} instance to use instead
333
+ # of the default `Math` random method.
334
+ #
335
+ # Returns a [Point]{agt.geom.Point}.
336
+ randomPointInSurface: (random) ->
337
+ unless random?
338
+ random = new agt.random.Random new agt.random.MathRandom
339
+
340
+ a1 = random.get()
341
+ a2 = random.get()
342
+ p = @a.add(@ab().scale(a1))
343
+ .add(@ca().scale(a2 * -1))
344
+
345
+ if @contains p
346
+ p
347
+ else
348
+ p.add @bcCenter().subtract(p).scale(2)
349
+
350
+ # Returns the length {Number} of the triangle perimeter.
351
+ #
352
+ # Returns a {Number}.
353
+ length: -> @ab().length() + @bc().length() + @ca().length()
354
+
355
+ # Returns a [Point]{agt.geom.Point} on the triangle perimeter using
356
+ # a {Number} between `0` and `1`.
357
+ #
358
+ # <script>drawGeometry(exampleKey, {paths: [0, 1/3, 2/3]})</script>
359
+ #
360
+ # n - A {Number} between `0` and `1`a {Number} between `0` and `1`.
361
+ # pathBasedOnLength - A {Boolean} of whether the position on the path
362
+ # consider the length of the path segments or not.
363
+ # When true, each segment will only weight as much
364
+ # as their own length.
365
+ # When false, every segment have the same weight,
366
+ # resulting in a difference in speed when animating
367
+ # an object along a path.
368
+ #
369
+ # Returns a [Point]{agt.geom.Point}.
370
+ pathPointAt: (n, pathBasedOnLength=true) ->
371
+ [l1, l2] = @pathSteps pathBasedOnLength
372
+
373
+ if n < l1
374
+ @a.add @ab().scale Math.map n, 0, l1, 0, 1
375
+ else if n < l2
376
+ @b.add @bc().scale Math.map n, l1, l2, 0, 1
377
+ else
378
+ @c.add @ca().scale Math.map n, l2, 1, 0, 1
379
+
380
+ # Returns the orientation of the path at the given position.
381
+ #
382
+ # <script>drawGeometry(exampleKey, {paths: [0, 1/3, 2/3]})</script>
383
+ #
384
+ # n - A {Number} between `0` and `1`a {Number} between `0` and `1`.
385
+ # pathBasedOnLength - A {Boolean} of whether the position on the path
386
+ # consider the length of the path segments or not.
387
+ # When true, each segment will only weight as much
388
+ # as their own length.
389
+ # When false, every segment have the same weight,
390
+ # resulting in a difference in speed when animating
391
+ # an object along a path.
392
+ #
393
+ # Returns a [Point]{agt.geom.Point}.
394
+ pathOrientationAt: (n, pathBasedOnLength=true) ->
395
+ [l1, l2] = @pathSteps pathBasedOnLength
396
+
397
+ if n < l1
398
+ @ab().angle()
399
+ else if n < l2
400
+ @bc().angle()
401
+ else
402
+ @ca().angle()
403
+
404
+ # Internal: Calculates the proportions of each step of the triangle path.
405
+ #
406
+ # Returns an {Array}.
407
+ pathSteps: (pathBasedOnLength) ->
408
+ if pathBasedOnLength
409
+ l = @length()
410
+ l1 = @ab().length() / l
411
+ l2 = l1 + @bc().length() / l
412
+ else
413
+ l1 = 1 / 3
414
+ l2 = 2 / 3
415
+
416
+ [l1,l2]
417
+
418
+ # {Delegates to: agt.geom.Geometry.drawPath}
419
+ drawPath: (context) ->
420
+ context.beginPath()
421
+ context.moveTo @a.x, @a.y
422
+ context.lineTo @b.x, @b.y
423
+ context.lineTo @c.x, @c.y
424
+ context.lineTo @a.x, @a.y
425
+ context.closePath()
426
+
427
+ # Generates the memoization key for this instance's state.
428
+ #
429
+ # For a circle, a memoized value will be invalidated whenever one of the
430
+ # following properties changes:
431
+ # - a
432
+ # - b
433
+ # - c
434
+ #
435
+ # Returns a {String}.
436
+ memoizationKey: -> "#{@a.x};#{@a.y};#{@b.x};#{@b.y};#{@c.x};#{@c.y}"
437
+
438
+ triangleFrom: Triangle.triangleFrom
439
+
440
+ ### Internal ###
441
+
442
+ invalidPoint: (k,v) -> throw new Error "Invalid point #{v} for vertex #{k}"