예제 #1
0
        /// <summary>
        /// Reduce the number of sides in this footprint
        /// </summary>
        /// <param name="footprint"></param>
        /// <returns></returns>
        private static IReadOnlyList <Vector2> Reduce(IReadOnlyList <Vector2> footprint)
        {
            Contract.Requires(footprint != null);
            Contract.Ensures(Contract.Result <IReadOnlyList <Vector2> >() != null);
            Contract.Ensures(Contract.Result <IReadOnlyList <Vector2> >().Count <= footprint.Count);

            //Early exit, we can't do anything useful with a line!
            if (footprint.Count <= 3)
            {
                return(footprint);
            }

            //Create a list with the points in
            var p = new Point2DList();

            p.AddRange(footprint.Select(a => new Point2D(a.X, a.Y)).ToArray());

            //If two consecutive points are in the same position, remove one
            p.RemoveDuplicateNeighborPoints();

            //Merge edges which are parallel (with a tolerance of 1 degree)
            //p.MergeParallelEdges(0.01745240643);
            p.Simplify();

            //Ensure shape is clockwise wound
            p.CalculateWindingOrder();
            if (p.WindingOrder != Point2DList.WindingOrderType.Clockwise)
            {
                if (p.WindingOrder != Point2DList.WindingOrderType.AntiClockwise)
                {
                    throw new InvalidOperationException("Winding order is neither clockwise or anticlockwise");
                }

                //We're done (but we need to correct the winding)
                return(p.Select(a => new Vector2(a.Xf, a.Yf)).ToArray());
            }

            //We're done :D
            return(p.Select(a => new Vector2(a.Xf, a.Yf)).ToArray());
        }
예제 #2
0
 /// <summary>
 /// Performs one or more polygon operations on the 2 provided polygons
 /// </summary>
 /// <param name="polygon1">The first polygon.</param>
 /// <param name="polygon2">The second polygon</param>
 /// <param name="subtract">The result of the polygon subtraction</param>
 /// <returns>error code</returns>
 public static PolygonUtil.PolyUnionError PolygonOperation(PolygonUtil.PolyOperation operations, Point2DList polygon1, Point2DList polygon2, out Dictionary<uint, Point2DList> results)
 {
     PolygonOperationContext ctx = new PolygonOperationContext();
     ctx.Init(operations, polygon1, polygon2);
     results = ctx.mOutput;
     return PolygonUtil.PolygonOperation(ctx);
 }
예제 #3
0
 /// Merges two polygons, given that they intersect.
 /// </summary>
 /// <param name="polygon1">The first polygon.</param>
 /// <param name="polygon2">The second polygon.</param>
 /// <param name="union">The union of the two polygons</param>
 /// <returns>The error returned from union</returns>
 public static PolygonUtil.PolyUnionError PolygonUnion(Point2DList polygon1, Point2DList polygon2, out Point2DList union)
 {
     PolygonOperationContext ctx = new PolygonOperationContext();
     ctx.Init(PolygonUtil.PolyOperation.Union, polygon1, polygon2);
     PolygonUnionInternal(ctx);
     union = ctx.Union;
     return ctx.mError;
 }
예제 #4
0
 /// <summary>
 /// Subtracts one polygon from another.
 /// </summary>
 /// <param name="polygon1">The base polygon.</param>
 /// <param name="polygon2">The polygon to subtract from the base.</param>
 /// <param name="subtract">The result of the polygon subtraction</param>
 /// <returns>error code</returns>
 public static PolygonUtil.PolyUnionError PolygonSubtract(Point2DList polygon1, Point2DList polygon2, out Point2DList subtract)
 {
     PolygonOperationContext ctx = new PolygonOperationContext();
     ctx.Init(PolygonUtil.PolyOperation.Subtract, polygon1, polygon2);
     PolygonSubtractInternal(ctx);
     subtract = ctx.Subtract;
     return ctx.mError;
 }
