Пример #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;
                }
            }
        }
        /// <summary>
        /// Locates an edge e, such that either v is on e, or e is an edge of a triangle containing v.
        /// The search starts from the last located edge amd proceeds on the general direction of v.
        /// </summary>
        public QuadEdge Locate(Vertex v)
        {
            if (! _lastEdge.IsLive)
            {
                Init();
            }

            QuadEdge e = _subdiv.LocateFromEdge(v, _lastEdge);
            _lastEdge = e;
            return e;
        }
        /// <summary>
        /// Locates an edge e, such that either v is on e, or e is an edge of a triangle containing v.
        /// The search starts from the last located edge and proceeds on the general direction of v.
        /// </summary>
        public QuadEdge Locate(Vertex v)
        {
            if (!_lastEdge.IsLive)
            {
                Init();
            }

            var e = _subdiv.LocateFromEdge(v, _lastEdge);

            _lastEdge = e;
            return(e);
        }
 private static bool CanMoveForward(QuadEdge e, QuadEdge baseEdge)
 {
     if (e == baseEdge)
     {
         return(true);
     }
     if (e == baseEdge.Inverse)
     {
         return(false);
     }
     return(e.Origin.X > e.Destination.X);
 }
Пример #5
0
        /// <summary>
        /// True if <paramref name="pos"/> inside the convex hull formed by the triangulation.
        /// </summary>
        /// <param name="pos">The position to test</param>
        public bool InsideConvexHull(Vec3 pos)
        {
            QuadEdge <T> boundEdge = RightMostEdge;

            foreach (QuadEdge <T> hullEdge in boundEdge.RightEdges(CCW:false))
            {
                if (Geometry.RightOf(pos, hullEdge))
                {
                    // Must be outside because hullEdge right face is outside
                    return(false);
                }
            }

            return(true);
        }
        private async Task <Partition> Subdivide(int lidx, int ridx)
        {
            Debug.Assert(lidx != ridx);
            if (ridx - lidx == 1) //2 points
            {
                QuadEdge edge = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                return(new Partition
                {
                    Left = edge,
                    Right = edge.Inverse
                });
            }
            if (ridx - lidx == 2) //3 points
            {
                QuadEdge a = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                QuadEdge b = QuadEdge.MakeEdge(this._points[lidx + 1], this._points[lidx + 2]);
                QuadEdge.Splice(a.Inverse, b);

                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 1], this._points[lidx + 2]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = a,
                        Right = b.Inverse
                    });
                }
                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 2], this._points[lidx + 1]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = c.Inverse,
                        Right = c
                    });
                }
                return(new Partition
                {
                    Left = a,
                    Right = b.Inverse
                });
            }
            int midx = (lidx + ridx) / 2;
            Task <Partition> leftTask  = Subdivide(lidx, midx);
            Task <Partition> rightTask = Subdivide(midx + 1, ridx);

            return(Merge(await leftTask, await rightTask));
        }
