简体   繁体   English

给定一个多边形列表,构造带孔的多边形

[英]Given a list of polygons, construct Polygon with Holes

For polygon, the points are given in counter clock wise manner.对于多边形,点以逆时针方式给出。 For holes, the points are given in clock wise manner.对于孔,点以顺时针方式给出。 So given a list of polygons, how to construct all the polygon with holes?那么给定一个多边形列表,如何构建所有带孔的多边形?

Here are a few precondition of the polygon list:以下是多边形列表的一些先决条件:

  1. One polygon can contain multiple holes, but a hole must be contained inside a polygon.一个多边形可以包含多个孔,但一个孔必须包含在一个多边形内。
  2. A hole cannot contain one or more polygon.一个洞不能包含一个或多个多边形。
  3. All the holes must be located inside polygon, and not outside of it所有孔必须位于多边形内,而不是位于多边形之外
  4. Hole's edge can touch on polygon's.孔的边缘可以接触多边形的边缘。
  5. And all the polygons and holes must not intersect with other polygons/holes, although they can touch one another.并且所有的多边形和孔都不能与其他多边形/孔相交,尽管它们可以相互接触。
  6. The polygon/ hole can be either convex or concave.多边形/孔可以是凸面凹面。

How to devise an algorithm that construct polygons with holes?如何设计一种算法来构造带孔的多边形?

I am looking for a general algorithm, so any code in the form of C#, C++ and matlab are welcomed.我正在寻找通用算法,因此欢迎任何 C#、C++ 和 matlab 形式的代码。