예제 #5
0
        /// <summary>
        /// Check and return polygon intersections
        /// </summary>
        /// <param name="polygon1"></param>
        /// <param name="polygon2"></param>
        /// <param name="intersections"></param>
        /// <returns></returns>
        private bool VerticesIntersect(Point2DList polygon1, Point2DList polygon2, out List<EdgeIntersectInfo> intersections)
        {
            intersections = new List<EdgeIntersectInfo>();
            double epsilon = Math.Min(polygon1.Epsilon, polygon2.Epsilon);

            // Iterate through polygon1's edges
            for (int i = 0; i < polygon1.Count; i++)
            {
                // Get edge vertices
                Point2D p1 = polygon1[i];
                Point2D p2 = polygon1[polygon1.NextIndex(i)];

                // Get intersections between this edge and polygon2
                for (int j = 0; j < polygon2.Count; j++)
                {
                    Point2D point = new Point2D();

                    Point2D p3 = polygon2[j];
                    Point2D p4 = polygon2[polygon2.NextIndex(j)];

                    // Check if the edges intersect
                    if (TriangulationUtil.LinesIntersect2D(p1, p2, p3, p4, ref point, epsilon))
                    {
                        // Rounding is not needed since we compare using an epsilon.
                        //// Here, we round the returned intersection point to its nearest whole number.
                        //// This prevents floating point anomolies where 99.9999-> is returned instead of 100.
                        //point = new Point2D((float)Math.Round(point.X, 0), (float)Math.Round(point.Y, 0));
                        // Record the intersection
                        intersections.Add(new EdgeIntersectInfo(new Edge(p1, p2), new Edge(p3, p4), point));
                    }
                }
            }

            // true if any intersections were found.
            return (intersections.Count > 0);
        }
예제 #6
0
        /// <summary>
        /// * ref: http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/  - Solution 2 
        /// * Compute the sum of the angles made between the test point and each pair of points making up the polygon. 
        /// * If this sum is 2pi then the point is an interior point, if 0 then the point is an exterior point. 
        /// </summary>
        public bool PointInPolygonAngle(Point2D point, Point2DList polygon)
        {
            double angle = 0;

            // Iterate through polygon's edges
            for (int i = 0; i < polygon.Count; i++)
            {
                // Get points
                Point2D p1 = polygon[i] - point;
                Point2D p2 = polygon[polygon.NextIndex(i)] - point;

                angle += VectorAngle(p1, p2);
            }

            if (Math.Abs(angle) < Math.PI)
            {
                return false;
            }

            return true;
        }
