Exemple #1
0
        /// <summary>
        /// Check the bounding relationships between two islands.
        /// </summary>
        /// <param name="leftIsl">A node belonging to the first island.</param>
        /// <param name="rightIsl">A node belonging to the second island.</param>
        /// <param name="recheck">If true, validates that both islands are
        /// closed loops before doing the actual check.</param>
        /// <returns></returns>
        public static BoundingMode GetLoopBoundingMode(BNode leftIsl, BNode rightIsl, bool recheck)
        {
            // The basic algorithm works by taking a point on the path at a most extreme
            // (e.g., minx, miny, maxx, maxy - in this case we're doing maxx) and moving farther
            // in that direction to test intersection with the other island.
            //
            // Even though we're focused on rightward movement and ray casting, keep in mind
            // the leftIsl and rightIsl are just two separate islands - it's not to be implied that
            // leftIsl is to the left of rightIsl - that kind of information is unknown going
            // in (and because of the arbitrary shapes of these islands, it's actually impossible
            // to define such things).
            if (recheck == true)
            {
                BNode.EndpointQuery eqL = leftIsl.GetPathLeftmost();
                BNode.EndpointQuery eqR = rightIsl.GetPathLeftmost();

                if (eqL.result == BNode.EndpointResult.SuccessfulEdge)
                {
                    return(BoundingMode.NoCollision);
                }

                if (eqR.result == BNode.EndpointResult.SuccessfulEdge)
                {
                    return(BoundingMode.NoCollision);
                }

                leftIsl  = eqL.node;
                rightIsl = eqL.node;
            }

            List <BNode> leftNodes   = new List <BNode>(leftIsl.Travel());
            List <BNode> righttNodes = new List <BNode>(rightIsl.Travel());

            return(GetLoopBoundingMode(leftNodes, righttNodes));
        }
Exemple #2
0
        /// <summary>
        /// Count how many separate islands are in the loop.
        ///
        /// An island is a chain of nodes that are unconnected to another chain.
        /// </summary>
        /// <returns>The number of islands found.</returns>
        public int CalculateIslands()
        {
            HashSet <BNode> nl  = new HashSet <BNode>(this.nodes);
            int             ret = 0;

            // If anything's left, then there's an island
            while (nl.Count > 0)
            {
                // Count the island
                ++ret;

                // Get any point
                BNode bn = Utils.GetFirstInHash(nl);
                // Find a starting point to remove the island from our record
                BNode.EndpointQuery eq = bn.GetPathLeftmost();

                // Remove the island from our record
                nl.Remove(eq.node);
                for (BNode it = eq.node.next; it != null && it != eq.node; it = it.next)
                {
                    nl.Remove(it);
                }
            }

            return(ret);
        }
Exemple #3
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);
        }
Exemple #4
0
        /// <summary>
        /// Get a list of all the islands and the type of loop they are, whether they're
        /// opened or closed.
        /// </summary>
        /// <returns>A list of endpoint queries containing a reference to all the islands.</returns>
        public List <BNode.EndpointQuery> GetIslandsDescriptive()
        {
            List <BNode.EndpointQuery> ret       = new List <BNode.EndpointQuery>();
            HashSet <BNode>            nodesLeft = new HashSet <BNode>(this.nodes);

            while (nodesLeft.Count > 0)
            {
                BNode n = Utils.GetFirstInHash <BNode>(nodesLeft);
                BNode.EndpointQuery eq = n.GetPathLeftmost();

                ret.Add(eq);

                BNode it = eq.node;
                while (it != null)
                {
                    nodesLeft.Remove(it);

                    it = it.next;
                    if (it == eq.node)
                    {
                        break;
                    }
                }
            }

            return(ret);
        }
Exemple #5
0
        /// <summary>
        /// Counts how many islands are opened and closed.
        /// </summary>
        /// <param name="open">The number of open islands found in the loop object.</param>
        /// <param name="closed">The number of closed islands in the loop object.</param>
        /// <returns></returns>
        public void CountOpenAndClosed(out int open, out int closed)
        {
            open   = 0;
            closed = 0;

            HashSet <BNode> nodesLeft = new HashSet <BNode>(this.nodes);

            while (nodesLeft.Count > 0)
            {
                BNode n = Utils.GetFirstInHash <BNode>(nodesLeft);
                BNode.EndpointQuery eq = n.GetPathLeftmost();

                if (eq.result == BNode.EndpointResult.Cyclical)
                {
                    ++closed;
                }
                else
                {
                    ++open;
                }

                BNode it = eq.node;
                while (it != null)
                {
                    nodesLeft.Remove(it);

                    it = it.next;
                    if (it == eq.node)
                    {
                        break;
                    }
                }
            }
        }
