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