예제 #7
0
        public bool Init(PolygonUtil.PolyOperation operations, Point2DList polygon1, Point2DList polygon2)
        {
            Clear();

            mOperations = operations;
            mOriginalPolygon1 = polygon1;
            mOriginalPolygon2 = polygon2;

            // Make a copy of the polygons so that we dont modify the originals, and
            // force vertices to integer (pixel) values.
            mPoly1 = new Point2DList(polygon1);
            mPoly1.WindingOrder = Point2DList.WindingOrderType.Default;
            mPoly2 = new Point2DList(polygon2);
            mPoly2.WindingOrder = Point2DList.WindingOrderType.Default;

            // Find intersection points
            if (!VerticesIntersect(mPoly1, mPoly2, out mIntersections))
            {
                // No intersections found - polygons do not overlap.
                mError = PolygonUtil.PolyUnionError.NoIntersections;
                return false;
            }

            // make sure edges that intersect more than once are updated to have correct start points
            int numIntersections = mIntersections.Count;
            for (int i = 0; i < numIntersections; ++i)
            {
                for (int j = i + 1; j < numIntersections; ++j)
                {
                    if (mIntersections[i].EdgeOne.EdgeStart.Equals(mIntersections[j].EdgeOne.EdgeStart) &&
                        mIntersections[i].EdgeOne.EdgeEnd.Equals(mIntersections[j].EdgeOne.EdgeEnd))
                    {
                        mIntersections[j].EdgeOne.EdgeStart = mIntersections[i].IntersectionPoint;
                    }
                    if (mIntersections[i].EdgeTwo.EdgeStart.Equals(mIntersections[j].EdgeTwo.EdgeStart) &&
                        mIntersections[i].EdgeTwo.EdgeEnd.Equals(mIntersections[j].EdgeTwo.EdgeEnd))
                    {
                        mIntersections[j].EdgeTwo.EdgeStart = mIntersections[i].IntersectionPoint;
                    }
                }
            }

            // Add intersection points to original polygons, ignoring existing points.
            foreach (EdgeIntersectInfo intersect in mIntersections)
            {
                if (!mPoly1.Contains(intersect.IntersectionPoint))
                {
                    mPoly1.Insert(mPoly1.IndexOf(intersect.EdgeOne.EdgeStart) + 1, intersect.IntersectionPoint);
                }

                if (!mPoly2.Contains(intersect.IntersectionPoint))
                {
                    mPoly2.Insert(mPoly2.IndexOf(intersect.EdgeTwo.EdgeStart) + 1, intersect.IntersectionPoint);
                }
            }

            mPoly1VectorAngles = new List<int>();
            for (int i = 0; i < mPoly2.Count; ++i)
            {
                mPoly1VectorAngles.Add(-1);
            }
            mPoly2VectorAngles = new List<int>();
            for (int i = 0; i < mPoly1.Count; ++i)
            {
                mPoly2VectorAngles.Add(-1);
            }

            // Find starting point on the edge of polygon1 that is outside of
            // the intersected area to begin polygon trace.
            int currentIndex = 0;
            do
            {
                bool bPointInPolygonAngle = PointInPolygonAngle(mPoly1[currentIndex], mPoly2);
                mPoly2VectorAngles[currentIndex] = bPointInPolygonAngle ? 1 : 0;
                if (bPointInPolygonAngle)
                {
                    mStartingIndex = currentIndex;
                    break;
                }
                currentIndex = mPoly1.NextIndex(currentIndex);
            } while (currentIndex != 0);

            // If we don't find a point on polygon1 thats outside of the
            // intersect area, the polygon1 must be inside of polygon2,
            // in which case, polygon2 IS the union of the two.
            if (mStartingIndex == -1)
            {
                mError = PolygonUtil.PolyUnionError.Poly1InsidePoly2;
                return false;
            }

            return true;
        }
예제 #8
0
 public void Clear()
 {
     mOperations = PolygonUtil.PolyOperation.None;
     mOriginalPolygon1 = null;
     mOriginalPolygon2 = null;
     mPoly1 = null;
     mPoly2 = null;
     mIntersections = null;
     mStartingIndex = -1;
     mError = PolygonUtil.PolyUnionError.None;
     mPoly1VectorAngles = null;
     mPoly2VectorAngles = null;
     mOutput = new Dictionary<uint, Point2DList>();
 }
예제 #9
0
        private static List<Point2DList> SplitComplexPolygonCleanup(IList<Point2D> orig)
        {
            List<Point2DList> l = new List<Point2DList>();
            Point2DList origP2DL = new Point2DList(orig);
            l.Add(origP2DL);
            int listIdx = 0;
            int numLists = l.Count;
            while (listIdx < numLists)
            {
                int numPoints = l[listIdx].Count;
                for (int i = 0; i < numPoints; ++i)
                {
                    for (int j = i + 1; j < numPoints; ++j)
                    {
                        if (l[listIdx][i].Equals(l[listIdx][j], origP2DL.Epsilon))
                        {
                            // found a self-intersection loop - split it off into it's own list
                            int numToRemove = j - i;
                            Point2DList newList = new Point2DList();
                            for (int k = i + 1; k <= j; ++k)
                            {
                                newList.Add(l[listIdx][k]);
                            }
                            l[listIdx].RemoveRange(i + 1, numToRemove);
                            l.Add(newList);
                            ++numLists;
                            numPoints -= numToRemove;
                            j = i + 1;
                        }
                    }
                }
                l[listIdx].Simplify();
                ++listIdx;
            }

            return l;
        }
