diff --git a/context.js b/context.js index 45cfcb4..d0de57b 100644 --- a/context.js +++ b/context.js @@ -1030,6 +1030,63 @@ export default (function () { this.__currentPosition = {x: endX, y: endY}; }; + /** + * Ellipse command! + */ + Context.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterClockwise) { + if (startAngle === endAngle) { + 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; + + startAngle = startAngle % (2*Math.PI); + endAngle = endAngle % (2*Math.PI); + if(startAngle === endAngle) { + endAngle = ((endAngle + (2*Math.PI)) - 0.001 * (counterClockwise ? -1 : 1)) % (2*Math.PI); + } + var endX = x + Math.cos(-rotation) * radiusX * Math.cos(endAngle) + + Math.sin(-rotation) * radiusY * Math.sin(endAngle), + endY = y - Math.sin(-rotation) * radiusX * Math.cos(endAngle) + + Math.cos(-rotation) * radiusY * Math.sin(endAngle), + startX = x + Math.cos(-rotation) * radiusX * Math.cos(startAngle) + + Math.sin(-rotation) * radiusY * Math.sin(startAngle), + startY = y - Math.sin(-rotation) * radiusX * Math.cos(startAngle) + + Math.cos(-rotation) * radiusY * Math.sin(startAngle), + sweepFlag = counterClockwise ? 0 : 1, + largeArcFlag = 0, + diff = endAngle - startAngle; + + if(diff < 0) { + diff += 2*Math.PI; + } + + if(counterClockwise) { + largeArcFlag = diff > Math.PI ? 0 : 1; + } else { + largeArcFlag = diff > Math.PI ? 1 : 0; + } + + this.lineTo(startX / scaleX, startY / scaleY); + this.__addPathCommand(format("A {rx} {ry} {xAxisRotation} {largeArcFlag} {sweepFlag} {endX} {endY}", + { + rx:radiusX, + ry:radiusY, + xAxisRotation:rotation*(180/Math.PI), + largeArcFlag:largeArcFlag, + sweepFlag:sweepFlag, + endX:endX, + endY:endY + })); + + this.__currentPosition = {x: endX, y: endY}; + }; + /** * Generates a ClipPath from the clip command. */ diff --git a/test/index.js b/test/index.js index e5cfbdd..8bdf8ee 100644 --- a/test/index.js +++ b/test/index.js @@ -3,6 +3,7 @@ import arc from './tests/arc' import arcTo from './tests/arcTo' import arcTo2 from './tests/arcTo2' import emptyArc from './tests/emptyArc' +import ellipse from './tests/ellipse' import fillstyle from './tests/fillstyle' import globalAlpha from './tests/globalalpha' import gradient from './tests/gradient' @@ -23,6 +24,7 @@ const tests = [ arcTo, arcTo2, emptyArc, + ellipse, fillstyle, globalAlpha, gradient, diff --git a/test/rendering.test.js b/test/rendering.test.js index b52cdec..a2fe1da 100644 --- a/test/rendering.test.js +++ b/test/rendering.test.js @@ -4,6 +4,7 @@ import arc from './tests/arc' import arcTo from './tests/arcTo' import arcTo2 from './tests/arcTo2' import emptyArc from './tests/emptyArc' +import ellipse from './tests/ellipse' import fillstyle from './tests/fillstyle' import globalAlpha from './tests/globalalpha' import gradient from './tests/gradient' @@ -24,6 +25,7 @@ const tests = { arcTo, arcTo2, emptyArc, + ellipse, fillstyle, globalAlpha, gradient, diff --git a/test/tests/ellipse.js b/test/tests/ellipse.js new file mode 100644 index 0000000..d2f90dc --- /dev/null +++ b/test/tests/ellipse.js @@ -0,0 +1,25 @@ +export default function ellipse(ctx) { + // Draw shapes + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 3; j++) { + ctx.beginPath(); + var x = 25 + j * 50; // x coordinate + var y = 25 + i * 50; // y coordinate + var radiusX = 20; // Arc radius + var radiusY = 10; // Arc radius + var rotation = Math.PI + (Math.PI * (i+j)) / 8; + var startAngle = 0; // Starting point on circle + var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle + var clockwise = i % 2 == 0 ? false : true; // clockwise or anticlockwise + + ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, clockwise); + + if (i > 1) { + ctx.fill(); + } else { + ctx.stroke(); + } + } + } + +}; \ No newline at end of file