Beispiel #1
0
 // TODO: Remove, looks like a placeholder that never got implemented
 public static void Exclusion(BLoop left, BLoop right, bool removeRight)
 {
     if (left == right || left == null || right == null)
     {
         return;
     }
 }
Beispiel #2
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="dstLoop">The loop to add nodes from requested insertions.</param>
 /// <param name="delCollisions">The segment intersections detected between two islands.</param>
 /// <param name="createdSubdivs">A dictionary that gets filled in with reorganized data
 /// from delCollisions.</param>
 public SplitCollection(
     BLoop dstLoop,
     List <Utils.BezierSubdivSample> delCollisions,
     Dictionary <Utils.NodeTPos, BNode> createdSubdivs)
 {
     this.SetupFromCollisionData(dstLoop, delCollisions, createdSubdivs);
 }
Beispiel #3
0
        /// <summary>
        /// Find the list of unique parent loops from a set of nodes.
        ///
        /// This function is not actually means for boolean operations, but instead for
        /// parsing potential targets from a group of nodes to be involved in boolean operations.
        /// To paraphrase: instead of only involving the nodes or islands from the nodes parameter,
        /// this function is used to involve their entire parent loops.
        /// </summary>
        /// <param name="firstLoop">The first found parent loop - or null if none were found.</param>
        /// <param name="nodes">All the parent loops found, EXCEPT for the first one which will be
        /// placed in output parameter firstLoop.</param>
        /// <returns>The list of unique loops. They are sorted as the order they were first encountered
        /// from iterating through the nodes parameter.</returns>
        public static List <BLoop> GetUniqueLoopsInEncounteredOrder(out BLoop firstLoop, IEnumerable <BNode> nodes)
        {
            List <BLoop> ret = new List <BLoop>();

            firstLoop = null;

            HashSet <BLoop> loopHash = new HashSet <BLoop>();

            foreach (BNode bn in nodes)
            {
                if (loopHash.Add(bn.parent) == false)
                {
                    continue;
                }

                if (firstLoop == null)
                {
                    firstLoop = bn.parent;
                }
                else
                {
                    ret.Add(bn.parent);
                }
            }
            return(ret);
        }
Beispiel #4
0
        public BLoop AddLoop(params BNode.BezierInfo [] pathData)
        {
            BLoop loop = new BLoop(this, pathData);

            this.AddLoop(loop);
            return(loop);
        }
Beispiel #5
0
        public BLoop AddLoop()
        {
            BLoop loop = new BLoop(this);

            this.AddLoop(loop);
            return(loop);
        }
Beispiel #6
0
        /// <summary>
        /// Given a node this in the loop, extract its entire island, and move it
        /// into a newly created loop.
        /// </summary>
        /// <param name="member">A node in the loop that should be moved into its own
        /// new loop.</param>
        /// <param name="createInParent">If true, the created loop will be added to the
        /// parent shape. Else, it will be created as an orphan.</param>
        /// <returns>The newly created loop, or null if the operation fails.</returns>
        public BLoop ExtractIsland(BNode member, bool createInParent = true)
        {
            if (member.parent != this)
            {
                return(null);
            }

            BLoop bl = new BLoop(createInParent ? this.shape : null);

            BNode.EndpointQuery eq = member.GetPathLeftmost();

            eq.node.parent = bl;
            this.nodes.Remove(eq.node);
            bl.nodes.Add(eq.node);

            for (BNode bn = eq.node.next; bn != null && bn != eq.node; bn = bn.next)
            {
                bn.parent = bl;
                this.nodes.Remove(bn);
                bl.nodes.Add(bn);
            }

            bl.FlagDirty();
            this.FlagDirty();

            return(bl);
        }