Пример #7
0
        /// <summary>
        /// Construct triangles based on Delaunay triangulation.
        /// </summary>
        protected override IEnumerable <Vec3> ExportTriangles()
        {
            // FIFO
            var queue = new Queue <QuadEdge <TEdge> >();

            // Start at the far right
            QuadEdge <TEdge> first = _mesh.RightMostEdge;

            queue.Enqueue(first);

            // Visit all edge of the convex hull in CW order and
            // add opposite edges to queue
            foreach (QuadEdge <TEdge> hullEdge in first.RightEdges(CCW:false))
            {
                // Enqueue same edge but with opposite direction
                queue.Enqueue(hullEdge.Sym);
                hullEdge.Tag = !_mesh.VisitedTagState;

                // Because mesh does not have any boundary this is also a triangle
                yield return(hullEdge.Origin);
            }

            // Convex hull now closed. Start triangles construction
            while (queue.Count > 0)
            {
                QuadEdge <TEdge> edge = queue.Dequeue();
                if (edge.Tag == _mesh.VisitedTagState)
                {
                    foreach (QuadEdge <TEdge> current in edge.RightEdges(CCW:false))
                    {
                        if (current.Sym.Tag == _mesh.VisitedTagState)
                        {
                            queue.Enqueue(current.Sym);
                        }
                        current.Tag = !_mesh.VisitedTagState;
                        yield return(current.Origin);
                    }
                }
            }

            // Inverse flag to be able to traverse again at next call
            _mesh.SwitchInternalFlag();
        }
        private static List <Point> GetResult(Partition partition)
        {
            var      result   = new List <Point>();
            QuadEdge baseEdge = QuadEdge.ConnectLeft(partition.Left.Inverse, partition.Right);
            QuadEdge e        = baseEdge.DestinationNext.Inverse;
            Point    u        = baseEdge.Destination;

            while (true)
            {
                while (e != baseEdge && !CanMoveForward(e.DestinationNext, baseEdge))
                {
                    u = e.Destination;
                    e = e.DestinationNext.Inverse;
                }

                if (e != baseEdge)
                {
                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }

                while (!CanMoveForward(e.OriginNext, baseEdge))
                {
                    result.Add(u);
                    if (u == baseEdge.Destination)
                    {
                        baseEdge.Delete();
                        return(result);
                    }
                    e = e.OriginNext.Inverse;
                    while (CanMoveForward(e.DestinationNext, baseEdge))
                    {
                        e = e.DestinationNext;
                    }
                    u = e.Origin;

                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }
                e = e.OriginNext;
            }
        }
 private static void LowestCommonTangent(Partition left, Partition right, out QuadEdge leftLowest,
                                         out QuadEdge rightLowest)
 {
     leftLowest  = left.Right;
     rightLowest = right.Left;
     while (true)
     {
         if (rightLowest.Origin.IsLeftOf(leftLowest))
         {
             leftLowest = leftLowest.LeftNext;
         }
         else if (leftLowest.Origin.IsRightOf(rightLowest))
         {
             rightLowest = rightLowest.RightPrevious;
         }
         else
         {
             break;
         }
     }
 }
Пример #10
0
        /// <summary>
        /// Find correct position for a voronoi site that should be at infinite
        /// Assume primalEdge.Rot.Origin as the vertex to compute, that is
        /// there should be no vertex on the right of primalEdge.
        /// Site computed is the destination of a segment in a direction normal to
        /// the tangent vector of the primalEdge (destination - origin) with
        /// its symetrical (primalEdge.RotSym.Origin) as origin.
        /// Radius should be choose higher enough to avoid neighbor voronoi points
        /// to be further on. A good guest is the maximal distance between non infinite
        /// voronoi vertices or five times the maximal distance between delaunay vertices.
        /// </summary>
        /// <remarks>
        /// If primalEdge.RotSym.Origin is null, then its value is computed first
        /// using CircumCenter2D because this vertex is always inside a delaunay triangle.
        /// </remarks>
        protected Vec3 ConstructAtInfinity(QuadEdge <TEdge> primalEdge, double radius,
                                           Func <Vec3, Vec3, Vec3, Vec3> centerCalculator)
        {
            var rotSym = primalEdge.RotSym;

            // Find previous voronoi site
            if (rotSym.Origin == null)
            {
                rotSym.Origin = centerCalculator(primalEdge.Origin,
                                                 primalEdge.Destination,
                                                 primalEdge.Onext.Destination);
            }
            double xCenter = rotSym.Origin.X;
            double yCenter = rotSym.Origin.Y;

            // Compute normalized tangent of primal edge scaled by radius
            double xTangent = primalEdge.Destination.X - primalEdge.Origin.X;
            double yTangent = primalEdge.Destination.Y - primalEdge.Origin.Y;
            double dist     = Math.Sqrt(xTangent * xTangent + yTangent * yTangent);

            xTangent /= dist;
            yTangent /= dist;
            xTangent *= radius;
            yTangent *= radius;

            // Add vertex using edge dual destination as origin
            // in direction normal to the primal edge
            Vec3 normal = new Vec3(xCenter - yTangent, yCenter + xTangent, rotSym.Origin.Z);

            // If new voronoi vertex is on the left of the primal edge
            // we used the wrong normal vector --> get its opposite
            if (Geometry.LeftOf(normal, primalEdge))
            {
                normal = new Vec3(xCenter + yTangent, yCenter - xTangent, rotSym.Origin.Z);
            }
            return(normal);
        }
 private void Init()
 {
     _lastEdge = FindEdge();
 }
