Drawing Rotated and Skewed Ellipses

Introduction

Rectangles, Rounded Rectangles and Ellipses in Windows can only be drawn by GDI as axis-aligned figures. If one wishes to draw rotated or skewed figures under Windows NT, one can use World Transforms but they are unfortunately not available under Windows 95/98. For a cross-platform solution, one needs to do more of the work oneself. Rectangles can be simulated trivially using polygons with four vertices, which can then be rotated or skewed. However, what is one do for ellipses? There are basically three choices:

Alternatives

  • Use a custom function to draw the ellipse.

    The mathematics for ellipses are relatively simple and there are modified Bresenham equations for rotated ellipses in standard texts. However, this means that one must perform the rasterization oneself, which can get complicated for thick lines. This amount of effort is probably only worthwhile if one is already drawing to an off-screen surface (e.g. DirectDraw) or a bitmap.

  • Drawing ellipses as connected line segments.

    The actual lines can be drawn via LineTo(...) or Polyline(...) GDI calls. One can perform the approximation of the ellipses oneself or take advantage of the GDI FlattenPath(...) function.

  • Use cubic bezier curves to approximate ellipses.

    This is the method illustrated here.

Drawing ellipses as connected bezier curves

Using just four bezier curves, each representing 90 degrees of the original axis-aligned ellipse, one can arrive at a fair approximation with a maximum error of just 0.027%. This is equivalent to a maximum error of less than one pixel for ellipses with radii up to 3700, more than enough accuracy for our purposes.

Advantages

  1. Simplicity.

    Only four GDI calls are needed. The calculations of the bezier control points are trivial.

  2. Speed.

    One can take advantage of hardware support of curve drawing on newer video cards. On my system, this is as fast as, if not faster than, GDI calls to Ellipse(...)

  3. Transformations.

    Because bezier curves are invariant under rotation, scaling and translation, one only needs to transform the control points to apply the same transformations to one's ellipse. More precisely, since each point on a cubic bezier curve is a barycentric combination of the control points, the relationship of the curve to the control points is not changed under affine maps.

  4. Device independence.

    If one is converting ellipses to line segments or rasterizing oneself, each time the resolution of one's surfaces or device context changes (for example when drawing to a printer DC), one must re-rasterize. This is not necessary with bezier curves. An additional advantage is that the ellipses can be exported via metafiles to drawing programmes such as CORELDRAW, which can then scale the figures without gross distortions or artefacts.

Procedure

First start off with a rectangle bounding an axis aligned ellipse (as in a normal GDI call). The thirteen bezier control points (labelled 0-12 below) defining the four bezier curves making up the ellipse can be calculated easily using an empirically derived "magical constant". The following code produces the control points for mapping modes where +ve Y is downwards (e.g. MM_TEXT). Where the opposite is true, just have a negative y value for the offset variable as indicated in the comments.

    // Create points to simulate ellipse using beziers void EllipseToBezier(CRect& r, CPoint* cCtlPt) { // MAGICAL CONSTANT to map ellipse to beziers // 2/3*(sqrt(2)-1) const double EToBConst = 0.2761423749154; CSize offset((int)(r.Width() * EToBConst), (int)(r.Height() * EToBConst)); // Use the following line instead for mapping systems where +ve Y is upwards // CSize offset((int)(r.Width() * EToBConst), -(int)(r.Height() * EToBConst)); CPoint centre((r.left + r.right) / 2, (r.top + r.bottom) / 2); cCtlPt[0].x = //------------------------/ cCtlPt[1].x = // / cCtlPt[11].x = // 2___3___4 / cCtlPt[12].x = r.left; // 1 5 / cCtlPt[5].x = // | | / cCtlPt[6].x = // | | / cCtlPt[7].x = r.right; // 0,12 6 / cCtlPt[2].x = // | | / cCtlPt[10].x = centre.x - offset.cx; // | | / cCtlPt[4].x = // 11 7 / cCtlPt[8].x = centre.x + offset.cx; // 10___9___8 / cCtlPt[3].x = // / cCtlPt[9].x = centre.x; //------------------------* cCtlPt[2].y = cCtlPt[3].y = cCtlPt[4].y = r.top; cCtlPt[8].y = cCtlPt[9].y = cCtlPt[10].y = r.bottom; cCtlPt[7].y = cCtlPt[11].y = centre.y + offset.cy; cCtlPt[1].y = cCtlPt[5].y = centre.y - offset.cy; cCtlPt[0].y = cCtlPt[12].y = cCtlPt[6].y = centre.y; }
