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

Answer:

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
      ACanvas.MoveTo(Round(CenterX + RadiusX * cos(StartAngle)),
        Round(CenterY - RadiusY * sin(StartAngle)));
    ACanvas.LineTo(Round(CenterX + RadiusX * cos(StartAngle)),
      Round(CenterY - RadiusY * sin(StartAngle)));
    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;
  RadiusX, RadiusY: 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;
  if RadiusX < 0 then
    RadiusX := 0;
  if RadiusY < 0 then
    RadiusY := 0;
  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)),
    Round(CenterY - RadiusY * sin(StartAngle)));
  UseMoveTo := True;
  while SweepRange > PI / 2 do
  begin
    DrawArcWithBezier(ACanvas, CenterX, CenterY, RadiusX, RadiusY, StartAngle, PI / 2, UseMoveTo);
    SweepRange := SweepRange - PI / 2;
    StartAngle := StartAngle + PI / 2;
    UseMoveTo := False;
  end;
  if SweepRange >= 0 then
    DrawArcWithBezier(ACanvas, CenterX, CenterY, RadiusX, RadiusY,
      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;

Nincsenek megjegyzések:

Megjegyzés küldése