Edit: This is my input ( in C#)编辑:这是我的输入(在 C# 中)

public class PolygonWithHoles
{
   public List<Point> OuterBoundary;
   public List<List<Point>> Holes;
}


//input: a list of point list. If the point list constructs a polygon in Counter clock wise, then it is a general polygon, else it is a holes
public List<PolygonWithHoles> ConstuctPolyHoles(List<List<Point>> Polys)
{
}

Point in polygon tests are vulnerable due to floating point rounding errors. 由于浮点舍入错误,多边形测试中的点很容易受到攻击。 They usually only work for non-trivial polygons. 它们通常仅适用于非平凡多边形。

A more robust approach should be based on a scan-line algorithm, where vertices are first sorted according to their x and y values. 一种更可靠的方法应基于扫描线算法,其中首先根据顶点的x和y值对其进行排序。 A scan (or sweep) line then moves eg. 然后,扫描(或扫描)线移动。 from left to right. 从左到右。 The algorithm then typically maintains a list of lines intersecting the scan line by adding a line when its left vertex "hits" the scan line, and removing it when its right vertex hits the line. 然后,该算法通常维护一条与扫描线相交的线的列表,方法是在左顶点“击中”扫描线时添加一条线,而在其右顶点击中该线时将其删除。

After each move of the scan line, the intersections of the current lines with the scan line is updated, and the lines re-ordered according to the y value of the intersection. 每次扫描线移动后,将更新当前线与扫描线的交点,并根据交点的y值对行进行重新排序。 Whenever two lines need to be re-ordered during the sorting operation, then this means that they have an intersection, which can then be recorded. 每当在排序操作期间需要对两行进行重新排序时,这意味着它们具有交点,然后可以对其进行记录。

After finding all the intersections, contours and holes can be identified reliably. 找到所有交叉点后,便可以可靠地识别出轮廓和孔。

The following projects use this approach: 以下项目使用此方法:

There are others, and the following site (which promotes the PolyBoolean library) compares the most important ones: http://www.complex-a5.ru/polyboolean/comp.html . 还有其他一些,下面的站点(该站点促进了PolyBoolean库)比较了最重要的站点: http : //www.complex-a5.ru/polyboolean/comp.html

Just as a warning: I myself have implemented a polygon library supporting Boolean operations as well as hole detection. 就像一个警告:我本人已经实现了一个支持布尔运算以及孔检测的多边形库。 This library is being used in a commercial product, and I spent several years (!) to fine-tune the algorithm to ensure a correct result for any given input polygon in a little time as possible (other libraries may be a few % faster, but fail with some input data). 该库正用于商业产品,我花了几年时间(!)来微调算法,以确保在任何短时间内对任何给定的输入多边形都能获得正确的结果(其他库的速度可能要快几%,但由于输入数据而失败)。 In fact, a single approach algorithm may not be capable of solving all possible problems, so I had to implement several ones. 实际上,单一方法算法可能无法解决所有可能的问题,因此我不得不实施多个方法。

Good luck! 祝好运!

Maybe you are looking for a simple solution similar to:也许您正在寻找类似于以下内容的简单解决方案:

public sealed class PolygonWithHoles : Shape
{

    #region Constructors
    
    /// <summary>
    /// Instantiates a new instance of a polygon.
    /// </summary>
    public PolygonWithHoles()
    {
    }

    #endregion Constructors

    #region Dynamic Properties
    
    /// <summary>
    /// Holes property
    /// </summary>
    public static readonly DependencyProperty HolesProperty = DependencyProperty.Register(
            "Holes", typeof(List<PointCollection>), typeof(PolygonWithHoles),
            new FrameworkPropertyMetadata(new List<PointCollection>(), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender)
        );

    /// <summary>
    /// Holes property
    /// </summary>
    public List<PointCollection> Holes
    {
        get
        {
            return (List<PointCollection>)GetValue(HolesProperty);
        }
        set
        {
            SetValue(HolesProperty, value);
        }
    }

    /// <summary>
    /// Points property
    /// </summary>
    public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(
            "Points", typeof(PointCollection), typeof(PolygonWithHoles),
            new FrameworkPropertyMetadata(new PointCollection(), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender)
        );

    /// <summary>
    /// Points property
    /// </summary>
    public PointCollection Points
    {
        get
        {
            return (PointCollection)GetValue(PointsProperty);
        }
        set
        {
            SetValue(PointsProperty, value);
        }
    }

    /// <summary>
    /// FillRule property
    /// </summary>
    public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register(
        "FillRule",
        typeof(FillRule),
        typeof(PolygonWithHoles),
        new FrameworkPropertyMetadata(
            FillRule.EvenOdd,
            FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender)
        );

    /// <summary>
    /// FillRule property
    /// </summary>
    public FillRule FillRule
    {
        get
        {
            return (FillRule)GetValue(FillRuleProperty);
        }
        set
        {
            SetValue(FillRuleProperty, value);
        }
    }

    #endregion Dynamic Properties

    #region Protected Methods and properties

    /// <summary>
    /// Get the polygon that defines this shape
    /// </summary>
    protected override Geometry DefiningGeometry
    {
        get
        {
            return _polygonGeometry;
        }
    }

    #endregion

    #region Protected Methods

    /// <summary>
    /// Updates DesiredSize of the shape.  Called by parent UIElement during is the first pass of layout.
    /// </summary>
    /// <param name="constraint">Constraint size is an "upper limit" that should not exceed.</param>
    /// <returns>Shape's desired size.</returns>
    protected override Size MeasureOverride(Size constraint)
    {
        CacheDefiningGeometry();

        return base.MeasureOverride(constraint);
    }

    #endregion

    #region Internal Methods

    internal void CacheDefiningGeometry()
    {
        PointCollection pointCollection = Points;
        
        if (pointCollection == null)
        {
            _polygonGeometry = Geometry.Empty;
            return;
        }

        PathFigure pathFigure = new PathFigure();
        if (pointCollection.Count > 0)
        {
            pathFigure.StartPoint = pointCollection[0];

            if (pointCollection.Count > 1)
            {
                Point[] array = new Point[pointCollection.Count - 1];

                for (int i = 1; i < pointCollection.Count; i++)
                {
                    array[i - 1] = pointCollection[i];
                }

                pathFigure.Segments.Add(new PolyLineSegment(array, true));
            }

            pathFigure.IsClosed = true;
        }

        PathGeometry polygonGeometry = new PathGeometry();
        polygonGeometry.Figures.Add(pathFigure);
        polygonGeometry.FillRule = FillRule.Nonzero;

        GeometryGroup geometryGroup = new GeometryGroup();
        // Set FillRule
        geometryGroup.FillRule = FillRule;
        geometryGroup.Children.Add(polygonGeometry);

        // Holes
        List<PointCollection> holesCollection = Holes;
        if (holesCollection != null)
        {
            foreach (PointCollection holePointCollection in holesCollection)
            {
                PathFigure holePathFigure = new PathFigure();

                if (holePointCollection.Count > 0)
                {
                    holePathFigure.StartPoint = holePointCollection[0];

                    if (holePointCollection.Count > 1)
                    {
                        Point[] array = new Point[holePointCollection.Count - 1];

                        for (int i = 1; i < holePointCollection.Count; i++)
                        {
                            array[i - 1] = holePointCollection[i];
                        }

                        holePathFigure.Segments.Add(new PolyLineSegment(array, true));
                    }

                    holePathFigure.IsClosed = true;
                }

                PathGeometry holePolygonGeometry = new PathGeometry();
                holePolygonGeometry.Figures.Add(holePathFigure);
                holePolygonGeometry.FillRule = FillRule.Nonzero;

                geometryGroup.Children.Add(holePolygonGeometry);
            }

        }
        

        _polygonGeometry = geometryGroup;
    }

    #endregion Internal Methods

    #region Private Methods and Members

    private Geometry _polygonGeometry;

    #endregion
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM