diff --git a/context.js b/context.js index 0787292..4256edf 100644 --- a/context.js +++ b/context.js @@ -1041,12 +1041,13 @@ export default (function () { return; } - x = this.__matrixTransform(x, y).x; - y = this.__matrixTransform(x, y).y; - var scaleX = Math.hypot(this.__transformMatrix.a, this.__transformMatrix.b); - var scaleY = Math.hypot(this.__transformMatrix.c, this.__transformMatrix.d); - radiusX = radiusX * scaleX; - radiusY = radiusY * scaleY; + var transformedCenter = this.__matrixTransform(x, y); + x = transformedCenter.x; + y = transformedCenter.y; + var scale = this.__getTransformScale(); + radiusX = radiusX * scale.x; + radiusY = radiusY * scale.y; + rotation = rotation + this.__getTransformRotation() startAngle = startAngle % (2*Math.PI); endAngle = endAngle % (2*Math.PI); @@ -1074,8 +1075,14 @@ export default (function () { } else { largeArcFlag = diff > Math.PI ? 1 : 0; } - - this.lineTo(startX / scaleX, startY / scaleY); + + // Transform is already applied, so temporarily remove since lineTo + // will apply it again. + var currentTransform = this.__transformMatrix; + this.resetTransform(); + this.lineTo(startX, startY); + this.__transformMatrix = currentTransform; + this.__addPathCommand(format("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", { rx:radiusX, @@ -1335,6 +1342,25 @@ export default (function () { return new DOMPoint(x, y).matrixTransform(this.__transformMatrix) } + /** + * + * @returns The scale component of the transform matrix as {x,y}. + */ + Context.prototype.__getTransformScale = function() { + return { + x: Math.hypot(this.__transformMatrix.a, this.__transformMatrix.b), + y: Math.hypot(this.__transformMatrix.c, this.__transformMatrix.d) + }; + } + + /** + * + * @returns The rotation component of the transform matrix in radians. + */ + Context.prototype.__getTransformRotation = function() { + return Math.atan2(this.__transformMatrix.b, this.__transformMatrix.a); + } + /** * * @param {*} sx The x-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted. diff --git a/test/index.js b/test/index.js index 371484f..c7efca8 100644 --- a/test/index.js +++ b/test/index.js @@ -5,6 +5,7 @@ import arcTo2 from './tests/arcTo2' import arcToScaled from './tests/arcToScaled' import emptyArc from './tests/emptyArc' import ellipse from './tests/ellipse' +import ellipse2 from './tests/ellipse2' import fillstyle from './tests/fillstyle' import globalAlpha from './tests/globalalpha' import gradient from './tests/gradient' @@ -27,6 +28,7 @@ const tests = [ arcToScaled, emptyArc, ellipse, + ellipse2, fillstyle, globalAlpha, gradient, diff --git a/test/rendering.test.js b/test/rendering.test.js index 0318730..62a03b1 100644 --- a/test/rendering.test.js +++ b/test/rendering.test.js @@ -6,6 +6,7 @@ import arcTo2 from './tests/arcTo2' import arcToScaled from './tests/arcToScaled' import emptyArc from './tests/emptyArc' import ellipse from './tests/ellipse' +import ellipse2 from './tests/ellipse2' import fillstyle from './tests/fillstyle' import globalAlpha from './tests/globalalpha' import gradient from './tests/gradient' @@ -28,6 +29,7 @@ const tests = { arcToScaled, emptyArc, ellipse, + ellipse2, fillstyle, globalAlpha, gradient, diff --git a/test/tests/ellipse2.js b/test/tests/ellipse2.js new file mode 100644 index 0000000..73feaaa --- /dev/null +++ b/test/tests/ellipse2.js @@ -0,0 +1,33 @@ + +export default function ellipse2(ctx) { + // Draw a cylinder using ellipses and lines. + var w = 100, h = 100, rx = 50, ry = 10; + var scaleX = 1.5, scaleY = 1.2; + + ctx.rotate(Math.PI / 10); + ctx.scale(scaleX, scaleY); + ctx.translate(200, 25); + + ctx.beginPath(); + ctx.moveTo(-w / 2, -h / 2 + ry); + // upper arc top + ctx.ellipse(0, -h / 2 + ry, rx, ry, Math.PI, 0, Math.PI, 0); + ctx.moveTo(-w / 2, -h / 2 + ry); + // upper arc bottom + ctx.ellipse(0, -h / 2 + ry, rx, ry, Math.PI, 0, Math.PI, 1); + ctx.moveTo(-w / 2, -h / 2 + ry); + // left line + ctx.lineTo(-w / 2, + h / 2 - ry); + // lower arc + ctx.ellipse(0, h / 2 - ry, rx, ry, Math.PI, 0, Math.PI, 1); + // right line + ctx.lineTo(w / 2, -h / 2 + ry); + ctx.moveTo(-w / 2, -h / 2 + ry); + ctx.closePath(); + + // Remove scale before stroking because the SVG conversion is not correctly + // scaling the stroke as well. Without this the pixel differences are too + // high. + ctx.resetTransform(); + ctx.stroke(); +}; \ No newline at end of file