Пример #12
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 });
        }
Пример #13
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);
        }
Пример #14
0
 /// <summary>
 /// Locate the closest with respect to following constraints:
 ///   - <paramref name="pos"/> is on the line of returned edge
 ///   - <paramref name="pos"/> is inside the left face of returned edge
 ///
 /// If site outside the convex hull Locate will loop forever looking for
 /// a corresponding edge that does not exists ... unless you first check
 /// you are in the convex hull or set <paramref name="checkBoundFirst"/> to true.
 /// </summary>
 /// <param name="pos">The position to locate</param>
 /// <param name="edge">Edge used to start locate process. Can be used to speed up search.</param>
 /// <param name="safe">If true, first check if pos in convex hull of triangulation</param>
 public QuadEdge <TEdge> Locate(Vec3 pos, QuadEdge <TEdge> edge = null, bool safe = false)
 {
     return(_mesh.Locate(pos, edge, safe));
 }
Пример #15
0
        // PROTECTED METHOD

        /// <summary>
        /// Construct voronoi face based on Delaunay triangulation. Vertices at infinity
        /// are define based on radius parameter. It should be large enough to avoid
        /// some circumcenters (finite voronoi vertices) to be further on.
        /// </summary>
        /// <remarks>
        /// Each face is yield just after their construction. Then it's neighborhood
        /// is not guarantee to be constructed.
        /// </remarks>
        /// <param name="radius">Distance used to construct site that are at infinity.</param>
        protected IEnumerable <Face <TEdge, TFace> > ExportFaces(Func <Vec3, Vec3, Vec3, Vec3> centerCalculator,
                                                                 double radius)
        {
            // FIFO
            var queue = new Queue <QuadEdge <TEdge> >();

            // Start at the far left
            QuadEdge <TEdge> first = _mesh.LeftMostEdge;

            // @TODO Bounds
            List <QuadEdge <TEdge> > bounds = new List <QuadEdge <TEdge> >();


            // Visit all edge of the convex hull to compute dual vertices
            // at infinity by looping in a CW order over edges with same left face.
            foreach (QuadEdge <TEdge> hullEdge in first.LeftEdges(CCW:false))
            {
                // Construct a new face
                // First infinite voronoi vertex
                if (hullEdge.Rot.Destination == null)
                {
                    hullEdge.Rot.Destination = ConstructAtInfinity(hullEdge.Sym,
                                                                   radius,
                                                                   centerCalculator);
                }

                // Add other vertices by looping over hullEdge origin in CW order (Oprev)
                foreach (QuadEdge <TEdge> current in hullEdge.EdgesFrom(CCW:false))
                {
                    if (current.Rot.Origin == null)
                    {
                        // Delaunay edge on the boundary
                        if (Geometry.LeftOf(current.Oprev.Destination, current))
                        {
                            current.Rot.Origin = ConstructAtInfinity(current,
                                                                     radius,
                                                                     centerCalculator);
                        }
                        else
                        {
                            current.Rot.Origin = centerCalculator(current.Origin,
                                                                  current.Destination,
                                                                  current.Oprev.Destination);

                            // Speed up computation of point coordinates
                            // All edges sharing the same origin should have same
                            // geometrical origin
                            foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom())
                            {
                                otherDual.Origin = current.Rot.Origin;
                            }
                        }
                    }

                    if (current.Sym.Tag == _mesh.VisitedTagState)
                    {
                        queue.Enqueue(current.Sym);
                        bounds.Add(current.Sym);
                    }
                    current.Tag = !_mesh.VisitedTagState;
                }

                // After face construction over
                yield return(new Face <TEdge, TFace>(hullEdge, true, true));
            }

            // Convex hull now closed --> Construct bounded voronoi faces
            while (queue.Count > 0)
            {
                QuadEdge <TEdge> edge = queue.Dequeue();

                if (edge.Tag == _mesh.VisitedTagState)
                {
                    // Construct a new face
                    foreach (QuadEdge <TEdge> current in edge.EdgesFrom(CCW:false))
                    {
                        if (current.Rot.Origin == null)
                        {
                            current.Rot.Origin = centerCalculator(current.Origin,
                                                                  current.Destination,
                                                                  current.Oprev.Destination);
                            // Speed up computation of point coordinates
                            // All edges sharing the same origin have same
                            // geometrical origin
                            foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom())
                            {
                                otherDual.Origin = current.Rot.Origin;
                            }
                        }
                        if (current.Sym.Tag == _mesh.VisitedTagState)
                        {
                            queue.Enqueue(current.Sym);
                        }
                        current.Tag = !_mesh.VisitedTagState;
                    }

                    // After face construction over
                    if (bounds.Contains(edge))
                    {
                        yield return(new Face <TEdge, TFace>(edge, true, false));
                    }
                    else
                    {
                        yield return(new Face <TEdge, TFace>(edge, false, false));
                    }
                }
            }

            // Inverse flag to be able to traverse again at next call
            _mesh.SwitchInternalFlag();
        }
