February 2012
    Get Version

January 2007
    Bezier Text

December 2005
» Rotated Ellipses

December 2004
    PDF Page Count

January 2003
    Boolean Blues

March 2002
    Networked Drives

January 2002
    Treeview Troubles
    Appending to Exe's



Freeware Components
Rotated Ellipses December 2005
There is no native Windows function to draw ellipses at rotated angles, so every programmer has to do the drawing themselves. It took me a while to find some decent code on the internet. However, I did find this excellent C++ code which I've used as the basis of my Delphi code below. The rotated ellipse is constructed by a series of 4 bezier curves. The codeguru site linked to above also has a good summary of other ways to draw rotated ellipses so I wont repeat here what was said there.

Although not demonstrated in the code below, a reasonable approximation of the bounding rectangle of the rotated ellipse - aligned with the x-y axis - can be derived by finding both the minimum and maximum values for x and y from the 13 points used to define the bezier curves.

The calling function Button1Click() also demonstrates how to assign a record constant, something newcomers to Delphi may not be aware is possible.

You can see where I've used this function if you download my collection of 16 Delphi Draw Objects components here.

Anyhow, here's my Delphi function DrawRotatedEllipse().

Code snippet ...
uses Math;

procedure RotatePts(var pts: array of TPoint;
  origin: TPoint; radians: single);
var
  i,x,y: integer;
  cosAng, sinAng: single;
begin
  cosAng := cos(radians);
  sinAng := sin(radians);
  for i := low(pts) to high(pts) do
  begin
    x := pts[i].X - origin.X;
    y := pts[i].Y - origin.Y;
    pts[i].X := round((x * cosAng) - (y * sinAng)) + origin.X;
    pts[i].Y := round((x * sinAng) + (y * cosAng)) + origin.Y;
  end;
end;
//--------------------------------------------------------------

//see - http://www.codeguru.com/Cpp/G-M/gdi/article.php/c131

procedure DrawRotatedEllipse(canvas: TCanvas;
  rec: TRect; degrees: integer);
const
  //Magic constant = 2/3*(sqrt(2)-1)
  offset: single = 0.27614237;
var
  midx, midy, offx, offy: integer;
  pts: array [0..12] of TPoint;
  radians: single;
begin
  degrees := degrees mod 360;
  if degrees < 0 then inc(degrees, 360);
  radians := degrees *pi / 180;

  //if there's no rotation, use the standard Windows function
  if radians = 0 then
    canvas.Ellipse(rec)
  else
  begin
    with rec do
    begin
      dec(right); dec(bottom); //empirically this seems better
      midx := (right + left) div 2;
      midy := (bottom + top) div 2;
      offx := round((right - left) * offset);
      offy := round((bottom - top) * offset);
      pts[0]  := Point(left, midy);
      pts[1]  := Point(left, midy - offy);
      pts[2]  := Point(midx - offx, top);
      pts[3]  := Point(midx, top);
      pts[4]  := Point(midx + offx, top);
      pts[5]  := Point(right, midy - offy);
      pts[6]  := Point(right, midy);
      pts[7]  := Point(right, midy + offy);
      pts[8]  := Point(midx + offx, bottom);
      pts[9]  := Point(midx, bottom);
      pts[10] := Point(midx - offx, bottom);
      pts[11] := Point(left, midy + offy);
      pts[12] := pts[0];
      //rotate all points about the ellipse center ...
      RotatePts(pts, Point(midx,midy), radians);
    end;
    with canvas do
    begin
      beginPath(Handle);
      canvas.PolyBezier(pts);
      EndPath(Handle);
      if canvas.Brush.Style = bsClear then
        StrokePath(Handle) else
        StrokeAndFillPath(Handle);
    end;
  end;
end;
//--------------------------------------------------------------

procedure TForm1.Button1Click(Sender: TObject);
const
  rec: TRect = (left:100; top:100; right:300; bottom:200);
begin
  canvas.Pen.Width := 2;

  canvas.Brush.Color := clWhite;
  DrawRotatedEllipse(Canvas, rec, 0);
  //now overlay the same sized ellipse,
  //but rotated at several angles ...
  canvas.Brush.Style := bsClear;
  DrawRotatedEllipse(Canvas, rec, -45);
  DrawRotatedEllipse(Canvas, rec, 45);
  DrawRotatedEllipse(Canvas, rec, 90);
end;


Copyright © 2002-2006 Angus Johnson