Exemplo n.º 1
0
        public static void TraceUnion(
            List <BNode> islands, // input and outputs
            BLoop loopInto,
            bool removeInputs = true)
        {
            for (int i = 0; i < islands.Count - 1;)
            {
                bool mergedAny = false;
                for (int j = i + 1; j < islands.Count;)
                {
                    BNode newIsl;

                    BLoop srcLoop = islands[i].parent;
                    if (TraceUnion(islands[i], islands[j], srcLoop, out newIsl, removeInputs) == true)
                    {
                        islands[i] = newIsl;
                        mergedAny  = true;
                        islands.RemoveAt(j);
                    }
                    else
                    {
                        BoundingMode bm = GetLoopBoundingMode(islands[i], islands[j], false);
                        if (bm == BoundingMode.LeftSurroundsRight)
                        {
                            islands[j].RemoveIsland(false);
                            islands.RemoveAt(j);
                        }
                        else if (bm == BoundingMode.RightSurroundsLeft)
                        {
                            islands[i].RemoveIsland(false);
                            islands[i] = islands[j];
                            islands.RemoveAt(j);
                            mergedAny = true;
                        }
                        else
                        {
                            ++j;
                        }
                    }
                }

                if (mergedAny == false)
                {
                    ++i;
                }
            }
        }
