Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable arbitrary rotation of datapoints #5319

Merged
merged 9 commits into from Jul 7, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/charts/bubble.md
Expand Up @@ -51,6 +51,7 @@ The bubble chart allows a number of properties to be specified for each dataset.
| [`hitRadius`](#interactions) | `Number` | Yes | Yes | `1`
| [`label`](#labeling) | `String` | - | - | `undefined`
| [`pointStyle`](#styling) | `String` | Yes | Yes | `circle`
| [`pointRotation`](#styling) | `Number` | Yes | Yes | `0`
| [`radius`](#styling) | `Number` | Yes | Yes | `3`

### Labeling
Expand All @@ -67,6 +68,7 @@ The style of each bubble can be controlled with the following properties:
| `borderColor` | bubble border color
| `borderWidth` | bubble border width (in pixels)
| `pointStyle` | bubble [shape style](../configuration/elements#point-styles)
| `rotation` | bubble rotation (in degrees)

This comment was marked as outdated.

| `radius` | bubble radius (in pixels)

All these values, if `undefined`, fallback to the associated [`elements.point.*`](../configuration/elements.md#point-configuration) options.
Expand Down
1 change: 1 addition & 0 deletions docs/charts/line.md
Expand Up @@ -63,6 +63,7 @@ All point* properties can be specified as an array. If these are set to an array
| `pointBorderWidth` | `Number/Number[]` | The width of the point border in pixels.
| `pointRadius` | `Number/Number[]` | The radius of the point shape. If set to 0, the point is not rendered.
| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](../configuration/elements#point-styles)
| `pointRotation` | `Number/Number[]` | The rotation of the point in degrees.
| `pointHitRadius` | `Number/Number[]` | The pixel size of the non-displayed point that reacts to mouse events.
| `pointHoverBackgroundColor` | `Color/Color[]` | Point background color when hovered.
| `pointHoverBorderColor` | `Color/Color[]` | Point border color when hovered.
Expand Down
1 change: 1 addition & 0 deletions docs/charts/radar.md
Expand Up @@ -82,6 +82,7 @@ All point* properties can be specified as an array. If these are set to an array
| `pointBorderColor` | `Color/Color[]` | The border color for points.
| `pointBorderWidth` | `Number/Number[]` | The width of the point border in pixels.
| `pointRadius` | `Number/Number[]` | The radius of the point shape. If set to 0, the point is not rendered.
| `pointRotation` | `Number/Number[]` | The rotation of the point in degrees.
| `pointStyle` | `String/String[]/Image/Image[]` | Style of the point. [more...](#pointstyle)
| `pointHitRadius` | `Number/Number[]` | The pixel size of the non-displayed point that reacts to mouse events.
| `pointHoverBackgroundColor` | `Color/Color[]` | Point background color when hovered.
Expand Down
1 change: 1 addition & 0 deletions docs/configuration/elements.md
Expand Up @@ -19,6 +19,7 @@ Global point options: `Chart.defaults.global.elements.point`
| -----| ---- | --------| -----------
| `radius` | `Number` | `3` | Point radius.
| [`pointStyle`](#point-styles) | `String` | `circle` | Point style.
| `rotation` | `Number` | `0` | Point rotation (in degrees).
| `backgroundColor` | `Color` | `'rgba(0,0,0,0.1)'` | Point fill color.
| `borderWidth` | `Number` | `1` | Point stroke width.
| `borderColor` | `Color` | `'rgba(0,0,0,0.1)'` | Point stroke color.
Expand Down
5 changes: 3 additions & 2 deletions src/controllers/controller.bubble.js
Expand Up @@ -87,6 +87,7 @@ module.exports = function(Chart) {
borderWidth: options.borderWidth,
hitRadius: options.hitRadius,
pointStyle: options.pointStyle,
pointRotation: options.rotation,
radius: reset ? 0 : options.radius,
skip: custom.skip || isNaN(x) || isNaN(y),
x: x,
Expand Down Expand Up @@ -154,7 +155,8 @@ module.exports = function(Chart) {
'hoverBorderWidth',
'hoverRadius',
'hitRadius',
'pointStyle'
'pointStyle',
'rotation'

This comment was marked as resolved.

];

for (i = 0, ilen = keys.length; i < ilen; ++i) {
Expand All @@ -173,7 +175,6 @@ module.exports = function(Chart) {
dataset.radius,
options.radius
], context, index);

return values;
}
});
Expand Down
14 changes: 14 additions & 0 deletions src/controllers/controller.line.js
Expand Up @@ -148,6 +148,19 @@ module.exports = function(Chart) {
return borderWidth;
},

getPointRotation: function(point, index) {
var pointRotation = this.chart.options.elements.point.rotation;
var dataset = this.getDataset();
var custom = point.custom || {};

if (!isNaN(custom.rotation)) {
pointRotation = custom.rotation;
} else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
}
return pointRotation;
},

updateElement: function(point, index, reset) {
var me = this;
var meta = me.getMeta();
Expand Down Expand Up @@ -185,6 +198,7 @@ module.exports = function(Chart) {
// Appearance
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
rotation: me.getPointRotation(point, index),
backgroundColor: me.getPointBackgroundColor(point, index),
borderColor: me.getPointBorderColor(point, index),
borderWidth: me.getPointBorderWidth(point, index),
Expand Down
1 change: 1 addition & 0 deletions src/controllers/controller.radar.js
Expand Up @@ -106,6 +106,7 @@ module.exports = function(Chart) {
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),

// Tooltip
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
Expand Down
3 changes: 2 additions & 1 deletion src/elements/element.point.js
Expand Up @@ -68,6 +68,7 @@ module.exports = Element.extend({
var model = this._model;
var ctx = this._chart.ctx;
var pointStyle = vm.pointStyle;
var rotation = vm.rotation;
var radius = vm.radius;
var x = vm.x;
var y = vm.y;
Expand All @@ -82,7 +83,7 @@ module.exports = Element.extend({
ctx.strokeStyle = vm.borderColor || defaultColor;
ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
ctx.fillStyle = vm.backgroundColor || defaultColor;
helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
}
}
});
72 changes: 39 additions & 33 deletions src/helpers/helpers.canvas.js
Expand Up @@ -44,8 +44,9 @@ var exports = module.exports = {
}
},

drawPoint: function(ctx, style, radius, x, y) {
drawPoint: function(ctx, style, radius, x, y, rotation) {
var type, edgeLength, xOffset, yOffset, height, size;
rotation = rotation || 0;

if (style && typeof style === 'object') {
type = style.toString();
Expand All @@ -59,34 +60,38 @@ var exports = module.exports = {
return;
}

ctx.save();
ctx.translate(x, y);
ctx.rotate(rotation * Math.PI / 180);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how we can do differently right now but I'm a bit worry of these extra save / translate / rotate / restore because drawing point is already pretty slow. Anyway, if we are translating we should remove the use of x & y everywhere in the switch (and remove x = 0 and y = 0).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!


switch (style) {
// Default includes circle
default:
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.arc(0, 0, 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.moveTo(-edgeLength / 2, height / 3);
ctx.lineTo(edgeLength / 2, height / 3);
ctx.lineTo(0, -2 * height / 3);
ctx.closePath();
ctx.fill();
break;
case 'rect':
size = 1 / Math.SQRT2 * radius;
ctx.beginPath();
ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
ctx.fillRect(-size, -size, 2 * size, 2 * size);
ctx.strokeRect(-size, -size, 2 * size, 2 * size);
break;
case 'rectRounded':
var offset = radius / Math.SQRT2;
var leftX = x - offset;
var topY = y - offset;
var leftX = -offset;
var topY = -offset;
var sideSize = Math.SQRT2 * radius;
ctx.beginPath();
this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
Expand All @@ -96,60 +101,61 @@ var exports = module.exports = {
case 'rectRot':
size = 1 / Math.SQRT2 * radius;
ctx.beginPath();
ctx.moveTo(x - size, y);
ctx.lineTo(x, y + size);
ctx.lineTo(x + size, y);
ctx.lineTo(x, y - size);
ctx.moveTo(-size, 0);
ctx.lineTo(0, size);
ctx.lineTo(size, 0);
ctx.lineTo(0, -size);
ctx.closePath();
ctx.fill();
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.moveTo(0, radius);
ctx.lineTo(0, -radius);
ctx.moveTo(-radius, 0);
ctx.lineTo(radius, 0);
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.moveTo(-xOffset, -yOffset);
ctx.lineTo(xOffset, yOffset);
ctx.moveTo(-xOffset, yOffset);
ctx.lineTo(xOffset, -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);
ctx.moveTo(0, radius);
ctx.lineTo(0, -radius);
ctx.moveTo(-radius, 0);
ctx.lineTo(radius, 0);
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.moveTo(-xOffset, -yOffset);
ctx.lineTo(xOffset, yOffset);
ctx.moveTo(-xOffset, yOffset);
ctx.lineTo(xOffset, -yOffset);
ctx.closePath();
break;
case 'line':
ctx.beginPath();
ctx.moveTo(x - radius, y);
ctx.lineTo(x + radius, y);
ctx.moveTo(-radius, 0);
ctx.lineTo(radius, 0);
ctx.closePath();
break;
case 'dash':
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + radius, y);
ctx.moveTo(0, 0);
ctx.lineTo(radius, 0);
ctx.closePath();
break;
}

ctx.stroke();
ctx.restore();
},

clipArea: function(ctx, area) {
Expand Down