Như bạn đã thấy, ArcTo không thể vẽ hình elip hoàn chỉnh. Trong thực tế, nó trở nên số không ổn định khi bạn cố gắng giảm diện tích "phẳng". Một xem xét khác là bản vẽ vòng cung chậm hơn Bezier vẽ trên phần cứng hiện đại. Hệ thống hiện đại nhất này sử dụng bốn đường cong bezier để ước tính một hình elip hơn là vẽ một hình elip thực.
Bạn có thể thấy rằng EllipseGeometry WPF của thực hiện điều này bằng cách thực hiện đoạn mã sau, phá vỡ trên gọi phương thức DrawBezierFigure, và kiểm tra việc PathFigure trong debugger:
using(var ctx = geometry.Open())
{
var ellipse = new EllipseGeometry(new Point(100,100), 10, 10);
var figure = PathGeometry.CreateFromGeometry(ellipse).Figures[0];
DrawBezierFigure(ctx, figure);
}
void DrawBezierFigure(StreamGeometryContext ctx, PathFigure figure)
{
ctx.BeginFigure(figure.StartPoint, figure.IsFilled, figure.IsClosed);
foreach(var segment in figure.Segments.OfType<BezierSegment>())
ctx.BezierTo(segment.Point1, segment.Point2, segment.Point3, segment.IsStroked, segment.IsSmoothJoin);
}
Đoạn mã trên là một cách đơn giản để vẽ một hiệu quả hình elip vào một StreamGeometry, nhưng là trường hợp rất đặc biệt mã. Trong thực tế tôi sử dụng một số phương pháp mở rộng mục đích chung được xác định để vẽ một Geometry tùy ý vào một StreamGeometryContext vì vậy tôi chỉ có thể viết:
using(var ctx = geometry.Open())
{
ctx.DrawGeometry(new EllipseGeometry(new Point(100,100), 10, 10));
}
Đây là việc thực hiện các phương pháp khuyến nông DrawGeometry:
public static class GeometryExtensions
{
public static void DrawGeometry(this StreamGeometryContext ctx, Geometry geo)
{
var pathGeometry = geo as PathGeometry ?? PathGeometry.CreateFromGeometry(geo);
foreach(var figure in pathGeometry.Figures)
ctx.DrawFigure(figure);
}
public static void DrawFigure(this StreamGeometryContext ctx, PathFigure figure)
{
ctx.BeginFigure(figure.StartPoint, figure.IsFilled, figure.IsClosed);
foreach(var segment in figure.Segments)
{
var lineSegment = segment as LineSegment;
if(lineSegment!=null) { ctx.LineTo(lineSegment.Point, lineSegment.IsStroked, lineSegment.IsSmoothJoin); continue; }
var bezierSegment = segment as BezierSegment;
if(bezierSegment!=null) { ctx.BezierTo(bezierSegment.Point1, bezierSegment.Point2, bezierSegment.Point3, bezierSegment.IsStroked, bezierSegment.IsSmoothJoin); continue; }
var quadraticSegment = segment as QuadraticBezierSegment;
if(quadraticSegment!=null) { ctx.QuadraticBezierTo(quadraticSegment.Point1, quadraticSegment.Point2, quadraticSegment.IsStroked, quadraticSegment.IsSmoothJoin); continue; }
var polyLineSegment = segment as PolyLineSegment;
if(polyLineSegment!=null) { ctx.PolyLineTo(polyLineSegment.Points, polyLineSegment.IsStroked, polyLineSegment.IsSmoothJoin); continue; }
var polyBezierSegment = segment as PolyBezierSegment;
if(polyBezierSegment!=null) { ctx.PolyBezierTo(polyBezierSegment.Points, polyBezierSegment.IsStroked, polyBezierSegment.IsSmoothJoin); continue; }
var polyQuadraticSegment = segment as PolyQuadraticBezierSegment;
if(polyQuadraticSegment!=null) { ctx.PolyQuadraticBezierTo(polyQuadraticSegment.Points, polyQuadraticSegment.IsStroked, polyQuadraticSegment.IsSmoothJoin); continue; }
var arcSegment = segment as ArcSegment;
if(arcSegment!=null) { ctx.ArcTo(arcSegment.Point, arcSegment.Size, arcSegment.RotationAngle, arcSegment.IsLargeArc, arcSegment.SweepDirection, arcSegment.IsStroked, arcSegment.IsSmoothJoin); continue; }
}
}
}
Một lựa chọn khác là tự mình tính toán điểm. Phép tính gần đúng nhất với hình elip được tìm thấy bằng cách thiết lập các điểm điều khiển thành (Math.Sqrt (2) -1) * 4/3 của bán kính. Vì vậy, bạn có thể tính toán một cách rõ ràng những điểm và vẽ Bezier như sau:
const double ControlPointRatio = (Math.Sqrt(2)-1)*4/3;
var x0 = centerX - radiusX;
var x1 = centerX - radiusX * ControlPointRatio;
var x2 = centerX;
var x3 = centerX + radiusX * ControlPointRatio;
var x4 = centerX + radiusX;
var y0 = centerY - radiusY;
var y1 = centerY - radiusY * ControlPointRatio;
var y2 = centerY;
var y3 = centerY + radiusY * ControlPointRatio;
var y4 = centerY + radiusY;
ctx.BeginFigure(new Point(x2,y0), true, true);
ctx.BezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4,y2), true, true);
ctx.BezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2,y4), true, true);
ctx.BezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0,y2), true, true);
ctx.BezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2,y0), true, true);
Một lựa chọn khác là sử dụng hai ArcTo gọi, nhưng như tôi đã đề cập trước đây là kém hiệu quả. Tôi chắc rằng bạn có thể tìm ra các chi tiết của hai cuộc gọi ArcTo nếu bạn muốn đi theo cách đó.
Cảm ơn câu trả lời chi tiết! –