Exemplo n.º 2
0
        public static void TraceIntersection(
            List <BNode> islsA,
            List <BNode> islsB,
            BLoop loopInto,
            List <BNode> outShapes,
            bool removeInputs = true)
        {
            for (int i = 0; i < islsA.Count; ++i)
            {
                for (int j = 0; j < islsB.Count; ++j)
                {
                    if (TraceIntersection(islsA[i], islsB[i], loopInto, outShapes, true) == true)
                    {
                        // Do nothing
                    }
                    else
                    {
                        BoundingMode bm = Boolean.GetLoopBoundingMode(islsA[i], islsB[j], false);
                        if (bm == BoundingMode.LeftSurroundsRight)
                        {
                            islsB[j].Clone(loopInto);
                        }
                        else if (bm == Boolean.BoundingMode.RightSurroundsLeft)
                        {
                            islsA[i].Clone(loopInto);
                        }
                    }
                }
            }

            if (removeInputs == true)
            {
                foreach (BNode bn in islsA)
                {
                    bn.RemoveIsland(false);
                }

                foreach (BNode bn in islsB)
                {
                    bn.RemoveIsland(false);
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Perform an intersection operation between two islands using a reflow strategy.
        /// </summary>
        /// <param name="dst">The destination of where the intersected path will be placed.</param>
        /// <param name="islandSegsA">A list of all the nodes in island A.</param>
        /// <param name="islandSegsB">A list of all the nodes in island B.</param>
        /// <param name="onIslA">If the islands were processed, this output parameter contains a node
        /// on the new shape.</param>
        /// <returns>The results from the operation.</returns>
        /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not
        /// be confused with all nodes in the parent loop.</remarks>
        public static BoundingMode Intersection(
            BLoop dst,
            List <BNode> islandSegsA,
            List <BNode> islandSegsB,
            out BNode onIslA)
        {
            // For the intersection, if there's ANY geometry return, it's a copy.
            // It's expected after this operation that the original islands will
            // be destroyed and only the copies will be left.

            float leftWinding  = BNode.CalculateWinding(islandSegsA[0].Travel());
            float rightWinding = BNode.CalculateWinding(islandSegsB[0].Travel());

            if (leftWinding > 0.0f != rightWinding > 0.0f)
            {
                islandSegsB[0].ReverseChainOrder();
            }

            onIslA = null;

            List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>();

            GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions);
            Utils.BezierSubdivSample.CleanIntersectionList(delCollisions);

            if (delCollisions.Count == 0)
            {
                BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB);

                if (bm == BoundingMode.NoCollision)
                {
                    return(BoundingMode.NoCollision);
                }
                else if (bm == BoundingMode.RightSurroundsLeft)
                {
                    // If the right is fully surrounded, the right is kept.
                    Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsA, false);
                    foreach (BNode bn in insertCloneMap.Values)
                    {
                        onIslA = bn;
                        bn.SetParent(dst);
                    }

                    onIslA = islandSegsA[0];
                    return(BoundingMode.RightSurroundsLeft);
                }
                else if (bm == BoundingMode.LeftSurroundsRight)
                {
                    // If the left is fully surrounded, the left is kept.
                    Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsB, false);
                    foreach (BNode bn in insertCloneMap.Values)
                    {
                        onIslA = bn;
                        bn.SetParent(dst);
                    }

                    onIslA = islandSegsA[0];
                    return(BoundingMode.LeftSurroundsRight);
                }
            }

            // Make copies of both, remap and keep their intersection.
            Dictionary <BNode, BNode> cloneMapA = BNode.CloneNodes(islandSegsA, false);
            Dictionary <BNode, BNode> cloneMapB = BNode.CloneNodes(islandSegsB, false);

            foreach (BNode bn in cloneMapA.Values)
            {
                bn.SetParent(dst);
            }

            foreach (BNode bn in cloneMapB.Values)
            {
                bn.SetParent(dst);
            }

            for (int i = 0; i < delCollisions.Count; ++i)
            {
                Utils.BezierSubdivSample bss = delCollisions[i];

                bss.a.node = cloneMapA[bss.a.node];
                bss.b.node = cloneMapB[bss.b.node];


                delCollisions[i] = bss;
            }

            Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions);
            Dictionary <Utils.NodeTPos, BNode> createdSubdivs             = new Dictionary <Utils.NodeTPos, BNode>();
            SplitCollection splitCol = new SplitCollection(dst, delCollisions, createdSubdivs);

            //left.nodes.Clear();
            //foreach(BNode bn in islandSegsA)
            //    bn.SetParent(null, false);
            //
            ////right.DumpInto(dst); // Move everything in from the other loop
            foreach (BNode bn in islandSegsB)
            {
                bn.SetParent(dst, false);
            }

            HashSet <BNode> looseEnds = new HashSet <BNode>();

            foreach (Utils.BezierSubdivSample bss in delCollisions)
            {
                BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()];
                BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()];
                float wind    = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut);
                BNode colNode = createdSubdivs[bss.GetTPosA()];
                onIslA = colNode;

                if (wind < 0.0f != leftWinding < 0.0f)
                {
                    BNode nA = splitCol.GetNextTo(bss.GetTPosA());
                    BNode nB = splitCol.GetPreviousTo(bss.GetTPosB());

                    nA.TanIn  = sdiA.nextIn;
                    nB.TanOut = sdiB.prevOut;

                    colNode.UseTanIn  = bss.b.node.IsLine() == false;
                    colNode.UseTanOut = bss.a.node.IsLine() == false;
                    colNode.TanIn     = sdiB.subIn;
                    colNode.TanOut    = sdiA.subOut;

                    nB.next      = colNode;
                    colNode.prev = nB;
                    nA.prev      = colNode;
                    colNode.next = nA;

                    looseEnds.Add(bss.a.node);
                    looseEnds.Add(splitCol.GetSplitInfo(bss.b.node).origNext);
                }
                else
                {
                    BNode nA = splitCol.GetPreviousTo(bss.GetTPosA());
                    BNode nB = splitCol.GetNextTo(bss.GetTPosB());

                    nA.TanOut = sdiA.prevOut;
                    nB.TanIn  = sdiB.nextIn;

                    colNode.UseTanIn  = bss.a.node.IsLine() == false;
                    colNode.UseTanOut = bss.b.node.IsLine() == false;
                    colNode.TanIn     = sdiA.subIn;
                    colNode.TanOut    = sdiB.subOut;

                    nA.next      = colNode;
                    colNode.prev = nA;
                    nB.prev      = colNode;
                    colNode.next = nB;

                    looseEnds.Add(splitCol.GetSplitInfo(bss.a.node).origNext);
                    looseEnds.Add(bss.b.node);
                }
            }

            // Figure out what internal items need to be removed by
            // checking which nodes have unmatching connectivity.
            ClipLooseEnds(looseEnds);
            return(BoundingMode.Collision);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Perform a difference operation between two islands using a reflow strategy.
        /// </summary>
        /// <param name="dstloop">The destination of where the differenced path will be placed.</param>
        /// <param name="islandSegsA">A list of all the nodes in island A.</param>
        /// <param name="islandSegsB">A list of all the nodes in island B.</param>
        /// <param name="onIslA">A node on the resulting path.</param>
        /// <param name="processFullOverlaps">If true, check and handle is one of the parameter islands
        /// completly wraps around the other island.</param>
        /// <returns>The results from the operation.</returns>
        /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not
        /// be confused with all nodes in the parent loop.</remarks>
        public static BoundingMode Difference(BLoop dstloop, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA, bool processFullOverlaps)
        {
            // If there is any interaction, a copy of islandB is made and used - this means
            // if islandB should be removed, it is up to the caller to remove it themselves.
            //
            // This is done because we don't know if the shape being subtracted is part of a
            // bigger operation where it's subtracted against multiple islands for multi-island
            // loop subtraction, or shape subtraction.

            float leftWind  = BNode.CalculateWinding(islandSegsA[0].Travel());
            float rightWind = BNode.CalculateWinding(islandSegsB[0].Travel());

            // They need to have opposite windings.
            if (leftWind > 0.0f == rightWind > 0.0f)
            {
                islandSegsB[0].ReverseChainOrder();
            }


            List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>();

            GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions);
            Utils.BezierSubdivSample.CleanIntersectionList(delCollisions);

            onIslA = null;

            // If we have an odd number of collisions, we have a problem because we have
            // any entry without an exit which will lead to corrupt topology. For now we
            // don't have a way to resolve that, but at least we can exit on 1 without
            // causing issues (in theory at least).
            if (delCollisions.Count == 1)
            {
                return(BoundingMode.NoCollision);
            }

            if (delCollisions.Count == 0)
            {
                BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB);

                if (processFullOverlaps == false)
                {
                    onIslA = islandSegsA[0];
                    return(bm);
                }

                if (bm == BoundingMode.NoCollision)
                {
                    onIslA = islandSegsA[0];
                    return(BoundingMode.NoCollision);
                }
                else if (bm == BoundingMode.RightSurroundsLeft)
                {
                    // Everything was subtracted out
                    foreach (BNode bn in islandSegsA)
                    {
                        onIslA = bn;
                        bn.SetParent(null);
                    }

                    return(BoundingMode.RightSurroundsLeft);
                }
                else if (bm == BoundingMode.LeftSurroundsRight)
                {
                    // Leave the reverse winding inside as a hollow cavity - and
                    // nothing needs to be changed.
                    Dictionary <BNode, BNode> insertCloneMap = BNode.CloneNodes(islandSegsB, false);
                    foreach (BNode bn in insertCloneMap.Values)
                    {
                        bn.SetParent(dstloop);
                    }

                    onIslA = islandSegsA[0];

                    return(BoundingMode.LeftSurroundsRight);
                }
            }

            // Make sure we have no overlaps of intersections. We currently
            // can't handle such things.
            HashSet <Utils.NodeTPos> intersections = new HashSet <Utils.NodeTPos>();

            foreach (Utils.BezierSubdivSample bss in delCollisions)
            {
                if (intersections.Add(bss.a.TPos()) == false)
                {
                    return(BoundingMode.Degenerate);
                }

                if (intersections.Add(bss.b.TPos()) == false)
                {
                    return(BoundingMode.Degenerate);
                }
            }

            // Add everything in the copy in. We'll clip loose ends later to get
            // rid of the trash.
            Dictionary <BNode, BNode> cloneMap = BNode.CloneNodes(islandSegsB, false);

            foreach (BNode bn in cloneMap.Values)
            {
                bn.SetParent(dstloop);
            }

            // Well, if we're going to make a copy in its place, that means we need to remap
            // all references...
            for (int i = 0; i < delCollisions.Count; ++i)
            {
                Utils.BezierSubdivSample bss = delCollisions[i];
                bss.b.node       = cloneMap[bss.b.node];
                delCollisions[i] = bss;
            }

            Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions);
            Dictionary <Utils.NodeTPos, BNode> createdSubdivs             = new Dictionary <Utils.NodeTPos, BNode>();
            SplitCollection splitCol = new SplitCollection(dstloop, delCollisions, createdSubdivs);

            HashSet <BNode> looseEnds = new HashSet <BNode>();

            // Note that nothing from B will be tagged as a loose end. Instead, we're
            // forcing the entire island of B to be removed after the Per-Island
            // processing.
            foreach (Utils.BezierSubdivSample bss in delCollisions)
            {
                BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()];
                BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()];
                float wind    = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut);
                BNode colNode = createdSubdivs[bss.GetTPosA()];
                onIslA = colNode;

                if (leftWind > 0 == wind > 0.0f)
                {
                    // A CCW transition will go from A to B.
                    BNode nA = splitCol.GetPreviousTo(bss.GetTPosA());
                    BNode nB = splitCol.GetNextTo(bss.GetTPosB());

                    nA.TanOut = sdiA.prevOut;
                    nB.TanIn  = sdiB.nextIn;

                    colNode.UseTanIn  = bss.a.node.IsLine() == false;
                    colNode.UseTanOut = bss.b.node.IsLine() == false;
                    colNode.TanIn     = sdiA.subIn;
                    colNode.TanOut    = sdiB.subOut;

                    nA.next      = colNode;
                    colNode.prev = nA;
                    nB.prev      = colNode;
                    colNode.next = nB;

                    looseEnds.Add(bss.b.node);
                    looseEnds.Add(bss.a.node.next);
                }
                else
                {
                    // A CW transition will go from the other to it.
                    BNode nA = splitCol.GetNextTo(bss.GetTPosA());
                    BNode nB = splitCol.GetPreviousTo(bss.GetTPosB());

                    nA.TanIn  = sdiA.nextIn;
                    nB.TanOut = sdiB.prevOut;

                    colNode.UseTanIn  = bss.b.node.IsLine() == false;
                    colNode.UseTanOut = bss.a.node.IsLine() == false;
                    colNode.TanIn     = sdiB.subIn;
                    colNode.TanOut    = sdiA.subOut;

                    nB.next      = colNode;
                    colNode.prev = nB;
                    nA.prev      = colNode;
                    colNode.next = nA;

                    looseEnds.Add(bss.b.node.next);
                    looseEnds.Add(bss.a.node);
                }
            }

            // Figure out what internal items need to be removed by
            // checking which nodes have unmatching connectivity.
            ClipLooseEnds(looseEnds);

            return(BoundingMode.Collision);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Perform a union operation between two islands using a reflow strategy.
        /// </summary>
        /// <param name="dst">The destination of where the conjoined path will be placed.</param>
        /// <param name="islandSegsA">A list of all the nodes in island A. </param>
        /// <param name="islandSegsB">A list of all the nodes in island B.</param>
        /// <param name="onIslA">If the islands were processed, this output parameter contains a node
        /// on the new shape.</param>
        /// <param name="mergeNonCol">If true, other parts that didn't collide (and weren't merged) will be
        /// moved into the final output destination (dst).</param>
        /// <returns>The results from the operation.</returns>
        /// <remarks>islandSegsA and islandSegsB should only contain elements in the island, and should not
        /// be confused with all nodes in the parent loop.</remarks>
        public static BoundingMode Union(BLoop dst, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA, bool mergeNonCol)
        {
            float leftWind  = BNode.CalculateWinding(islandSegsA[0].Travel());
            float rightWind = BNode.CalculateWinding(islandSegsB[0].Travel());

            onIslA = islandSegsA[0];

            // It can be either winding, but they must be the same - since islandB is going to be used up in the process,
            // we'll modify that winding to keep islandA the same.
            if (leftWind > 0 != rightWind > 0)
            {
                islandSegsB[0].ReverseChainOrder();
            }

            List <Utils.BezierSubdivSample> delCollisions = new List <Utils.BezierSubdivSample>();

            GetLoopCollisionInfo(islandSegsA, islandSegsB, delCollisions);
            Utils.BezierSubdivSample.CleanIntersectionList(delCollisions);

            // If we didn't find any collisions, it's either because they don't overlap
            // at all, or one island fully wraps around another island.
            if (delCollisions.Count == 0)
            {
                BoundingMode bm = GetLoopBoundingMode(islandSegsA, islandSegsB);

                // If an island is completely surrounded by another island, one of the
                // islands gets "smothered out of existence."
                if (bm == BoundingMode.LeftSurroundsRight)
                {
                    // Just remember everything from the right out of existence.
                    foreach (BNode bn in islandSegsB)
                    {
                        bn.SetParent(null);
                    }

                    return(BoundingMode.LeftSurroundsRight);
                }
                else if (bm == BoundingMode.RightSurroundsLeft)
                {
                    // Remove everything from the left out of existence, and
                    // move everything from the right island into the left;
                    foreach (BNode bn in islandSegsA)
                    {
                        bn.SetParent(null, false);
                    }

                    foreach (BNode bn in islandSegsB)
                    {
                        onIslA = bn;
                        bn.SetParent(dst, false);
                    }

                    return(BoundingMode.RightSurroundsLeft);
                }

                if (mergeNonCol == true)
                {
                    foreach (BNode bn in islandSegsB)
                    {
                        bn.SetParent(dst, false);
                    }
                }

                return(BoundingMode.NoCollision);
            }

            // Dump B into A, and if there's anything straggling,
            // we'll clip it as a loose end afterwards.
            foreach (BNode bn in islandSegsB)
            {
                bn.SetParent(dst, false);
            }

            Dictionary <Utils.NodeTPos, BNode.SubdivideInfo> colSlideInfo = SliceCollisionInfo(delCollisions);

            Dictionary <Utils.NodeTPos, BNode> createdSubdivs = new Dictionary <Utils.NodeTPos, BNode>();
            SplitCollection splitCol = new SplitCollection(dst, delCollisions, createdSubdivs);

            HashSet <BNode> looseEnds = new HashSet <BNode>();

            foreach (Utils.BezierSubdivSample bss in delCollisions)
            {
                BNode.SubdivideInfo sdiA = colSlideInfo[bss.GetTPosA()];
                BNode.SubdivideInfo sdiB = colSlideInfo[bss.GetTPosB()];
                float wind    = Utils.Vector2Cross(sdiA.subOut, sdiB.subOut);
                BNode colNode = createdSubdivs[bss.GetTPosA()];
                onIslA = colNode;

                if (leftWind <= 0.0f != wind <= 0.0f)
                {
                    // A CCW transition will go from A to B.
                    BNode nA = splitCol.GetPreviousTo(bss.GetTPosA());
                    BNode nB = splitCol.GetNextTo(bss.GetTPosB());

                    nA.TanOut = sdiA.prevOut;
                    nB.TanIn  = sdiB.nextIn;

                    colNode.UseTanIn  = bss.a.node.IsLine() == false;
                    colNode.UseTanOut = bss.b.node.IsLine() == false;
                    colNode.TanIn     = sdiA.subIn;
                    colNode.TanOut    = sdiB.subOut;

                    nA.next      = colNode;
                    colNode.prev = nA;
                    nB.prev      = colNode;
                    colNode.next = nB;

                    looseEnds.Add(bss.b.node);
                    looseEnds.Add(splitCol.GetSplitInfo(bss.a.node).origNext);
                }
                else
                {
                    // A CW transition will go from the other to it.
                    BNode nA = splitCol.GetNextTo(bss.GetTPosA());
                    BNode nB = splitCol.GetPreviousTo(bss.GetTPosB());

                    nA.TanIn  = sdiA.nextIn;
                    nB.TanOut = sdiB.prevOut;

                    colNode.UseTanIn  = bss.b.node.IsLine() == false;
                    colNode.UseTanOut = bss.a.node.IsLine() == false;
                    colNode.TanIn     = sdiB.subIn;
                    colNode.TanOut    = sdiA.subOut;

                    nB.next      = colNode;
                    colNode.prev = nB;
                    nA.prev      = colNode;
                    colNode.next = nA;

                    looseEnds.Add(splitCol.GetSplitInfo(bss.b.node).origNext);
                    looseEnds.Add(bss.a.node);
                }
            }

            // Figure out what internal items need to be removed by
            // checking which nodes have unmatching connectivity.
            ClipLooseEnds(looseEnds);

            return(BoundingMode.Collision);
        }
