简体   繁体   中英

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.

Edit: This is my input ( in 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. 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. 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 .

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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