/// <summary>
        /// Attempts to move the positions of vertices such that they have roughly uniform density, with a bias towards also making sure that the surface areas of the faces also become more uniform.
        /// </summary>
        /// <param name="topology">The topology to relax.</param>
        /// <param name="vertexPositions">The original positions of the vertices to relax.</param>
        /// <param name="totalArea">The total surface area of the internal faces of the topology.</param>
        /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
        /// <param name="relaxedVertexPositions">A pre-allocated collection in which the relaxed vertex positions will be stored.  Should not be the same collection as <paramref name="vertexPositions"/>.</param>
        /// <param name="faceCentroids">A pre-allocated collection in which the intermediate face centroid positions will be stored.</param>
        /// <param name="vertexAreas">A pre-allocated collection in which the intermediate nearby surface areas of vertices will be stored.</param>
        /// <returns>The relaxed vertex positions.</returns>
        public static IVertexAttribute <Vector3> RelaxVertexPositionsForEqualArea(Topology topology, IVertexAttribute <Vector3> vertexPositions, float totalArea, bool lockBoundaryPositions, IVertexAttribute <Vector3> relaxedVertexPositions, IFaceAttribute <Vector3> faceCentroids, IVertexAttribute <float> vertexAreas)
        {
            var idealArea = totalArea / topology.vertices.Count;

            FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions, faceCentroids);
            VertexAttributeUtility.CalculateVertexAreasFromVertexPositionsAndFaceCentroids(topology.vertices, vertexPositions, faceCentroids, vertexAreas);

            for (int i = 0; i < topology.vertices.Count; ++i)
            {
                relaxedVertexPositions[i] = new Vector3(0f, 0f, 0f);
            }

            foreach (var vertex in topology.vertices)
            {
                var multiplier = Mathf.Sqrt(idealArea / vertexAreas[vertex]);
                foreach (var edge in vertex.edges)
                {
                    var neighborVertex         = edge.vertex;
                    var neighborRelativeCenter = vertexPositions[edge.twin];
                    relaxedVertexPositions[neighborVertex] += (vertexPositions[neighborVertex] - neighborRelativeCenter) * multiplier + neighborRelativeCenter;
                }
            }

            foreach (var vertex in topology.vertices)
            {
                if (!lockBoundaryPositions || !vertex.hasExternalFaceNeighbor)
                {
                    relaxedVertexPositions[vertex] /= vertex.neighborCount;
                }
                else
                {
                    relaxedVertexPositions[vertex] = vertexPositions[vertex];
                }
            }

            return(relaxedVertexPositions);
        }
        public override IEnumerator BeginGeneration()
        {
            var surface         = surfaceInputSlot.GetAsset <Surface>();
            var topology        = topologyInputSlot.GetAsset <Topology>();
            var vertexPositions = vertexPositionsInputSlot.source != null?vertexPositionsInputSlot.GetAsset <IVertexAttribute <Vector3> >() : null;

            var random = randomness.GetRandom();

            System.Func <float> relaxIterationFunction = null;
            System.Func <bool>  repairFunction         = null;
            System.Action       relaxationLoopFunction = null;

            if (vertexPositions != null)
            {
                if (surface is QuadrilateralSurface)
                {
                    if (relaxForRegularityWeight >= 1.0f)
                    {
                        var relaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        relaxIterationFunction = () =>
                        {
                            PlanarManifoldUtility.RelaxVertexPositionsForRegularity(topology, vertexPositions, lockBoundaryPositions, relaxedVertexPositions);
                            var relaxationAmount = PlanarManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }
                    else if (relaxForRegularityWeight <= 0.0f)
                    {
                        var relaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var faceCentroids          = PositionalFaceAttribute.Create(surface, topology.internalFaces.Count);
                        var vertexAreas            = new float[topology.vertices.Count].AsVertexAttribute();

                        FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions, faceCentroids);
                        VertexAttributeUtility.CalculateVertexAreasFromVertexPositionsAndFaceCentroids(topology.vertices, vertexPositions, faceCentroids, vertexAreas);

                        var totalArea = 0f;
                        foreach (var vertexArea in vertexAreas)
                        {
                            totalArea += vertexArea;
                        }

                        relaxIterationFunction = () =>
                        {
                            PlanarManifoldUtility.RelaxVertexPositionsForEqualArea(topology, vertexPositions, totalArea, lockBoundaryPositions, relaxedVertexPositions, faceCentroids, vertexAreas);
                            var relaxationAmount = PlanarManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }
                    else
                    {
                        var regularityWeight = Mathf.Clamp01(relaxForRegularityWeight);
                        var equalAreaWeight  = 1f - regularityWeight;

                        var regularityRelaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var equalAreaRelaxedVertexPositions  = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var relaxedVertexPositions           = regularityRelaxedVertexPositions;
                        var faceCentroids = PositionalFaceAttribute.Create(surface, topology.internalFaces.Count);
                        var vertexAreas   = new float[topology.vertices.Count].AsVertexAttribute();

                        FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions, faceCentroids);
                        VertexAttributeUtility.CalculateVertexAreasFromVertexPositionsAndFaceCentroids(topology.vertices, vertexPositions, faceCentroids, vertexAreas);

                        var totalArea = 0f;
                        foreach (var vertexArea in vertexAreas)
                        {
                            totalArea += vertexArea;
                        }

                        relaxIterationFunction = () =>
                        {
                            PlanarManifoldUtility.RelaxVertexPositionsForRegularity(topology, vertexPositions, lockBoundaryPositions, regularityRelaxedVertexPositions);
                            PlanarManifoldUtility.RelaxVertexPositionsForEqualArea(topology, vertexPositions, totalArea, lockBoundaryPositions, equalAreaRelaxedVertexPositions, faceCentroids, vertexAreas);
                            for (int i = 0; i < relaxedVertexPositions.Count; ++i)
                            {
                                relaxedVertexPositions[i] = regularityRelaxedVertexPositions[i] * regularityWeight + equalAreaRelaxedVertexPositions[i] * equalAreaWeight;
                            }
                            var relaxationAmount = PlanarManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }

                    repairFunction = () =>
                    {
                        return(PlanarManifoldUtility.ValidateAndRepair(topology, ((QuadrilateralSurface)surface).normal, vertexPositions, repairRate, lockBoundaryPositions));
                    };

                    relaxationLoopFunction = TopologyRandomizer.CreateRelaxationLoopFunction(maxRelaxIterations, maxRepairIterations, relaxRelativePrecision, relaxIterationFunction, repairFunction);
                }
                else if (surface is SphericalSurface)
                {
                    var sphericalSurface = (SphericalSurface)surface;
                    if (relaxForRegularityWeight >= 1.0f)
                    {
                        var relaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        relaxIterationFunction = () =>
                        {
                            SphericalManifoldUtility.RelaxVertexPositionsForRegularity(sphericalSurface, topology, vertexPositions, lockBoundaryPositions, relaxedVertexPositions);
                            var relaxationAmount = SphericalManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }
                    else if (relaxForRegularityWeight <= 0.0f)
                    {
                        var relaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var faceCentroids          = PositionalFaceAttribute.Create(surface, topology.internalFaces.Count);
                        var faceCentroidAngles     = new float[topology.faceEdges.Count].AsEdgeAttribute();
                        var vertexAreas            = new float[topology.vertices.Count].AsVertexAttribute();
                        relaxIterationFunction = () =>
                        {
                            SphericalManifoldUtility.RelaxVertexPositionsForEqualArea(sphericalSurface, topology, vertexPositions, lockBoundaryPositions, relaxedVertexPositions, faceCentroids, faceCentroidAngles, vertexAreas);
                            var relaxationAmount = SphericalManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }
                    else
                    {
                        var regularityWeight = Mathf.Clamp01(relaxForRegularityWeight);
                        var equalAreaWeight  = 1f - regularityWeight;

                        var regularityRelaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var equalAreaRelaxedVertexPositions  = new Vector3[topology.vertices.Count].AsVertexAttribute();
                        var relaxedVertexPositions           = regularityRelaxedVertexPositions;
                        var faceCentroids      = PositionalFaceAttribute.Create(surface, topology.internalFaces.Count);
                        var faceCentroidAngles = new float[topology.faceEdges.Count].AsEdgeAttribute();
                        var vertexAreas        = new float[topology.vertices.Count].AsVertexAttribute();
                        relaxIterationFunction = () =>
                        {
                            SphericalManifoldUtility.RelaxVertexPositionsForRegularity(sphericalSurface, topology, vertexPositions, lockBoundaryPositions, regularityRelaxedVertexPositions);
                            SphericalManifoldUtility.RelaxVertexPositionsForEqualArea(sphericalSurface, topology, vertexPositions, lockBoundaryPositions, equalAreaRelaxedVertexPositions, faceCentroids, faceCentroidAngles, vertexAreas);
                            for (int i = 0; i < relaxedVertexPositions.Count; ++i)
                            {
                                relaxedVertexPositions[i] = regularityRelaxedVertexPositions[i] * regularityWeight + equalAreaRelaxedVertexPositions[i] * equalAreaWeight;
                            }
                            var relaxationAmount = SphericalManifoldUtility.CalculateRelaxationAmount(vertexPositions, relaxedVertexPositions);
                            for (int i = 0; i < vertexPositions.Count; ++i)
                            {
                                vertexPositions[i] = relaxedVertexPositions[i];
                            }
                            return(relaxationAmount);
                        };
                    }

                    repairFunction = () =>
                    {
                        return(SphericalManifoldUtility.ValidateAndRepair(sphericalSurface, topology, vertexPositions, repairRate, lockBoundaryPositions));
                    };

                    relaxationLoopFunction = TopologyRandomizer.CreateRelaxationLoopFunction(maxRelaxIterations, maxRepairIterations, relaxRelativePrecision, relaxIterationFunction, repairFunction);
                }
            }

            yield return(executive.GenerateConcurrently(() =>
            {
                TopologyRandomizer.Randomize(topology, passCount, frequency,
                                             minVertexNeighbors, maxVertexNeighbors, minFaceNeighbors, maxFaceNeighbors,
                                             lockBoundaryPositions, random, relaxationLoopFunction);
            }));

            EditorUtility.SetDirty(topology);

            if (vertexPositions != null)
            {
                EditorUtility.SetDirty((Object)vertexPositions);
            }

            yield break;
        }