Exemplo n.º 6
0
        public static void TraceDifference(
            List <BNode> islsA,
            List <BNode> islsB,
            BLoop loopInto,
            bool removeInputs = true)
        {
            // If there's nothing we're subtracting, just abort.
            if (islsB.Count == 0)
            {
                return;
            }

            // If we're not subtracing away from anything but still go through
            // with the subtraction, the end result would be that the right
            // side goes away.
            if (islsA.Count == 0)
            {
                if (removeInputs == true)
                {
                    foreach (BNode bn in islsB)
                    {
                        bn.RemoveIsland(false);
                    }
                }
                return;
            }

            // Make sure the islands are the appropriate winding order. We're
            // assuming the positive shape is counter-clockwise, and the
            // negative shape is counter clockwise.

            foreach (BNode bnislA in islsA)
            {
                if (BNode.CalculateWinding(bnislA.Travel()) > 0)
                {
                    bnislA.ReverseChainOrder();
                }
            }

            foreach (BNode bnislB in islsB)
            {
                if (BNode.CalculateWinding(bnislB.Travel()) < 0)
                {
                    bnislB.ReverseChainOrder();
                }
            }

            // Were any paths created from intersections?
            bool anyClipped = false;

            // Negative islands (from islsB) that should be kept
            // because they create hollow holes that are surrounded
            // by positive regions.
            HashSet <BNode> negativeHoles = new HashSet <BNode>();

            // Compare each element of islaA with islsB. If there's a collision,
            // islaA is replaced with the created geometry. Note that the number of
            // generated islands is variable.
            for (int i = 0; i < islsA.Count;)
            {
                bool incr = true;
                for (int j = 0; j < islsB.Count; ++j)
                {
                    List <BNode> newTracedIslands = new List <BNode>();

                    // Note how the removeInputs parameter for TraceDifference is false.
                    // That's something we're managing at this current level when operating
                    // in batch.
                    if (TraceDifference(islsA[i], islsB[j], loopInto, newTracedIslands, false) == true)
                    {
                        if (removeInputs == true)
                        {
                            islsA[i].RemoveIsland(false);
                        }

                        // Replace
                        islsA.RemoveAt(i);
                        islsA.InsertRange(i, newTracedIslands);

                        anyClipped = true;
                    }
                    else
                    {
                        BoundingMode bm = GetLoopBoundingMode(islsA[i], islsB[j], false);
                        if (bm == BoundingMode.LeftSurroundsRight)
                        {
                            // Do nothing, leave the inside with a reverse hole in it.
                            negativeHoles.Add(islsB[j]);
                        }
                        else if (bm == Boolean.BoundingMode.RightSurroundsLeft)
                        {
                            if (removeInputs == true)
                            {
                                islsA[i].RemoveIsland(false);
                            }

                            islsA.RemoveAt(i);
                            incr = false;
                        }
                    }
                }

                if (incr == true)
                {
                    ++i;
                }
            }

            if (removeInputs == true)
            {
                foreach (BNode bn in islsB)
                {
                    if (negativeHoles.Contains(bn) == false)
                    {
                        bn.RemoveIsland(false);
                    }
                }
            }

            if (anyClipped == true && negativeHoles.Count > 0)
            {
                // If we have any negative holes and clipping happened, combine the
                // negative regions and see if that punches a hole past the positive
                // region through recurion.
                //
                // In theory, this should only happen at most once.

                List <BNode> negs = new List <BNode>(negativeHoles);
                TraceUnion(negs, loopInto, true);

                TraceDifference(islsA, negs, loopInto, removeInputs);
            }
        }