2010-06-05 37 views
6

Phương pháp duy nhất trong một số StreamGeometryContext có vẻ liên quan đến hình elip là phương pháp ArcTo. Thật không may nó là rất nhiều hướng để tham gia dòng chứ không phải là vẽ hình elip.Làm thế nào để vẽ một hình elip đầy đủ trong một StreamGeometry trong WPF?

Cụ thể, vị trí của vòng cung được xác định bằng điểm bắt đầu và điểm kết thúc. Đối với một hình elip đầy đủ hai trùng khớp rõ ràng, và định hướng chính xác trở thành không xác định.

Cho đến nay cách tốt nhất để vẽ một hình elip tập trung vào 100.100 kích thước 10,10 mà tôi tìm thấy là như thế này:

using (var ctx = geometry.Open()) 
{ 
    ctx.BeginFigure(new Point(100+5, 100), isFilled: true, isClosed: true); 
    ctx.ArcTo(
     new Point(100 + 5*Math.Cos(0.01), 100 + 5*Math.Sin(0.01)), // need a small angle but large enough that the ellipse is positioned accurately 
     new Size(10/2, 10/2), // docs say it should be 10,10 but in practice it appears that this should be half the desired width/height... 
     0, true, SweepDirection.Counterclockwise, true, true); 
} 

Đó là khá xấu xí, và cũng có thể để lại một diện tích nhỏ "phẳng" (mặc dù không hiển thị ở các mức thu phóng bình thường).

Tôi có thể vẽ hình elip đầy đủ bằng cách sử dụng StreamGeometryContext như thế nào?

Trả lời

26

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 đó.

+0

Cảm ơn câu trả lời chi tiết! –

Các vấn đề liên quan