/// <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(); } }
/// <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); }