// Calculate the distance between two SimplePoints
        private static double Distance3d(SimplePoint3d p1, SimplePoint3d p2)
        {
            double result = 0;

            result = Math.Sqrt(Math.Pow((p2.x - p1.x), 2) + Math.Pow((p2.y - p1.y), 2));
            return(result);
        }
        private static void CalculateCircumcircle3d(SimplePoint3d p1, SimplePoint3d p2, SimplePoint3d p3, out SimplePoint3d circumCentre, out double radius)
        {
            // Calculate the length of each side of the triangle
            double a = Distance3d(p2, p3); // side a is opposite point 1
            double b = Distance3d(p1, p3); // side b is opposite point 2
            double c = Distance3d(p1, p2); // side c is opposite point 3

            // Calculate the radius of the circumcircle
            double area = Math.Abs((double)(p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)) / 2);

            radius = a * b * c / (4 * area);

            // Define area coordinates to calculate the circumcentre
            double pp1 = Math.Pow(a, 2) * (Math.Pow(b, 2) + Math.Pow(c, 2) - Math.Pow(a, 2));
            double pp2 = Math.Pow(b, 2) * (Math.Pow(c, 2) + Math.Pow(a, 2) - Math.Pow(b, 2));
            double pp3 = Math.Pow(c, 2) * (Math.Pow(a, 2) + Math.Pow(b, 2) - Math.Pow(c, 2));

            // Normalise
            double t1 = pp1 / (pp1 + pp2 + pp3);
            double t2 = pp2 / (pp1 + pp2 + pp3);
            double t3 = pp3 / (pp1 + pp2 + pp3);

            // Convert to Cartesian
            double x = t1 * p1.x + t2 * p2.x + t3 * p3.x;
            double y = t1 * p1.y + t2 * p2.y + t3 * p3.y;

            circumCentre = new SimplePoint3d(x, y, 0);
        }