Rotation of the Ellipse can be accomplished using code similar to:
    // LDPoint is an equivalent type to CPoint but with floating point precision void Rotate(double radians, const CPoint& c, CPoint* vCtlPt, unsigned Cnt) { double sinAng = sin(radians); double cosAng = cos(radians); LDPoint constTerm( c.x - c.x * cosAng - c.y * sinAng, c.y + c.x * sinAng - c.y * cosAng); for (int i = Cnt-1; i>=0; --i) { vCtlPt[i] = (LDPoint( vCtlPt[i].x * cosAng + vCtlPt[i].y * sinAng, -vCtlPt[i].x * sinAng + vCtlPt[i].y * cosAng) + constTerm).GetCPoint(); } } // Create Ellipse CRect rect; GetClientRect(&rect); CPoint ellipsePts[13]; EllipseToBezier(ellipseR, ellipsePts); // Rotate Rotate(m_Radians, midPoint, ellipsePts, 13);

Filled Ellipses

Of course, four bezier curves together only make up the outline of an ellipse, whether rotated or not. Thankfully, Win32 Path functions are there for filled ellipses. One only needs to enclose the PolyBezier(...) call in a Path Bracket. The resulting path can be stroked and filled to one's satisfaction. If one is feeling adventurous, further special fills like gradients, custom bitmaps or fractals etc. can be achieved by first setting the clipping region to the path via SelectClipPath(...).

    dc.BeginPath(); dc.PolyBezier(ellipsePts); dc.EndPath(); dc.StrokePath; // or FillPath(); // or StrokeAndFillPath(); // or PathToRegion(dc.m_hDC);

Thick Dashed or Dotted Ellipses Outlines under Win95/8

Win95/8 only supports solid thick lines. However, dashed or dotted ellipse outlines can easily be simulated with a series of bezier segments.



Comments

  • Drawing rotated and skewed ellipses

    Posted by Legacy on 01/04/2004 12:00am

    Originally posted by: Chryssa Sferidou

    My target is to draw several continuous Bezier curves by clicking with the mouse points on the screen, not by pre-defining the coordinates of the points in the code - How can I achieve this?
    Regards

    Reply
  • How to use the code?Any exmaple?

    Posted by Legacy on 06/05/2003 12:00am

    Originally posted by: hero3blade

    I got a strange drawing using the code.Please help me.

    Reply
  • http://www.ucancode.net

    Posted by Legacy on 03/23/2003 12:00am

    Originally posted by: Cindy wang

    Very cool article,I like it.

    Reply
  • How to rotate a total 2D drawing drawn in picture box control with a user defined angle

    Posted by Legacy on 03/13/2003 12:00am

    Originally posted by: Amit Thite

    How to rotate a total 2D drawing drawn in picture box control with a user defined angle
    

    Reply
  • sin() error-- undefined identifier

    Posted by Legacy on 12/10/2002 12:00am

    Originally posted by: Caroline

    while i have included math.h..
    why?

    Reply
  • z-axis with win GDI ?

    Posted by Legacy on 08/10/2002 12:00am

    Originally posted by: Jason

    How do you rotate or position a polygon in the z-axis
    Im trying to build a small software renderer for 3D
    using the GDI, Instead of using OpenGL or Direct3D for rendering. -just for fun :)and I need to learn how you can implement 3D with the GDI

    Reply
  • How to rotate static control,Edit box control

    Posted by Legacy on 07/20/2002 12:00am

    Originally posted by: Sachin V. Kulkarni

    Please any body tell me how to rottate static text box control,Edit box control or bitmap.

    Thanks in advance

    Reply
  • Very Elegant

    Posted by Legacy on 05/22/2002 12:00am

    Originally posted by: Stan Mlynek

    Looks very good and solves a problem with trying to use the windows api ellispe function for W95/98 under extreme conditions

    Reply
  • rotation algorithm shrinks ellipse........fixed

    Posted by Legacy on 04/11/2002 12:00am

    Originally posted by: lis

    the rotation algorithm given is great, but it shrinks the ellipse....if you want a better (and simpler) one convert to polar rotate and convert back again.....here is an example....in VB where atan2 is a special function that will compute the tangent in the correct quadrant....

    For i = 0 To 12
    'first center the circle we want to rotate around....
    x(i) = A(i).x - center.x
    y(i) = A(i).y - center.y

    'then get the polar coordinates
    r = Sqr(x(i) ^ 2 + y(i) ^ 2)
    theta = Atan2(y(i), x(i))

    'then add the new angle
    theta = theta + angle

    'then convert back into cartesian
    x(i) = r * Cos(theta)
    y(i) = r * Sin(theta)

    'then move back to center
    x(i) = x(i) + center.x
    y(i) = y(i) + center.y

    'now set a(i) = x, y
    A(i).x = x(i)
    A(i).y = y(i)
    Next

    Reply
  • piechart and barchart

    Posted by Legacy on 03/25/2002 12:00am

    Originally posted by: senthilprakash

    how to draw a piechart and biechart using the mfc sdi environment?
    
    how to pass the parameters of the recordset to graph?

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Relying on outside companies to manage your network and server environments for your business and applications to meet the needs and demands of your users can be stressful. This is especially true as many Managed Hosting organizations fail to meet their service level agreements. Read this Forrester total economic impact report and learn what makes INetU different and how they exceed their customers' managed hosting expectations.

Most Popular Programming Stories

More for Developers

RSS Feeds