};
/**
- *
* @private
*/
DygraphCanvasRenderer.prototype._drawStyledLine = function(
- ctx, i, setName, color, strokeWidth, strokePattern, drawPoints,
+ ctx, setIdx, setName, color, strokeWidth, strokePattern, drawPoints,
drawPointCallback, pointSize) {
// TODO(konigsberg): Compute attributes outside this method call.
var stepPlot = this.attr_("stepPlot");
- var firstIndexInSet = this.layout.setPointsOffsets[i];
- var setLength = this.layout.setPointsLengths[i];
- var points = this.layout.points;
if (!Dygraph.isArrayLike(strokePattern)) {
strokePattern = null;
}
var drawGapPoints = this.dygraph_.attr_('drawGapEdgePoints', setName);
- var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ var points = this.layout.points[setIdx];
+ var iter = Dygraph.createIterator(points, 0, points.length,
DygraphCanvasRenderer._getIteratorPredicate(
this.attr_("connectSeparatedPoints")));
ctx.strokeStyle = color;
ctx.lineWidth = strokeWidth;
- for (var i = iter.start_; i < iter.end_; i++) {
- // while (iter.hasNext) {
- point = iter.array_[i];
- if (iter.predicate_) {
- while (i < iter.end_ && !iter.predicate_(iter.array_, i)) {
+ // NOTE: we break the iterator's encapsulation here for about a 25% speedup.
+ var arr = iter.array_;
+ var limit = iter.end_;
+ var predicate = iter.predicate_;
+
+ for (var i = iter.start_; i < limit; i++) {
+ point = arr[i];
+ if (predicate) {
+ while (i < limit && !predicate(arr, i)) {
i++;
}
- if (i == iter.end_) break;
- point = iter.array_[i];
+ if (i == limit) break;
+ point = arr[i];
}
if (point.canvasy === null || point.canvasy != point.canvasy) {
} else {
isIsolated = false;
if (drawGapPoints || !prevCanvasX) {
- // nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
- // var next_i = i + 1;
- // while (next_i < iter.end_ && (!iter.predicate_ || !iter.predicate_(iter.array_, next_i))) {
- // next_i++;
- // }
iter.nextIdx_ = i;
var peek = iter.next();
nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;
- // nextCanvasY = next_i < iter.end_ ? iter.array_[next_i].canvasy : null;
- // TODO: we calculate isNullOrNaN for this point, and the next, and then,
- // when we iterate, test for isNullOrNaN again. Why bother?
var isNextCanvasYNullOrNaN = nextCanvasY === null ||
nextCanvasY != nextCanvasY;
isIsolated = (!prevCanvasX && isNextCanvasYNullOrNaN);
ctx.lineTo(point.canvasx, prevCanvasY);
prevCanvasX = point.canvasx;
}
+
+ // TODO(danvk): this moveTo is rarely necessary
ctx.moveTo(prevCanvasX, prevCanvasY);
ctx.lineTo(point.canvasx, point.canvasy);
}
// TODO(bhs): this loop is a hot-spot for high-point-count charts. These
// transformations can be pushed into the canvas via linear transformation
// matrices.
- var points = this.layout.points;
- for (i = points.length; i--;) {
- var point = points[i];
- point.canvasx = this.area.w * point.x + this.area.x;
- point.canvasy = this.area.h * point.y + this.area.y;
+ // NOTE(danvk): this is trickier than it sounds at first. The transformation
+ // needs to be done before the .moveTo() and .lineTo() calls, but must be
+ // undone before the .stroke() call to ensure that the stroke width is
+ // unaffected. An alternative is to reduce the stroke width in the
+ // transformed coordinate space, but you can't specify different values for
+ // each dimension (as you can with .scale()). The speedup here is ~12%.
+ var sets = this.layout.points;
+ for (i = sets.length; i--;) {
+ var points = sets[i];
+ for (var j = points.length; j--;) {
+ var point = points[j];
+ point.canvasx = this.area.w * point.x + this.area.x;
+ point.canvasy = this.area.h * point.y + this.area.y;
+ }
}
// Draw any "fills", i.e. error bars or the filled area under a series.
var newYs;
- for (var i = 0; i < setCount; i++) {
- var setName = setNames[i];
+ for (var setIdx = 0; setIdx < setCount; setIdx++) {
+ var setName = setNames[setIdx];
var axis = this.dygraph_.axisPropertiesForSeries(setName);
var color = this.colors[setName];
- var firstIndexInSet = this.layout.setPointsOffsets[i];
- var setLength = this.layout.setPointsLengths[i];
-
- var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ var points = this.layout.points[setIdx];
+ var iter = Dygraph.createIterator(points, 0, points.length,
DygraphCanvasRenderer._getIteratorPredicate(
this.attr_("connectSeparatedPoints")));
continue;
}
- // TODO(danvk): here
if (stepPlot) {
newYs = [ point.y_bottom, point.y_top ];
prevY = point.y;
var currBaseline;
// process sets in reverse order (needed for stacked graphs)
- for (var i = setCount - 1; i >= 0; i--) {
- var setName = setNames[i];
+ for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
+ var setName = setNames[setIdx];
var color = this.colors[setName];
var axis = this.dygraph_.axisPropertiesForSeries(setName);
var axisY = 1.0 + axis.minyval * axis.yscale;
if (axisY < 0.0) axisY = 0.0;
else if (axisY > 1.0) axisY = 1.0;
axisY = this.area.h * axisY + this.area.y;
- var firstIndexInSet = this.layout.setPointsOffsets[i];
- var setLength = this.layout.setPointsLengths[i];
- var iter = Dygraph.createIterator(points, firstIndexInSet, setLength,
+ var points = this.layout.points[setIdx];
+ var iter = Dygraph.createIterator(points, 0, points.length,
DygraphCanvasRenderer._getIteratorPredicate(
this.attr_("connectSeparatedPoints")));