/// <summary>
        /// Apply the Douglas Peucker reduction algorithm
        /// </summary>
        /// <param name="positions"></param>
        /// <returns></returns>
        public static List <Vector3> ApplyDoublesPeucker(List <Vector3> positions, float douglasPeuckerReductionTolerance)
        {
            if (douglasPeuckerReductionTolerance == 0)
            {
                return(positions);
            }

            // convert to vector2
            List <Vector2> vector2List = positions.ConvertAll <Vector2>(item => new Vector2(item.x, item.z));

            // use vspro's DouglasPeuckerReduction algorithm
            vector2List = PolygonUtility.DouglasPeuckerReduction(vector2List, douglasPeuckerReductionTolerance);

            // convert back to vector3
            if (vector2List.Count >= 3)
            {
                positions = vector2List.ConvertAll <Vector3>(item => new Vector3(item.x, 0, item.y));
            }

            return(positions);
        }
        public void GenerateHullNodes(float tolerance)
        {
            List <Vector2> worldSpacePointList = new List <Vector2>();

            MeshFilter[] mersFilters = GetComponentsInChildren <MeshFilter>();
            for (int i = 0; i <= mersFilters.Length - 1; i++)
            {
                Mesh mesh = mersFilters[i].sharedMesh;
                if (mesh)
                {
                    List <Vector3> verticeList = new List <Vector3>();
                    mesh.GetVertices(verticeList);
                    for (int j = 0; j <= verticeList.Count - 1; j++)
                    {
                        Vector3 worldSpacePosition = mersFilters[i].transform.TransformPoint(verticeList[j]);
                        Vector2 worldSpacePoint    = new Vector2
                        {
                            x = worldSpacePosition.x,
                            y = worldSpacePosition.z
                        };
                        worldSpacePointList.Add(worldSpacePoint);
                    }
                }
            }

            List <Vector2> hullPointList    = PolygonUtility.GetConvexHull(worldSpacePointList);
            List <Vector2> reducedPointList = PolygonUtility.DouglasPeuckerReduction(hullPointList, tolerance);


            if (reducedPointList.Count >= 3)
            {
                ClearNodes();
                for (int i = 0; i <= reducedPointList.Count - 1; i++)
                {
                    Vector3 worldSpacePosition = new Vector3(reducedPointList[i].x, 0, reducedPointList[i].y);
                    AddNode(worldSpacePosition);
                }
                PositionNodes();
            }
        }
Beispiel #3
0
        /// <summary>
        /// Modifies a polygon and creates a random shape.
        ///
        /// Algorithm:
        ///
        /// * create a new polygon, subdivided this way:
        ///   + get center of polygon
        ///   + create a list of angles for iteration
        ///   + iterate through the angles, create a line using the angle and find the intersection with a line of the polygon
        /// * iterate through the subdivided polygon
        ///   + move the vertex towards the center
        ///   + consider previous vertex in order to not move in too large steps
        /// </summary>
        public static List <Vector3> CreateRandomShape(List <Vector3> polygon, float ellipseRelaxationFactor, int angleStepCount, bool randomAngleMovement, bool keepOriginalShape, float douglasPeuckerReductionTolerance)
        {
            Vector3 meanVector = PolygonUtils.GetMeanVector(polygon);
            float   length     = meanVector.magnitude * 2;


            #region create angles

            // get the list of angles to step through
            List <float> angleRadList = CreateAngleList(angleStepCount, randomAngleMovement);

            #endregion create angles

            #region create new polygon using angles

            List <Vector3> subdividedPolygon = new List <Vector3>();

            // add existing points in order to keep the general voronoi shape
            if (keepOriginalShape)
            {
                subdividedPolygon.AddRange(polygon);
            }

            for (int i = 0; i < angleRadList.Count; i++)
            {
                // convert angle from deg to rad
                float angle = angleRadList[i];

                float x = meanVector.x + length * Mathf.Cos(angle);
                float z = meanVector.z + length * Mathf.Sin(angle);

                // position on the ellipse
                Vector3 line1PositionA = meanVector;
                Vector3 line1PositionB = new Vector3(x, 0, z);

                for (var j = 0; j < polygon.Count; j++)
                {
                    int currIndex = j;
                    int nextIndex = j + 1;
                    if (nextIndex == polygon.Count)
                    {
                        nextIndex = 0;
                    }

                    Vector3 line2PositionA = new Vector3(polygon[currIndex].x, 0, polygon[currIndex].z);
                    Vector3 line2PositionB = new Vector3(polygon[nextIndex].x, 0, polygon[nextIndex].z);


                    Vector3 intersection = Vector3.zero;

                    // try and find an intersection. if one is found, add the point to the list
                    if (PolygonUtils.LineSegmentsIntersection(line1PositionA, line1PositionB, line2PositionA, line2PositionB, out intersection))
                    {
                        subdividedPolygon.Add(intersection);
                        break;
                    }
                }
            }

            // sort again
            PolygonUtils.SortClockWise(subdividedPolygon);

            #endregion create new polygon using angles


            #region create new polygon using the intersections
            List <Vector3> newPolygon = new List <Vector3>();

            float prevDistance = 0;

            for (int i = 0; i < subdividedPolygon.Count; i++)
            {
                Vector3 curr = subdividedPolygon[i];

                // position on the ellipse
                Vector3 position = curr;

                // from center to position
                float   distance  = (position - meanVector).magnitude;
                Vector3 direction = (position - meanVector).normalized;

                // move from center towards new position. but not too much, let it depend on the previous distance
                {
                    // move initially from 0 to max distance. otherwise use the previous value
                    float min = i == 0 ? distance * ellipseRelaxationFactor : prevDistance * ellipseRelaxationFactor;
                    float max = distance;

                    // the radius must be smaller during the next iteration, we are navigating around an ellipse => clamp the values
                    if (min > max)
                    {
                        min = max;
                    }

                    float moveDistance = UnityEngine.Random.Range(min, max);

                    distance = moveDistance;
                }

                position = meanVector + distance * direction;

                newPolygon.Add(position);

                prevDistance = distance;
            }
            #endregion create new polygon using the intersections

            if (douglasPeuckerReductionTolerance > 0)
            {
                // convert to vector2
                List <Vector2> vector2List = newPolygon.ConvertAll <Vector2>(item => new Vector2(item.x, item.z));

                // use vspro's DouglasPeuckerReduction algorithm
                vector2List = PolygonUtility.DouglasPeuckerReduction(vector2List, douglasPeuckerReductionTolerance);

                // convert back to vector3
                if (vector2List.Count >= 3)
                {
                    newPolygon = vector2List.ConvertAll <Vector3>(item => new Vector3(item.x, 0, item.y));
                }
            }

            return(newPolygon);
        }