How to draw an arc or ellipse with Bezier curves (Views: 1213)
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; |