Пример #16
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="safe"/> inside the convex hull.</param>
 public bool Insert(Vec3 newPos, QuadEdge <TEdge> edge = null, bool safe = false)
 {
     return(_mesh.Insert(newPos, edge, safe));
 }
Пример #17
0
 /// <summary>
 /// Checks whether this instance is to the right of <paramref name="edge"/>
 /// </summary>
 /// <param name="edge"></param>
 /// <returns></returns>
 public bool IsRightOf(QuadEdge edge)
 {
     return(IsCounterClockWise(this, edge.Destination, edge.Origin));
 }
Пример #18
0
        /// <summary>
        /// Construct voronoi face based on Delaunay triangulation. Vertices at infinity
        /// are define based on radius parameter. It should be large enough to avoid
        /// some circumcenters (finite voronoi vertices) to be further on.
        /// </summary>
        /// <remarks>
        /// Each face is yield just after their construction. Then it's neighborhood
        /// is not guarantee to be constructed.
        /// </remarks>
        protected IEnumerable <Face <TEdge, TFace> > ExportFaces(Func <Vec3, Vec3, Vec3, Vec3> centerCalculator,
                                                                 double scaleFactor)
        {
            // FIFO
            var queue = new Queue <QuadEdge <TEdge> >();

            // Start at the far left
            QuadEdge <TEdge> first = LeftMostEdge;

            // @TODO Make sure CyclingMerge compute a valide triangulation
            // Construct first face using Centroid because
            // triangulation is not necessary delaunay
            foreach (QuadEdge <TEdge> current in first.EdgesFrom(CCW:false))
            {
                if (current.Rot.Origin == null)
                {
                    current.Rot.Origin = Geometry.Centroid(Geometry.InvStereographicProjection(current.Origin),
                                                           Geometry.InvStereographicProjection(current.Destination),
                                                           Geometry.InvStereographicProjection(current.Oprev.Destination));
                    double invDistanceScaled = scaleFactor / current.Rot.Origin.Magnitude;
                    current.Rot.Origin *= invDistanceScaled;

                    // Speed up computation of point coordinates
                    // All edges sharing the same origin have same
                    // geometrical origin
                    foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom())
                    {
                        otherDual.Origin = current.Rot.Origin;
                    }
                }
                if (current.Sym.Tag == _mesh.VisitedTagState)
                {
                    queue.Enqueue(current.Sym);
                }
                current.Tag = !_mesh.VisitedTagState;
            }
            yield return(new Face <TEdge, TFace>(first, false, false));

            // Convex hull now closed --> Construct bounded voronoi faces
            while (queue.Count > 0)
            {
                QuadEdge <TEdge> edge = queue.Dequeue();

                if (edge.Tag == _mesh.VisitedTagState)
                {
                    // Construct a new face
                    foreach (QuadEdge <TEdge> current in edge.EdgesFrom(CCW:false))
                    {
                        if (current.Rot.Origin == null)
                        {
                            current.Rot.Origin = centerCalculator(Geometry.InvStereographicProjection(current.Origin),
                                                                  Geometry.InvStereographicProjection(current.Destination),
                                                                  Geometry.InvStereographicProjection(current.Oprev.Destination));
                            double invDistanceScaled = scaleFactor / current.Rot.Origin.Magnitude;
                            current.Rot.Origin *= invDistanceScaled;

                            // Speed up computation of point coordinates
                            // All edges sharing the same origin have same
                            // geometrical origin
                            foreach (QuadEdge <TEdge> otherDual in current.Rot.EdgesFrom())
                            {
                                otherDual.Origin = current.Rot.Origin;
                            }
                        }
                        if (current.Sym.Tag == _mesh.VisitedTagState)
                        {
                            queue.Enqueue(current.Sym);
                        }
                        current.Tag = !_mesh.VisitedTagState;
                    }

                    yield return(new Face <TEdge, TFace>(edge, false, false));
                }
            }

            // Inverse flag to be able to traverse again at next call
            _mesh.SwitchInternalFlag();
        }
