1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | "use strict";
|
29 |
|
30 | import * as utils from './dygraph-utils';
|
31 | import Dygraph from './dygraph';
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) {
|
51 | this.dygraph_ = dygraph;
|
52 |
|
53 | this.layout = layout;
|
54 | this.element = element;
|
55 | this.elementContext = elementContext;
|
56 |
|
57 | this.height = dygraph.height_;
|
58 | this.width = dygraph.width_;
|
59 |
|
60 |
|
61 | if (!utils.isCanvasSupported(this.element)) {
|
62 | throw "Canvas is not supported.";
|
63 | }
|
64 |
|
65 |
|
66 | this.area = layout.getPlotArea();
|
67 |
|
68 |
|
69 |
|
70 | var ctx = this.dygraph_.canvas_ctx_;
|
71 | ctx.beginPath();
|
72 | ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
|
73 | ctx.clip();
|
74 |
|
75 | ctx = this.dygraph_.hidden_ctx_;
|
76 | ctx.beginPath();
|
77 | ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
|
78 | ctx.clip();
|
79 | };
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | DygraphCanvasRenderer.prototype.clear = function() {
|
88 | this.elementContext.clearRect(0, 0, this.width, this.height);
|
89 | };
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | DygraphCanvasRenderer.prototype.render = function() {
|
99 |
|
100 | this._updatePoints();
|
101 |
|
102 |
|
103 | this._renderLineChart();
|
104 | };
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | DygraphCanvasRenderer._getIteratorPredicate = function(connectSeparatedPoints) {
|
113 | return connectSeparatedPoints ?
|
114 | DygraphCanvasRenderer._predicateThatSkipsEmptyPoints :
|
115 | null;
|
116 | };
|
117 |
|
118 | DygraphCanvasRenderer._predicateThatSkipsEmptyPoints =
|
119 | function(array, idx) {
|
120 | return array[idx].yval !== null;
|
121 | };
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | DygraphCanvasRenderer._drawStyledLine = function(e,
|
129 | color, strokeWidth, strokePattern, drawPoints,
|
130 | drawPointCallback, pointSize) {
|
131 | var g = e.dygraph;
|
132 |
|
133 | var stepPlot = g.getBooleanOption("stepPlot", e.setName);
|
134 |
|
135 | if (!utils.isArrayLike(strokePattern)) {
|
136 | strokePattern = null;
|
137 | }
|
138 |
|
139 | var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);
|
140 |
|
141 | var points = e.points;
|
142 | var setName = e.setName;
|
143 | var iter = utils.createIterator(points, 0, points.length,
|
144 | DygraphCanvasRenderer._getIteratorPredicate(
|
145 | g.getBooleanOption("connectSeparatedPoints", setName)));
|
146 |
|
147 | var stroking = strokePattern && (strokePattern.length >= 2);
|
148 |
|
149 | var ctx = e.drawingContext;
|
150 | ctx.save();
|
151 | if (stroking) {
|
152 | if (ctx.setLineDash) ctx.setLineDash(strokePattern);
|
153 | }
|
154 |
|
155 | var pointsOnLine = DygraphCanvasRenderer._drawSeries(
|
156 | e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
|
157 | DygraphCanvasRenderer._drawPointsOnLine(
|
158 | e, pointsOnLine, drawPointCallback, color, pointSize);
|
159 |
|
160 | if (stroking) {
|
161 | if (ctx.setLineDash) ctx.setLineDash([]);
|
162 | }
|
163 |
|
164 | ctx.restore();
|
165 | };
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | DygraphCanvasRenderer._drawSeries = function(e,
|
176 | iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {
|
177 |
|
178 | var prevCanvasX = null;
|
179 | var prevCanvasY = null;
|
180 | var nextCanvasY = null;
|
181 | var isIsolated;
|
182 | var point;
|
183 | var pointsOnLine = [];
|
184 | var first = true;
|
185 |
|
186 | var ctx = e.drawingContext;
|
187 | ctx.beginPath();
|
188 | ctx.strokeStyle = color;
|
189 | ctx.lineWidth = strokeWidth;
|
190 |
|
191 |
|
192 | var arr = iter.array_;
|
193 | var limit = iter.end_;
|
194 | var predicate = iter.predicate_;
|
195 |
|
196 | for (var i = iter.start_; i < limit; i++) {
|
197 | point = arr[i];
|
198 | if (predicate) {
|
199 | while (i < limit && !predicate(arr, i)) {
|
200 | i++;
|
201 | }
|
202 | if (i == limit) break;
|
203 | point = arr[i];
|
204 | }
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | if (point.canvasy === null || point.canvasy != point.canvasy) {
|
210 | if (stepPlot && prevCanvasX !== null) {
|
211 |
|
212 | ctx.moveTo(prevCanvasX, prevCanvasY);
|
213 | ctx.lineTo(point.canvasx, prevCanvasY);
|
214 | }
|
215 | prevCanvasX = prevCanvasY = null;
|
216 | } else {
|
217 | isIsolated = false;
|
218 | if (drawGapPoints || prevCanvasX === null) {
|
219 | iter.nextIdx_ = i;
|
220 | iter.next();
|
221 | nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
|
222 |
|
223 | var isNextCanvasYNullOrNaN = nextCanvasY === null ||
|
224 | nextCanvasY != nextCanvasY;
|
225 | isIsolated = (prevCanvasX === null && isNextCanvasYNullOrNaN);
|
226 | if (drawGapPoints) {
|
227 |
|
228 |
|
229 | if ((!first && prevCanvasX === null) ||
|
230 | (iter.hasNext && isNextCanvasYNullOrNaN)) {
|
231 | isIsolated = true;
|
232 | }
|
233 | }
|
234 | }
|
235 |
|
236 | if (prevCanvasX !== null) {
|
237 | if (strokeWidth) {
|
238 | if (stepPlot) {
|
239 | ctx.moveTo(prevCanvasX, prevCanvasY);
|
240 | ctx.lineTo(point.canvasx, prevCanvasY);
|
241 | }
|
242 |
|
243 | ctx.lineTo(point.canvasx, point.canvasy);
|
244 | }
|
245 | } else {
|
246 | ctx.moveTo(point.canvasx, point.canvasy);
|
247 | }
|
248 | if (drawPoints || isIsolated) {
|
249 | pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
|
250 | }
|
251 | prevCanvasX = point.canvasx;
|
252 | prevCanvasY = point.canvasy;
|
253 | }
|
254 | first = false;
|
255 | }
|
256 | ctx.stroke();
|
257 | return pointsOnLine;
|
258 | };
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 | DygraphCanvasRenderer._drawPointsOnLine = function(
|
268 | e, pointsOnLine, drawPointCallback, color, pointSize) {
|
269 | var ctx = e.drawingContext;
|
270 | for (var idx = 0; idx < pointsOnLine.length; idx++) {
|
271 | var cb = pointsOnLine[idx];
|
272 | ctx.save();
|
273 | drawPointCallback.call(e.dygraph,
|
274 | e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
|
275 | ctx.restore();
|
276 | }
|
277 | };
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | DygraphCanvasRenderer.prototype._updatePoints = function() {
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 | var sets = this.layout.points;
|
297 | for (var i = sets.length; i--;) {
|
298 | var points = sets[i];
|
299 | for (var j = points.length; j--;) {
|
300 | var point = points[j];
|
301 | point.canvasx = this.area.w * point.x + this.area.x;
|
302 | point.canvasy = this.area.h * point.y + this.area.y;
|
303 | }
|
304 | }
|
305 | };
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 | DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) {
|
322 | var ctx = opt_ctx || this.elementContext;
|
323 | var i;
|
324 |
|
325 | var sets = this.layout.points;
|
326 | var setNames = this.layout.setNames;
|
327 | var setName;
|
328 |
|
329 | this.colors = this.dygraph_.colorsMap_;
|
330 |
|
331 |
|
332 | var plotter_attr = this.dygraph_.getOption("plotter");
|
333 | var plotters = plotter_attr;
|
334 | if (!utils.isArrayLike(plotters)) {
|
335 | plotters = [plotters];
|
336 | }
|
337 |
|
338 | var setPlotters = {};
|
339 | for (i = 0; i < setNames.length; i++) {
|
340 | setName = setNames[i];
|
341 | var setPlotter = this.dygraph_.getOption("plotter", setName);
|
342 | if (setPlotter == plotter_attr) continue;
|
343 |
|
344 | setPlotters[setName] = setPlotter;
|
345 | }
|
346 |
|
347 | for (i = 0; i < plotters.length; i++) {
|
348 | var plotter = plotters[i];
|
349 | var is_last = (i == plotters.length - 1);
|
350 |
|
351 | for (var j = 0; j < sets.length; j++) {
|
352 | setName = setNames[j];
|
353 | if (opt_seriesName && setName != opt_seriesName) continue;
|
354 |
|
355 | var points = sets[j];
|
356 |
|
357 |
|
358 | var p = plotter;
|
359 | if (setName in setPlotters) {
|
360 | if (is_last) {
|
361 | p = setPlotters[setName];
|
362 | } else {
|
363 |
|
364 | continue;
|
365 | }
|
366 | }
|
367 |
|
368 | var color = this.colors[setName];
|
369 | var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);
|
370 |
|
371 | ctx.save();
|
372 | ctx.strokeStyle = color;
|
373 | ctx.lineWidth = strokeWidth;
|
374 | p({
|
375 | points: points,
|
376 | setName: setName,
|
377 | drawingContext: ctx,
|
378 | color: color,
|
379 | strokeWidth: strokeWidth,
|
380 | dygraph: this.dygraph_,
|
381 | axis: this.dygraph_.axisPropertiesForSeries(setName),
|
382 | plotArea: this.area,
|
383 | seriesIndex: j,
|
384 | seriesCount: sets.length,
|
385 | singleSeriesName: opt_seriesName,
|
386 | allSeriesPoints: sets
|
387 | });
|
388 | ctx.restore();
|
389 | }
|
390 | }
|
391 | };
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 | DygraphCanvasRenderer._Plotters = {
|
398 | linePlotter: function(e) {
|
399 | DygraphCanvasRenderer._linePlotter(e);
|
400 | },
|
401 |
|
402 | fillPlotter: function(e) {
|
403 | DygraphCanvasRenderer._fillPlotter(e);
|
404 | },
|
405 |
|
406 | errorPlotter: function(e) {
|
407 | DygraphCanvasRenderer._errorPlotter(e);
|
408 | }
|
409 | };
|
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 | DygraphCanvasRenderer._linePlotter = function(e) {
|
416 | var g = e.dygraph;
|
417 | var setName = e.setName;
|
418 | var strokeWidth = e.strokeWidth;
|
419 |
|
420 |
|
421 |
|
422 |
|
423 | var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
|
424 | var drawPointCallback = g.getOption("drawPointCallback", setName) ||
|
425 | utils.Circles.DEFAULT;
|
426 | var strokePattern = g.getOption("strokePattern", setName);
|
427 | var drawPoints = g.getBooleanOption("drawPoints", setName);
|
428 | var pointSize = g.getNumericOption("pointSize", setName);
|
429 |
|
430 | if (borderWidth && strokeWidth) {
|
431 | DygraphCanvasRenderer._drawStyledLine(e,
|
432 | g.getOption("strokeBorderColor", setName),
|
433 | strokeWidth + 2 * borderWidth,
|
434 | strokePattern,
|
435 | drawPoints,
|
436 | drawPointCallback,
|
437 | pointSize
|
438 | );
|
439 | }
|
440 |
|
441 | DygraphCanvasRenderer._drawStyledLine(e,
|
442 | e.color,
|
443 | strokeWidth,
|
444 | strokePattern,
|
445 | drawPoints,
|
446 | drawPointCallback,
|
447 | pointSize
|
448 | );
|
449 | };
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 | DygraphCanvasRenderer._errorPlotter = function(e) {
|
458 | var g = e.dygraph;
|
459 | var setName = e.setName;
|
460 | var errorBars = g.getBooleanOption("errorBars") ||
|
461 | g.getBooleanOption("customBars");
|
462 | if (!errorBars) return;
|
463 |
|
464 | var fillGraph = g.getBooleanOption("fillGraph", setName);
|
465 | if (fillGraph) {
|
466 | console.warn("Can't use fillGraph option with customBars or errorBars option");
|
467 | }
|
468 |
|
469 | var ctx = e.drawingContext;
|
470 | var color = e.color;
|
471 | var fillAlpha = g.getNumericOption('fillAlpha', setName);
|
472 | var stepPlot = g.getBooleanOption("stepPlot", setName);
|
473 | var points = e.points;
|
474 |
|
475 | var iter = utils.createIterator(points, 0, points.length,
|
476 | DygraphCanvasRenderer._getIteratorPredicate(
|
477 | g.getBooleanOption("connectSeparatedPoints", setName)));
|
478 |
|
479 | var newYs;
|
480 |
|
481 |
|
482 | var prevX = NaN;
|
483 | var prevY = NaN;
|
484 | var prevYs = [-1, -1];
|
485 |
|
486 | var rgb = utils.toRGB_(color);
|
487 | var err_color =
|
488 | 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
|
489 | ctx.fillStyle = err_color;
|
490 | ctx.beginPath();
|
491 |
|
492 | var isNullUndefinedOrNaN = function(x) {
|
493 | return (x === null ||
|
494 | x === undefined ||
|
495 | isNaN(x));
|
496 | };
|
497 |
|
498 | while (iter.hasNext) {
|
499 | var point = iter.next();
|
500 | if ((!stepPlot && isNullUndefinedOrNaN(point.y)) ||
|
501 | (stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY))) {
|
502 | prevX = NaN;
|
503 | continue;
|
504 | }
|
505 |
|
506 | newYs = [ point.y_bottom, point.y_top ];
|
507 | if (stepPlot) {
|
508 | prevY = point.y;
|
509 | }
|
510 |
|
511 |
|
512 |
|
513 | if (isNaN(newYs[0])) newYs[0] = point.y;
|
514 | if (isNaN(newYs[1])) newYs[1] = point.y;
|
515 |
|
516 | newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
|
517 | newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
|
518 | if (!isNaN(prevX)) {
|
519 | if (stepPlot) {
|
520 | ctx.moveTo(prevX, prevYs[0]);
|
521 | ctx.lineTo(point.canvasx, prevYs[0]);
|
522 | ctx.lineTo(point.canvasx, prevYs[1]);
|
523 | } else {
|
524 | ctx.moveTo(prevX, prevYs[0]);
|
525 | ctx.lineTo(point.canvasx, newYs[0]);
|
526 | ctx.lineTo(point.canvasx, newYs[1]);
|
527 | }
|
528 | ctx.lineTo(prevX, prevYs[1]);
|
529 | ctx.closePath();
|
530 | }
|
531 | prevYs = newYs;
|
532 | prevX = point.canvasx;
|
533 | }
|
534 | ctx.fill();
|
535 | };
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 | DygraphCanvasRenderer._fastCanvasProxy = function(context) {
|
545 | var pendingActions = [];
|
546 | var lastRoundedX = null;
|
547 | var lastFlushedX = null;
|
548 |
|
549 | var LINE_TO = 1,
|
550 | MOVE_TO = 2;
|
551 |
|
552 | var actionCount = 0;
|
553 |
|
554 |
|
555 |
|
556 | var compressActions = function(opt_losslessOnly) {
|
557 | if (pendingActions.length <= 1) return;
|
558 |
|
559 |
|
560 | for (var i = pendingActions.length - 1; i > 0; i--) {
|
561 | var action = pendingActions[i];
|
562 | if (action[0] == MOVE_TO) {
|
563 | var prevAction = pendingActions[i - 1];
|
564 | if (prevAction[1] == action[1] && prevAction[2] == action[2]) {
|
565 | pendingActions.splice(i, 1);
|
566 | }
|
567 | }
|
568 | }
|
569 |
|
570 |
|
571 | for (var i = 0; i < pendingActions.length - 1; ) {
|
572 | var action = pendingActions[i];
|
573 | if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) {
|
574 | pendingActions.splice(i, 1);
|
575 | } else {
|
576 | i++;
|
577 | }
|
578 | }
|
579 |
|
580 |
|
581 | if (pendingActions.length > 2 && !opt_losslessOnly) {
|
582 |
|
583 | var startIdx = 0;
|
584 | if (pendingActions[0][0] == MOVE_TO) startIdx++;
|
585 | var minIdx = null, maxIdx = null;
|
586 | for (var i = startIdx; i < pendingActions.length; i++) {
|
587 | var action = pendingActions[i];
|
588 | if (action[0] != LINE_TO) continue;
|
589 | if (minIdx === null && maxIdx === null) {
|
590 | minIdx = i;
|
591 | maxIdx = i;
|
592 | } else {
|
593 | var y = action[2];
|
594 | if (y < pendingActions[minIdx][2]) {
|
595 | minIdx = i;
|
596 | } else if (y > pendingActions[maxIdx][2]) {
|
597 | maxIdx = i;
|
598 | }
|
599 | }
|
600 | }
|
601 | var minAction = pendingActions[minIdx],
|
602 | maxAction = pendingActions[maxIdx];
|
603 | pendingActions.splice(startIdx, pendingActions.length - startIdx);
|
604 | if (minIdx < maxIdx) {
|
605 | pendingActions.push(minAction);
|
606 | pendingActions.push(maxAction);
|
607 | } else if (minIdx > maxIdx) {
|
608 | pendingActions.push(maxAction);
|
609 | pendingActions.push(minAction);
|
610 | } else {
|
611 | pendingActions.push(minAction);
|
612 | }
|
613 | }
|
614 | };
|
615 |
|
616 | var flushActions = function(opt_noLossyCompression) {
|
617 | compressActions(opt_noLossyCompression);
|
618 | for (var i = 0, len = pendingActions.length; i < len; i++) {
|
619 | var action = pendingActions[i];
|
620 | if (action[0] == LINE_TO) {
|
621 | context.lineTo(action[1], action[2]);
|
622 | } else if (action[0] == MOVE_TO) {
|
623 | context.moveTo(action[1], action[2]);
|
624 | }
|
625 | }
|
626 | if (pendingActions.length) {
|
627 | lastFlushedX = pendingActions[pendingActions.length - 1][1];
|
628 | }
|
629 | actionCount += pendingActions.length;
|
630 | pendingActions = [];
|
631 | };
|
632 |
|
633 | var addAction = function(action, x, y) {
|
634 | var rx = Math.round(x);
|
635 | if (lastRoundedX === null || rx != lastRoundedX) {
|
636 |
|
637 |
|
638 | var hasGapOnLeft = (lastRoundedX - lastFlushedX > 1),
|
639 | hasGapOnRight = (rx - lastRoundedX > 1),
|
640 | hasGap = hasGapOnLeft || hasGapOnRight;
|
641 | flushActions(hasGap);
|
642 | lastRoundedX = rx;
|
643 | }
|
644 | pendingActions.push([action, x, y]);
|
645 | };
|
646 |
|
647 | return {
|
648 | moveTo: function(x, y) {
|
649 | addAction(MOVE_TO, x, y);
|
650 | },
|
651 | lineTo: function(x, y) {
|
652 | addAction(LINE_TO, x, y);
|
653 | },
|
654 |
|
655 |
|
656 |
|
657 | stroke: function() { flushActions(true); context.stroke(); },
|
658 | fill: function() { flushActions(true); context.fill(); },
|
659 | beginPath: function() { flushActions(true); context.beginPath(); },
|
660 | closePath: function() { flushActions(true); context.closePath(); },
|
661 |
|
662 | _count: function() { return actionCount; }
|
663 | };
|
664 | };
|
665 |
|
666 |
|
667 |
|
668 |
|
669 |
|
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 | DygraphCanvasRenderer._fillPlotter = function(e) {
|
677 |
|
678 | if (e.singleSeriesName) return;
|
679 |
|
680 |
|
681 | if (e.seriesIndex !== 0) return;
|
682 |
|
683 | var g = e.dygraph;
|
684 | var setNames = g.getLabels().slice(1);
|
685 |
|
686 |
|
687 |
|
688 |
|
689 | for (var i = setNames.length; i >= 0; i--) {
|
690 | if (!g.visibility()[i]) setNames.splice(i, 1);
|
691 | }
|
692 |
|
693 | var anySeriesFilled = (function() {
|
694 | for (var i = 0; i < setNames.length; i++) {
|
695 | if (g.getBooleanOption("fillGraph", setNames[i])) return true;
|
696 | }
|
697 | return false;
|
698 | })();
|
699 |
|
700 | if (!anySeriesFilled) return;
|
701 |
|
702 | var area = e.plotArea;
|
703 | var sets = e.allSeriesPoints;
|
704 | var setCount = sets.length;
|
705 |
|
706 | var stackedGraph = g.getBooleanOption("stackedGraph");
|
707 | var colors = g.getColors();
|
708 |
|
709 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
716 | var baseline = {};
|
717 | var currBaseline;
|
718 | var prevStepPlot;
|
719 |
|
720 |
|
721 | var traceBackPath = function(ctx, baselineX, baselineY, pathBack) {
|
722 | ctx.lineTo(baselineX, baselineY);
|
723 | if (stackedGraph) {
|
724 | for (var i = pathBack.length - 1; i >= 0; i--) {
|
725 | var pt = pathBack[i];
|
726 | ctx.lineTo(pt[0], pt[1]);
|
727 | }
|
728 | }
|
729 | };
|
730 |
|
731 |
|
732 | for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
|
733 | var ctx = e.drawingContext;
|
734 | var setName = setNames[setIdx];
|
735 | if (!g.getBooleanOption('fillGraph', setName)) continue;
|
736 |
|
737 | var fillAlpha = g.getNumericOption('fillAlpha', setName);
|
738 | var stepPlot = g.getBooleanOption('stepPlot', setName);
|
739 | var color = colors[setIdx];
|
740 | var axis = g.axisPropertiesForSeries(setName);
|
741 | var axisY = 1.0 + axis.minyval * axis.yscale;
|
742 | if (axisY < 0.0) axisY = 0.0;
|
743 | else if (axisY > 1.0) axisY = 1.0;
|
744 | axisY = area.h * axisY + area.y;
|
745 |
|
746 | var points = sets[setIdx];
|
747 | var iter = utils.createIterator(points, 0, points.length,
|
748 | DygraphCanvasRenderer._getIteratorPredicate(
|
749 | g.getBooleanOption("connectSeparatedPoints", setName)));
|
750 |
|
751 |
|
752 | var prevX = NaN;
|
753 | var prevYs = [-1, -1];
|
754 | var newYs;
|
755 |
|
756 | var rgb = utils.toRGB_(color);
|
757 | var err_color =
|
758 | 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
|
759 | ctx.fillStyle = err_color;
|
760 | ctx.beginPath();
|
761 | var last_x, is_first = true;
|
762 |
|
763 |
|
764 |
|
765 | if (points.length > 2 * g.width_ || Dygraph.FORCE_FAST_PROXY) {
|
766 | ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx);
|
767 | }
|
768 |
|
769 |
|
770 |
|
771 |
|
772 |
|
773 | var pathBack = [];
|
774 |
|
775 |
|
776 |
|
777 |
|
778 | var point;
|
779 | while (iter.hasNext) {
|
780 | point = iter.next();
|
781 | if (!utils.isOK(point.y) && !stepPlot) {
|
782 | traceBackPath(ctx, prevX, prevYs[1], pathBack);
|
783 | pathBack = [];
|
784 | prevX = NaN;
|
785 | if (point.y_stacked !== null && !isNaN(point.y_stacked)) {
|
786 | baseline[point.canvasx] = area.h * point.y_stacked + area.y;
|
787 | }
|
788 | continue;
|
789 | }
|
790 | if (stackedGraph) {
|
791 | if (!is_first && last_x == point.xval) {
|
792 | continue;
|
793 | } else {
|
794 | is_first = false;
|
795 | last_x = point.xval;
|
796 | }
|
797 |
|
798 | currBaseline = baseline[point.canvasx];
|
799 | var lastY;
|
800 | if (currBaseline === undefined) {
|
801 | lastY = axisY;
|
802 | } else {
|
803 | if(prevStepPlot) {
|
804 | lastY = currBaseline[0];
|
805 | } else {
|
806 | lastY = currBaseline;
|
807 | }
|
808 | }
|
809 | newYs = [ point.canvasy, lastY ];
|
810 |
|
811 | if (stepPlot) {
|
812 |
|
813 |
|
814 | if (prevYs[0] === -1) {
|
815 | baseline[point.canvasx] = [ point.canvasy, axisY ];
|
816 | } else {
|
817 | baseline[point.canvasx] = [ point.canvasy, prevYs[0] ];
|
818 | }
|
819 | } else {
|
820 | baseline[point.canvasx] = point.canvasy;
|
821 | }
|
822 |
|
823 | } else {
|
824 | if (isNaN(point.canvasy) && stepPlot) {
|
825 | newYs = [ area.y + area.h, axisY ];
|
826 | } else {
|
827 | newYs = [ point.canvasy, axisY ];
|
828 | }
|
829 | }
|
830 | if (!isNaN(prevX)) {
|
831 |
|
832 | if (stepPlot) {
|
833 | ctx.lineTo(point.canvasx, prevYs[0]);
|
834 | ctx.lineTo(point.canvasx, newYs[0]);
|
835 | } else {
|
836 | ctx.lineTo(point.canvasx, newYs[0]);
|
837 | }
|
838 |
|
839 |
|
840 | if (stackedGraph) {
|
841 | pathBack.push([prevX, prevYs[1]]);
|
842 | if (prevStepPlot && currBaseline) {
|
843 |
|
844 | pathBack.push([point.canvasx, currBaseline[1]]);
|
845 | } else {
|
846 | pathBack.push([point.canvasx, newYs[1]]);
|
847 | }
|
848 | }
|
849 | } else {
|
850 | ctx.moveTo(point.canvasx, newYs[1]);
|
851 | ctx.lineTo(point.canvasx, newYs[0]);
|
852 | }
|
853 | prevYs = newYs;
|
854 | prevX = point.canvasx;
|
855 | }
|
856 | prevStepPlot = stepPlot;
|
857 | if (newYs && point) {
|
858 | traceBackPath(ctx, point.canvasx, newYs[1], pathBack);
|
859 | pathBack = [];
|
860 | }
|
861 | ctx.fill();
|
862 | }
|
863 | };
|
864 |
|
865 | export default DygraphCanvasRenderer;
|