Rotated Oval Tutorial

rotated oval

Making the transition from drawing circles to drawing ovals is pretty simple but when you need to go from drawing ovals to drawing rotated ovals, you might run into a few problems. This article documents some of the pitfalls I encountered and presents them as a learning experience. If you just want some solid code to draw rotated ovals or twisted ellipses, skip ahead to the end of the article and copy the last block of code. If you think you might gain from my experience then by all means… read on.

In the simplest of terms, the goal is to plot the points of the rotated oval just like you would plot the points of a normal oval, but you also want to add a “twist” to each point so the whole oval ends up being rotated. “No duh!” right? If you want to rotate the whole oval, you have to rotate each and every point in the oval. Thanks for the tip Captain Obvious, Commander of the S.S. A’Durrrrrrr.

While the concept of rotating each point may be a no-brainer, simply adding “twist” won’t do the trick.

Here’s the standard oval drawing code (for comparison purposes):

//
//
function drawOval(centerX, centerY, xRadius, yRadius, sides){
    this.moveTo(centerX + xRadius,  centerY);
    for(var i=0; i<=sides; i++){
        var pointRatio = i/sides;
        var radians = pointRatio * 2 * Math.PI;
        var xSteps = Math.cos(radians);
        var ySteps = Math.sin(radians);
        var pointX = centerX + xSteps * xRadius;
        var pointY = centerY + ySteps * yRadius;
        this.lineTo(pointX, pointY);
    }
}
//
lineStyle(0);
//
//         X,   Y,   W,   H, steps.
drawOval(250, 200, 200, 150, 100);
//

And here’s my first failed attempt at simply adding “twist” to each point:

//
// Add "twist" argument.
function drawOval(centerX, centerY, xRadius, yRadius, twist, sides){
    //
    // Convert "twist" to radians.
    var twistRadians = twist * 2*Math.PI;
    this.moveTo(centerX + xRadius, centerY);
    for(var i=0; i<=sides; i++){
        var pointRatio = i/sides;
        var radians = pointRatio * 2 * Math.PI;
        //
        // Add "twist" to "radians" .
        radians += twistRadians;
        var xSteps = Math.cos(radians);
        var ySteps = Math.sin(radians);
        var pointX = centerX + xSteps * xRadius;
        var pointY = centerY + ySteps * yRadius;
        //
        this.lineTo(pointX, pointY);
    }
}
//
lineStyle(0);
//
//         X,   Y,   W,   H,  twist, steps.
drawOval(250, 200, 200, 150, 45/360, 100);
//

The above code produced this SWF:

FAIL!

All that did was find a point on the circle, spin said point around the circle (by adding twist), and THEN scale the X and Y axis to make it an oval. Twisting or spinning the points on a circle before they are scaled is useless, you must scale first and then twist.

*NOTE: The straight line is caused by the "moveTo" command sending the pen to the standard oval's starting point (3 O'clock) and then jumping to the (supposedly) "rotated" oval's new starting point 45 degrees around.

It might be clearer now that one possible solution is to:

  1. Plot the point as if you were drawing a standard non-rotated oval being sure to scale X and Y (by multiplying times the xRadius and yRadius).
  2. After the point has been scaled, find its new distance from center.
  3. Find the point's new angle from center.
  4. Add "twist" to the new angle.
  5. Use distance and rotation values to plot the point at its new "spun" location.

Here's what it looks like in code:

//
function drawOval(centerX, centerY, xRadius, yRadius, spin, steps){
    var ratio, scaledAngle, ratioRadians, scaledRadius, angl, i;
    var spinRadians = spin * 2 * Math.PI;
    var xx = centerX + Math.cos(spinRadians) * xRadius;
    var yy = centerY + Math.sin(spinRadians) * xRadius;
    moveTo(xx, yy);
    for(i=1; i<=steps; i++){
        ratio = i/steps;
        ratioRadians = ratio * 2 * Math.PI;
        //
        // Scale the points like you would with a standard oval.
        xx = Math.cos(ratioRadians) * xRadius;
        yy = Math.sin(ratioRadians) * yRadius;
        //
        // Find the scaled point's new distance from center.
        scaledRadius = Math.sqrt(xx*xx + yy*yy);
        //
        // Find the scaled point's new angle from center.
        scaledAngle = Math.atan2(yy, xx);
        //
        // Spin the point.
        scaledAngle += spinRadians;
        //
        // Plot the point with its new scaled and rotated values.
        xx = centerX + Math.cos(scaledAngle) * scaledRadius;
        yy = centerY + Math.sin(scaledAngle) * scaledRadius;
        lineTo(xx, yy);
    }
}
//
lineStyle(0);
drawOval(250, 200, 200, 100, 45/360, 100);
//

And here's the SWF created using the above code:

SUCCESS!

That's pretty sweet but there's still room for improvement because "Math.atan2" and "Math.sqrt" are redundant and use more resources than necessary. There's a way to reduce the calculations above into a nice and compact formula but I must admit that the logic of the Math is a bit beyond me.

diagram
For an oval where...

  • h = centerX;
  • k = centerY;
  • a = xRadius;
  • b = yRadius;
  • Φ = spin angle
  • t = initial angle (point ratio)

Here's what the above formula looks like after it's been adapted to ActionScript: 

//
function drawOval(centerX, centerY, radiusX, radiusY, spin, steps) {
    var i, radian, radianSin, radianCos;
    var spinRadians = spin * 2 * Math.PI;
    var spinSin = Math.sin(spinRadians);
    var spinCos = Math.cos(spinRadians);
    var xx = centerX + spinCos * radiusX;
    var yy = centerY + spinSin * radiusX;
    moveTo(xx, yy);
    for (i=1; i<=steps; i++) {
        radian = i/steps * 2 * Math.PI;
        radianSin = Math.sin(radian);
        radianCos = Math.cos(radian);
        //
        // Apply the compact and efficient oval formula.
        xx = centerX+(radiusX*radianCos*spinCos-radiusY*radianSin*spinSin);
        yy = centerY+(radiusX*radianCos*spinSin+radiusY*radianSin*spinCos);
        lineTo(xx, yy);
    }
}
//
lineStyle(0);
drawOval(250, 200, 200, 100, 45/360, 100);
//

The above code may be a lot harder to remember but it's tight and runs about 60% faster than the previous code sample. 😀

One Response to “Rotated Oval Tutorial”

  1. […] While researching how to draw rotated ovals I learned some interesting facts about ellipses and thought I’d share them with you […]

Leave a Reply

PixelWit.com's Comment Guidelines


Warning: Undefined variable $user_ID in /home2/pixelwit/public_html/blog/wp-content/themes/fvariant2/comments.php on line 57

You must be logged in to post a comment.

© Sean O'Shell 2007-2024