Пример #19
0
        /// <summary>
        /// Locate the closest with respect to following constraints:
        ///   - <paramref name="pos"/> is on the line of returned edge
        ///   - <paramref name="pos"/> is inside the left face of returned edge
        ///
        /// If site outside the convex hull Locate will loop forever looking for
        /// a corresponding edge that does not exists ... unless you first check
        /// you are in the convex hull or set <paramref name="safe"/> to true.
        /// </summary>
        /// <param name="pos">The position to locate</param>
        /// <param name="edge">Edge used to start locate process. Can be used to speed up search.</param>
        /// <param name="safe">If true, first check if pos in convex hull of triangulation</param>
        public QuadEdge <T> Locate(Vec3 pos, QuadEdge <T> edge = null, bool safe = false)
        {
            // Check boundary first
            if (safe)
            {
                QuadEdge <T> result = ClosestBoundingEdge(pos);
                if (result != null)
                {
                    // pos outside
                    return(result);
                }
            }

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

            // Assume it must be inside ...
            while (true)
            {
                if (pos == edge.Origin || pos == edge.Destination)
                {
                    return(edge);
                }
                else if (Geometry.RightOf(pos, edge))
                {
                    edge = edge.Sym;
                }
                else if (Geometry.LeftOf(pos, edge.Onext))
                {
                    edge = edge.Onext;
                }
                else if (Geometry.LeftOf(pos, edge.Dprev))
                {
                    edge = edge.Dprev;
                }
                else
                {
                    // Previous triangle edge
                    QuadEdge <T> otherE = edge.Lprev;
                    if (Geometry.AlmostColinear(pos, otherE.Origin,
                                                otherE.Destination))
                    {
                        return(otherE);
                    }


                    // Next triangle edge
                    otherE = edge.Lnext;
                    if (Geometry.AlmostColinear(pos, otherE.Origin,
                                                otherE.Destination))
                    {
                        return(otherE);
                    }

                    return(edge);
                }
            }
        }
 private void Init()
 {
     _lastEdge = FindEdge();
 }
Пример #21
0
 /// <summary>
 /// Return true if Geometry.RightOf(edge.Destination, baseEdge) is true.
 /// </summary>
 protected bool IsValid(QuadEdge <T> edge, QuadEdge <T> baseEdge)
 {
     // Geometry.Ccw called directly.
     return(Geometry.Ccw(edge.Destination, baseEdge.Destination, baseEdge.Origin));
 }