Example #3
0
 public SimpleTriangle3d(int a, int b, int c, SimplePoint3d circumcentre, double radius)
 {
     this.a            = a;
     this.b            = b;
     this.c            = c;
     this.circumcentre = circumcentre;
     this.radius       = radius;
 }
        // Construct a triangle from 3 vertices
        private static SqlGeometry Triangle3dFromPoints(SimplePoint3d p1, SimplePoint3d p2, SimplePoint3d p3, int srid)
        {
            SqlGeometryBuilder TriangleBuilder = new SqlGeometryBuilder();

            TriangleBuilder.SetSrid(srid);
            TriangleBuilder.BeginGeometry(OpenGisGeometryType.Polygon);
            TriangleBuilder.BeginFigure(p1.x, p1.y, p1.z, null);
            TriangleBuilder.AddLine(p2.x, p2.y, p2.z, null);
            TriangleBuilder.AddLine(p3.x, p3.y, p3.z, null);
            TriangleBuilder.AddLine(p1.x, p1.y, p1.z, null);
            TriangleBuilder.EndFigure();
            TriangleBuilder.EndGeometry();
            return(TriangleBuilder.ConstructedGeometry);
        }
            // Implement IComparable CompareTo method to enable sorting
            int IComparable.CompareTo(object obj)
            {
                SimplePoint3d other = (SimplePoint3d)obj;

                if (this.x > other.x)
                {
                    return(1);
                }
                else if (this.x < other.x)
                {
                    return(-1);
                }
                else
                {
                    return(0);
                }
            }
        public static void GeometryTriangulate3d(SqlGeometry MultiPoint)
        {
            // Retrieve the SRID
            int srid = (int)MultiPoint.STSrid;

            // Check valid input
            if (!(MultiPoint.STGeometryType() == "MULTIPOINT" && MultiPoint.STNumPoints() > 3))
            {
                throw new ArgumentException("Input must be a MultiPoint containing at least three points");
            }

            // Initialise a list of vertices
            List <SimplePoint3d> Vertices = new List <SimplePoint3d>();

            // Add all the original supplied points
            for (int i = 1; i <= MultiPoint.STNumPoints(); i++)
            {
                SqlGeometry   p     = MultiPoint.STPointN(i);
                SimplePoint3d Point = new SimplePoint3d((double)p.STX, (double)p.STY, p.HasZ ? (double)p.Z : 0);
                // MultiPoints can contain the same point twice, but this messes up Delauney
                if (!Vertices.Contains(Point))
                {
                    Vertices.Add(Point);
                }
            }

            // Important - count the number of points in the array, NOT using STNumPoints of the supplied geometry, as some duplicate points
            // may have been removed
            int numPoints = Vertices.Count;

            // Important! Sort the list so that points sweep from left - right
            Vertices.Sort();

            // Calculate the "supertriangle" that encompasses the pointset
            SqlGeometry Envelope = MultiPoint.STEnvelope();
            // Width
            double dx = (double)(Envelope.STPointN(2).STX - Envelope.STPointN(1).STX);
            // Height
            double dy = (double)(Envelope.STPointN(4).STY - Envelope.STPointN(1).STY);
            // Maximum dimension
            double dmax = (dx > dy) ? dx : dy;
            // Centre
            double avgx = (double)Envelope.STCentroid().STX;
            double avgy = (double)Envelope.STCentroid().STY;
            // Create the points at corners of the supertriangle
            SimplePoint3d a = new SimplePoint3d(avgx - 2 * dmax, avgy - dmax, 0);
            SimplePoint3d b = new SimplePoint3d(avgx + 2 * dmax, avgy - dmax, 0);
            SimplePoint3d c = new SimplePoint3d(avgx, avgy + 2 * dmax, 0);

            // Add the supertriangle vertices to the end of the vertex array
            Vertices.Add(a);
            Vertices.Add(b);
            Vertices.Add(c);

            double        radius;
            SimplePoint3d circumcentre;

            CalculateCircumcircle3d(a, b, c, out circumcentre, out radius);

            // Create a triangle from the vertices
            SimpleTriangle3d SuperTriangle = new SimpleTriangle3d(numPoints, numPoints + 1, numPoints + 2, circumcentre, radius);

            // Add the supertriangle to the list of triangles
            List <SimpleTriangle3d> Triangles = new List <SimpleTriangle3d>();

            Triangles.Add(SuperTriangle);

            List <SimpleTriangle3d> CompletedTriangles = new List <SimpleTriangle3d>();

            // Loop through each point
            for (int i = 0; i < numPoints; i++)
            {
                // Initialise the edge buffer
                List <int[]> Edges = new List <int[]>();

                // Loop through each triangle
                for (int j = Triangles.Count - 1; j >= 0; j--)
                {
                    // If the point lies within the circumcircle of this triangle
                    if (Distance3d(Triangles[j].circumcentre, Vertices[i]) < Triangles[j].radius)
                    {
                        // Add the triangle edges to the edge buffer
                        Edges.Add(new int[] { Triangles[j].a, Triangles[j].b });
                        Edges.Add(new int[] { Triangles[j].b, Triangles[j].c });
                        Edges.Add(new int[] { Triangles[j].c, Triangles[j].a });

                        // Remove this triangle from the list
                        Triangles.RemoveAt(j);
                    }

                    // If this triangle is complete
                    else if (Vertices[i].x > Triangles[j].circumcentre.x + Triangles[j].radius)
                    {
                        {
                            CompletedTriangles.Add(Triangles[j]);
                        }
                        Triangles.RemoveAt(j);
                    }
                }

                // Remove duplicate edges
                for (int j = Edges.Count - 1; j > 0; j--)
                {
                    for (int k = j - 1; k >= 0; k--)
                    {
                        // Compare if this edge match in either direction
                        if (Edges[j][0].Equals(Edges[k][1]) && Edges[j][1].Equals(Edges[k][0]))
                        {
                            // Remove both duplicates
                            Edges.RemoveAt(j);
                            Edges.RemoveAt(k);

                            // We've removed an item from lower down the list than where j is now, so update j
                            j--;
                            break;
                        }
                    }
                }

                // Create new triangles for the current point
                for (int j = 0; j < Edges.Count; j++)
                {
                    CalculateCircumcircle3d(Vertices[Edges[j][0]], Vertices[Edges[j][1]], Vertices[i], out circumcentre, out radius);
                    SimpleTriangle3d T = new SimpleTriangle3d(Edges[j][0], Edges[j][1], i, circumcentre, radius);
                    Triangles.Add(T);
                }
            }

            // We've finished triangulation. Move any remaining triangles onto the completed list
            CompletedTriangles.AddRange(Triangles);

            // Define the metadata of the results column
            SqlMetaData metadata = new SqlMetaData("Triangle", SqlDbType.Udt, typeof(SqlGeometry));

            // Create a record based on this metadata
            SqlDataRecord record = new SqlDataRecord(metadata);

            // Send the results back to the client
            SqlContext.Pipe.SendResultsStart(record);
            foreach (SimpleTriangle3d Tri in CompletedTriangles)
            {
                // Check that this is a triangle formed only from vertices in the original multipoint
                // i.e. not from the vertices of the supertriangle.
                if (Tri.a < numPoints && Tri.b < numPoints && Tri.c < numPoints)
                {
                    SqlGeometry triangle = Triangle3dFromPoints(Vertices[Tri.a], Vertices[Tri.b], Vertices[Tri.c], srid);
                    record.SetValue(0, triangle);
                    SqlContext.Pipe.SendResultsRow(record);
                }
            }
            SqlContext.Pipe.SendResultsEnd();
        }