]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Enable arbitrary rotation of datapoints (#5319)
authorJoel Hamilton <joelhamilton5@gmail.com>
Sat, 7 Jul 2018 15:54:05 +0000 (11:54 -0400)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Sat, 7 Jul 2018 15:54:05 +0000 (17:54 +0200)
docs/charts/bubble.md
docs/charts/line.md
docs/charts/radar.md
docs/configuration/elements.md
src/controllers/controller.bubble.js
src/controllers/controller.line.js
src/controllers/controller.radar.js
src/elements/element.point.js
src/helpers/helpers.canvas.js
test/specs/element.point.tests.js

index 4cb2ee6994fa11ef6dfa6c4f3fb4d7e18ef28cc8..e9f8a7216b194df4e1489ba533c40fb4aed082c0 100644 (file)
@@ -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`
+| [`rotation`](#styling) | `Number` | Yes | Yes | `0`
 | [`radius`](#styling) | `Number` | Yes | Yes | `3`
 
 ### Labeling
@@ -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)
 | `radius` | bubble radius (in pixels)
 
 All these values, if `undefined`, fallback to the associated [`elements.point.*`](../configuration/elements.md#point-configuration) options.
index 90471e462dd8a289c5f433e82c1c9e84d693a96c..db0245b1e0c370058b19844aaf1193ab3893896a 100644 (file)
@@ -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.
index b8a41c8384c44eab0bd1c3a5e91cdfca5a5eb9a2..947e15a3102925501a2cafb79a132f21119bcc2c 100644 (file)
@@ -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.
index 5375a7e9f1f99cd07e38249ec1a744e4a1a38f0f..148b5f39a54e522a024597f9aca4d4b96721d9a0 100644 (file)
@@ -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.
index b808b366b1f6e29e5fca804fa0a51ec12de35ae8..f14e512300ff50ba5c4c7875addda3901989c813 100644 (file)
@@ -87,6 +87,7 @@ module.exports = function(Chart) {
                                borderWidth: options.borderWidth,
                                hitRadius: options.hitRadius,
                                pointStyle: options.pointStyle,
+                               rotation: options.rotation,
                                radius: reset ? 0 : options.radius,
                                skip: custom.skip || isNaN(x) || isNaN(y),
                                x: x,
@@ -146,7 +147,8 @@ module.exports = function(Chart) {
                                'hoverBorderWidth',
                                'hoverRadius',
                                'hitRadius',
-                               'pointStyle'
+                               'pointStyle',
+                               'rotation'
                        ];
 
                        for (i = 0, ilen = keys.length; i < ilen; ++i) {
@@ -165,7 +167,6 @@ module.exports = function(Chart) {
                                dataset.radius,
                                options.radius
                        ], context, index);
-
                        return values;
                }
        });
index 2d1856e01e1295b197e1d262601179b406766542..4a18bdadb3c4de952379fe5ca4c0ff3cf5b0a5d7 100644 (file)
@@ -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();
@@ -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),
index 10d71cdacb56cd9d8f68321d483e67a5520a2468..89717157a3746f21a2683ff0cddbdbd02298566c 100644 (file)
@@ -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)
index fa7c42ec641f23d637118ab2d017154320a08c06..2bcdc88f0f83b858e38402d9a594b1981a0a710d 100644 (file)
@@ -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;
@@ -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);
                }
        }
 });
index 4bfae9c48fd89eaa60ed260cdee417c79dac0346..26f7d37212f31da8a803011d9c78c6ec5c9f343d 100644 (file)
@@ -46,8 +46,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();
@@ -61,11 +62,15 @@ var exports = module.exports = {
                        return;
                }
 
+               ctx.save();
+               ctx.translate(x, y);
+               ctx.rotate(rotation * Math.PI / 180);
+
                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;
@@ -73,22 +78,22 @@ var exports = module.exports = {
                        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();
 
@@ -104,60 +109,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) {
index 0321112fb127ee9f211d4123d1bd71fa475f05c3..b2f803416b5bf2e136fdb9c996aa145296045e97 100644 (file)
@@ -108,6 +108,7 @@ describe('Point element tests', function() {
                point._view = {
                        radius: 2,
                        pointStyle: 'circle',
+                       rotation: 25,
                        hitRadius: 3,
                        borderColor: 'rgba(1, 2, 3, 1)',
                        borderWidth: 6,
@@ -128,12 +129,21 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'arc',
-                       args: [10, 15, 2, 0, 2 * Math.PI]
+                       args: [0, 0, 2, 0, 2 * Math.PI]
                }, {
                        name: 'closePath',
                        args: [],
@@ -143,6 +153,9 @@ describe('Point element tests', function() {
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -158,18 +171,27 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10 - 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3]
+                       args: [0 - 3 * 2 / Math.sqrt(3) / 2, 0 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3]
                }, {
                        name: 'lineTo',
-                       args: [10 + 3 * 2 / Math.sqrt(3) / 2, 15 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
+                       args: [0 + 3 * 2 / Math.sqrt(3) / 2, 0 + 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
                }, {
                        name: 'lineTo',
-                       args: [10, 15 - 2 * 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
+                       args: [0, 0 - 2 * 3 * 2 / Math.sqrt(3) * Math.sqrt(3) / 2 / 3],
                }, {
                        name: 'closePath',
                        args: [],
@@ -179,6 +201,9 @@ describe('Point element tests', function() {
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -194,18 +219,30 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'fillRect',
-                       args: [10 - 1 / Math.SQRT2 * 2, 15 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
+                       args: [0 - 1 / Math.SQRT2 * 2, 0 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
                }, {
                        name: 'strokeRect',
-                       args: [10 - 1 / Math.SQRT2 * 2, 15 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
+                       args: [0 - 1 / Math.SQRT2 * 2, 0 - 1 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2, 2 / Math.SQRT2 * 2]
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                var drawRoundedRectangleSpy = jasmine.createSpy('drawRoundedRectangle');
@@ -218,8 +255,8 @@ describe('Point element tests', function() {
 
                expect(drawRoundedRectangleSpy).toHaveBeenCalledWith(
                        mockContext,
-                       10 - offset,
-                       15 - offset,
+                       0 - offset,
+                       0 - offset,
                        Math.SQRT2 * 2,
                        Math.SQRT2 * 2,
                        2 * 0.425
@@ -245,21 +282,30 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10 - 1 / Math.SQRT2 * 2, 15]
+                       args: [0 - 1 / Math.SQRT2 * 2, 0]
                }, {
                        name: 'lineTo',
-                       args: [10, 15 + 1 / Math.SQRT2 * 2]
+                       args: [0, 0 + 1 / Math.SQRT2 * 2]
                }, {
                        name: 'lineTo',
-                       args: [10 + 1 / Math.SQRT2 * 2, 15],
+                       args: [0 + 1 / Math.SQRT2 * 2, 0],
                }, {
                        name: 'lineTo',
-                       args: [10, 15 - 1 / Math.SQRT2 * 2],
+                       args: [0, 0 - 1 / Math.SQRT2 * 2],
                }, {
                        name: 'closePath',
                        args: []
@@ -269,6 +315,9 @@ describe('Point element tests', function() {
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -284,27 +333,39 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10, 17]
+                       args: [0, 2]
                }, {
                        name: 'lineTo',
-                       args: [10, 13],
+                       args: [0, -2],
                }, {
                        name: 'moveTo',
-                       args: [8, 15],
+                       args: [-2, 0],
                }, {
                        name: 'lineTo',
-                       args: [12, 15],
+                       args: [2, 0],
                }, {
                        name: 'closePath',
                        args: [],
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -320,27 +381,39 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
+                       args: [0 - Math.cos(Math.PI / 4) * 2, 0 - Math.sin(Math.PI / 4) * 2]
                }, {
                        name: 'lineTo',
-                       args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+                       args: [0 + Math.cos(Math.PI / 4) * 2, 0 + Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'moveTo',
-                       args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+                       args: [0 - Math.cos(Math.PI / 4) * 2, 0 + Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'lineTo',
-                       args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
+                       args: [0 + Math.cos(Math.PI / 4) * 2, 0 - Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'closePath',
                        args: [],
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -356,39 +429,51 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10, 17]
+                       args: [0, 2]
                }, {
                        name: 'lineTo',
-                       args: [10, 13],
+                       args: [0, -2],
                }, {
                        name: 'moveTo',
-                       args: [8, 15],
+                       args: [-2, 0],
                }, {
                        name: 'lineTo',
-                       args: [12, 15],
+                       args: [2, 0],
                }, {
                        name: 'moveTo',
-                       args: [10 - Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2]
+                       args: [0 - Math.cos(Math.PI / 4) * 2, 0 - Math.sin(Math.PI / 4) * 2]
                }, {
                        name: 'lineTo',
-                       args: [10 + Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+                       args: [0 + Math.cos(Math.PI / 4) * 2, 0 + Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'moveTo',
-                       args: [10 - Math.cos(Math.PI / 4) * 2, 15 + Math.sin(Math.PI / 4) * 2],
+                       args: [0 - Math.cos(Math.PI / 4) * 2, 0 + Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'lineTo',
-                       args: [10 + Math.cos(Math.PI / 4) * 2, 15 - Math.sin(Math.PI / 4) * 2],
+                       args: [0 + Math.cos(Math.PI / 4) * 2, 0 - Math.sin(Math.PI / 4) * 2],
                }, {
                        name: 'closePath',
                        args: [],
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -404,21 +489,33 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [8, 15]
+                       args: [-2, 0]
                }, {
                        name: 'lineTo',
-                       args: [12, 15],
+                       args: [2, 0],
                }, {
                        name: 'closePath',
                        args: [],
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
                mockContext.resetCalls();
@@ -434,21 +531,33 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0, 255, 0)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [25 * Math.PI / 180]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'moveTo',
-                       args: [10, 15]
+                       args: [0, 0]
                }, {
                        name: 'lineTo',
-                       args: [12, 15],
+                       args: [2, 0],
                }, {
                        name: 'closePath',
                        args: [],
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
 
        });
@@ -483,12 +592,21 @@ describe('Point element tests', function() {
                }, {
                        name: 'setFillStyle',
                        args: ['rgba(0,0,0,0.1)']
+               }, {
+                       name: 'save',
+                       args: []
+               }, {
+                       name: 'translate',
+                       args: [10, 15]
+               }, {
+                       name: 'rotate',
+                       args: [0]
                }, {
                        name: 'beginPath',
                        args: []
                }, {
                        name: 'arc',
-                       args: [10, 15, 2, 0, 2 * Math.PI]
+                       args: [0, 0, 2, 0, 2 * Math.PI]
                }, {
                        name: 'closePath',
                        args: [],
@@ -498,6 +616,9 @@ describe('Point element tests', function() {
                }, {
                        name: 'stroke',
                        args: []
+               }, {
+                       name: 'restore',
+                       args: []
                }]);
        });