/// <summary> /// Insert a new node between every node segment of the area /// </summary> public static void Subdivide(VegetationMaskArea mask) { List <Node> originalNodes = new List <Node>(); originalNodes.AddRange(mask.Nodes); for (var i = 0; i < originalNodes.Count; i++) { Node curr = originalNodes[i]; Node next = mask.GetNextNode(curr); Vector3[] segment = new Vector3[] { curr.Position, next.Position }; Vector3 meanVector = PolygonUtils.GetMeanVector(segment); int index = mask.GetNodeIndex(curr); Node newNode = new Node() { Position = meanVector }; mask.Nodes.Insert(index + 1, newNode); } UpdateMask(mask); }
/// <summary> /// Create a biome mask. /// The blend distance is simply calculated using the mean vector. /// </summary> /// <param name="gameObjectName"></param> /// <param name="maskName"></param> /// <param name="position"></param> /// <param name="nodes"></param> private void CreateBiomeMaskArea(string gameObjectName, string maskName, Vector3 position, List <Vector3> nodes) { float blendDistanceMin = editor.extension.biomeSettings.biomeBlendDistanceMin; float blendDistanceMax = editor.extension.biomeSettings.biomeBlendDistanceMax; float biomeBlendDistance = UnityEngine.Random.Range(blendDistanceMin, blendDistanceMax); // mean vector is just a simple measure. please adapt if you need more accuracy Vector3 meanVector = PolygonUtils.GetMeanVector(nodes); float blendDistance = meanVector.magnitude / 2f * biomeBlendDistance; // create the mask using the provided parameters editor.CreateBiomeMaskArea(gameObjectName, maskName, position, nodes, blendDistance); }
/// <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); }
/// <summary> /// Get the center of the mask polygon /// </summary> /// <param name="mask"></param> /// <returns></returns> public static Vector3 GetMaskCenter(VegetationMaskArea mask) { List <Vector3> worldPositions = mask.GetWorldSpaceNodePositions(); return(PolygonUtils.GetMeanVector(worldPositions.ToArray())); }
/// <summary> /// Initializes a new instance of the ClockwiseComparer class and sets the origin to the mean vector, depending on the positions. /// </summary> /// <param name="origin">Origin.</param> public ClockwiseComparer(List <Vector3> positions) { m_Origin = PolygonUtils.GetMeanVector(positions); }