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