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,145 @@
1
+ namespace('agt.geom')
2
+ # Public: Every geometry should have the properties of a path. For closed
3
+ # geometries, the path starts and ends at the same point of the geometry
4
+ # shape. This point can vary from one geometry to another.
5
+ #
6
+ # Path properties includes:
7
+ #
8
+ # - **Length** - A path has a {::length} expressed in pixels.
9
+ # - **Positionning** - A path offers to get a [Point]{agt.geom.Point}
10
+ # along the geometry shape based on a {Number} in the range `0..1`.
11
+ # With this point it's also possible to retrieve the orientation
12
+ # of the path as well as the tangent vector at that point.
13
+ #
14
+ # ```coffeescript
15
+ # class DummyPath
16
+ # @include agt.geom.Path
17
+ #
18
+ # # Your path implementation
19
+ # ```
20
+ #
21
+ # The default implementation include length and path computations based
22
+ # on the distances between the [points]{agt.geom.Geometry::points}
23
+ # of the geometry.
24
+ #
25
+ # If a more accurate or faster implementation exists the path geometries
26
+ # generally overwrites the default methods with their implementations (see
27
+ # the [Circle]{agt.geom.Circle} or [Rectangle]{agt.geom.Rectangle} classes
28
+ # for real word examples).
29
+ #
30
+ # <script>window.exampleKey = 'geometry'</script>
31
+ #
32
+ class agt.geom.Path
33
+
34
+ ### Public ###
35
+
36
+ # Returns the length of the path in pixels.
37
+ #
38
+ # Returns a {Number}.
39
+ length: ->
40
+ sum = 0
41
+ points = @points()
42
+ if points.length > 1
43
+ for i in [1..points.length]
44
+ sum += points[i-1].distance(points[i])
45
+ sum
46
+
47
+ # Returns the coordinates on the path at the given
48
+ # {Number} position.
49
+ #
50
+ # <script>drawGeometry(exampleKey, {paths: [0.1, 0.45, 0.73]})</script>
51
+ #
52
+ # pos - The {Number} between `0` and `1` at which get the path coordinates.
53
+ # pathBasedOnLength - A {Boolean} of whether the position on the path
54
+ # consider the length of the path segments or not.
55
+ # With the default implementation, when true, each
56
+ # segment will only weight as much as their own length.
57
+ # When false, every segment have the same weight,
58
+ # resulting in a difference in speed when animating
59
+ # an object along a path.
60
+ #
61
+ # Returns a [Point]{agt.geom.Point}.
62
+ pathPointAt: (pos, pathBasedOnLength=true) ->
63
+ pos = 0 if pos < 0
64
+ pos = 1 if pos > 1
65
+ points = @points()
66
+
67
+ return points[0] if pos is 0
68
+ return points[points.length - 1] if pos is 1
69
+
70
+ if pathBasedOnLength
71
+ @walkPathBasedOnLength pos, points
72
+ else
73
+ @walkPathBasedOnSegments pos, points
74
+
75
+ # Returns the orientation on the path at the given
76
+ # {Number} position.
77
+ #
78
+ # <script>drawGeometry(exampleKey, {paths: [0.1, 0.45, 0.73]})</script>
79
+ #
80
+ # pos - The {Number} between `0` and `1` at which get the path coordinates.
81
+ # pathBasedOnLength - A {Boolean} of whether the position on the path
82
+ # consider the length of the path segments or not.
83
+ # With the default implementation, when true, each
84
+ # segment will only weight as much as their own length.
85
+ # When false, every segment have the same weight,
86
+ # resulting in a difference in speed when animating
87
+ # an object along a path.
88
+ #
89
+ # Returns a {Number}.
90
+ pathOrientationAt: (pos, pathBasedOnLength=true) ->
91
+ p1 = @pathPointAt pos - 0.01, pathBasedOnLength
92
+ p2 = @pathPointAt pos + 0.01, pathBasedOnLength
93
+ d = p2.subtract p1
94
+
95
+ return d.angle()
96
+
97
+ # Returns the tangent vector at the given {Number} position.
98
+ #
99
+ # <script>drawGeometry(exampleKey, {paths: [0.1, 0.45, 0.73]})</script>
100
+ #
101
+ # pos - The {Number} between `0` and `1` at which get the path coordinates.
102
+ # accuracy - A {Number} giving the distance, relatively to the path length,
103
+ # at which sample path data around the position to approximate
104
+ # the tangent.
105
+ # pathBasedOnLength - A {Boolean} of whether the position on the path
106
+ # consider the length of the path segments or not.
107
+ # With the default implementation, when true, each
108
+ # segment will only weight as much as their own length.
109
+ # When false, every segment have the same weight,
110
+ # resulting in a difference in speed when animating
111
+ # an object along a path.
112
+ #
113
+ # Returns a [Point]{agt.geom.Point}
114
+ pathTangentAt: (pos, accuracy=1 / 100, pathBasedOnLength=true) ->
115
+ [pathBasedOnLength, accuracy] = [accuracy, 1/100] if typeof accuracy is 'boolean'
116
+ @pathPointAt((pos + accuracy) % 1, pathBasedOnLength)
117
+ .subtract(@pathPointAt((1 + pos - accuracy) % 1), pathBasedOnLength)
118
+ .normalize(1)
119
+
120
+ ### Internal ###
121
+
122
+ walkPathBasedOnLength: (pos, points) ->
123
+ walked = 0
124
+ length = @length()
125
+
126
+ for i in [1..points.length-1]
127
+ p1 = points[i-1]
128
+ p2 = points[i]
129
+ stepLength = p1.distance(p2) / length
130
+
131
+ if walked + stepLength > pos
132
+ innerStepPos = Math.map pos, walked, walked + stepLength, 0, 1
133
+ return @pointInSegment innerStepPos, [p1, p2]
134
+
135
+ walked += stepLength
136
+
137
+ walkPathBasedOnSegments: (pos, points) ->
138
+ segments = points.length - 1
139
+ pos = pos * segments
140
+ segment = Math.floor pos
141
+ segment -= 1 if segment is segments
142
+ @pointInSegment pos - segment, points[segment..segment+1]
143
+
144
+ pointInSegment: (pos, segment) ->
145
+ segment[0].add segment[1].subtract(segment[0]).scale(pos)
@@ -0,0 +1,9 @@
1
+ namespace('agt.geom')
2
+ # Public:
3
+ class agt.geom.Proxyable
4
+
5
+ @included: (klass) ->
6
+ klass.proxy = (targets..., options={}) ->
7
+ type = options.as
8
+ for k in targets
9
+ klass::[k].proxyable = type
@@ -0,0 +1,329 @@
1
+ namespace('agt.geom')
2
+ # Public: A spline is a curve made of [vertices]{agt.geom.Point} that
3
+ # controls the resulting geometry.
4
+ #
5
+ # The `Spline` mixin set the ground for all other curves classes such as
6
+ # the [CubicBezier]{agt.geom.CubicBezier} or the
7
+ # [LinearSpline]{agt.geom.LinearSpline} classes.
8
+ #
9
+ # <script>window.exampleKey = 'cubic_spline'</script>
10
+ # <script>drawGeometry('cubic_spline', {highlight: true})</script>
11
+ # <script>drawGeometry('linear_spline', {highlight: true})</script>
12
+ #
13
+ # ```coffeescript
14
+ # class DummySpline
15
+ # @include agt.geom.Spine(1)
16
+ #
17
+ # # Your spline implementation
18
+ # ```
19
+ #
20
+ # segmentSize - The {Number} of points constituting a segment minus one.
21
+ # For example, the [LinearSpline]{agt.geom.LinearSpline}
22
+ # class sets a segment size of `1`, meaning that for points
23
+ # `[a, b, c, d]` it will have 3 segments `ab`, `bc` and `cd`.
24
+ #
25
+ # Returns a {ConcreteSpline} mixin.
26
+ agt.geom.Spline = (segmentSize) ->
27
+
28
+ # Public: The concrete mixin as returned by the
29
+ # [Spline](../files/geom/mixins/spline.coffee.html) method.
30
+ #
31
+ # ### Concrete Splines
32
+ #
33
+ # - {agt.geom.LinearSpline}
34
+ # - {agt.geom.CubicBezier}
35
+ # - {agt.geom.QuadBezier}
36
+ # - {agt.geom.QuintBezier}
37
+ #
38
+ # ### Included Mixins
39
+ #
40
+ # - [agt.mixins.Memoizable](../../../files/mixins/memoizable.coffee.html)
41
+ #
42
+ # <script>window.exampleKey = 'cubic_spline'</script>
43
+ class ConcreteSpline
44
+ @include agt.mixins.Memoizable
45
+
46
+ ### Public ###
47
+
48
+ # The `Spline` mixin is intended to be included, but it decorates
49
+ # the target class with a class method to return the segment size defined
50
+ # for the class.
51
+ #
52
+ # klass - The [Class]{Function} that receive the mixin.
53
+ @included: (klass) ->
54
+ klass.segmentSize = -> segmentSize
55
+
56
+ # Initializes the oject's spline properties. It will also proceed
57
+ # to the validation of the spline's `vertices` based on the segment
58
+ # size specified at the mixin creation.
59
+ #
60
+ # A vertices {Array} is valid when its length is equal
61
+ # to `x * segmentSize + 1` where `x` is any integer greater
62
+ # or equal to `1`.
63
+ #
64
+ # vertices - The {Array} of [Points]{agt.geom.Point} that forms the
65
+ # spline shape.
66
+ # bias - The {Number} of steps per segments when generating the points
67
+ # of the final geometry.
68
+ initSpline: (@vertices, @bias=20) ->
69
+ unless @validateVertices @vertices
70
+ throw new Error "The number of vertices for #{this} doesn't match"
71
+
72
+ # Returns the center of the spline by averaging its vertices.
73
+ #
74
+ # <script>drawGeometry(exampleKey, {center: true})</script>
75
+ #
76
+ # Returns a [Point]{agt.geom.Point}.
77
+ center: ->
78
+ x = y = 0
79
+
80
+ for vertex in @vertices
81
+ x += vertex.x
82
+ y += vertex.y
83
+
84
+ x = x / @vertices.length
85
+ y = y / @vertices.length
86
+
87
+ new agt.geom.Point x, y
88
+
89
+ # Applies a translation represented by the passed-in [point]{agt.geom.Point}
90
+ # to every vertices of the spline.
91
+ #
92
+ # <script>drawTransform(exampleKey, {type: 'translate', args: [50, 0], width: 150})</script>
93
+ #
94
+ # x - A {Number} for the x coordinate or a point-like {Object}.
95
+ # y - A {Number} for the y coordinate if the first argument is also a number.
96
+ #
97
+ # Returns this {ConcreteSpline}.
98
+ translate: (x,y) ->
99
+ {x,y} = agt.geom.Point.pointFrom x,y
100
+ for vertex,i in @vertices
101
+ @vertices[i] = vertex.add x, y
102
+ this
103
+
104
+ # Rotates every vertices around the spline center by an amount of `rotation`
105
+ # radians.
106
+ #
107
+ # <script>drawTransform(exampleKey, {type: 'rotate', args: [Math.PI / 3]})</script>
108
+ #
109
+ # rotation - The {Number} of radians to rotate the spline.
110
+ #
111
+ # Returns this {ConcreteSpline}.
112
+ rotate: (rotation) ->
113
+ center = @center()
114
+ for vertex,i in @vertices
115
+ @vertices[i] = vertex.rotateAround center, rotation
116
+ this
117
+
118
+ # Scales the spline by moving every vertices on the vector they forms with
119
+ # the spline center.
120
+ #
121
+ # <script>drawTransform(exampleKey, {type: 'scale', args: [0.6]})</script>
122
+ #
123
+ # scale - The scaling factor {Number}, a value of `0.5` will scale down
124
+ # the spline at half its original size when a value of `2` will
125
+ # double the size of the spline.
126
+ #
127
+ # Returns this {ConcreteSpline}.
128
+ scale: (scale) ->
129
+ center = @center()
130
+ for vertex,i in @vertices
131
+ @vertices[i] = center.add vertex.subtract(center).scale(scale)
132
+ this
133
+
134
+ # Returns the *final* points of the curve as determined by the `bias`
135
+ # property of the current spline. The total number of points for a geometry
136
+ # is always the result of the following equation: `segment size * number
137
+ # of segments`.
138
+ #
139
+ # <script>drawGeometryPoints(exampleKey, 'points')</script>
140
+ #
141
+ # Returns an {Array} of [Points]{agt.geom.Point}.
142
+ points: ->
143
+ return @memoFor('points').concat() if @memoized 'points'
144
+ segments = @segments() * @bias
145
+ points = (@pathPointAt i / segments for i in [0..segments])
146
+ @memoize('points', points).concat()
147
+
148
+ # Internal: Validates the length of the vertices {Array}.
149
+ #
150
+ # vertices - The {Array} of vertices to validate.
151
+ #
152
+ # Returns a {Boolean}.
153
+ validateVertices: (vertices) ->
154
+ vertices.length % segmentSize is 1 and
155
+ vertices.length >= segmentSize + 1
156
+
157
+ # Returns the {Number} of segments of the current spline based on the
158
+ # spline configuration.
159
+ #
160
+ # Returns a {Number}.
161
+ segments: ->
162
+ return 0 if not @vertices? or @vertices.length is 0
163
+ return @memoFor 'segments' if @memoized 'segments'
164
+ @memoize 'segments', (@vertices.length - 1) / segmentSize
165
+
166
+ # Returns the size {Number} of the spline segments.
167
+ #
168
+ # Returns a {Number}.
169
+ segmentSize: -> segmentSize
170
+
171
+ # Returns the segment at `index`. A segment is a pair of
172
+ # [Points]{agt.geom.Point} of each extremity of the segment.
173
+ #
174
+ # index - The index {Number} of the segment.
175
+ #
176
+ # Returns an {Array} of [Points]{agt.geom.Point}.
177
+ segment: (index) ->
178
+ if index < @segments()
179
+ k = "segment#{index}"
180
+ return @memoFor k if @memoized k
181
+
182
+ [start, end] = [index * segmentSize, (index + 1) * segmentSize + 1]
183
+ @memoize k, @vertices[start..end]
184
+ else
185
+ null
186
+
187
+ # Returns the length of the spline in pixels.
188
+ #
189
+ # This an approximative value based on the current spline `bias`, so the
190
+ # bigger the `bias` the more accurate the length is, with the downside
191
+ # of poorer performances.
192
+ #
193
+ # Returns a {Number}.
194
+ length: -> @measure @bias
195
+
196
+ # Internal: Measures the spline using the passed-in bias.
197
+ #
198
+ # bias - The {Number} of steps used to walk the spline segments.
199
+ #
200
+ # Returns a {Number}.
201
+ measure: (bias) ->
202
+ return @memoFor 'measure' if @memoized 'measure'
203
+ length = 0
204
+ length += @measureSegment @segment(i), bias for i in [0..@segments()-1]
205
+ @memoize 'measure', length
206
+
207
+ # Internal: Measures the given segment using the passed-in bias.
208
+ #
209
+ # segment - The index {Number} of the segment to measure.
210
+ # bias - The {Number} of steps used to walk the segment.
211
+ #
212
+ # Returns a {Number}.
213
+ measureSegment: (segment, bias) ->
214
+ k = "segment#{segment}_#{bias}Length"
215
+ return @memoFor k if @memoized k
216
+
217
+ step = 1 / bias
218
+ length = 0
219
+
220
+ for i in [1..bias]
221
+ length += @pointInSegment((i-1) * step, segment)
222
+ .distance(@pointInSegment(i * step, segment))
223
+
224
+ @memoize k, length
225
+
226
+ # {Delegates to: agt.geom.Path.pathPointAt}
227
+ pathPointAt: (pos, pathBasedOnLength=true) ->
228
+ pos = 0 if pos < 0
229
+ pos = 1 if pos > 1
230
+
231
+ return @vertices[0] if pos is 0
232
+ return @vertices[@vertices.length - 1] if pos is 1
233
+
234
+ if pathBasedOnLength
235
+ @walkPathBasedOnLength pos
236
+ else
237
+ @walkPathBasedOnSegments pos
238
+
239
+ # Internal: Iterates over the spline steps until the given `pos` and
240
+ # returns the corresponding coordinates.
241
+ #
242
+ # In this implementation the path is walked with each segment taking
243
+ # a space in the total spline length based on its own length.
244
+ #
245
+ # pos - The position {Number} between `0` and `1`.
246
+ #
247
+ # Returns a [Point]{agt.geom.Point}.
248
+ walkPathBasedOnLength: (pos) ->
249
+ walked = 0
250
+ length = @length()
251
+ segments = @segments()
252
+
253
+ for i in [0..segments-1]
254
+ segment = @segment i
255
+ stepLength = @measureSegment(segment, @bias) / length
256
+
257
+ if walked + stepLength > pos
258
+ innerStepPos = Math.map pos, walked, walked + stepLength, 0, 1
259
+ return @pointInSegment innerStepPos, segment
260
+
261
+ walked += stepLength
262
+
263
+ # Internal: Iterates over the spline steps until the given `pos` and
264
+ # returns the corresponding coordinates.
265
+ #
266
+ # In this implementation the path is walked with each segment taking
267
+ # based on the number of segments in the spline.
268
+ #
269
+ # pos - The position {Number} between `0` and `1`.
270
+ #
271
+ # Returns a [Point]{agt.geom.Point}.
272
+ walkPathBasedOnSegments: (pos) ->
273
+ segments = @segments()
274
+ pos = pos * segments
275
+ segment = Math.floor pos
276
+ segment -= 1 if segment is segments
277
+ @pointInSegment pos - segment, @segment segment
278
+
279
+ # **Unsupported** - The {agt.geom.Geometry::fill} method is not supported
280
+ # by splines as they are not closed geometry.
281
+ fill: ->
282
+
283
+ # {Delegates to: agt.geom.Geometry.drawPath}
284
+ drawPath: (context) ->
285
+ points = @points()
286
+ start = points.shift()
287
+ context.beginPath()
288
+ context.moveTo(start.x,start.y)
289
+ context.lineTo(p.x,p.y) for p in points
290
+
291
+ # Draws the spline vertices onto the passed-in canvas context.
292
+ #
293
+ # <script>drawGeometry(exampleKey, {vertices: true})</script>
294
+ #
295
+ # context - The canvas context to draw in.
296
+ # color - The color {String} to use for the vertices.
297
+ drawVertices: (context, color=agt.COLORS.VERTICES) ->
298
+ context.fillStyle = color
299
+ for vertex in @vertices
300
+ context.beginPath()
301
+ context.arc vertex.x, vertex.y, 2, 0, Math.PI*2
302
+ context.fill()
303
+ context.closePath()
304
+
305
+ # Draws the segments between each vertex onto the passed-in canvas context.
306
+ #
307
+ # <script>drawGeometry(exampleKey, {verticesConnections: true})</script>
308
+ #
309
+ # context - The canvas context to draw in.
310
+ # color - The color {String} to use for the vertices connections.
311
+ drawVerticesConnections: (context, color=agt.COLORS.VERTICES_CONNECTIONS) ->
312
+ context.strokeStyle = color
313
+ for i in [1..@vertices.length-1]
314
+ vertexStart = @vertices[i-1]
315
+ vertexEnd = @vertices[i]
316
+ context.beginPath()
317
+ context.moveTo vertexStart.x, vertexStart.y
318
+ context.lineTo vertexEnd.x, vertexEnd.y
319
+ context.stroke()
320
+ context.closePath()
321
+
322
+ # The memoization key of a spline is the concatenation of its vertices.
323
+ #
324
+ # Returns a {String}.
325
+ memoizationKey: ->
326
+ @vertices.map((pt) -> "#{pt.x};#{pt.y}").join(';')
327
+
328
+ # {Delegates to: ConcreteCloneable.clone}
329
+ clone: -> new @constructor @vertices.map((pt) -> pt.clone()), @bias