Beispiel #7
0
        public static void TraceDifference(
            BLoop loopA,
            BLoop loopB,
            BLoop loopInto,
            bool removeInputs = true)
        {
            List <BNode> islsA = loopA.GetIslands(IslandTypeRequest.Closed);
            List <BNode> islsB = loopB.GetIslands(IslandTypeRequest.Closed);

            TraceDifference(islsA, islsB, loopInto, removeInputs);
        }
Beispiel #8
0
        public static void TraceIntersection(
            BLoop loopA,
            BLoop loopB,
            BLoop loopInto,
            List <BNode> outShapes,
            bool removeInputs = true)
        {
            List <BNode> islsA = loopA.GetIslands(IslandTypeRequest.Closed);
            List <BNode> islsB = loopB.GetIslands(IslandTypeRequest.Closed);

            TraceIntersection(islsA, islsB, loopInto, outShapes, removeInputs);
        }
Beispiel #9
0
 /// <summary>
 /// Perform a union operation between the path of two loops.
 /// </summary>
 /// <remarks>This function assumes the left and right loops have only one
 /// path that's a closed island.</remarks>
 /// <param name="dst">The loop to move the path of the created loop into.</param>
 /// <param name="onIslA">The island being added.</param>
 /// <param name="others">The other island being added.</param>
 public static void Union(BLoop dst, out BNode onIslA, params BLoop[] others)
 {
     onIslA = null;
     foreach (BLoop bl in others)
     {
         PerIslandBoolean(
             dst,
             bl,
             Union,
             out onIslA,
             true);
     }
 }
Beispiel #10
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="docPos">The position of the shape.</param>
        /// <param name="rotation">The object rotation of the shape.</param>
        /// <param name="initialLoop">The loop to initialize the shape with.</param>
        public BShape(Vector2 docPos, float rotation, BLoop initialLoop)
        {
            this.docPos   = docPos;
            this.rotation = rotation;

            if (initialLoop != null)
            {
                this.AddLoop(initialLoop);
            }

#if DEVELOPMENT_BUILD || UNITY_EDITOR
            this.debugCounter = Utils.RegisterCounter();
#endif
        }
Beispiel #11
0
        /// <summary>
        /// Move all the nodes in the loop into another loop.
        /// </summary>
        /// <param name="dst">The destination loop to dump node contents into.</param>
        /// <remarks>If the dst isn't the same loop, the loop will end up empty. Of the dst is the same
        /// loop, the operation is skipped.</remarks>
        /// <remarks>The function only changes loop parenting. No changes are made to node topology/linked-lists.</remarks>
        public void DumpInto(BLoop dst)
        {
            if (dst == this)
            {
                return;
            }

            foreach (BNode bn in this.nodes)
            {
                bn.parent = dst;
                dst.nodes.Add(bn);
            }

            this.nodes.Clear();
        }
Beispiel #12
0
        /// <summary>
        /// Add a loop to the shape.
        /// </summary>
        /// <param name="loop">The loop to add.</param>
        public void AddLoop(BLoop loop)
        {
            if (loop.shape != null)
            {
                if (loop.shape == this)
                {
                    return;
                }

                loop.shape.loops.Remove(loop);
            }

            loop.shape = this;
            this.loops.Add(loop);
            this.FlagDirty();
        }
Beispiel #13
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;
                }
            }
        }
Beispiel #14
0
        /// <summary>
        /// Perform a difference between the path of two loops.
        /// </summary>
        /// <remarks>This function assumes the left and right loops have only one
        /// path that's a closed island.</remarks>
        /// <param name="left">The island being subtracted from.</param>
        /// <param name="right">The island being subtracted.</param>
        /// <param name="onIslA">A node that's on the island, or null if no geometry
        /// is left afterwards.</param>
        public static void Difference(
            BLoop left,
            BLoop right,
            out BNode onIslA)
        {
            // we always remove B, which may contain extra stuff from
            // subtraction shapes that didn't find a target to remove.
            PerIslandBoolean(
                left,
                right,
                Difference,
                out onIslA,
                true);

            right.Clear();
            RemoveLoop(right, true);
        }
