-DygraphCanvasRenderer.prototype._drawPointsOnLine = function(ctx, pointsOnLine, drawPointCallback, setName, color, pointSize) {
- for (var idx = 0; idx < pointsOnLine.length; idx++) {
- var cb = pointsOnLine[idx];
- ctx.save();
- drawPointCallback(
- this.dygraph_, setName, ctx, cb[0], cb[1], color, pointSize);
- ctx.restore();
- }
-}
-
-DygraphCanvasRenderer.prototype._drawNonTrivialLine = function(
- ctx, iter, setName, color, strokeWidth, strokePattern, drawPointCallback, pointSize, drawPoints, drawGapPoints, stepPlot) {
- var prevX = null;
- var prevY = null;
- var nextY = null;
- var point;
- var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
- var first = true;
- while(iter.hasNext()) {
- point = iter.next();
- nextY = iter.hasNext() ? iter.peek().canvasy : null;
- if (DygraphCanvasRenderer.isNullOrNaN_(point.canvasy)) {
- if (stepPlot && prevX !== null) {
- // Draw a horizontal line to the start of the missing data
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = this.attr_('strokeWidth');
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- ctx.stroke();
- }
- // this will make us move to the next point, not draw a line to it.
- prevX = prevY = null;
- } else {
- // A point is "isolated" if it is non-null but both the previous
- // and next points are null.
- var isIsolated = (!prevX && DygraphCanvasRenderer.isNullOrNaN_(nextY));
- if (drawGapPoints) {
- // Also consider a point to be is "isolated" if it's adjacent to a
- // null point, excluding the graph edges.
- if ((!first && !prevX) ||
- (iter.hasNext() && DygraphCanvasRenderer.isNullOrNaN_(nextY))) {
- isIsolated = true;
- }
- }
- if (prevX === null) {
- prevX = point.canvasx;
- prevY = point.canvasy;
- } else {
- // Skip over points that will be drawn in the same pixel.
- if (Math.round(prevX) == Math.round(point.canvasx) &&
- Math.round(prevY) == Math.round(point.canvasy)) {
- continue;
- }
- // TODO(antrob): skip over points that lie on a line that is already
- // going to be drawn. There is no need to have more than 2
- // consecutive points that are collinear.
- if (strokeWidth) {
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = strokeWidth;
- if (stepPlot) {
- this._dashedLine(ctx, prevX, prevY, point.canvasx, prevY, strokePattern);
- prevX = point.canvasx;
- }
- this._dashedLine(ctx, prevX, prevY, point.canvasx, point.canvasy, strokePattern);
- prevX = point.canvasx;
- prevY = point.canvasy;
- ctx.stroke();
+
+/**
+ * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are
+ * superfluous. It accumulates all movements which haven't changed the x-value
+ * and only applies the two with the most extreme y-values.
+ *
+ * Calls to lineTo/moveTo must have non-decreasing x-values.
+ */
+DygraphCanvasRenderer._fastCanvasProxy = function(context) {
+ var pendingActions = []; // array of [type, x, y] tuples
+ var lastRoundedX = null;
+
+ var LINE_TO = 1,
+ MOVE_TO = 2;
+
+ var actionCount = 0; // number of moveTos and lineTos passed to context.
+
+ // Drop superfluous motions
+ // Assumes all pendingActions have the same (rounded) x-value.
+ var compressActions = function(opt_losslessOnly) {
+ if (pendingActions.length <= 1) return;
+
+ // Lossless compression: drop inconsequential moveTos.
+ for (var i = pendingActions.length - 1; i > 0; i--) {
+ var action = pendingActions[i];
+ if (action[0] == MOVE_TO) {
+ var prevAction = pendingActions[i - 1];
+ if (prevAction[1] == action[1] && prevAction[2] == action[2]) {
+ pendingActions.splice(i, 1);