Пример #22
0
        private static Partition Merge(Partition leftPartition, Partition rightPartition)
        {
            QuadEdge left  = leftPartition.Left;                                                             //ldo
            QuadEdge right = rightPartition.Right;                                                           //rdo

            LowestCommonTangent(leftPartition, rightPartition, out QuadEdge lowLeft, out QuadEdge lowRight); //ldi, rdi

            QuadEdge edgeBase = QuadEdge.ConnectLeft(lowRight.Inverse, lowLeft);
            QuadEdge lcand    = edgeBase.RightPrevious;
            QuadEdge rcand    = edgeBase.OriginPrevious;

            if (edgeBase.Origin == right.Origin)
            {
                right = edgeBase;
            }
            if (edgeBase.Destination == left.Origin)
            {
                left = edgeBase.Inverse;
            }

            while (true)
            {
                QuadEdge temp = lcand.OriginNext;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Origin.InCircle(lcand.Destination, temp.Destination, lcand.Origin))
                    {
                        lcand.Delete();
                        lcand = temp;
                        temp  = lcand.OriginNext;
                    }
                }

                temp = rcand.OriginPrevious;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Destination.InCircle(temp.Destination, rcand.Destination, rcand.Origin))
                    {
                        rcand.Delete();
                        rcand = temp;
                        temp  = rcand.OriginPrevious;
                    }
                }

                bool leftValid  = Point.IsCounterClockWise(edgeBase.Origin, lcand.Destination, edgeBase.Destination);
                bool rightValid = Point.IsCounterClockWise(edgeBase.Origin, rcand.Destination, edgeBase.Destination);
                if (!leftValid && !rightValid)
                {
                    break;
                }

                if (!leftValid ||
                    rightValid && rcand.Destination.InCircle(lcand.Destination, lcand.Origin, rcand.Origin))
                {
                    edgeBase = QuadEdge.ConnectLeft(rcand, edgeBase.Inverse);
                    rcand    = edgeBase.Inverse.LeftNext;
                }
                else
                {
                    edgeBase = QuadEdge.ConnectRight(lcand, edgeBase).Inverse;
                    lcand    = edgeBase.RightPrevious;
                }
            }

            return(new Partition
            {
                Left = left,
                Right = right
            });
        }
Пример #23
0
 /// <summary>
 /// Return true if pt is on the left of edge segment
 /// (edge.Origin -> edge.Destination)
 /// </summary>
 public static bool LeftOf <T>(Vec3 pt, QuadEdge <T> edge)
 {
     return(Ccw(pt, edge.Origin, edge.Destination));
 }