Beispiel #15
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);
                }
            }
        }
Beispiel #16
0
            /// <summary>
            /// Initialize data for the SplitCollection and its use.
            /// </summary>
            /// <param name="dstLoop">The loop to create new diced nodes in.</param>
            /// <param name="delCollisions">The segment intersections detected between two islands.</param>
            /// <param name="createdSubdivs">A dictionary that gets filled in with reorganized data
            /// from delCollisions.</param>
            public void SetupFromCollisionData(
                BLoop dstLoop,
                List <Utils.BezierSubdivSample> delCollisions,
                Dictionary <Utils.NodeTPos, BNode> createdSubdivs)
            {
                foreach (Utils.BezierSubdivSample bss in delCollisions)
                {
                    Vector2 pos        = bss.a.node.CalculatetPoint(bss.a.lEst);
                    BNode   newSubNode = new BNode(dstLoop, pos);
                    dstLoop.nodes.Add(newSubNode);

                    createdSubdivs.Add(bss.GetTPosA(), newSubNode);
                    createdSubdivs.Add(bss.GetTPosB(), newSubNode);

                    SplitInfo sia = this.GetSplitInfo(bss.a.node);
                    sia.AddEntry(bss.a.lEst, newSubNode);

                    SplitInfo sib = this.GetSplitInfo(bss.b.node);
                    sib.AddEntry(bss.b.lEst, newSubNode);
                }
            }
Beispiel #17
0
        public static void TraceUnion(List <BLoop> loops, BLoop loopInto, List <BNode> finalOutlines, bool removeInputs = true)
        {
            List <BNode> islands = new List <BNode>();

            foreach (BLoop bl in loops)
            {
                List <BNode> lisls = bl.GetIslands(IslandTypeRequest.Closed);
                if (lisls != null)
                {
                    islands.AddRange(lisls);
                }
            }

            TraceUnion(islands, loopInto, removeInputs);

            // the island is used as a parameter that's both the input of island to unionize, and
            // the set of islands that are left afterwards.
            if (finalOutlines != null)
            {
                finalOutlines.InsertRange(0, islands);
            }
        }
Beispiel #18
0
        /// <summary>
        /// Performs a generic per-island operation between the islands contained in two loops.
        /// </summary>
        /// <remarks>Not a generic function - only meant for specific boolean reflow operations.</remarks>
        /// <param name="left">The collection of islands for the operation.</param>
        /// <param name="right">The other collection of islands for the operation.</param>
        /// <param name="op">Function delegate containing the boolean operation.</param>
        /// <param name="onIslA">An output node that exists on the remaining path(s).</param>
        /// <param name="removeRight">If true, remove the contents of the right loop parameter after
        /// the operation.</param>
        public static void PerIslandBoolean(BLoop left, BLoop right, BooleanImpl op, out BNode onIslA, bool removeRight)
        {
            if (left == right || left == null || right == null)
            {
                onIslA = left.nodes[0];
                return;
            }

            onIslA = null;

            // If we're doing a boolean, it's no longer a generated shape - even if it ends
            // up untouched.
            if (left.shape != null)
            {
                left.shape.shapeGenerator = null;
            }

            right.shape = null;

            List <BNode> islandsA = left.GetIslands(IslandTypeRequest.Closed);
            List <BNode> islandB  = right.GetIslands(IslandTypeRequest.Closed);


            foreach (BNode islA in islandsA)
            {
                onIslA = islA;
                foreach (BNode islB in islandB)
                {
                    List <BNode> islandSegsA = new List <BNode>(islA.Travel());
                    List <BNode> islandSegsB = new List <BNode>(islB.Travel());

                    op(left, islandSegsA, islandSegsB, out onIslA);
                }
            }
            if (removeRight == true)
            {
                RemoveLoop(right, true);
            }
        }
