public IReadOnlyList <GaussPoint> GenerateIntegrationPoints(XContinuumElement2D element)
        {
            SortedSet <CartesianPoint>   cartesianDelaunyPoints = crack.FindTriangleVertices(element);
            IReadOnlyList <NaturalPoint> naturalDelaunyPoints   =
                FindNaturalPointsForTriangulation(element, cartesianDelaunyPoints);
            IReadOnlyList <Triangle2D <NaturalPoint> > subtriangles;

            if (double.IsPositiveInfinity(triangleOverElementArea))
            {
                subtriangles = triangulator.CreateMesh(naturalDelaunyPoints);
            }
            else
            {
                double elementArea = (ConvexPolygon2D.CreateUnsafe(element.Nodes)).ComputeArea();
                subtriangles = triangulator.CreateMesh(naturalDelaunyPoints, triangleOverElementArea * elementArea);
            }

            var integrationPoints = new List <GaussPoint>();

            foreach (Triangle2D <NaturalPoint> triangle in subtriangles)
            {
                integrationPoints.AddRange(GenerateIntegrationPointsOfTriangle(triangle));
            }
            return(integrationPoints);
        }
        // TODO: I should really cache these somehow, so that they can be accessible from the crack object. They are used at various points.
        private (double positiveArea, double negativeArea) FindSignedAreasOfElement(ISingleCrack crack,
                                                                                    XContinuumElement2D element)
        {
            SortedSet <CartesianPoint> triangleVertices            = crack.FindTriangleVertices(element);
            IReadOnlyList <Triangle2D <CartesianPoint> > triangles = triangulator.CreateMesh(triangleVertices);

            double positiveArea = 0.0;
            double negativeArea = 0.0;

            foreach (var triangle in triangles)
            {
                CartesianPoint v0   = triangle.Vertices[0];
                CartesianPoint v1   = triangle.Vertices[1];
                CartesianPoint v2   = triangle.Vertices[2];
                double         area = 0.5 * Math.Abs(v0.X * (v1.Y - v2.Y) + v1.X * (v2.Y - v0.Y) + v2.X * (v0.Y - v1.Y));

                // The sign of the area can be derived from any node with body level set != 0
                int sign = 0;
                foreach (var vertex in triangle.Vertices)
                {
                    XNode vertexAsNode = null;
                    foreach (var node in element.Nodes) // TODO: find a faster way to do this
                    {
                        if ((vertex.X == node.X) && (vertex.Y == node.Y))
                        {
                            vertexAsNode = node;
                            break;
                        }
                    }
                    if (vertexAsNode != null)
                    {
                        double distance = crack.SignedDistanceOf(vertexAsNode);
                        if (Math.Abs(distance) <= zeroDistanceTolerance)
                        {
                            sign = 0;
                        }
                        else
                        {
                            sign = Math.Sign(distance);
                        }
                        if (sign != 0)
                        {
                            break;
                        }
                    }
                }

                // If no node with non-zero body level set is found, then find the body level set of its centroid
                if (sign == 0)
                {
                    // Report this instance in DEBUG messages. It should not happen with linear level sets and only 1 crack.
                    //if (reports)
                    //{
                    //    Console.WriteLine("--- DEBUG: Triangulation resulted in a triangle where no vertex is an element node. ---");
                    //}


                    var          centroid        = new CartesianPoint((v0.X + v1.X + v2.X) / 3.0, (v0.Y + v1.Y + v2.Y) / 3.0);
                    NaturalPoint centroidNatural = element.Interpolation.
                                                   CreateInverseMappingFor(element.Nodes).TransformPointCartesianToNatural(centroid);
                    EvalInterpolation2D centroidInterpolation =
                        element.Interpolation.EvaluateAllAt(element.Nodes, centroidNatural);
                    sign = Math.Sign(crack.SignedDistanceOf(centroidNatural, element, centroidInterpolation));
                }

                if (sign > 0)
                {
                    positiveArea += area;
                }
                else if (sign < 0)
                {
                    negativeArea += area;
                }
                else
                {
                    throw new Exception(
                              "Even after finding the signed distance of its centroid, the sign of the area is unidentified");
                }
            }

            return(positiveArea, negativeArea);
        }