Пример #24
0
        } // End Function GetConcaveHull


        public Geometry ComputeConcaveHull()
        {
            ConformingDelaunayTriangulationBuilder cdtb = new ConformingDelaunayTriangulationBuilder();

            cdtb.SetSites(this.geometries);

            QuadEdgeSubdivision qes = cdtb.GetSubdivision();

            IList <QuadEdge>         quadEdges   = qes.GetEdges();
            IList <QuadEdgeTriangle> qeTriangles = QuadEdgeTriangle.CreateOn(qes);
            IEnumerable <Vertex>     qeVertices  = qes.GetVertices(false);

            int iV = 0;

            foreach (Vertex v in qeVertices)
            {
                this.coordinates[v.Coordinate] = iV;
                this.vertices[iV] = new Vertex(iV, v.Coordinate);
                iV++;
            }

            List <QuadEdge> qeFrameBorder = new List <QuadEdge>();
            List <QuadEdge> qeFrame       = new List <QuadEdge>();
            List <QuadEdge> qeBorder      = new List <QuadEdge>();

            // here each one more
            foreach (QuadEdge qe in quadEdges)
            {
                if (qes.IsFrameBorderEdge(qe))
                {
                    qeFrameBorder.Add(qe);
                }

                if (qes.IsFrameEdge(qe))
                {
                    qeFrame.Add(qe);
                }
            } // Next qe

            // border
            for (int j = 0; j < qeFrameBorder.Count; j++)
            {
                QuadEdge q = qeFrameBorder[j];
                if (!qeFrame.Contains(q))
                {
                    qeBorder.Add(q);
                }
            } // Next j

            // deletion of exterior edges
            foreach (QuadEdge qe in qeFrame)
            {
                qes.Delete(qe);
            }



            Dictionary <QuadEdge, double> qeDistances = new Dictionary <QuadEdge, double>();

            foreach (QuadEdge qe in quadEdges)
            {
                qeDistances.Add(qe, qe.ToLineSegment().Length);
            }


            DoubleComparator dc = new DoubleComparator(qeDistances);
            // This doesn't work with dictionary - missing duplicates ...
            List <KeyValuePair <QuadEdge, double> > qeSorted = new List <KeyValuePair <QuadEdge, double> >();

            foreach (KeyValuePair <QuadEdge, double> thisDistance in qeDistances)
            {
                qeSorted.Add(thisDistance);
            }
            qeSorted.Sort(dc);


            // edges creation
            int i = 0;

            foreach (KeyValuePair <QuadEdge, double> kvp in qeSorted)
            {
                LineSegment s = kvp.Key.ToLineSegment();
                s.Normalize();

                int    idS = this.coordinates[s.P0];
                int    idD = this.coordinates[s.P1];
                Vertex oV  = this.vertices[idS];
                Vertex eV  = this.vertices[idD];

                Edge edge;
                if (qeBorder.Contains(kvp.Key))
                {
                    oV.IsBorder = true;
                    eV.IsBorder = true;

                    edge = new Edge(i, s, oV, eV, true);

                    if (s.Length < this.threshold)
                    {
                        this.shortLengths[i] = edge;
                    }
                    else
                    {
                        this.lengths[i] = edge;
                    }
                }
                else
                {
                    edge = new Edge(i, s, oV, eV, false);
                }

                this.edges[i]    = edge;
                this.segments[s] = i;
                i++;
            } // Next qe

            // hm of linesegment and hm of edges // with id as key
            // hm of triangles using hm of ls and connection with hm of edges

            i = 0;
            foreach (QuadEdgeTriangle qet in qeTriangles)
            {
                LineSegment sA = qet.GetEdge(0).ToLineSegment();
                LineSegment sB = qet.GetEdge(1).ToLineSegment();
                LineSegment sC = qet.GetEdge(2).ToLineSegment();

                sA.Normalize();
                sB.Normalize();
                sC.Normalize();

                Edge edgeA = this.edges[this.segments[sA]];
                Edge edgeB = this.edges[this.segments[sB]];
                Edge edgeC = this.edges[this.segments[sC]];

                Triangle triangle = new Triangle(i, qet.IsBorder() ? true : false);
                triangle.AddEdge(edgeA);
                triangle.AddEdge(edgeB);
                triangle.AddEdge(edgeC);

                edgeA.AddTriangle(triangle);
                edgeB.AddTriangle(triangle);
                edgeC.AddTriangle(triangle);

                this.triangles[i] = triangle;
                i++;
            } // Next qet


            // add triangle neighbourood
            foreach (Edge edge in this.edges.Values)
            {
                if (edge.Triangles.Count != 1)
                {
                    Triangle tA = edge.Triangles[0];
                    Triangle tB = edge.Triangles[1];
                    tA.AddNeighbour(tB);
                    tB.AddNeighbour(tA);
                }
            }

            // concave hull algorithm
            int index = 0;

            while (index != -1)
            {
                index = -1;

                Edge e = null;

                // find the max length (smallest id so first entry)
                int si = this.lengths.Count;

                if (si != 0)
                {
                    KeyValuePair <int, Edge> entry = this.lengths.First();

                    int ind = entry.Key;
                    if (entry.Value.Geometry.Length > this.threshold)
                    {
                        index = ind;
                        e     = entry.Value;
                    }
                } // End if (si != 0)

                if (index != -1)
                {
                    Triangle        triangle   = e.Triangles[0];
                    List <Triangle> neighbours = triangle.Neighbours;

                    // irregular triangle test
                    if (neighbours.Count == 1)
                    {
                        this.shortLengths[e.Id] = e;
                        this.lengths.Remove(e.Id);
                    }
                    else
                    {
                        Edge e0 = triangle.Edges[0];
                        Edge e1 = triangle.Edges[1];

                        // test if all the vertices are on the border
                        if (e0.OV.IsBorder && e0.EV.IsBorder &&
                            e1.OV.IsBorder && e1.EV.IsBorder)
                        {
                            this.shortLengths[e.Id] = e;
                            this.lengths.Remove(e.Id);
                        }
                        else
                        {
                            // management of triangles
                            Triangle tA = neighbours[0];
                            Triangle tB = neighbours[1];
                            tA.Border = true; // FIXME not necessarily useful
                            tB.Border = true; // FIXME not necessarily useful
                            this.triangles.Remove(triangle.Id);
                            tA.RemoveNeighbour(triangle);
                            tB.RemoveNeighbour(triangle);

                            // new edges
                            List <Edge> ee = triangle.Edges;
                            Edge        eA = ee[0];
                            Edge        eB = ee[1];
                            Edge        eC = ee[2];

                            if (eA.Border)
                            {
                                this.edges.Remove(eA.Id);
                                eB.Border = true;

                                eB.OV.IsBorder = true;
                                eB.EV.IsBorder = true;

                                eC.Border = true;

                                eC.OV.IsBorder = true;
                                eC.EV.IsBorder = true;

                                // clean the relationships with the triangle
                                eB.RemoveTriangle(triangle);
                                eC.RemoveTriangle(triangle);

                                if (eB.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eB.Id] = eB;
                                }
                                else
                                {
                                    this.lengths[eB.Id] = eB;
                                }

                                if (eC.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eC.Id] = eC;
                                }
                                else
                                {
                                    this.lengths[eC.Id] = eC;
                                }

                                this.lengths.Remove(eA.Id);
                            } // End if (eA.Border)
                            else if (eB.Border)
                            {
                                this.edges.Remove(eB.Id);
                                eA.Border      = true;
                                eA.OV.IsBorder = true;
                                eA.EV.IsBorder = true;
                                eC.Border      = true;
                                eC.OV.IsBorder = true;
                                eC.EV.IsBorder = true;

                                // clean the relationships with the triangle
                                eA.RemoveTriangle(triangle);
                                eC.RemoveTriangle(triangle);

                                if (eA.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eA.Id] = eA;
                                }
                                else
                                {
                                    this.lengths[eA.Id] = eA;
                                }
                                if (eC.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eC.Id] = eC;
                                }
                                else
                                {
                                    this.lengths[eC.Id] = eC;
                                }

                                this.lengths.Remove(eB.Id);
                            } // End else if (eB.Border)
                            else
                            {
                                this.edges.Remove(eC.Id);
                                eA.Border = true;

                                eA.OV.IsBorder = true;
                                eA.EV.IsBorder = true;
                                eB.Border      = true;
                                eB.OV.IsBorder = true;
                                eB.EV.IsBorder = true;

                                // clean the relationships with the triangle
                                eA.RemoveTriangle(triangle);
                                eB.RemoveTriangle(triangle);

                                if (eA.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eA.Id] = eA;
                                }
                                else
                                {
                                    this.lengths[eA.Id] = eA;
                                }

                                if (eB.Geometry.Length < this.threshold)
                                {
                                    this.shortLengths[eB.Id] = eB;
                                }
                                else
                                {
                                    this.lengths[eB.Id] = eB;
                                }

                                this.lengths.Remove(eC.Id);
                            } // End Else of if (e0.OV.Border && e0.EV.Border && e1.OV.Border && e1.EV.Border)
                        }     // End Else of if
                    }         // End Else of if (neighbours.Count == 1)
                }             // End if (index != -1)
            }                 // Whend

            // concave hull creation
            List <LineString> edges = new List <LineString>();

            foreach (Edge e in this.lengths.Values)
            {
                LineString l = e.Geometry.ToGeometry(this.geomFactory);
                edges.Add(l);
            }

            foreach (Edge e in this.shortLengths.Values)
            {
                LineString l = e.Geometry.ToGeometry(this.geomFactory);
                edges.Add(l);
            }

            // merge
            Operation.Linemerge.LineMerger lineMerger = new Operation.Linemerge.LineMerger();
            lineMerger.Add(edges);

            LineString merge = null;

            using (IEnumerator <Geometry> en = lineMerger.GetMergedLineStrings().GetEnumerator())
            {
                en.MoveNext();
                merge = (LineString)en.Current;
            }

            if (merge.IsRing)
            {
                LinearRing lr          = new LinearRing(merge.CoordinateSequence, this.geomFactory);
                Polygon    concaveHull = new Polygon(lr, null, this.geomFactory);
                return(concaveHull);
            }

            return(merge);
        } // End Function ComputeConcaveHull