Beispiel #19
0
        /// <summary>
        /// Create a deep copy of the shape.
        /// </summary>
        /// <param name="layer">The layer to insert the shape into.</param>
        /// <returns>The duplicate layer.</returns>
        public BShape Clone(Layer layer)
        {
            BShape ret = new BShape(this.docPos, this.rotation);

            ret.layer = layer;
            if (layer != null)
            {
                layer.shapes.Add(ret);
            }

            Dictionary <BNode, BNode> conversion = new Dictionary <BNode, BNode>();

            foreach (BLoop loop in this.loops)
            {
                BLoop newLoop = new BLoop(ret);
                foreach (BNode bn in loop.nodes)
                {
                    BNode newNode = bn.Clone(newLoop);
                    newLoop.nodes.Add(newNode);

                    conversion.Add(bn, newNode);
                }

                foreach (BNode bn in newLoop.nodes)
                {
                    if (bn.prev != null)
                    {
                        bn.prev = conversion[bn.prev];
                    }

                    if (bn.next != null)
                    {
                        bn.next = conversion[bn.next];
                    }
                }
            }

            return(ret);
        }
Beispiel #20
0
        /// <summary>
        /// Remove a loop from its parent shape, and optionally remove
        /// the shape from the layer if it's null.
        /// </summary>
        /// <param name="loop">The loop to remove from its parent.</param>
        /// <param name="rmShapeIfEmpty">If true, the parent shape will be removed from
        /// its parent layer if the operation leaves that shape empty. </param>
        public static void RemoveLoop(BLoop loop, bool rmShapeIfEmpty)
        {
            if (loop.shape == null)
            {
                return;
            }

            BShape shape = loop.shape;

            shape.loops.Remove(loop);
            loop.shape = null;

            if (rmShapeIfEmpty == false || shape.loops.Count > 0)
            {
                return;
            }

            if (shape.layer != null)
            {
                shape.layer.shapes.Remove(shape);
                shape.layer = null;
            }
        }
Beispiel #21
0
        /// <summary>
        /// Given two loops, calculate information on where they collide with each other.
        ///
        /// Does not handle self intersections.
        /// </summary>
        /// <param name="loopA">The loop containing the geometry for the left path.</param>
        /// <param name="loopB">The loop containing the geometry for the right path.</param>
        /// <returns>The points where segments from the left path and the right path intersect
        /// each other.</returns>
        /// <remarks>The terms "left" path and "right" path do not specifically refer to left and right,
        /// but instead are used to different the two paths from each other.</remarks>
        public static List <Utils.BezierSubdivSample> GetLoopCollisionInfo(
            BLoop loopA,
            BLoop loopB)
        {
            List <BNode> islandsA = loopA.GetIslands();
            List <BNode> islandsB = loopB.GetIslands();

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

            foreach (BNode isA in islandsA)
            {
                BNode.EndpointQuery eqA = isA.GetPathLeftmost();
                // Only closed loops count
                if (eqA.result == BNode.EndpointResult.SuccessfulEdge)
                {
                    continue;
                }

                List <BNode> segsA = new List <BNode>(eqA.Enumerate());
                foreach (BNode isB in islandsB)
                {
                    BNode.EndpointQuery eqB = isB.GetPathLeftmost();
                    // Only closed loops count
                    if (eqB.result == BNode.EndpointResult.SuccessfulEdge)
                    {
                        continue;
                    }

                    List <BNode> segsB = new List <BNode>(eqB.Enumerate());

                    GetLoopCollisionInfo(segsA, segsB, collisions);
                }
            }

            Utils.BezierSubdivSample.CleanIntersectionList(collisions);
            return(collisions);
        }
Beispiel #22
0
        public override void Reconstruct()
        {
            this.shape.Clear();

            BLoop bl = this.shape.AddLoop();

            BNode lastNode = null;

            foreach (Vector2 v2 in this.polyPoints)
            {
                BNode bn = new BNode(bl, v2);
                bl.nodes.Add(bn);

                if (lastNode != null)
                {
                    lastNode.next = bn;
                    bn.prev       = lastNode;
                }

                lastNode = bn;
            }

            this.FlagDirty();
        }