예제 #10
0
        /// <summary>
        /// Trace the edge of a non-simple polygon and return a simple polygon.
        /// 
        ///Method:
        ///Start at vertex with minimum y (pick maximum x one if there are two).  
        ///We aim our "lastDir" vector at (1.0, 0)
        ///We look at the two rays going off from our start vertex, and follow whichever
        ///has the smallest angle (in -Pi . Pi) wrt lastDir ("rightest" turn)
        ///
        ///Loop until we hit starting vertex:
        ///
        ///We add our current vertex to the list.
        ///We check the seg from current vertex to next vertex for intersections
        ///  - if no intersections, follow to next vertex and continue
        ///  - if intersections, pick one with minimum distance
        ///    - if more than one, pick one with "rightest" next point (two possibilities for each)
        ///    
        /// </summary>
        /// <param name="verts"></param>
        /// <returns></returns>
        public static List<Point2DList> SplitComplexPolygon(Point2DList verts, double epsilon)
        {
            int numVerts = verts.Count;
            int nNodes = 0;
            List<SplitComplexPolygonNode> nodes = new List<SplitComplexPolygonNode>();

            //Add base nodes (raw outline)
            for (int i = 0; i < verts.Count; ++i)
            {
                SplitComplexPolygonNode newNode = new SplitComplexPolygonNode(new Point2D(verts[i].X, verts[i].Y));
                nodes.Add(newNode);
            }
            for (int i = 0; i < verts.Count; ++i)
            {
                int iplus = (i == numVerts - 1) ? 0 : i + 1;
                int iminus = (i == 0) ? numVerts - 1 : i - 1;
                nodes[i].AddConnection(nodes[iplus]);
                nodes[i].AddConnection(nodes[iminus]);
            }
            nNodes = nodes.Count;

            //Process intersection nodes - horribly inefficient
            bool dirty = true;
            int counter = 0;
            while (dirty)
            {
                dirty = false;
                for (int i = 0; !dirty && i < nNodes; ++i)
                {
                    for (int j = 0; !dirty && j < nodes[i].NumConnected; ++j)
                    {
                        for (int k = 0; !dirty && k < nNodes; ++k)
                        {
                            if (k == i || nodes[k] == nodes[i][j])
                            {
                                continue;
                            }
                            for (int l = 0; !dirty && l < nodes[k].NumConnected; ++l)
                            {
                                if (nodes[k][l] == nodes[i][j] || nodes[k][l] == nodes[i])
                                {
                                    continue;
                                }
                                //Check intersection
                                Point2D intersectPt = new Point2D();
                                //if (counter > 100) printf("checking intersection: %d, %d, %d, %d\n",i,j,k,l);
                                bool crosses = TriangulationUtil.LinesIntersect2D(  nodes[i].Position,
                                                                                    nodes[i][j].Position,
                                                                                    nodes[k].Position,
                                                                                    nodes[k][l].Position,
                                                                                    true, true, true,
                                                                                    ref intersectPt,
                                                                                    epsilon);
                                if (crosses)
                                {
                                    /*if (counter > 100) {
                                        printf("Found crossing at %f, %f\n",intersectPt.x, intersectPt.y);
                                        printf("Locations: %f,%f - %f,%f | %f,%f - %f,%f\n",
                                                        nodes[i].position.x, nodes[i].position.y,
                                                        nodes[i].connected[j].position.x, nodes[i].connected[j].position.y,
                                                        nodes[k].position.x,nodes[k].position.y,
                                                        nodes[k].connected[l].position.x,nodes[k].connected[l].position.y);
                                        printf("Memory addresses: %d, %d, %d, %d\n",(int)&nodes[i],(int)nodes[i].connected[j],(int)&nodes[k],(int)nodes[k].connected[l]);
                                    }*/
                                    dirty = true;
                                    //Destroy and re-hook connections at crossing point
                                    SplitComplexPolygonNode intersectionNode = new SplitComplexPolygonNode(intersectPt);
                                    int idx = nodes.IndexOf(intersectionNode);
                                    if (idx >= 0 && idx < nodes.Count)
                                    {
                                        intersectionNode = nodes[idx];
                                    }
                                    else
                                    {
                                        nodes.Add(intersectionNode);
                                        nNodes = nodes.Count;
                                    }

                                    SplitComplexPolygonNode nodei = nodes[i];
                                    SplitComplexPolygonNode connij = nodes[i][j];
                                    SplitComplexPolygonNode nodek = nodes[k];
                                    SplitComplexPolygonNode connkl = nodes[k][l];
                                    connij.RemoveConnection(nodei);
                                    nodei.RemoveConnection(connij);
                                    connkl.RemoveConnection(nodek);
                                    nodek.RemoveConnection(connkl);
                                    if (!intersectionNode.Position.Equals(nodei.Position, epsilon))
                                    {
                                        intersectionNode.AddConnection(nodei);
                                        nodei.AddConnection(intersectionNode);
                                    }
                                    if (!intersectionNode.Position.Equals(nodek.Position, epsilon))
                                    {
                                        intersectionNode.AddConnection(nodek);
                                        nodek.AddConnection(intersectionNode);
                                    }
                                    if (!intersectionNode.Position.Equals(connij.Position, epsilon))
                                    {
                                        intersectionNode.AddConnection(connij);
                                        connij.AddConnection(intersectionNode);
                                    }
                                    if (!intersectionNode.Position.Equals(connkl.Position, epsilon))
                                    {
                                        intersectionNode.AddConnection(connkl);
                                        connkl.AddConnection(intersectionNode);
                                    }
                                }
                            }
                        }
                    }
                }
                ++counter;
                //if (counter > 100) printf("Counter: %d\n",counter);
            }

            //    /*
            //    // Debugging: check for connection consistency
            //    for (int i=0; i<nNodes; ++i) {
            //        int nConn = nodes[i].nConnected;
            //        for (int j=0; j<nConn; ++j) {
            //            if (nodes[i].connected[j].nConnected == 0) Assert(false);
            //            SplitComplexPolygonNode* connect = nodes[i].connected[j];
            //            bool found = false;
            //            for (int k=0; k<connect.nConnected; ++k) {
            //                if (connect.connected[k] == &nodes[i]) found = true;
            //            }
            //            Assert(found);
            //        }
            //    }*/

            //Collapse duplicate points
            bool foundDupe = true;
            int nActive = nNodes;
            double epsilonSquared = epsilon * epsilon;
            while (foundDupe)
            {
                foundDupe = false;
                for (int i = 0; i < nNodes; ++i)
                {
                    if (nodes[i].NumConnected == 0)
                    {
                        continue;
                    }
                    for (int j = i + 1; j < nNodes; ++j)
                    {
                        if (nodes[j].NumConnected == 0)
                        {
                            continue;
                        }
                        Point2D diff = nodes[i].Position - nodes[j].Position;
                        if (diff.MagnitudeSquared() <= epsilonSquared)
                        {
                            if (nActive <= 3)
                            {
                                throw new Exception("Eliminated so many duplicate points that resulting polygon has < 3 vertices!");
                            }

                            //printf("Found dupe, %d left\n",nActive);
                            --nActive;
                            foundDupe = true;
                            SplitComplexPolygonNode inode = nodes[i];
                            SplitComplexPolygonNode jnode = nodes[j];
                            //Move all of j's connections to i, and remove j
                            int njConn = jnode.NumConnected;
                            for (int k = 0; k < njConn; ++k)
                            {
                                SplitComplexPolygonNode knode = jnode[k];
                                //Debug.Assert(knode != jnode);
                                if (knode != inode)
                                {
                                    inode.AddConnection(knode);
                                    knode.AddConnection(inode);
                                }
                                knode.RemoveConnection(jnode);
                                //printf("knode %d on node %d now has %d connections\n",k,j,knode.nConnected);
                                //printf("Found duplicate point.\n");
                            }
                            jnode.ClearConnections();   // to help with garbage collection
                            nodes.RemoveAt(j);
                            --nNodes;
                        }
                    }
                }
            }

            //    /*
            //    // Debugging: check for connection consistency
            //    for (int i=0; i<nNodes; ++i) {
            //        int nConn = nodes[i].nConnected;
            //        printf("Node %d has %d connections\n",i,nConn);
            //        for (int j=0; j<nConn; ++j) {
            //            if (nodes[i].connected[j].nConnected == 0) {
            //                printf("Problem with node %d connection at address %d\n",i,(int)(nodes[i].connected[j]));
            //                Assert(false);
            //            }
            //            SplitComplexPolygonNode* connect = nodes[i].connected[j];
            //            bool found = false;
            //            for (int k=0; k<connect.nConnected; ++k) {
            //                if (connect.connected[k] == &nodes[i]) found = true;
            //            }
            //            if (!found) printf("Connection %d (of %d) on node %d (of %d) did not have reciprocal connection.\n",j,nConn,i,nNodes);
            //            Assert(found);
            //        }
            //    }//*/

            //Now walk the edge of the list

            //Find node with minimum y value (max x if equal)
            double minY = double.MaxValue;
            double maxX = -double.MaxValue;
            int minYIndex = -1;
            for (int i = 0; i < nNodes; ++i)
            {
                if (nodes[i].Position.Y < minY && nodes[i].NumConnected > 1)
                {
                    minY = nodes[i].Position.Y;
                    minYIndex = i;
                    maxX = nodes[i].Position.X;
                }
                else if (nodes[i].Position.Y == minY && nodes[i].Position.X > maxX && nodes[i].NumConnected > 1)
                {
                    minYIndex = i;
                    maxX = nodes[i].Position.X;
                }
            }

            Point2D origDir = new Point2D(1.0f, 0.0f);
            List<Point2D> resultVecs = new List<Point2D>();
            SplitComplexPolygonNode currentNode = nodes[minYIndex];
            SplitComplexPolygonNode startNode = currentNode;
            //Debug.Assert(currentNode.nConnected > 0);
            SplitComplexPolygonNode nextNode = currentNode.GetRightestConnection(origDir);
            if (nextNode == null)
            {
                // Borked, clean up our mess and return
                return PolygonUtil.SplitComplexPolygonCleanup(verts);
            }

            resultVecs.Add(startNode.Position);
            while (nextNode != startNode)
            {
                if (resultVecs.Count > (4 * nNodes))
                {
                    //printf("%d, %d, %d\n",(int)startNode,(int)currentNode,(int)nextNode);
                    //printf("%f, %f . %f, %f\n",currentNode.position.x,currentNode.position.y, nextNode.position.x, nextNode.position.y);
                    //verts.printFormatted();
                    //printf("Dumping connection graph: \n");
                    //for (int i=0; i<nNodes; ++i)
                    //{
                    //    printf("nodex[%d] = %f; nodey[%d] = %f;\n",i,nodes[i].position.x,i,nodes[i].position.y);
                    //    printf("//connected to\n");
                    //    for (int j=0; j<nodes[i].nConnected; ++j)
                    //    {
                    //        printf("connx[%d][%d] = %f; conny[%d][%d] = %f;\n",i,j,nodes[i].connected[j].position.x, i,j,nodes[i].connected[j].position.y);
                    //    }
                    //}
                    //printf("Dumping results thus far: \n");
                    //for (int i=0; i<nResultVecs; ++i)
                    //{
                    //    printf("x[%d]=map(%f,-3,3,0,width); y[%d] = map(%f,-3,3,height,0);\n",i,resultVecs[i].x,i,resultVecs[i].y);
                    //}
                    //Debug.Assert(false);
                    //nodes should never be visited four times apiece (proof?), so we've probably hit a loop...crap
                    throw new Exception("nodes should never be visited four times apiece (proof?), so we've probably hit a loop...crap");
                }
                resultVecs.Add(nextNode.Position);
                SplitComplexPolygonNode oldNode = currentNode;
                currentNode = nextNode;
                //printf("Old node connections = %d; address %d\n",oldNode.nConnected, (int)oldNode);
                //printf("Current node connections = %d; address %d\n",currentNode.nConnected, (int)currentNode);
                nextNode = currentNode.GetRightestConnection(oldNode);
                if (nextNode == null)
                {
                    return PolygonUtil.SplitComplexPolygonCleanup(resultVecs);
                }
                // There was a problem, so jump out of the loop and use whatever garbage we've generated so far
                //printf("nextNode address: %d\n",(int)nextNode);
            }

            if (resultVecs.Count < 1)
            {
                // Borked, clean up our mess and return
                return PolygonUtil.SplitComplexPolygonCleanup(verts);
            }
            else
            {
                return PolygonUtil.SplitComplexPolygonCleanup(resultVecs);
            }
        }
