// Returns neighbor of face_i that has largest area. private int FindNeighborOnLongestEdge(int face_ind) { VoronoiFace face_i = faces[face_ind]; if (face_i.neighbors.Count == 0) { return(-1); } int current_neighbor_on_longest_edge_ind = -1; float current_longest_edge_length = -1f; foreach (HalfEdge he in face_i.half_edges) { float len = Vector3.Distance(vertices[he.start], vertices[he.end]); if (len > current_longest_edge_length && he.twin_id > -1 && halfedges[he.twin_id].face > -1) { current_neighbor_on_longest_edge_ind = halfedges[he.twin_id].face; } } if (current_neighbor_on_longest_edge_ind > -1) { return(current_neighbor_on_longest_edge_ind); } return(-1); }
// Returns neighbor of face_i that has largest area. private int FindNeighborWithLargestArea(int face_ind) { VoronoiFace face_i = faces[face_ind]; //float area = ComputeFaceArea(i); float area = face_i.area; if (face_i.neighbors.Count == 0) { return(-1); } // Iterate over neighbors, find one with largest area. int current_largest_area_face_index = face_i.neighbors[0]; float current_largest_area = (face_i.neighbors[0] != -1) ? faces[face_i.neighbors[0]].area : -1f; for (int j = 1; j < face_i.neighbors.Count; ++j) { int current_face_index = face_i.neighbors[j]; //float current_face_area = ComputeFaceArea(face_i.neighbors[j]); float current_face_area = (face_i.neighbors[j] != -1) ? faces[face_i.neighbors[j]].area : -1f; if (((current_face_area > current_largest_area || current_largest_area < 0) && current_face_area > 0)) { current_largest_area_face_index = current_face_index; } } // Merge faces with the neighbor of least area. if (current_largest_area_face_index >= 0) { return(current_largest_area_face_index); } return(-1); }
// Assigns the VoronoiFace struct to the FaceManager object belonging to the object containing the mesh. private void AssignVoronoiFace(VoronoiFace face_struct, FaceManager face_manager) { face_manager.id = face_struct.id; face_manager.origin_x = face_struct.origin_x; face_manager.origin_y = face_struct.origin_y; face_manager.half_edges = face_struct.half_edges; face_manager.neighbors_ids = face_struct.neighbors; face_manager.area = face_struct.area; face_manager.merged = face_struct.merged; }
private void CreateVoronoiCells() { // foreach (var point in Points) // { // var triangles = Triangulation.Where(t => t.HasVertex(point)); // var circumcenters = new List<Vector3>(); // // // foreach (var triangle in triangles) // { // circumcenters.Add(triangle.Circumcenter); // } // // SortClockwise(ref circumcenters); // voronoiFaces.Add(new VoronoiFace(circumcenters, new Plane(Vector3.back, Vector3.zero))); // } foreach (var point in Points) { var neighbors = Triangulation.Where(t => t.HasVertex(point)); List <Vector3> neighborVertices = new List <Vector3>(); foreach (var triangle in neighbors) { neighborVertices.Add(triangle.V1); neighborVertices.Add(triangle.V2); neighborVertices.Add(triangle.V3); } neighborVertices = neighborVertices.Distinct().ToList(); neighborVertices.Remove(point); foreach (var vertex in neighborVertices) { var dir = (vertex - point).normalized; var mid = (vertex + point) / 2; Plane p = new Plane(dir, mid); List <Vector3> VoronoiVertices = new List <Vector3>(); foreach (var triangle in neighbors) { if (Math.Abs(p.GetDistanceToPoint(triangle.Circumcenter)) < 0.01) { VoronoiVertices.Add(triangle.Circumcenter); } } var face = new VoronoiFace(VoronoiVertices.ToList(), p); voronoiFaces.Add(face); } } }
// Computes face area by iterating over the composing triangles. private float ComputeFaceArea(int face_index) { if (face_index < 0) { return(-1); } VoronoiFace f = faces[face_index]; float area = 0f; for (int i = 0; i < f.half_edges.Count; ++i) { HalfEdge he = f.half_edges[i]; area += TriangleArea(vertices[he.start], vertices[he.end], new Vector3(f.origin_x, 0, f.origin_y)); } return(area); }
// Finds the center of a face by averaging the vertices. private void RecomputeFaceCenter(int face_index) { VoronoiFace f = faces[face_index]; // Assuming the face is convex, we can just put the center at the // average of the vertices. Vector3 new_center = Vector3.zero; for (int i = 0; i < f.half_edges.Count; ++i) { new_center += vertices[f.half_edges[i].start]; } new_center = new_center / f.half_edges.Count; faces[face_index].origin_x = new_center.x; faces[face_index].origin_y = new_center.z; }
public List <Vector3> ClipPolygon() { var mesh = meshToCut.GetComponent <MeshFilter>().mesh; var faces = new List <VoronoiFace>(); for (var index = 0; index < mesh.triangles.Length; index += 3) { var v1 = meshToCut.transform.TransformPoint(mesh.vertices[mesh.triangles[index]]); var v2 = meshToCut.transform.TransformPoint(mesh.vertices[mesh.triangles[index + 1]]); var v3 = meshToCut.transform.TransformPoint(mesh.vertices[mesh.triangles[index + 2]]); Plane p = new Plane(v1, v2, v3); var face = new VoronoiFace(new List <Vector3> { v1, v2, v3 }, p); foreach (var meshVertex in mesh.vertices) { var point = meshToCut.transform.TransformPoint(meshVertex); var dot = Vector3.Dot(v1 - point, p.normal); if (Math.Abs(dot) < 0.00000001f) { face.points.Add(point); } } face.points = face.points.Distinct().ToList(); faces.Add(face); } faces = faces.GroupBy(x => x.Plane).Select(g => g.First()).ToList(); var meshCell = new VoronoiCell(faces); var outputList = new List <Vector3>(); foreach (var cell in voronoiCells) { foreach (var voronoiFace in cell.Faces) { var inputList = faces.ToList(); faces.Clear(); foreach (var face in inputList) { Vector3 intersectionPoint = Vector3.zero; Vector3 intersectionDir = Vector3.zero; ComputePlanePlaneIntersection(out intersectionPoint, out intersectionDir, voronoiFace.Plane, voronoiFace.points[0], face.Plane, face.points[0]); if (intersectionPoint == Vector3.zero && intersectionDir == Vector3.zero) { continue; } var tmpIntersections = new List <Vector3>(); foreach (var edge in voronoiFace.GetEdges()) { Plane p = new Plane(edge.Start, edge.End, new Vector3(10, 10, 10)); Vector3 intersection = Vector3.zero; ComputeLinePlaneIntersection(out intersection, intersectionDir, intersectionPoint, p.normal, edge.Start); List <Vector3> isIntersecting = new List <Vector3> { edge.Start, edge.End, intersection }; isIntersecting = isIntersecting.OrderBy(x => Vector3.Dot(edge.End - edge.Start, x)).ToList(); if (intersection != Vector3.zero && isIntersecting[1] == intersection) { tmpIntersections.Add(intersection); } } if (!tmpIntersections.Any()) { continue; } tmpIntersections = tmpIntersections.OrderBy(x => Vector3.Dot(intersectionDir, x)).ToList(); var voronoiEdge = new Edge(tmpIntersections.First(), tmpIntersections.Last()); tmpIntersections.Clear(); foreach (var edge in face.GetEdges()) { Plane p = new Plane(edge.Start, edge.End, new Vector3(10, 10, 10)); Vector3 intersection = Vector3.zero; ComputeLinePlaneIntersection(out intersection, intersectionDir, intersectionPoint, p.normal, edge.Start); List <Vector3> isIntersecting = new List <Vector3> { edge.Start, edge.End, intersection }; isIntersecting = isIntersecting.OrderBy(x => Vector3.Dot(edge.End - edge.Start, x)).ToList(); if (intersection != Vector3.zero && isIntersecting[1] == intersection) { //tmpIntersections.Add((intersection, 'f')); tmpIntersections.Add(intersection); } } if (!tmpIntersections.Any()) { continue; } tmpIntersections = tmpIntersections.OrderBy(x => Vector3.Dot(intersectionDir, x)).ToList(); var meshEdge = new Edge(tmpIntersections.First(), tmpIntersections.Last()); Vector3 min1 = new Vector3(Mathf.Min(voronoiEdge.Start.x, voronoiEdge.End.x), Mathf.Min(voronoiEdge.Start.y, voronoiEdge.End.y), Mathf.Min(voronoiEdge.Start.z, voronoiEdge.End.z)); Vector3 max1 = new Vector3(Mathf.Max(voronoiEdge.Start.x, voronoiEdge.End.x), Mathf.Max(voronoiEdge.Start.y, voronoiEdge.End.y), Mathf.Max(voronoiEdge.Start.z, voronoiEdge.End.z)); Vector3 min2 = new Vector3(Mathf.Min(meshEdge.Start.x, meshEdge.End.x), Mathf.Min(meshEdge.Start.y, meshEdge.End.y), Mathf.Min(meshEdge.Start.z, meshEdge.End.z)); Vector3 max2 = new Vector3(Mathf.Max(meshEdge.Start.x, meshEdge.End.x), Mathf.Max(meshEdge.Start.y, meshEdge.End.y), Mathf.Max(meshEdge.Start.z, meshEdge.End.z)); Vector3 minIntersection = new Vector3(Math.Max(min1.x, min2.x), Math.Max(min1.y, min2.y), Math.Max(min1.z, min2.z)); Vector3 maxIntersection = new Vector3(Math.Min(max1.x, max2.x), Math.Min(max1.y, max2.y), Math.Min(min1.z, min2.z)); outputList.Add(minIntersection); outputList.Add(maxIntersection); // face.SortEdges(); // var outputFace = face.points.ToList(); // face.points.Clear(); // for (int i = 0; i < face.points.Count; i++) // { // Vector3 currentPoint = face.points[i]; // var index = i - 1; // if (index < 0) index = face.points.Count - 1; // Vector3 prevPoint = face.points[index]; // // // Vector3 intersection = Vector3.zero; // var success = ComputeLinePlaneIntersection(out intersection, currentPoint - prevPoint, prevPoint, // voronoiFace.Plane.normal, voronoiFace.points[0]); // // var prevInside = !voronoiFace.Plane.GetSide(prevPoint); // var currentInside = !voronoiFace.Plane.GetSide(currentPoint); // // if (currentInside) // { // if (!prevInside) // { // outputFace.Add(intersection); // } // // outputFace.Add(currentPoint); // } // else if (prevInside) // { // outputFace.Add(intersection); // outputFace.Add(prevPoint); // } // } } } //outputList.Add(new VoronoiCell(faces)); } return(outputList); }
// Iterate over faces. Identify small ones. Merge with neighbors. private void MergeSmallFaces() { bool merged_faces = false; int iterations = 0; do { merged_faces = false; print("Face merge iteration " + iterations.ToString()); ++iterations; for (int i = 0; i < faces.Length; ++i) { //print(i); VoronoiFace face_i = faces[i]; if (face_i.merged) { //print("FACE ALREADY MERGED."); continue; } //float area = ComputeFaceArea(i); float area = face_i.area; if (area < MIN_FACE_AREA) { // FIX THIS. if (face_i.neighbors.Count == 0) { //print("NO NEIGHBORS, PROCEEDING."); continue; } int neighbor_to_merge_with = -1; switch (defaultFaceMergingRule) { case FaceMergingRule.SMALLEST_AREA_NEIGHBOR: neighbor_to_merge_with = FindNeighborWithSmallestArea(i); break; case FaceMergingRule.LARGEST_AREA_NEIGHBOR: neighbor_to_merge_with = FindNeighborWithLargestArea(i); break; case FaceMergingRule.LARGEST_EDGE_NEIGHBOR: neighbor_to_merge_with = FindNeighborOnLongestEdge(i); break; } // Merge faces with the neighbor of least area. if (neighbor_to_merge_with >= 0) { print("Merging " + i + ", " + neighbor_to_merge_with); MergeFaces(i, neighbor_to_merge_with); merged_faces = true; } } } } while (merged_faces && iterations < MAX_MERGING_ITERATIONS); print("COMPLETED MERGING FACES."); }
// Generates meshes from vertices and edges. private void GenerateMesh() { for (int i = 0; i < faces.Length; ++i) { // Skip if face area is 0.. faces[i].area = ComputeFaceArea(i); //float area = ComputeFaceArea(i); VoronoiFace current_face = faces[i]; List <int> triangles = new List <int>(); Vector3 faceCenter = new Vector3(current_face.origin_x, 0, current_face.origin_y); List <Vector3> faceVertices = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); List <Vector2> uvs = new List <Vector2>(); foreach (HalfEdge he in current_face.half_edges) { Vector3 v0 = faceCenter; Vector3 v1 = vertices[he.start]; Vector3 v2 = vertices[he.end]; triangles.Add(faceVertices.Count); triangles.Add(faceVertices.Count + 1); triangles.Add(faceVertices.Count + 2); faceVertices.Add(v2); faceVertices.Add(v1); faceVertices.Add(v0); Vector3 normal = Vector3.Cross(v2 - v0, v1 - v0); normals.Add(normal); normals.Add(normal); normals.Add(normal); uvs.Add(new Vector2(0.0f, 0.0f)); uvs.Add(new Vector2(0.0f, 0.0f)); uvs.Add(new Vector2(0.0f, 0.0f)); } Mesh chunkMesh = new Mesh(); chunkMesh.vertices = faceVertices.ToArray(); chunkMesh.uv = uvs.ToArray(); chunkMesh.triangles = triangles.ToArray(); chunkMesh.normals = normals.ToArray(); Transform chunk = Instantiate <Transform>(chunkPrefab, transform.position, transform.rotation); chunk.GetComponent <MeshFilter>().mesh = chunkMesh; if (faces[i].area >= EPSILON) { chunk.GetComponent <MeshCollider>().sharedMesh = chunkMesh; } chunk.rotation = new Quaternion(0.707f, 0, 0, -0.707f); chunk.transform.parent = transform; chunk.gameObject.AddComponent <MapClickDetector>(); chunk.gameObject.GetComponent <MeshRenderer>().material.color = Color.red; faces[i].mesh = chunkMesh; faces[i].chunk = chunk; AssignVoronoiFace(faces[i], chunk.GetComponent <FaceManager>()); } print("SUCCESSFULLY GENERATED MESH!"); }