ela 3.2.0 → 3.3.0

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.
@@ -1,163 +0,0 @@
1
- ELA.Views ?= {}
2
- class ELA.Views.CurveGraph extends ELA.Views.BaseGraph
3
- initialize: (options = {}) ->
4
- super
5
-
6
- @bindCalculatorEvents()
7
-
8
- @model.curves.on 'change:selected', =>
9
- @bindCalculatorEvents()
10
- @requestRepaint()
11
-
12
- @model.on 'change:calculators', =>
13
- @calculateRanges()
14
- @bindCalculatorEvents()
15
- @requestRepaint()
16
-
17
- @model.on 'change:valueAtRange', @requestRepaint
18
- @model.on 'change:axisLabelingForCurve', @requestRepaint
19
-
20
- bindCalculatorEvents: ->
21
- # Remove current callbacks, add new ones for each curve
22
- @stopListening(@model.previous('calculators'))
23
-
24
- for calc in @model.get('calculators')
25
- for curve in @model.curves.history
26
- @listenTo calc, "change:#{curve.get('function')}", @requestRepaint
27
-
28
- @listenTo calc, 'change:maxX change:maxY', @calculateRanges
29
- # TODO: Add dynamic dependencies to calculator, so that we can
30
- # actually only listen to maxY changes and recalculate ranges.
31
- oldestCurve = @model.curves.history[0]
32
- if oldestCurve?
33
- @listenTo calc, "change:#{oldestCurve.get('function')}", @calculateRanges
34
-
35
- calculateRanges: =>
36
- @params.set xRange: @maxRangeX()
37
-
38
- maxRangeX: =>
39
- max = _.chain(@model.get('calculators'))
40
- .map (c) -> c.maxX()
41
- .max()
42
- .value()
43
- min = _.chain(@model.get('calculators'))
44
- .map (c) -> c.minX()
45
- .min()
46
- .value()
47
- range = 0
48
- range += max if max > 0
49
- range -= min if min < 0
50
- (Math.abs(range) or 10000) * @params.get('xScale') * 1.1
51
-
52
- maxRangeY: (func) =>
53
- unless func?
54
- func = @model.get('axisLabelingForCurve')?.get('function')
55
- max = _.chain(@model.get('calculators'))
56
- .map (c) -> c.maxY(func, c.minX(), c.maxX(), 30)
57
- .max()
58
- .value()
59
- min = _.chain(@model.get('calculators'))
60
- .map (c) -> c.minY(func, c.minX(), c.maxX(), 30)
61
- .min()
62
- .value()
63
- range = 0
64
- range += max if max > 0
65
- range -= min if min < 0
66
- Math.abs(range) * @params.get('yScale') * 1.1
67
-
68
- xAxisValueLabel: (val, stepsize) ->
69
- @axisLabel(val, stepsize)
70
-
71
- yAxisValueLabel: (val, stepsize) ->
72
- curve = @model.get('axisLabelingForCurve')
73
- val = @Present(curve).unitValue(val) if curve?
74
- @axisLabel(val, stepsize)
75
-
76
- yAxisLabel: ->
77
- curve = @model.get('axisLabelingForCurve')
78
- @Present(curve).fullYAxisLabel()
79
-
80
- renderCurves: ->
81
- for calc, i in @model.get('calculators')
82
- for curve, j in @model.curves.history
83
- func = curve.get('function')
84
-
85
- @context.strokeStyle = curve.strokeStyle()
86
- @context.lineWidth = 3
87
- if i == 1
88
- @context.setLineDash [4,5]
89
- else
90
- @context.setLineDash []
91
- xPos = 0
92
- brokenLine = 1
93
- plotted = 0
94
- @context.beginPath()
95
- xPos = 0
96
- yRange = @maxRangeY(func)
97
- yMin = -(@height - @yOrigin) * yRange / @height
98
- yMax = @yOrigin * yRange / @height
99
- for xPos in [-2 .. (@width + 3)]
100
- x = @xMin + xPos * @xRange / @width
101
- val = calc[func](x)
102
- y = val
103
- if y?
104
- yPos = (yMax - y) * @height / yRange
105
- if brokenLine > 0
106
- @context.moveTo(xPos, yPos) if brokenLine == 1
107
- brokenLine -= 1
108
- else
109
- @context.lineTo(xPos, yPos)
110
- plotted += 1
111
-
112
- @context.stroke()
113
- @context.closePath()
114
-
115
- renderRangeIndicator: ->
116
- @context.setLineDash []
117
- @context.beginPath()
118
- @context.strokeStyle = '#999999'
119
- @context.lineWidth = 2
120
- @context.moveTo(0, 0)
121
- @context.lineTo(@width, 0)
122
- @context.stroke()
123
-
124
- @context.beginPath()
125
- @context.strokeStyle = '#999999'
126
- @context.lineWidth = 2
127
- @context.moveTo(0, @height)
128
- @context.lineTo(@width, @height)
129
- @context.stroke()
130
-
131
- if @model.get('valueAtRange')?
132
- valueAtPoint = @model.get('valueAtRange')
133
-
134
- @context.beginPath()
135
- @context.strokeStyle = '#999999'
136
- @context.lineWidth = 2
137
- xPos = @xOrigin + valueAtPoint * @width / @xRange
138
- @context.moveTo(xPos, 0)
139
- @context.lineTo(xPos, @height)
140
- @context.stroke()
141
- @context.closePath()
142
-
143
- @context.beginPath()
144
- @context.lineWidth = 1
145
-
146
- # The fillStyle of the triangle should match the color of the
147
- # legend background set in screen.styl:
148
- @context.fillStyle = "#f2f2f2"
149
-
150
- @context.moveTo(xPos + 8, 0)
151
- @context.lineTo(xPos, 8)
152
- @context.lineTo(xPos - 8, 0)
153
- @context.moveTo(xPos - 8, @height)
154
- @context.lineTo(xPos, @height - 8)
155
- @context.lineTo(xPos + 8, @height)
156
- @context.stroke()
157
- @context.fill()
158
- @context.closePath()
159
-
160
- render: =>
161
- super
162
- @renderRangeIndicator()
163
- this
@@ -1,294 +0,0 @@
1
- ELA.Views ?= {}
2
- class ELA.Views.InterpolatedGraph extends ELA.Views.BaseGraph
3
- initialize: (options = {}) ->
4
- super
5
-
6
- @bindCalculatorEvents()
7
-
8
- @model.curves.on 'change:selected', =>
9
- @bindCalculatorEvents()
10
- @requestRepaint()
11
-
12
- @model.on 'change:calculators', =>
13
- @calculateRanges()
14
- @bindCalculatorEvents()
15
- @requestRepaint()
16
-
17
- for guide in @params.get('guides')
18
- @listenTo(@model, "change:#{guide.attribute}", @requestRepaint)
19
-
20
- @listenTo(@params, 'change:axisLabelingForCurve', @requestRepaint)
21
-
22
- bindCalculatorEvents: ->
23
- # Remove current callbacks, add new ones for each curve
24
- @stopListening(@model.previous('calculators'))
25
-
26
- for calc in @model.get('calculators')
27
- filteredCurves = @_filteredCurves()
28
- for curve in filteredCurves
29
- @listenTo calc, "change:#{curve.get('function')}", @requestRepaint
30
-
31
- @listenTo calc, 'change:maxX change:xRange', @calculateRangeX
32
- @listenTo calc, 'change:maxY change:yRange', @calculateRangeY
33
- # TODO: Add dynamic dependencies to calculator, so that we can
34
- # actually only listen to maxY changes and recalculate ranges.
35
- oldestCurve = filteredCurves[0]
36
- if oldestCurve?
37
- @listenTo calc, "change:#{oldestCurve.get('function')}", @calculateRanges
38
-
39
- maxRangeX: (func, xScale = @params.get('xScale')) =>
40
- func = @params.get('axisLabelingForCurve')?.get('function') unless func?
41
- ranges = (calc.xRange(func) for calc in @model.get('calculators'))
42
- Math.max.apply(Math, _.compact(ranges)) * xScale
43
-
44
- maxRangeY: (func, yScale = @params.get('yScale')) =>
45
- func = @params.get('axisLabelingForCurve')?.get('function') unless func?
46
- ranges = (calc.yRange(func) for calc in @model.get('calculators'))
47
- Math.max.apply(Math, _.compact(ranges)) * yScale
48
-
49
- xAxisValueLabel: (val, stepsize) ->
50
- @axisLabel(val, stepsize)
51
-
52
- yAxisValueLabel: (val, stepsize) ->
53
- curve = @params.get('axisLabelingForCurve')
54
- if curve?
55
- curvePresenter = @Present(curve)
56
- val = curvePresenter.unitValue(val)
57
- stepsize = curvePresenter.unitValue(stepsize)
58
- @axisLabel(val - @params.get('yOffset'), stepsize)
59
-
60
- genericAxisLabel: (axis) ->
61
- axisLabel = @params.get("#{axis}AxisLabel")
62
- return axisLabel.call(this) if axisLabel? and _.isFunction(axisLabel)
63
- return axisLabel if axisLabel?
64
-
65
- axisLabelLocale = @params.get("#{axis}AxisLabelLocale")
66
- return @loadLocale(axisLabelLocale) if axisLabelLocale?
67
-
68
- curve = @params.get('axisLabelingForCurve')
69
- if curve?
70
- return @Present(curve).fullXAxisLabel() if axis is 'x'
71
- return @Present(curve).fullYAxisLabel() if axis is 'y'
72
-
73
- @loadLocale("graph.#{axis}AxisLabel", defaultValue: '')
74
-
75
- xAxisLabel: -> @genericAxisLabel('x')
76
- yAxisLabel: -> @genericAxisLabel('y')
77
-
78
- getControlPoints: (x0, y0, x1, y1, x2, y2, tension) ->
79
- d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2))
80
- d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
81
- fa = tension * d01 / (d01 + d12)
82
- fb = tension - fa
83
- p1x = x1 + fa * (x0 - x2)
84
- p1y = y1 + fa * (y0 - y2)
85
- p2x = x1 - fb * (x0 - x2)
86
- p2y = y1 - fb * (y0 - y2)
87
- [p1x, p1y, p2x, p2y]
88
-
89
- drawSpline: (points, t, closed, xPos = ((x) -> x), yPos = ((y) -> y)) ->
90
- if points.length <= 4
91
- @context.moveTo(xPos(points[0]), yPos(points[1]))
92
- @context.lineTo(xPos(points[2]), yPos(points[3]))
93
- else
94
- cp = []
95
- n = points.length
96
- points = _.clone(points)
97
- if closed
98
- points.push points[0], points[1], points[2], points[3]
99
- points.unshift points[n - 1]
100
- points.unshift points[n - 1]
101
- for i in [0..n] by 2
102
- cp = cp.concat(@getControlPoints(points[i], points[i+1], points[i+2], points[i+3], points[i+4], points[i+5], t))
103
- cp = cp.concat(cp[0], cp[1])
104
- for i in [0..n] by 2
105
- @context.moveTo(xPos(points[i]), yPos(points[i+1]))
106
- @context.bezierCurveTo(xPos(cp[2*i-2]), yPos(cp[2*i-1]), xPos(cp[2*i]), yPos(cp[2*i+1]), xPos(points[i+2]), yPos(points[i+3]))
107
- else
108
- for i in [0..(n-4)] by 2
109
- cp = cp.concat(@getControlPoints(points[i], points[i+1], points[i+2], points[i+3], points[i+4], points[i+5], t))
110
-
111
- @context.moveTo(xPos(points[0]), yPos(points[1]))
112
- @context.quadraticCurveTo(xPos(cp[0]), yPos(cp[1]), xPos(points[2]), yPos(points[3]))
113
- for i in [2..(n-3)] by 2
114
- @context.moveTo(xPos(points[i]), yPos(points[i+1]))
115
- @context.bezierCurveTo(xPos(cp[2*i-2]), yPos(cp[2*i-1]), xPos(cp[2*i]), yPos(cp[2*i+1]), xPos(points[i+2]), yPos(points[i+3]))
116
- @context.moveTo(xPos(points[n-2]), yPos(points[n-1]))
117
- @context.quadraticCurveTo(xPos(cp[2*n-10]), yPos(cp[2*n-9]), xPos(points[n-4]), yPos(points[n-3]))
118
-
119
- drawCurve: (points, tension = 0, closed = false, xPos = ((x) -> x), yPos = ((y) -> y)) ->
120
- if tension is 0
121
- beginning = true
122
- if points.length >= 2
123
- @context.moveTo(xPos(points[0]), yPos(points[1]))
124
- for i in [2...points.length] by 2
125
- @context.lineTo(xPos(points[i]), yPos(points[i+1]))
126
- else
127
- @drawSpline(points, tension, closed, xPos, yPos)
128
-
129
- _filteredCurves: ->
130
- curves = @model.curves.history
131
- if graphCurves = @params.get('curves')
132
- _.filter curves, (curve) ->
133
- graphCurves.indexOf(curve.get('function')) >= 0
134
- else
135
- curves
136
-
137
- _sortedCurves: ->
138
- _.sortBy @_filteredCurves(), (curve) ->
139
- curve.get('zIndex')
140
-
141
-
142
- beforeRenderCurves: (resultCurves) ->
143
- afterRenderCurves: (resultCurves) ->
144
-
145
- renderCurves: ->
146
- resultCurves = []
147
- for calc, i in @model.get('calculators')
148
- for curve, j in @_sortedCurves()
149
- func = curve.get('function')
150
- resultCurves.push
151
- curve: curve
152
- result: calc[func](@xMin, @xMax)
153
- yRange: @maxRangeY(func)
154
- xRange: @maxRangeX(func)
155
- calculatorIdx: i
156
-
157
- @beforeRenderCurves(resultCurves)
158
-
159
- for resultCurve in resultCurves
160
- curve = resultCurve.curve
161
- result = resultCurve.result
162
- yRange = resultCurve.yRange
163
- xRange = resultCurve.xRange
164
- if _.isObject(result)
165
- xPos = (x) => @xOrigin + x * @width / xRange
166
- yPos = (y) => @yOrigin - (y + @yOffset) * @height / yRange
167
-
168
- if curve.hasSubcurves()
169
- for name, subcurve of curve.subcurves()
170
- if result[name].points?
171
- @renderCurve(subcurve, result[name], xPos, yPos, resultCurve.calculatorIdx)
172
- else if result[name].radius?
173
- @renderCircle(subcurve, result[name], xPos, yPos, resultCurve.calculatorIdx)
174
- else
175
- if result.points?
176
- @renderCurve(curve, result, xPos, yPos, resultCurve.calculatorIdx)
177
- else if result.radius?
178
- @renderCircle(curve, result, xPos, yPos, resultCurve.calculatorIdx)
179
-
180
- @afterRenderCurves(resultCurves)
181
-
182
- renderCircle: (curve, result, xPos, yPos, calculatorIdx) ->
183
- func = curve.get('function')
184
- yRange = @maxRangeY(func)
185
- @context.beginPath()
186
- width = Math.abs(xPos(2*result.radius) - @xOrigin)
187
- height = Math.abs(yPos(2*result.radius) - @yOrigin)
188
- centerX = xPos(result.center[0])
189
- centerY = yPos(result.center[1])
190
-
191
- aX = centerX - width/2
192
- aY = centerY - height/2
193
- hB = (width / 2) * .5522848
194
- vB = (height / 2) * .5522848
195
- eX = aX + width
196
- eY = aY + height
197
- mX = aX + width / 2
198
- mY = aY + height / 2
199
- @context.moveTo(aX, mY);
200
- @context.bezierCurveTo(aX, mY - vB, mX - hB, aY, mX, aY);
201
- @context.bezierCurveTo(mX + hB, aY, eX, mY - vB, eX, mY);
202
- @context.bezierCurveTo(eX, mY + vB, mX + hB, eY, mX, eY);
203
- @context.bezierCurveTo(mX - hB, eY, aX, mY + vB, aX, mY);
204
-
205
- @context.setLineDash(curve.lineDash(calculatorIdx))
206
- if curve.strokeStyle()
207
- @context.lineWidth = curve.get('lineWidth')
208
- @context.strokeStyle = curve.strokeStyle()
209
- @context.stroke()
210
- if fillStyle = curve.get('fillStyle')
211
- @context.fillStyle = fillStyle
212
- @context.fill()
213
- @context.closePath()
214
-
215
- renderCurve: (curve, result, xPos, yPos, calculatorIdx) ->
216
- @context.beginPath()
217
- @context.setLineDash(curve.lineDash(calculatorIdx))
218
- if result.multiplePaths
219
- for setOfPoints, i in result.points
220
- @drawCurve(setOfPoints, result.tension, false, xPos, yPos)
221
- else
222
- @drawCurve(result.points, result.tension, false, xPos, yPos)
223
- if curve.strokeStyle()
224
- @context.lineWidth = curve.get('lineWidth')
225
- @context.strokeStyle = curve.strokeStyle()
226
- @context.stroke()
227
- if fillStyle = curve.get('fillStyle')
228
- @context.fillStyle = fillStyle
229
- @context.fill()
230
- @context.closePath()
231
-
232
- renderGuide: (guide) ->
233
- valueAtPoint = @model.get(guide.attribute)
234
-
235
- @context.beginPath()
236
- @context.setLineDash []
237
- @context.strokeStyle = '#999999'
238
- @context.lineWidth = 2
239
-
240
- # Border below legend
241
- @context.moveTo(@width, 0)
242
- @context.lineTo(0, 0)
243
-
244
- if guide.orientation is 'vertical'
245
- # Border next to range handler
246
- @context.moveTo(0, @height)
247
- @context.lineTo(@width, @height)
248
- @context.lineTo(@width, @height-10)
249
-
250
- # Range handler line
251
- xPos = @xOrigin + valueAtPoint * @width / @xRange
252
- @context.moveTo(xPos, 0)
253
- @context.lineTo(xPos, @height)
254
- else
255
- # Border next to range handler
256
- @context.lineTo(0, @height)
257
-
258
- # Range handler line
259
- yPos = - valueAtPoint * @height / @yRange + @yOrigin
260
- @context.moveTo(0, yPos)
261
- @context.lineTo(@width, yPos)
262
-
263
- @context.stroke()
264
- @context.closePath()
265
-
266
- @context.beginPath()
267
- # The fillStyle of the triangle should match the color of the
268
- # background set for the legend and range handler in screen.styl:
269
- @context.fillStyle = "#f2f2f2"
270
- @context.lineWidth = 1
271
-
272
- if guide.orientation is 'vertical'
273
- @context.moveTo(xPos + 8, 0)
274
- @context.lineTo(xPos, 8)
275
- @context.lineTo(xPos - 8, 0)
276
- @context.moveTo(xPos - 8, @height)
277
- @context.lineTo(xPos, @height - 8)
278
- @context.lineTo(xPos + 8, @height)
279
- else
280
- @context.moveTo(0, yPos - 8)
281
- @context.lineTo(8, yPos)
282
- @context.lineTo(0, yPos + 8)
283
- @context.moveTo(@width, yPos - 8)
284
- @context.lineTo(@width - 8, yPos)
285
- @context.lineTo(@width, yPos + 8)
286
- @context.stroke()
287
- @context.fill()
288
- @context.closePath()
289
-
290
- render: =>
291
- super
292
- for guide in @params.get('guides')
293
- @renderGuide(guide)
294
- this