Beispiel #23
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>
 /// <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)
 {
     return(Difference(dstloop, islandSegsA, islandSegsB, out onIslA, true));
 }
Beispiel #24
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);
            }
        }
Beispiel #25
0
        /// <summary>
        /// Difference boolean operation using a tracing strategy.
        /// </summary>
        /// <param name="islA">A node, on an island, to be differenced (as the left side).</param>
        /// <param name="islB">A node, on another island, that is to be intersectioned.</param>
        /// <param name="loopInto">The destination loop, where newly traced contents
        /// will be parented to.</param>
        /// <param name="onIsle">Output parameter. If the return value is true, it returns a node on
        /// the newly created traced island.</param>
        /// <param name="removeInputs">If true, the contents in the islands of islA and islB will be
        /// removed from their parents if a traced path is created.</param>
        /// <returns>True if the parameter islands touch and a traced result was created. Else, false.</returns>
        public static bool TraceDifference(
            BNode islA,
            BNode islB,
            BLoop loopInto,
            List <BNode> generated,
            bool removeInputs = true)
        {
            List <BNode> allNodes = new List <BNode>();
            List <Utils.BezierSubdivSample> outList = new List <Utils.BezierSubdivSample>();
            Dictionary <BNode, List <Utils.BezierSubdivSample> > dictCol = new Dictionary <BNode, List <Utils.BezierSubdivSample> >();

            HashSet <BNode> islANodes = new HashSet <BNode>(islA.Travel());

            GatherTraceData(islA, islB, allNodes, outList, dictCol);

            if (outList.Count == 0)
            {
                return(false);
            }

            while (outList.Count > 0)
            {
                BNode   startnode = null;
                Vector2 rt        = Vector2.zero; // Point of rightmost
                float   lam       = 0.0f;         // Lambda of rightmost

                BNode.SubdivideInfo sdiStartA = outList[0].a.node.GetSubdivideInfo(outList[0].a.lEst);
                BNode.SubdivideInfo sdiStartB = outList[0].b.node.GetSubdivideInfo(outList[0].b.lEst);
                float wind;
                if (islANodes.Contains(outList[0].a.node) == true)
                {
                    wind = Utils.Vector2Cross(sdiStartA.windTangent, sdiStartB.windTangent);
                }
                else
                {
                    wind = Utils.Vector2Cross(sdiStartB.windTangent, sdiStartA.windTangent);
                }

                List <BNode> newPath  = new List <BNode>();
                BNode        firstNew = null;

                if (wind > 0.0f)
                {
                    startnode = outList[0].a.node;
                    lam       = outList[0].a.lEst;

                    firstNew           = new BNode(null, sdiStartA.subPos);
                    firstNew.TanIn     = sdiStartA.subIn;
                    firstNew.UseTanIn  = sdiStartA.subIn != Vector2.zero;
                    firstNew.TanOut    = sdiStartA.subOut;
                    firstNew.UseTanOut = sdiStartA.subOut != Vector2.zero;
                }
                else
                {
                    startnode = outList[0].b.node;
                    lam       = outList[0].b.lEst;

                    firstNew           = new BNode(null, sdiStartB.subPos);
                    firstNew.TanIn     = sdiStartB.subIn;
                    firstNew.UseTanIn  = sdiStartB.subIn != Vector2.zero;
                    firstNew.TanOut    = sdiStartB.subOut;
                    firstNew.UseTanOut = sdiStartB.subOut != Vector2.zero;
                }
                outList.RemoveAt(0);
                newPath.Add(firstNew);


                BNode bnIt  = startnode;
                float itLam = lam;

                BNode.SubdivideInfo sdiL;
                BNode.SubdivideInfo sdiR;

                while (true)
                {
                    BNode bnPrev = bnIt;
                    List <Utils.BezierSubdivSample> lstBss;
                    if (dictCol.TryGetValue(bnIt, out lstBss) == false)
                    {
                        // If there are no collisions in the segment, just
                        // add the whole segment
                        itLam = 0.0f;
                        bnIt  = bnIt.next;

                        if (bnIt == startnode && itLam == lam) // Are we back where we started?
                        {
                            // close the shape and we're done
                            firstNew.TanIn    = bnIt.TanIn;
                            firstNew.UseTanIn = true;

                            BNode prevToFirst = newPath[newPath.Count - 1];
                            prevToFirst.TanOut    = bnPrev.TanOut;
                            prevToFirst.UseTanOut = true;
                            break;
                        }
                        else
                        {
                            // Full copy. TanOut can be modified later.
                            BNode bnDirAdd = new BNode(null, bnIt.Pos);
                            bnDirAdd.TanOut    = bnIt.TanOut;
                            bnDirAdd.TanIn     = bnIt.TanIn;
                            bnDirAdd.UseTanIn  = bnIt.UseTanIn;
                            bnDirAdd.UseTanOut = bnIt.UseTanOut;
                            newPath.Add(bnDirAdd);

                            BNode prevToFirst = newPath[newPath.Count - 2];
                            prevToFirst.TanOut    = bnPrev.TanOut;
                            prevToFirst.UseTanOut = true;
                        }
                        continue;
                    }

                    // If there are collision points, trace only the segment
                    // and swap the segment chain we're tracing with the one
                    // we collided with.
                    //
                    // Where we've moved, in relationship to bnPrev.
                    float itStart = itLam;
                    float itEnd   = 1.0f;

                    bool nextProc = false;
                    for (int i = 0; i < lstBss.Count - 1; ++i)
                    {
                        if (lstBss[i].a.lEst == itLam && i != lstBss.Count - 1)
                        {
                            itEnd = lstBss[i + 1].a.lEst;

                            // Figure out where we continue after jumping to the next item
                            bnIt     = lstBss[i + 1].b.node;
                            itLam    = lstBss[i + 1].b.lEst;
                            nextProc = true;

                            if (outList.Remove(lstBss[i]) == false)
                            {
                                outList.Remove(lstBss[i].Reciprocal());
                            }

                            break;
                        }
                    }

                    if (nextProc == false)
                    {
                        if (itLam == 0.0f)
                        {
                            // The first collision in the segment
                            itStart = 0.0f;
                            itEnd   = lstBss[0].a.lEst;

                            // Swap as we were in the loop directly above
                            bnIt  = lstBss[0].b.node;
                            itLam = lstBss[0].b.lEst;
                        }
                        else
                        {
                            // The last collision to the end
                            itStart = lstBss[lstBss.Count - 1].a.lEst;
                            itEnd   = 1.0f;

                            if (outList.Remove(lstBss[lstBss.Count - 1]) == false)
                            {
                                outList.Remove(lstBss[lstBss.Count - 1].Reciprocal());
                            }

                            // Move on to the next node in the chain.
                            bnIt  = bnIt.next;
                            itLam = 0.0f;
                        }
                    }

                    bnPrev.GetSubdivideInfo(itStart, out sdiL, itEnd, out sdiR);

                    if (bnIt == startnode && itLam == lam) // Are we back where we started?
                    {
                        // close the shape and we're done
                        firstNew.TanIn    = sdiR.subIn;
                        firstNew.UseTanIn = true;

                        newPath[newPath.Count - 1].TanOut    = sdiL.subOut;
                        newPath[newPath.Count - 1].UseTanOut = true;
                        break;
                    }
                    else
                    {
                        // Add additional traced point.
                        BNode bnNew = new BNode(null, sdiR.subPos);
                        bnNew.TanIn    = sdiR.subIn;
                        bnNew.UseTanIn = true;

                        newPath[newPath.Count - 1].TanOut    = sdiL.subOut;
                        newPath[newPath.Count - 1].UseTanOut = true;

                        newPath.Add(bnNew);
                    }
                }

                for (int i = 0; i < newPath.Count; ++i)
                {
                    newPath[i].parent = loopInto;

                    if (loopInto != null)
                    {
                        loopInto.nodes.Add(newPath[i]);
                    }

                    newPath[i].next = newPath[(i + 1) % newPath.Count];
                    newPath[i].prev = newPath[((i - 1) + newPath.Count) % newPath.Count];
                }

                if (generated != null && newPath.Count > 0)
                {
                    generated.Add(newPath[0]);
                }
            }

            if (removeInputs == true)
            {
                foreach (BNode bn in allNodes)
                {
                    if (bn.parent != null)
                    {
                        bn.parent.RemoveNode(bn);
                    }
                }
            }

            return(true);
        }