예제 #11
0
        public static void InitializeHoles(List <Contour> holes, ITriangulatable parent, ConstrainedPointSet cps)
        {
            int numHoles = holes.Count;
            int holeIdx  = 0;

            // pass 1 - remove duplicates
            while (holeIdx < numHoles)
            {
                int hole2Idx = holeIdx + 1;
                while (hole2Idx < numHoles)
                {
                    bool bSamePolygon = PolygonUtil.PolygonsAreSame2D(holes[holeIdx], holes[hole2Idx]);
                    if (bSamePolygon)
                    {
                        // remove one of them
                        holes.RemoveAt(hole2Idx);
                        --numHoles;
                    }
                    else
                    {
                        ++hole2Idx;
                    }
                }
                ++holeIdx;
            }

            // pass 2: Intersections and Containment
            holeIdx = 0;
            while (holeIdx < numHoles)
            {
                bool bIncrementHoleIdx = true;
                int  hole2Idx          = holeIdx + 1;
                while (hole2Idx < numHoles)
                {
                    if (PolygonUtil.PolygonContainsPolygon(holes[holeIdx], holes[holeIdx].Bounds, holes[hole2Idx], holes[hole2Idx].Bounds, false))
                    {
                        holes[holeIdx].AddHole(holes[hole2Idx]);
                        holes.RemoveAt(hole2Idx);
                        --numHoles;
                    }
                    else if (PolygonUtil.PolygonContainsPolygon(holes[hole2Idx], holes[hole2Idx].Bounds, holes[holeIdx], holes[holeIdx].Bounds, false))
                    {
                        holes[hole2Idx].AddHole(holes[holeIdx]);
                        holes.RemoveAt(holeIdx);
                        --numHoles;
                        bIncrementHoleIdx = false;
                        break;
                    }
                    else
                    {
                        bool bIntersect = PolygonUtil.PolygonsIntersect2D(holes[holeIdx], holes[holeIdx].Bounds, holes[hole2Idx], holes[hole2Idx].Bounds);
                        if (bIntersect)
                        {
                            // this is actually an error condition
                            // fix by merging hole1 and hole2 into hole1 (including the holes inside hole2!) and delete hole2
                            // Then, because hole1 is now changed, restart it's check.
                            PolygonOperationContext ctx = new PolygonOperationContext();
                            if (!ctx.Init(PolygonUtil.PolyOperation.Union | PolygonUtil.PolyOperation.Intersect, holes[holeIdx], holes[hole2Idx]))
                            {
                                if (ctx.mError == PolygonUtil.PolyUnionError.Poly1InsidePoly2)
                                {
                                    holes[hole2Idx].AddHole(holes[holeIdx]);
                                    holes.RemoveAt(holeIdx);
                                    --numHoles;
                                    bIncrementHoleIdx = false;
                                    break;
                                }
                                else
                                {
                                    throw new Exception("PolygonOperationContext.Init had an error during initialization");
                                }
                            }
                            PolygonUtil.PolyUnionError pue = PolygonUtil.PolygonOperation(ctx);
                            if (pue == PolygonUtil.PolyUnionError.None)
                            {
                                Point2DList union        = ctx.Union;
                                Point2DList intersection = ctx.Intersect;

                                // create a new contour for the union
                                Contour c = new Contour(parent);
                                c.AddRange(union);
                                c.Name         = "(" + holes[holeIdx].Name + " UNION " + holes[hole2Idx].Name + ")";
                                c.WindingOrder = Point2DList.WindingOrderType.Default;

                                // add children from both of the merged contours
                                int numChildHoles = holes[holeIdx].GetNumHoles();
                                for (int i = 0; i < numChildHoles; ++i)
                                {
                                    c.AddHole(holes[holeIdx].GetHole(i));
                                }
                                numChildHoles = holes[hole2Idx].GetNumHoles();
                                for (int i = 0; i < numChildHoles; ++i)
                                {
                                    c.AddHole(holes[hole2Idx].GetHole(i));
                                }

                                // make sure we preserve the contours of the intersection
                                Contour cInt = new Contour(c);
                                cInt.AddRange(intersection);
                                cInt.Name         = "(" + holes[holeIdx].Name + " INTERSECT " + holes[hole2Idx].Name + ")";
                                cInt.WindingOrder = Point2DList.WindingOrderType.Default;
                                c.AddHole(cInt);

                                // replace the current contour with the merged contour
                                holes[holeIdx] = c;

                                // toss the second contour
                                holes.RemoveAt(hole2Idx);
                                --numHoles;

                                // current hole is "examined", so move to the next one
                                hole2Idx = holeIdx + 1;
                            }
                            else
                            {
                                throw new Exception("PolygonOperation had an error!");
                            }
                        }
                        else
                        {
                            ++hole2Idx;
                        }
                    }
                }
                if (bIncrementHoleIdx)
                {
                    ++holeIdx;
                }
            }

            numHoles = holes.Count;
            holeIdx  = 0;
            while (holeIdx < numHoles)
            {
                int numPoints = holes[holeIdx].Count;
                for (int i = 0; i < numPoints; ++i)
                {
                    int  j = holes[holeIdx].NextIndex(i);
                    uint constraintCode        = TriangulationConstraint.CalculateContraintCode(holes[holeIdx][i], holes[holeIdx][j]);
                    TriangulationConstraint tc = null;
                    if (!cps.TryGetConstraint(constraintCode, out tc))
                    {
                        tc = new TriangulationConstraint(holes[holeIdx][i], holes[holeIdx][j]);
                        cps.AddConstraint(tc);
                    }

                    // replace the points in the holes with valid points
                    if (holes[holeIdx][i].VertexCode == tc.P.VertexCode)
                    {
                        holes[holeIdx][i] = tc.P;
                    }
                    else if (holes[holeIdx][j].VertexCode == tc.P.VertexCode)
                    {
                        holes[holeIdx][j] = tc.P;
                    }
                    if (holes[holeIdx][i].VertexCode == tc.Q.VertexCode)
                    {
                        holes[holeIdx][i] = tc.Q;
                    }
                    else if (holes[holeIdx][j].VertexCode == tc.Q.VertexCode)
                    {
                        holes[holeIdx][j] = tc.Q;
                    }
                }
                ++holeIdx;
            }
        }