//TODO: This should work for any IOpenCurve2D. Same for all ICrackGeometryDescriptions.
        //TODO: The tangent stuff should be done by the initial curve.
        public void InitializeGeometry(PolyLine2D initialCrack)
        {
            foreach (var vertex in initialCrack.Vertices)
            {
                crackPath.Add(vertex);
            }

            crackMouth = initialCrack.Start;
            var lastSegment = initialCrack.Segments[initialCrack.Segments.Count - 1];

            double tangentX     = lastSegment.End.X - lastSegment.Start.X;
            double tangentY     = lastSegment.End.Y - lastSegment.Start.Y;
            double length       = Math.Sqrt(tangentX * tangentX + tangentY * tangentY);
            double tangentSlope = Math.Atan2(tangentY, tangentX);

            this.crackTip = initialCrack.End;
            tipSystem     = new TipCoordinateSystem(crackTip, tangentSlope);
            CrackTipEnrichments.TipSystem = tipSystem;

            tangentX /= length;
            tangentY /= length;

            foreach (XNode node in Mesh.Nodes)
            {
                levelSetsBody[node] = initialCrack.SignedDistanceOf(node);
                levelSetsTip[node]  = (node.X - crackTip.X) * tangentX + (node.Y - crackTip.Y) * tangentY;
            }

            if (LevelSetLogger != null)
            {
                LevelSetLogger.InitialLog();                         //TODO: handle this with a NullLogger.
            }
        }
        public void InitializeGeometry(CartesianPoint crackMouth, CartesianPoint crackTip)
        {
            this.crackMouth = crackMouth;
            crackPath.Add(crackMouth);
            var segment = new DirectedSegment2D(crackMouth, crackTip);

            double tangentX     = crackTip.X - crackMouth.X;
            double tangentY     = crackTip.Y - crackMouth.Y;
            double length       = Math.Sqrt(tangentX * tangentX + tangentY * tangentY);
            double tangentSlope = Math.Atan2(tangentY, tangentX);

            this.crackTip = crackTip;
            crackPath.Add(crackTip);
            tipSystem = new TipCoordinateSystem(crackTip, tangentSlope);
            CrackTipEnrichments.TipSystem = tipSystem;

            tangentX /= length;
            tangentY /= length;

            foreach (XNode node in Mesh.Nodes)
            {
                levelSetsBody[node] = segment.SignedDistanceOf(node);
                levelSetsTip[node]  = (node.X - crackTip.X) * tangentX + (node.Y - crackTip.Y) * tangentY;
            }

            if (LevelSetLogger != null)
            {
                LevelSetLogger.InitialLog();                         //TODO: handle this with a NullLogger.
            }
        }
        //TODO: make this private
        public void UpdateGeometry(double localGrowthAngle, double growthLength)
        {
            double globalGrowthAngle = MathUtilities.WrapAngle(localGrowthAngle + tipSystem.RotationAngle);
            double dx     = growthLength * Math.Cos(globalGrowthAngle);
            double dy     = growthLength * Math.Sin(globalGrowthAngle);
            var    oldTip = crackTip;
            var    newTip = new CartesianPoint(oldTip.X + dx, oldTip.Y + dy);

            crackTip = newTip;
            crackPath.Add(newTip);
            tipSystem = new TipCoordinateSystem(newTip, globalGrowthAngle);
            CrackTipEnrichments.TipSystem = tipSystem;

            //TODO: it is inconsistent that the modified body nodes are updated here, while the other in UpdateEnrichments();
            crackBodyNodesModified = levelSetUpdater.Update(oldTip, localGrowthAngle, growthLength, dx, dy, Mesh.Nodes,
                                                            crackBodyNodesAll, levelSetsBody, levelSetsTip);

            if (LevelSetLogger != null)
            {
                LevelSetLogger.Log();                         //TODO: handle this with a NullLogger.
            }
        }