/// <summary>
        /// Inserts a new point into a subdivision representing a Delaunay
        /// triangulation, and fixes the affected edges so that the result is still a
        /// Delaunay triangulation.
        /// </summary>
        /// <returns>a quadedge containing the inserted vertex</returns>
        public QuadEdge.QuadEdge InsertSite(Vertex v)
        {
            /*
             * This code is based on Guibas and Stolfi (1985), with minor modifications
             * and a bug fix from Dani Lischinski (Graphic Gems 1993). (The modification
             * I believe is the test for the inserted site falling exactly on an
             * existing edge. Without this test zero-width triangles have been observed
             * to be created)
             */
            var e = _subdiv.Locate(v);

            if (_subdiv.IsVertexOfEdge(e, v))
            {
                // point is already in subdivision.
                return(e);
            }
            if (_subdiv.IsOnEdge(e, v.Coordinate))
            {
                // the point lies exactly on an edge, so delete the edge
                // (it will be replaced by a pair of edges which have the point as a vertex)
                e = e.OPrev;
                _subdiv.Delete(e.ONext);
            }

            /*
             * Connect the new point to the vertices of the containing triangle
             * (or quadrilateral, if the new point fell on an existing edge.)
             */
            var baseQuadEdge = _subdiv.MakeEdge(e.Orig, v);

            QuadEdge.QuadEdge.Splice(baseQuadEdge, e);
            var startEdge = baseQuadEdge;

            do
            {
                baseQuadEdge = _subdiv.Connect(e, baseQuadEdge.Sym);
                e            = baseQuadEdge.OPrev;
            } while (e.LNext != startEdge);

            // Examine suspect edges to ensure that the Delaunay condition
            // is satisfied.
            do
            {
                var t = e.OPrev;
                if (t.Dest.RightOf(e) && v.IsInCircle(e.Orig, t.Dest, e.Dest))
                {
                    QuadEdge.QuadEdge.Swap(e);
                    e = e.OPrev;
                }
                else if (e.ONext == startEdge)
                {
                    return(baseQuadEdge); // no more suspect edges.
                }
                else
                {
                    e = e.ONext.LPrev;
                }
            } while (true);
        }
Beispiel #2
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