}
};
-
Chart.controllers.line = Chart.DatasetController.extend({
addElements: function() {
- var meta = this.getMeta();
+ var me = this;
+ var meta = me.getMeta();
+ var data = me.getDataset().data || [];
+ var value, i, ilen;
+
meta.dataset = meta.dataset || new Chart.elements.Line({
- _chart: this.chart.chart,
- _datasetIndex: this.index,
+ _chart: me.chart.chart,
+ _datasetIndex: me.index,
_points: meta.data
});
- helpers.each(this.getDataset().data, function(value, index) {
- meta.data[index] = meta.data[index] || new Chart.elements.Point({
- _chart: this.chart.chart,
- _datasetIndex: this.index,
- _index: index
+ for (i=0, ilen=data.length; i<ilen; ++i) {
+ value = data[i];
+ meta.data[i] = meta.data[i] || new Chart.elements.Point({
+ _chart: me.chart.chart,
+ _datasetIndex: me.index,
+ _index: i
});
- }, this);
+ }
},
addElementAndReset: function(index) {
+ var me = this;
+ var options = me.chart.options;
var point = new Chart.elements.Point({
- _chart: this.chart.chart,
- _datasetIndex: this.index,
+ _chart: me.chart.chart,
+ _datasetIndex: me.index,
_index: index
});
// Add to the points array and reset it
- this.getMeta().data.splice(index, 0, point);
- this.updateElement(point, index, true);
+ me.getMeta().data.splice(index, 0, point);
+ me.updateElement(point, index, true);
// Make sure bezier control points are updated
- if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0)
- this.updateBezierControlPoints();
+ if (options.showLines && options.elements.line.tension !== 0) {
+ me.updateBezierControlPoints();
+ }
},
update: function update(reset) {
- var meta = this.getMeta();
+ var me = this;
+ var meta = me.getMeta();
var line = meta.dataset;
- var lineElementOptions = this.chart.options.elements.line;
- var points = meta.data;
-
- var yScale = this.getScaleForId(meta.yAxisID);
- var xScale = this.getScaleForId(meta.xAxisID);
- var scaleBase;
+ var points = meta.data || [];
+ var options = me.chart.options;
+ var lineElementOptions = options.elements.line;
+ var yScale = me.getScaleForId(meta.yAxisID);
+ var xScale = me.getScaleForId(meta.xAxisID);
+ var scaleBase, i, ilen, dataset, custom;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
}
// Update Line
- if (this.chart.options.showLines) {
- // Utility
- line._scale = yScale;
- line._datasetIndex = this.index;
- // Data
- line._children = points;
- // Model
+ if (options.showLines) {
+ dataset = me.getDataset();
+ custom = line.custom || {};
- var dataset = this.getDataset();
- var custom = line.custom || {};
// Compatibility: If the properties are defined with only the old name, use those values
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
dataset.lineTension = dataset.tension;
}
+ // Utility
+ line._scale = yScale;
+ line._datasetIndex = me.index;
+ // Data
+ line._children = points;
+ // Model
line._model = {
// Appearance
tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
scaleBottom: yScale.bottom,
scaleZero: scaleBase
};
+
line.pivot();
}
// Update Points
- helpers.each(points, function(point, index) {
- this.updateElement(point, index, reset);
- }, this);
+ for (i=0, ilen=points.length; i<ilen; ++i) {
+ me.updateElement(points[i], i, reset);
+ }
- if (this.chart.options.showLines && lineElementOptions.tension !== 0) {
- this.updateBezierControlPoints();
+ if (options.showLines && lineElementOptions.tension !== 0) {
+ me.updateBezierControlPoints();
}
},
return backgroundColor;
},
+
getPointBorderColor: function(point, index) {
var borderColor = this.chart.options.elements.point.borderColor;
var dataset = this.getDataset();
return borderColor;
},
+
getPointBorderWidth: function(point, index) {
var borderWidth = this.chart.options.elements.point.borderWidth;
var dataset = this.getDataset();
},
updateElement: function(point, index, reset) {
- var meta = this.getMeta();
+ var me = this;
+ var meta = me.getMeta();
var custom = point.custom || {};
- var dataset = this.getDataset();
- var yScale = this.getScaleForId(meta.yAxisID);
- var xScale = this.getScaleForId(meta.xAxisID);
- var scaleBase;
+ var dataset = me.getDataset();
+ var datasetIndex = me.index;
+ var value = dataset.data[index];
+ var yScale = me.getScaleForId(meta.yAxisID);
+ var xScale = me.getScaleForId(meta.xAxisID);
+ var pointOptions = me.chart.options.elements.point;
+ var scaleBase, x, y;
if (yScale.min < 0 && yScale.max < 0) {
scaleBase = yScale.getPixelForValue(yScale.max);
scaleBase = yScale.getPixelForValue(0);
}
- // Utility
- point._chart = this.chart.chart;
- point._xScale = xScale;
- point._yScale = yScale;
- point._datasetIndex = this.index;
- point._index = index;
-
- // Desired view properties
-
// Compatibility: If the properties are defined with only the old name, use those values
- if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined))
- {
+ if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
dataset.pointRadius = dataset.radius;
}
- if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined))
- {
+ if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
dataset.pointHitRadius = dataset.hitRadius;
}
+ x = xScale.getPixelForValue(value, index, datasetIndex, me.chart.isCombo);
+ y = reset ? scaleBase : me.calculatePointY(value, index, datasetIndex, me.chart.isCombo);
+
+ // Utility
+ point._chart = me.chart.chart;
+ point._xScale = xScale;
+ point._yScale = yScale;
+ point._datasetIndex = datasetIndex;
+ point._index = index;
+
+ // Desired view properties
point._model = {
- x: xScale.getPixelForValue(dataset.data[index], index, this.index, this.chart.isCombo),
- y: reset ? scaleBase : this.calculatePointY(dataset.data[index], index, this.index, this.chart.isCombo),
+ x: x,
+ y: y,
+ skip: custom.skip || isNaN(x) || isNaN(y),
// Appearance
- radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius),
- pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, this.chart.options.elements.point.pointStyle),
- backgroundColor: this.getPointBackgroundColor(point, index),
- borderColor: this.getPointBorderColor(point, index),
- borderWidth: this.getPointBorderWidth(point, index),
+ radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
+ pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
+ backgroundColor: me.getPointBackgroundColor(point, index),
+ borderColor: me.getPointBorderColor(point, index),
+ borderWidth: me.getPointBorderWidth(point, index),
tension: meta.dataset._model ? meta.dataset._model.tension : 0,
// Tooltip
- hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, this.chart.options.elements.point.hitRadius)
+ hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
};
-
- point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
},
calculatePointY: function(value, index, datasetIndex, isCombo) {
- var meta = this.getMeta();
- var xScale = this.getScaleForId(meta.xAxisID);
- var yScale = this.getScaleForId(meta.yAxisID);
+ var me = this;
+ var chart = me.chart;
+ var meta = me.getMeta();
+ var xScale = me.getScaleForId(meta.xAxisID);
+ var yScale = me.getScaleForId(meta.yAxisID);
+ var sumPos = 0;
+ var sumNeg = 0;
+ var i, ds, dsMeta;
if (yScale.options.stacked) {
-
- var sumPos = 0,
- sumNeg = 0;
-
- for (var i = 0; i < datasetIndex; i++) {
- var ds = this.chart.data.datasets[i];
- var dsMeta = this.chart.getDatasetMeta(i);
- if (dsMeta.type === 'line' && this.chart.isDatasetVisible(i)) {
+ for (i = 0; i < datasetIndex; i++) {
+ ds = chart.data.datasets[i];
+ dsMeta = chart.getDatasetMeta(i);
+ if (dsMeta.type === 'line' && chart.isDatasetVisible(i)) {
if (ds.data[index] < 0) {
sumNeg += ds.data[index] || 0;
} else {
},
updateBezierControlPoints: function() {
- // Update bezier control points
var meta = this.getMeta();
- helpers.each(meta.data, function(point, index) {
- var controlPoints = helpers.splineCurve(
- helpers.previousItem(meta.data, index)._model,
- point._model,
- helpers.nextItem(meta.data, index)._model,
+ var area = this.chart.chartArea;
+ var points = meta.data || [];
+ var i, ilen, point, model, controlPoints;
+
+ for (i=0, ilen=points.length; i<ilen; ++i) {
+ point = points[i];
+ model = point._model;
+ controlPoints = helpers.splineCurve(
+ helpers.previousItem(points, i)._model,
+ model,
+ helpers.nextItem(points, i)._model,
meta.dataset._model.tension
);
// Prevent the bezier going outside of the bounds of the graph
- point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left);
- point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
-
- point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left);
- point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top);
+ model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, area.right), area.left);
+ model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, area.bottom), area.top);
+ model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, area.right), area.left);
+ model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, area.bottom), area.top);
// Now pivot the point for animation
point.pivot();
- }, this);
+ }
},
draw: function(ease) {
var meta = this.getMeta();
+ var points = meta.data || [];
var easingDecimal = ease || 1;
+ var i, ilen;
// Transition Point Locations
- helpers.each(meta.data, function(point) {
- point.transition(easingDecimal);
- });
+ for (i=0, ilen=points.length; i<ilen; ++i) {
+ points[i].transition(easingDecimal);
+ }
// Transition and Draw the line
- if (this.chart.options.showLines)
+ if (this.chart.options.showLines) {
meta.dataset.transition(easingDecimal).draw();
+ }
// Draw the points
- helpers.each(meta.data, function(point) {
- point.draw();
- });
+ for (i=0, ilen=points.length; i<ilen; ++i) {
+ points[i].draw();
+ }
},
setHoverStyle: function(point) {
var custom = point.custom || {};
var model = point._model;
- model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
- model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
- model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
- model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
+ model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
+ model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
+ model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
+ model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
},
removeHoverStyle: function(point) {
- var dataset = this.chart.data.datasets[point._datasetIndex];
+ var me = this;
+ var dataset = me.chart.data.datasets[point._datasetIndex];
var index = point._index;
var custom = point.custom || {};
var model = point._model;
// Compatibility: If the properties are defined with only the old name, use those values
- if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined))
- {
+ if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
dataset.pointRadius = dataset.radius;
}
- model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.chart.options.elements.point.radius);
- model.backgroundColor = this.getPointBackgroundColor(point, index);
- model.borderColor = this.getPointBorderColor(point, index);
- model.borderWidth = this.getPointBorderWidth(point, index);
+ model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
+ model.backgroundColor = me.getPointBackgroundColor(point, index);
+ model.borderColor = me.getPointBorderColor(point, index);
+ model.borderWidth = me.getPointBorderWidth(point, index);
}
});
};
hoverBorderWidth: 1
};
-
Chart.elements.Point = Chart.Element.extend({
inRange: function(mouseX, mouseY) {
var vm = this._view;
},
inLabelRange: function(mouseX) {
var vm = this._view;
- return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
+ return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
},
tooltipPosition: function() {
var vm = this._view;
};
},
draw: function() {
- var vm = this._view,
- x = vm.x,
- y = vm.y;
+ var vm = this._view;
var ctx = this._chart.ctx;
+ var pointStyle = vm.pointStyle;
+ var radius = vm.radius;
+ var x = vm.x;
+ var y = vm.y;
+ var type, edgeLength, xOffset, yOffset, height, size;
if (vm.skip) {
return;
}
- var pointStyle = vm.pointStyle;
- if (typeof pointStyle === 'object' && ((pointStyle.toString() === '[object HTMLImageElement]') || (pointStyle.toString() === '[object HTMLCanvasElement]'))) {
- ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
- return;
+ if (typeof pointStyle === 'object') {
+ type = pointStyle.toString();
+ if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
+ ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2);
+ return;
+ }
}
- if (!isNaN(vm.radius) && vm.radius > 0) {
-
- ctx.strokeStyle = vm.borderColor || defaultColor;
- ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
-
- ctx.fillStyle = vm.backgroundColor || defaultColor;
-
- var radius = vm.radius;
-
- var xOffset,
- yOffset;
+ if (isNaN(radius) || radius <= 0) {
+ return;
+ }
- switch (pointStyle) {
- // Default includes circle
- default:
- ctx.beginPath();
- ctx.arc(x, y, radius, 0, Math.PI * 2);
- ctx.closePath();
- ctx.fill();
- break;
- case 'triangle':
- ctx.beginPath();
- var edgeLength = 3 * radius / Math.sqrt(3);
- var height = edgeLength * Math.sqrt(3) / 2;
- ctx.moveTo(x - edgeLength / 2, y + height / 3);
- ctx.lineTo(x + edgeLength / 2, y + height / 3);
- ctx.lineTo(x, y - 2 * height / 3);
- ctx.closePath();
- ctx.fill();
- break;
- case 'rect':
- ctx.fillRect(x - 1 / Math.SQRT2 * radius, y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
- ctx.strokeRect(x - 1 / Math.SQRT2 * radius, y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
- break;
- case 'rectRot':
- ctx.translate(x, y);
- ctx.rotate(Math.PI / 4);
- ctx.fillRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
- ctx.strokeRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius);
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- break;
- case 'cross':
- ctx.beginPath();
- ctx.moveTo(x, y + radius);
- ctx.lineTo(x, y - radius);
- ctx.moveTo(x - radius, y);
- ctx.lineTo(x + radius, y);
- ctx.closePath();
- break;
- case 'crossRot':
- ctx.beginPath();
- xOffset = Math.cos(Math.PI / 4) * radius;
- yOffset = Math.sin(Math.PI / 4) * radius;
- ctx.moveTo(x - xOffset, y - yOffset);
- ctx.lineTo(x + xOffset, y + yOffset);
- ctx.moveTo(x - xOffset, y + yOffset);
- ctx.lineTo(x + xOffset, y - yOffset);
- ctx.closePath();
- break;
- case 'star':
- ctx.beginPath();
- ctx.moveTo(x, y + radius);
- ctx.lineTo(x, y - radius);
- ctx.moveTo(x - radius, y);
- ctx.lineTo(x + radius, y);
- xOffset = Math.cos(Math.PI / 4) * radius;
- yOffset = Math.sin(Math.PI / 4) * radius;
- ctx.moveTo(x - xOffset, y - yOffset);
- ctx.lineTo(x + xOffset, y + yOffset);
- ctx.moveTo(x - xOffset, y + yOffset);
- ctx.lineTo(x + xOffset, y - yOffset);
- ctx.closePath();
- break;
- case 'line':
- ctx.beginPath();
- ctx.moveTo(x - radius, y);
- ctx.lineTo(x + radius, y);
- ctx.closePath();
- break;
- case 'dash':
- ctx.beginPath();
- ctx.moveTo(x, y);
- ctx.lineTo(x + radius, y);
- ctx.closePath();
- break;
- }
+ ctx.strokeStyle = vm.borderColor || defaultColor;
+ ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
+ ctx.fillStyle = vm.backgroundColor || defaultColor;
- ctx.stroke();
+ switch (pointStyle) {
+ // Default includes circle
+ default:
+ ctx.beginPath();
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case 'triangle':
+ ctx.beginPath();
+ edgeLength = 3 * radius / Math.sqrt(3);
+ height = edgeLength * Math.sqrt(3) / 2;
+ ctx.moveTo(x - edgeLength / 2, y + height / 3);
+ ctx.lineTo(x + edgeLength / 2, y + height / 3);
+ ctx.lineTo(x, y - 2 * height / 3);
+ ctx.closePath();
+ ctx.fill();
+ break;
+ case 'rect':
+ size = 1 / Math.SQRT2 * radius;
+ ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
+ ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
+ break;
+ case 'rectRot':
+ ctx.translate(x, y);
+ ctx.rotate(Math.PI / 4);
+ size = 1 / Math.SQRT2 * radius;
+ ctx.fillRect(-size, -size, 2 * size, 2 * size);
+ ctx.strokeRect(-size, -size, 2 * size, 2 * size);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ break;
+ case 'cross':
+ ctx.beginPath();
+ ctx.moveTo(x, y + radius);
+ ctx.lineTo(x, y - radius);
+ ctx.moveTo(x - radius, y);
+ ctx.lineTo(x + radius, y);
+ ctx.closePath();
+ break;
+ case 'crossRot':
+ ctx.beginPath();
+ xOffset = Math.cos(Math.PI / 4) * radius;
+ yOffset = Math.sin(Math.PI / 4) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.moveTo(x - xOffset, y + yOffset);
+ ctx.lineTo(x + xOffset, y - yOffset);
+ ctx.closePath();
+ break;
+ case 'star':
+ ctx.beginPath();
+ ctx.moveTo(x, y + radius);
+ ctx.lineTo(x, y - radius);
+ ctx.moveTo(x - radius, y);
+ ctx.lineTo(x + radius, y);
+ xOffset = Math.cos(Math.PI / 4) * radius;
+ yOffset = Math.sin(Math.PI / 4) * radius;
+ ctx.moveTo(x - xOffset, y - yOffset);
+ ctx.lineTo(x + xOffset, y + yOffset);
+ ctx.moveTo(x - xOffset, y + yOffset);
+ ctx.lineTo(x + xOffset, y - yOffset);
+ ctx.closePath();
+ break;
+ case 'line':
+ ctx.beginPath();
+ ctx.moveTo(x - radius, y);
+ ctx.lineTo(x + radius, y);
+ ctx.closePath();
+ break;
+ case 'dash':
+ ctx.beginPath();
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + radius, y);
+ ctx.closePath();
+ break;
}
+
+ ctx.stroke();
}
});
-};
\ No newline at end of file
+};