Exemple #6
0
        /// <summary>
        /// Calculate the shape's winding based on the child nodes' segments.
        /// This can be used for closed loops to determine if an island is filled or hollow.
        /// </summary>
        /// <param name="node">A node in the island to calculate the winding for. The node must
        /// be a child of the loop.</param>
        /// <param name="rewindMember">If true, rewind the node to the start of the chain before calculating.</param>
        /// <returns>The winding value of the island. A negative value is filled, while a positive value is hollow.</returns>
        public float CalculateWindingSamples(BNode node, bool rewindMember = true)
        {
            if (rewindMember == true)
            {
                BNode.EndpointQuery eq = node.GetPathLeftmost();
                node = eq.node;
            }

            float ret = node.CalculateWindingSamples();

            for (BNode it = node.next; it != null && it != node; it = it.next)
            {
                ret += it.CalculateWindingSamples();
            }

            return(ret);
        }
Exemple #7
0
        /// <summary>
        /// Returns a node from each island found.
        /// </summary>
        /// <returns>A node from each island found in the loop.</returns>
        public List <BNode> GetIslands(IslandTypeRequest req = IslandTypeRequest.Any)
        {
            List <BNode>    ret       = new List <BNode>();
            HashSet <BNode> nodesLeft = new HashSet <BNode>(this.nodes);

            while (nodesLeft.Count > 0)
            {
                BNode n = Utils.GetFirstInHash <BNode>(nodesLeft);
                BNode.EndpointQuery eq = n.GetPathLeftmost();

                switch (req)
                {
                case IslandTypeRequest.Any:
                    ret.Add(eq.node);
                    break;

                case IslandTypeRequest.Closed:
                    if (eq.result == BNode.EndpointResult.Cyclical)
                    {
                        ret.Add(eq.node);
                    }
                    break;

                case IslandTypeRequest.Open:
                    if (eq.result == BNode.EndpointResult.SuccessfulEdge)
                    {
                        ret.Add(eq.node);
                    }
                    break;
                }

                BNode it = eq.node;
                while (it != null)
                {
                    nodesLeft.Remove(it);

                    it = it.next;
                    if (it == eq.node)
                    {
                        break;
                    }
                }
            }

            return(ret);
        }
Exemple #8
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);
        }
Exemple #9
0
        /// <summary>
        /// Given a shape, fill the session with its closed islands.
        /// </summary>
        /// <param name="shape">The shape to analyze.</param>
        /// <returns>The number of islands extracted.</returns>
        public int ExtractFillLoops(BShape shape)
        {
            int ret = 0;

            List <BNode> islandNodes = new List <BNode>();

            // For all loops in the shape, extract a single peice of each
            // unique circular island.
            foreach (BLoop loop in shape.loops)
            {
                HashSet <BNode> toScan = new HashSet <BNode>(loop.nodes);

                while (toScan.Count > 0)
                {
                    BNode bn = Utils.GetFirstInHash(toScan);

                    BNode.EndpointQuery eq = bn.GetPathLeftmost();

                    // If cyclical, save a single node to scan the island later
                    if (eq.result == BNode.EndpointResult.Cyclical)
                    {
                        islandNodes.Add(bn);
                    }

                    // And remove every part of it from what we're going to
                    //scan in the future.
                    BNode it = bn;
                    while (true)
                    {
                        toScan.Remove(it);

                        it = it.next;
                        if (it == null || it == eq.node)
                        {
                            break;
                        }
                    }
                }
            }

            // Extra islands from the looped nodes we've collected.
            foreach (BNode bisln in islandNodes)
            {
                BSample bstart = bisln.sample;
                BSample bit    = bstart;

                FillIsland island = new FillIsland();
                this.islands.Add(island);
                ++ret;

                FillSegment firstSeg = null;
                FillSegment lastSeg  = null;

                // For now we're going to assume the samples are well formed.
                while (true)
                {
                    FillSegment fs = new FillSegment();

                    // Transfer positions. We keep a local copy of positions, but by convention,
                    // the prevPos will match the prev's nextPos, and the nextPos will match
                    // the next's prevPos.
                    fs.pos = bit.pos;

                    island.segments.Add(fs);

                    if (firstSeg == null)
                    {
                        firstSeg = fs;
                    }
                    else
                    {
                        lastSeg.next = fs;
                        fs.prev      = lastSeg;
                    }

                    lastSeg = fs;

                    bit = bit.next;
                    if (bit == bstart)
                    {
                        break;
                    }
                }
                lastSeg.next  = firstSeg;
                firstSeg.prev = lastSeg;

                // Delme
                if (island.TestValidity() == false)
                {
                    throw new System.Exception("FillSession.ExtractFillLoops produced invalid island.");
                }
            }


            return(ret);
        }
