## 2011. február 1., kedd

### How to draw an arc or ellipse with Bezier curves

Problem/Question/Abstract:

How to draw an arc or ellipse with Bezier curves

Solve 1:

Example for painting an arc:

{Helper Function Draw Arc up to 90 degree}

procedure DrawArcWithBezier(ACanvas: TCanvas; CenterX, CenterY, RadiusX,
RadiusY, StartAngle, SweepRange: Double; UseMoveTo: Boolean = True);
type
TDoublePoint = record
x, y: Double;
end;
var
Coord: array[0..3] of TDoublePoint;
pts: array[0..3] of TPoint;
a, b, c, x, y: Double;
ss, cc: Double;
i: Integer;

{x, y = TDoublePoint}
function DPoint(x, y: Double): TDoublePoint;
begin
Result.x := x;
Result.y := y;
end;

begin
if SweepRange = 0 then
begin
if UseMoveTo then
Exit;
end;
b := sin(SweepRange / 2);
c := cos(SweepRange / 2);
a := 1 - c;
x := a * 4 / 3;
y := b - x * c / b;
ss := sin(StartAngle + SweepRange / 2);
cc := cos(StartAngle + SweepRange / 2);
Coord[0] := DPoint(c, b);
Coord[1] := DPoint(c + x, y);
Coord[2] := DPoint(c + x, -y);
Coord[3] := DPoint(c, -b);
for i := 0 to 3 do
begin
pts[i] := Point(Round(CenterX + RadiusX * (Coord[i].x * cc + Coord[i].y * ss) - 0.0001), Round(Centery + RadiusY * (-Coord[i].x * ss + Coord[i].y * cc) - 0.0001));
end;
if UseMoveTo then
ACanvas.MoveTo(pts[0].x, pts[0].y);
ACanvas.PolyBezierTo([pts[1], pts[2], pts[3]]);
end;

{Draw Arc with Bezier curves}

function DrawArc(ACanvas: TCanvas; x1, y1, x2, y2, x3, y3, x4, y4: Integer): TPoint;
var
CenterX, CenterY: Double;
StartAngle, EndAngle, SweepRange: Double;
UseMoveTo: Boolean;
begin
CenterX := (x1 + x2) / 2;
CenterY := (y1 + y2) / 2;
RadiusX := (abs(x1 - x2) - 1) / 2;
RadiusY := (abs(y1 - y2) - 1) / 2;
StartAngle := ArcTan2(-(y3 - CenterY) * RadiusX, (x3 - CenterX) * RadiusY);
EndAngle := ArcTan2(-(y4 - CenterY) * RadiusX, (x4 - CenterX) * RadiusY);
SweepRange := EndAngle - StartAngle;
if SweepRange < 0 then
SweepRange := SweepRange + 2 * PI;
Result := Point(Round(CenterX + RadiusX * cos(StartAngle)),
UseMoveTo := True;
while SweepRange > PI / 2 do
begin
SweepRange := SweepRange - PI / 2;
StartAngle := StartAngle + PI / 2;
UseMoveTo := False;
end;
if SweepRange >= 0 then
StartAngle, SweepRange, UseMoveTo);
end;

Solve 2:

This is the routine I use to convert a regular ellipse to a Bezier ellipse. It's in reals so you'll need to convert it if you are using an array of TPoint. Points[0] is the start(top left) of the ellipse and Points[2] is the finish(bottom right). The Points in this case are objects so if you are using array of TPoint remove the .AsPoint and replace RealPoint with Point. You'll also need to round some values.

{ ... }
const
Kappa = 0.5522847498;
var
dx, dy: single;
orgPt: TPoint;
begin
dx := (Points[2].x - Points[0].x) / 2;
dy := (Points[0].y - Points[2].y) / 2;
orgPt := RealPoint(Points[0].x + (dx), Points[0].y - (dy));
Points[0].AsPoint := RealPoint(orgPt.x - dx, orgPt.y);
Points[1].AsPoint := RealPoint(orgPt.x - dx, orgPt.y + (dy * Kappa));
Points[2].AsPoint := RealPoint(orgPt.x - (dx * Kappa), orgPt.y + dy);
Points[3].AsPoint := RealPoint(orgPt.x, orgPt.y + dy);
Points[4].AsPoint := RealPoint(orgPt.x + (dx * Kappa), orgPt.y + dy);
Points[5].AsPoint := RealPoint(orgPt.x + (dx), orgPt.y + (dy * Kappa));
Points[6].AsPoint := RealPoint(orgPt.x + dx, orgPt.y);
Points[7].AsPoint := RealPoint(orgPt.x + dx, orgPt.y - (dy * Kappa));
Points[8].AsPoint := RealPoint(orgPt.x + (dx * Kappa), orgPt.y - dy);
Points[9].AsPoint := RealPoint(orgPt.x, orgPt.y - dy);
Points[10].AsPoint := RealPoint(orgPt.x - (dx * Kappa), orgPt.y - dy);
Points[11].AsPoint := RealPoint(orgPt.x - dx, orgPt.y - (dy * Kappa));
Points[12].AsPoint := Points[0].AsPoint;
Points[13].AsPoint := Points[1].AsPoint;
Points[14].AsPoint := Points[2].AsPoint;
Points[15].AsPoint := Points[3].AsPoint;
end;