Beispiel #26
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);
        }
Beispiel #27
0
 /// <summary>
 /// Perform a union operation between two islands.
 /// </summary>
 /// <param name="dst">The destination loop where created content will be placed into.</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>
 public static BoundingMode Union(BLoop dst, List <BNode> islandSegsA, List <BNode> islandSegsB, out BNode onIslA)
 {
     return(Union(dst, islandSegsA, islandSegsB, out onIslA, true));
 }
Beispiel #28
0
        /// <summary>
        /// Perform an intersection operation between two islands using a reflow strategy.
        /// </summary>
        /// <param name="left">The collection of islands for the operation.</param>
        /// <param name="right">The other collection of islands for the operation.</param>
        /// <param name="onIslA">An output node that exists on the remaining path(s).</param>
        /// <param name="removeRight">If true, remove the contents of the right loop parameter after
        /// the operation.</param>
        public static void Intersection(BLoop left, BLoop right, out BNode onIslA, bool removeRight)
        {
            onIslA = null;
            // Sanity check on geometry, try to union each loop with its islands
            // so there's no overlapping regions within them.

            List <BNode> rightIslands = right.GetIslands();

            if (rightIslands.Count >= 2)
            {
                for (int i = 1; i < rightIslands.Count; ++i)
                {
                    List <BNode> RSegsA = new List <BNode>(rightIslands[0].Travel());
                    List <BNode> RSegsB = new List <BNode>(rightIslands[i].Travel());

                    Union(right, RSegsA, RSegsB, out onIslA, true);
                }
            }

            List <BNode> leftIslands = left.GetIslands();

            if (leftIslands.Count >= 2)
            {
                for (int i = 1; i < leftIslands.Count; ++i)
                {
                    List <BNode> LSegsA = new List <BNode>(leftIslands[0].Travel());
                    List <BNode> LSegsB = new List <BNode>(leftIslands[i].Travel());

                    Union(right, LSegsA, LSegsB, out onIslA, true);
                }
            }

            leftIslands  = left.GetIslands();
            rightIslands = right.GetIslands();

            foreach (BNode leftIsl in leftIslands)
            {
                List <BNode> leftIslandSegs = new List <BNode>(leftIsl.Travel());
                foreach (BNode rightIsl in rightIslands)
                {
                    List <BNode> rightIslandSegs = new List <BNode>(rightIsl.Travel());

                    Intersection(
                        left,
                        leftIslandSegs,
                        rightIslandSegs,
                        out onIslA);
                }
            }

            foreach (BNode bn in leftIslands)
            {
                bn.RemoveIsland(false);
            }

            foreach (BNode bn in rightIslands)
            {
                bn.RemoveIsland(false);
            }

            // TODO: For each island left, we need to see if there's
            // any shapes being fully contained by the other side.

            if (removeRight == true)
            {
                right.Clear();
                RemoveLoop(right, true);
            }
        }
Beispiel #29
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);
        }
Beispiel #30
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);
        }