Exemple #10
0
        public static void Edgify(BLoop loop, float pushOut, float pullIn = 0.0f)
        {
            if (pushOut == 0.0f && pullIn == 0.0f)
            {
                return;
            }

            List <BNode> islands = loop.GetIslands();

            foreach (BNode bisl in islands)
            {
                // This will probably just give us bisl back, but it that's the case, then it should
                // be minimal overhead - just to be safe though, and to see what kind of connectivity we're dealing with.
                BNode.EndpointQuery eq = bisl.GetPathLeftmost();

                List <BNode>          origs      = new List <BNode>();
                List <BNode>          copies     = new List <BNode>();
                List <InflationCache> inflations = new List <InflationCache>();
                foreach (BNode it in eq.Enumerate())
                {
                    origs.Add(it);

                    BNode cpy = new BNode(loop, it, false, true);
                    copies.Add(cpy);

                    loop.nodes.Add(cpy);

                    InflationCache ic = new InflationCache();
                    it.GetInflateDirection(out ic.selfInf, out ic.inInf, out ic.outInf);
                    inflations.Add(ic);
                }

                // Stitch the new chain - it should have a reverse winding.
                //
                // The loop is a little backwards, but basically we sub instead of add to
                // treat the prev item in the array like the next in the chain.
                for (int i = 1; i < copies.Count; ++i)
                {
                    copies[i].next     = copies[i - 1];
                    copies[i - 1].prev = copies[i];
                }

                int lastIdx = copies.Count - 1;
                if (eq.result == BNode.EndpointResult.Cyclical)
                {
                    // If it was cyclical, it should close in on itself and it should
                    // never touch the original outline;
                    //
                    // Remember we're treating copies in reverse.
                    copies[lastIdx].prev = copies[0];
                    copies[0].next       = copies[lastIdx];
                }
                else
                {
                    // Or else the opposite ends connect to each other.
                    // Remember we're treating copies in reverse.
                    origs[0].prev  = copies[0];
                    copies[0].next = origs[0];

                    origs[lastIdx].next  = copies[lastIdx];
                    copies[lastIdx].prev = origs[lastIdx];

                    origs[0].UseTanIn        = false;
                    origs[lastIdx].UseTanOut = false;
                    copies[0].UseTanOut      = false;
                    copies[lastIdx].UseTanIn = false;
                }

                if (pushOut != 0.0f)
                {
                    // Now that we have copies and connectivity set up, it's time
                    // to apply the thickening
                    for (int i = 0; i < origs.Count; ++i)
                    {
                        // Push out the original
                        origs[i].Pos    += pushOut * inflations[i].selfInf;
                        origs[i].TanIn  += pushOut * (inflations[i].inInf - inflations[i].selfInf);
                        origs[i].TanOut += pushOut * (inflations[i].outInf - inflations[i].selfInf);
                    }
                }

                if (pullIn != 0.0f)
                {
                    // We can optionally pull in the copy
                    for (int i = 0; i < copies.Count; ++i)
                    {
                        copies[i].Pos    += pullIn * inflations[i].selfInf;
                        copies[i].TanIn  += pullIn * (inflations[i].inInf - inflations[i].selfInf);
                        copies[i].TanOut += pullIn * (inflations[i].outInf - inflations[i].selfInf);
                    }
                }
            }
        }