Esempio n. 1
0
        // PROTECTED METHODS

        /// <summary>
        /// Merge left and right most edges together to construct a cycling triangulation.
        /// Needed for sphere for example after normal triangulation step.
        /// </summary>
        protected void CyclingMerge()
        {
            // @TODO Make sure CyclingMerge compute a valide triangulation
            QuadEdge <TEdge> baseEdge = _mesh.RightMostEdge.Rnext;
            QuadEdge <TEdge> lCand    = baseEdge.Sym.Onext;
            QuadEdge <TEdge> rCand    = baseEdge.Oprev;

            // Get edges CCW order from left extremum until reach right extremum
            // to complete the sphere triangulation
            // All edges must be stored first because pointer are updated
            // during construction and will eventually leads to infinite loop ...
            var toCheckEdge = rCand.LeftEdges(true).ToList();

            foreach (QuadEdge <TEdge> rightEdge in toCheckEdge)
            {
                if (rightEdge.Destination != lCand.Destination)
                {
                    // Connect rightEdge.Destination to baseEdge.Destination
                    // Construct a fan
                    baseEdge = QuadEdge <TEdge> .Connect(rightEdge, baseEdge.Sym);
                }
                else
                {
                    break;
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Triangulate the set of points using a divide and conquer approach.
        /// </summary>
        private QuadEdge <T>[] Triangulate(Vec3[] pts)
        {
            QuadEdge <T> a, b, c, nextCand;

            // Only two points -> One edge
            if (pts.Length == 2)
            {
                a = QuadEdge <T> .MakeEdge(pts[0], pts[1]);

                return(new QuadEdge <T>[] { a, a.Sym });
            }
            // Only tree points
            else if (pts.Length == 3)
            {
                a = QuadEdge <T> .MakeEdge(pts[0], pts[1]);

                b = QuadEdge <T> .MakeEdge(pts[1], pts[2]);

                QuadEdge <T> .Splice(a.Sym, b);

                // Closing triangle
                if (Geometry.Ccw(pts[0], pts[1], pts[2]))
                {
                    c = QuadEdge <T> .Connect(b, a);

                    return(new QuadEdge <T>[] { a, b.Sym });
                }
                else if (Geometry.Ccw(pts[0], pts[2], pts[1]))
                {
                    c = QuadEdge <T> .Connect(b, a);

                    return(new QuadEdge <T>[] { c.Sym, c });
                }
                else
                {
                    // Points are collinear
                    return(new QuadEdge <T>[] { a, b.Sym });
                }
            }

            // SPLITTING
            // Divide them halfsize recursively
            int halfLength = (pts.Length + 1) / 2;

            QuadEdge <T>[] left  = Triangulate(pts.Take(halfLength).ToArray());
            QuadEdge <T>[] right = Triangulate(pts.Skip(halfLength).ToArray());


            // MERGING
            // From left to right
            QuadEdge <T> ldo = left[0];
            QuadEdge <T> ldi = left[1];
            QuadEdge <T> rdi = right[0];
            QuadEdge <T> rdo = right[1];

            // Compute lower common tangent to be able to merge both triangulations
            bool crossEdgeNotFound = true;

            while (crossEdgeNotFound)
            {
                if (Geometry.LeftOf(rdi.Origin, ldi))
                {
                    ldi = ldi.Lnext;
                }
                else if (Geometry.RightOf(ldi.Origin, rdi))
                {
                    rdi = rdi.Rprev;
                }
                else
                {
                    crossEdgeNotFound = false;
                }
            }

            // Start merging
            // 1) Creation of the baseEdge quad edge (See Fig.21)
            QuadEdge <T> baseEdge = QuadEdge <T> .Connect(rdi.Sym, ldi);

            if (ldi.Origin == ldo.Origin)
            {
                ldo = baseEdge.Sym;
            }
            if (rdi.Origin == rdo.Origin)
            {
                rdo = baseEdge;
            }

            // 2) Rising bubble (See Fig. 22)
            bool upperCommonTangentNotFound = true;

            while (upperCommonTangentNotFound)
            {
                // Locate the first L site (lCand.Destination) to be encountered
                // by the rising bubble, and delete L edges out of baseEdge.Destination
                // that fail the circle test.
                QuadEdge <T> lCand = baseEdge.Sym.Onext;
                if (IsValid(lCand, baseEdge))
                {
                    while (Geometry.InCircumCercle2D(lCand.Onext.Destination,
                                                     baseEdge.Destination, baseEdge.Origin, lCand.Destination))
                    {
                        nextCand = lCand.Onext;
                        QuadEdge <T> .Delete(lCand);

                        lCand = nextCand;
                    }
                }
                // Same for the right part (Symetrically)
                QuadEdge <T> rCand = baseEdge.Oprev;
                if (IsValid(rCand, baseEdge))
                {
                    while (Geometry.InCircumCercle2D(rCand.Oprev.Destination,
                                                     baseEdge.Destination, baseEdge.Origin, rCand.Destination))
                    {
                        nextCand = rCand.Oprev;
                        QuadEdge <T> .Delete(rCand);

                        rCand = nextCand;
                    }
                }
                // Upper common tangent is baseEdge
                if (!IsValid(lCand, baseEdge) && !IsValid(rCand, baseEdge))
                {
                    upperCommonTangentNotFound = false;
                }
                // Construct new cross edge between left and right
                // The next cross edge is to be connected to either lcand.Dest or rCand.Dest
                // If both are valid, then choose the appropriate one using the
                // Geometry.InCircumCercle2D test
                else if (!IsValid(lCand, baseEdge) ||
                         (
                             IsValid(rCand, baseEdge) &&
                             Geometry.InCircumCercle2D(rCand.Destination,
                                                       lCand.Destination,
                                                       lCand.Origin,
                                                       rCand.Origin)
                         )
                         )
                {
                    // Cross edge baseEdge added from rCand.Destination to basel.Destination
                    baseEdge = QuadEdge <T> .Connect(rCand, baseEdge.Sym);
                }
                else
                {
                    // Cross edge baseEdge added from baseEdge.Origin to lCand.Destination
                    baseEdge = QuadEdge <T> .Connect(baseEdge.Sym, lCand.Sym);
                }
            }
            return(new QuadEdge <T>[] { ldo, rdo });
        }
Esempio n. 3
0
        /// <summary>
        /// Insert a new site inside an existing delaunay triangulation. New site
        /// must be inside the convex hull of previoulsy added sites.
        /// Set <paramref name="safe"/> to true to first test if new site is correct.
        /// </summary>
        /// <param name="newPos">The position to of new site</param>
        /// <param name="edge">Edge used to start locate process. Can be used to speed up search.</param>
        /// <param name="safe">If true, check if <paramref name="newPos"/> inside the convex hull.</param>
        public bool Insert(Vec3 newPos, QuadEdge <T> edge = null, bool safe = false)
        {
            if (safe)
            {
                bool result = InsideConvexHull(newPos);
                if (!result)
                {
                    // Cannot add site not already inside the convex hull
                    return(false);
                }
            }

            // Start somewhere if no hint
            if (edge == null)
            {
                edge = _leftRightEdges[1];
            }

            // Locate edge (must be inside the boundary)
            QuadEdge <T> foundE = Locate(newPos, edge, safe: false);

            // Site already triangulated
            if (Geometry.AlmostEquals(foundE.Origin, newPos) ||
                Geometry.AlmostEquals(foundE.Destination, newPos))
            {
                return(false);
            }

            // On an edge ?
            if (Geometry.AlmostColinear(foundE.Origin, foundE.Destination, newPos))
            {
                var temp = foundE.Oprev;
                QuadEdge <T> .Delete(foundE);

                foundE = temp;
            }

            // Create new edge to connect new site to neighbors
            QuadEdge <T> baseE = QuadEdge <T> .MakeEdge(foundE.Origin, newPos);

            Vec3 first = baseE.Origin;

            QuadEdge <T> .Splice(baseE, foundE);

            // Up to 4 vertices if new site on an edge
            do
            {
                baseE = QuadEdge <T> .Connect(foundE, baseE.Sym);

                foundE = baseE.Oprev;
            } while (foundE.Destination != first);


            // Fill star shaped polygon and swap suspect edges
            // Adding a new point can break old condition about InCircle test
            foundE = baseE.Oprev;
            bool shouldExit = false;

            do
            {
                var tempE = foundE.Oprev;
                if (Geometry.RightOf(tempE.Destination, foundE) &&
                    Geometry.InCircumCercle2D(newPos, foundE.Origin, tempE.Destination, foundE.Destination))
                {
                    QuadEdge <T> .Swap(foundE);

                    // tempE != foundE.Oprev after swap
                    foundE = foundE.Oprev;
                }
                else if (foundE.Origin == first)
                {
                    // No more suspect edge ... exit
                    shouldExit = true;
                }
                else
                {
                    // Get next suspect edge from top to bottom
                    foundE = foundE.Onext.Lprev;
                }
            } while (!shouldExit);

            return(true);
        }