什么是一种简单的方法来将凹面PathGeometry填充为凸面(找到凹顶点并将它们移除)?

时间:2010-07-08 22:36:30

标签: c# wpf geometry pathgeometry concave

我在一个PathFigure上有一个由LineSegments构建的PathGeometry(多边形),我想确保它是Convex。我有一个方法使用CrossProduct来确定几何是否是Convex,我假设我可以返回一个点列表,当它是假的时候使它凹入并删除那些点以填充多边形但是它不能正常工作。

这是我得到的代码:

    public static bool IsConvexPolygon(this IList<Point> polygon, out List<Point> concavePoints)
    {
        int n = polygon.Count;
        List<double> result = new List<double>();
        concavePoints = new List<Point>();
        for (int i = 0; i < n; i++)
        {
            result.Add(polygon[i].CrossProduct(polygon[i.RotateNext(n)]));
            if (result.Last() < 0.0)
            {
                concavePoints.Add(polygon[i.RotateNext(n)]);
            }
        }
        return (result.All(d => d >= 0.0));
    }

    public static double CrossProduct(this Point p1, Point p2)
        {
            return (p1.X * p2.Y) - (p1.Y * p2.X);
        }

    public static int RotateNext(this int index, int count)
        {
            return (index + 1) % count;
        }

    public static PointCollection ExtractPoints(this Geometry geometry)
        {
            PointCollection pc = new PointCollection();
            if (geometry is LineGeometry)
            {
                var lg = (LineGeometry)geometry;
                pc.Add(lg.StartPoint);
                pc.Add(lg.EndPoint);
                return pc;
            }
            else if (geometry is PathGeometry)
            {
                var pg = (PathGeometry)geometry;
                if (pg.Figures.Count > 0)
                {
                    List<Point> points;
                    if ((pg.Figures[0].Segments.Count > 0) && (pg.Figures[0].Segments[0] is PolyLineSegment))
                        points = ((PolyLineSegment)pg.Figures[0].Segments[0]).Points.ToList();
                    else
                        points = pg.Figures[0].Segments.Select(seg => (seg as LineSegment).Point).ToList();

                    pc.Add(pg.Figures[0].StartPoint);
                    foreach (Point p in points)
                        pc.Add(p);
                    return pc;
                }
            }
            else if (geometry is RectangleGeometry)
            {
                var rg = (RectangleGeometry)geometry;
                var rect = rg.Rect;
                pc.Add(rect.TopLeft);
                pc.Add(rect.TopRight);
                pc.Add(rect.BottomRight);
                pc.Add(rect.BottomLeft);
                return pc;
            }
            return pc;
        }

public static Geometry CreateGeometryFromPoints(this List<Point> pts)
{
    if (pts.Count < 2)
        return null;

    PathFigure pFig = new PathFigure() { StartPoint = pts[0] };
    for (int i = 1; i < pts.Count; i++)
    {
        pFig.Segments.Add(new LineSegment(pts[i], true));
    }
    pFig.IsClosed = true;

    PathGeometry pg = new PathGeometry(new List<PathFigure>() { pFig });
    return pg;
}
public static Path CreatePolygonFromGeometry(this Geometry geo, Brush fillBrush)
        {
            Path path = new Path() { Stroke = Brushes.Black, StrokeThickness = 1, Fill = fillBrush };
            path.Data = geo;
            return path;
        }

这是我在检查和修正多边形的地方:

        List<Point> outstuff;
        if (geo1.ExtractPoints().IsConvexPolygon(out outstuff) == false)
        {
            // Got to fill it in if it's concave
            var newpts = geo1.ExtractPoints().Except(outstuff).ToList();
            var z = newpts.CreateGeometryFromPoints().CreatePolygonFromGeometry(Brushes.Purple);
            z.MouseRightButtonDown += delegate { canvas.Children.Remove(z); };
            canvas.Children.Add(z);
        }

最终,我希望能够将我的凹面几何体变成像这样的凸面:

alt text http://i28.tinypic.com/34zhjzm.png

2 个答案:

答案 0 :(得分:0)

循环遍历相邻顶点的每个三元组(ABC,BCD,CDE等)。对于每个三元组,您计算链接第一个和第三个顶点的段的中间点(在ABC中连接A-C,在BCD中连接B-D等)。如果中间点在多边形内部,则转到下一个三元组。如果它在外面,则用连接三元组的2个段替换链接极值的一个段(即,删除中间点)。你继续,直到不再有替换为止。

如果你在纸上试试,你会得到你描述的结果。

如果我没弄错的话,你可以测试一个点是否属于Polygon.HitTestCore的多边形。

答案 1 :(得分:0)

我计算convex hull(同样:NTS)并删除生成的凸包多边形内部的任何顶点(使用point-in-polygon测试)。