private void HandleNewInput() { // Reset mesh mesh = null; voronoi = null; // Reset state settings.RefineMode = false; settings.ExceptionThrown = false; // Reset buttons btnMesh.Enabled = true; btnMesh.Text = "Triangulate"; btnSmooth.Enabled = false; // Update Statistic view statisticView.HandleNewInput(input); // Clear voronoi menuViewVoronoi.Checked = false; // Disable menu items menuFileSave.Enabled = false; menuFileExport.Enabled = false; menuViewVoronoi.Enabled = false; menuToolsCheck.Enabled = false; menuToolsRcm.Enabled = false; // Render input renderManager.Set(input); // Update window caption this.Text = "Triangle.NET - Mesh Explorer - " + settings.CurrentFile; }
/** * Iterate over every edge in the voronoi graph building edge objects and connecting centers and corners. * This method is very important to complete the graph to be used for all later stages! */ internal static List <Edge> createEdges(VoronoiBase voronoi, List <Center> centers, List <Corner> corners) { List <Edge> edges = new List <Edge>(); List <HalfEdge> halfEdges = voronoi.HalfEdges; foreach (HalfEdge e0 in halfEdges) { HalfEdge e1 = e0.Twin; if (e1.ID < e0.ID) { continue; } TriangleNet.Topology.DCEL.Vertex v0 = e0.Origin; TriangleNet.Topology.DCEL.Vertex v1 = e1.Origin; Corner corner0 = corners[v0.ID]; Corner corner1 = corners[v1.ID]; Face face0 = e0.Face; Face face1 = e1.Face; Center center0 = face0.ID < 0 ? null : centers[face0.ID]; Center center1 = face1.ID < 0 ? null : centers[face1.ID]; bool isBorder = center0 == null || center1 == null; edges.Add(makeEdge(isBorder, corner0, corner1, center0, center1)); } return(edges); }
public static VoronoiBase generateVoronoi(int seed, float worldSize, int pointCount) { System.Random pointRandom = new System.Random(seed); List <Vector2> cornerPoints = new List <Vector2>(4); cornerPoints.Add(new Vector2(0, 0)); cornerPoints.Add(new Vector2(worldSize, 0)); cornerPoints.Add(new Vector2(0, worldSize)); cornerPoints.Add(new Vector2(worldSize, worldSize)); List <Vector2> points = new List <Vector2>(pointCount); points.AddRange(cornerPoints); for (int i = 0; i < pointCount; i++) { float x = (float)pointRandom.NextDouble() * worldSize; float y = (float)pointRandom.NextDouble() * worldSize; points.Add(new Vector2(x, y)); } points = performLloydRelaxation(points, cornerPoints); points = performLloydRelaxation(points, cornerPoints); VoronoiBase voronoi = Triangulator.generateVoronoi(points); voronoi.ResolveBoundaryEdges(); return(voronoi); }
private static Graph generateGraph(int seed, float size, int pointCount) { VoronoiBase voronoi = WorldGeneratorUtils.generateVoronoi(seed, size, pointCount); List <Corner> corners = WorldGeneratorUtils.createCorners(voronoi.Vertices); List <Center> centers = WorldGeneratorUtils.createCenters(voronoi.Faces, corners); List <Edge> edges = WorldGeneratorUtils.createEdges(voronoi, centers, corners); WorldGeneratorUtils.recenterCorners(corners); return(new Graph(seed, size, centers, corners, edges)); }
private void HandleMeshChange() { // Update Statistic view statisticView.HandleMeshChange(mesh); // TODO: Should the Voronoi diagram automatically update? voronoi = null; menuViewVoronoi.Checked = false; // Enable menu items menuFileSave.Enabled = true; menuFileExport.Enabled = true; menuViewVoronoi.Enabled = true; menuToolsCheck.Enabled = true; menuToolsRcm.Enabled = true; }
public static void triangulateVoronoi(VoronoiBase voronoi, out List <int> outIndices, out List <Vector3> outVertices) { outIndices = new List <int>(); outVertices = new List <Vector3>(); foreach (Face face in voronoi.Faces) { int offset = outVertices.Count; HalfEdge[] edges = face.EnumerateEdges().ToArray(); edges.Reverse(); for (int i = 1; i < edges.Count() - 1; i++) { outIndices.Add(offset); outIndices.Add(offset + i); outIndices.Add(offset + i + 1); } outVertices.AddRange((from edge in edges select new Vector3((float)edge.Origin.X, 0f, (float)edge.Origin.Y)).ToList()); } }
/// <summary> /// Returns polygon with points at centroid /// Source: https://penetcedric.wordpress.com/2017/06/13/polygon-maps/ /// </summary> /// <param name="voronoi">VoronoiBase such as StandardVoronoi or Bounded Voronoi</param> public static Polygon LloydRelaxation(this VoronoiBase voronoi, Rectangle rectangle) { //create the new polygon Polygon centroid = new Polygon(voronoi.Faces.Count); //loop through the regions for (int i = 0; i < voronoi.Faces.Count; ++i) { Vector2 average = new Vector2(0, 0); //create the hash set of vertices -- this is neat as it will only contain 1 instance of each HashSet <Vector2> verts = new HashSet <Vector2>(); var edge = voronoi.Faces[i].Edge; var first = edge.Origin.ID; voronoi.Faces[i].LoopEdges(rectangle, true, (v1, v2) => { if (!verts.Contains(v1)) { verts.Add(v1); } if (!verts.Contains(v2)) { verts.Add(v2); } }); if (verts.Count == 0) { continue; } //compute the centroid var vertsEnum = verts.GetEnumerator(); while (vertsEnum.MoveNext()) { average += vertsEnum.Current; } average /= verts.Count; centroid.Add(average.ToVertex()); } return(centroid); }
private void HandleMeshImport() { voronoi = null; // Render mesh renderManager.Set(mesh, true); renderManager.Update(GetRegions(mesh)); this.Text = "Triangle.NET - Mesh Explorer - " + settings.CurrentFile; // Update Statistic view statisticView.HandleMeshImport(input, mesh); // Set refine mode btnMesh.Enabled = true; btnMesh.Text = "Refine"; settings.RefineMode = true; HandleMeshChange(); }
public static Polygon Lloyd_Relaxation(this VoronoiBase voronoi, Rectangle rectangle) { Polygon centroid = new Polygon(voronoi.Faces.Count); //loop for (int i = 0; i < voronoi.Faces.Count; ++i) { Vector2 average = new Vector2(0, 0); HashSet <Vector2> verts = new HashSet <Vector2>(); var edge = voronoi.Faces[i].Edge; var first = edge.Origin.ID; voronoi.Faces[i].BorderLooping(rectangle, true, (v1, v2) => { if (!verts.Contains(v1)) { verts.Add(v1); } if (!verts.Contains(v2)) { verts.Add(v2); } }); if (verts.Count == 0) { continue; } // centroid var vertsEnum = verts.GetEnumerator(); while (vertsEnum.MoveNext()) { average += vertsEnum.Current; } average /= verts.Count; centroid.Add(average.ToVertex()); } return(centroid); }
void CreateMap(VoronoiBase sv, float maxz, float yOffset) { Dictionary <int, TriangleNet.Geometry.Vertex> id2vert = new Dictionary <int, TriangleNet.Geometry.Vertex>(); List <TriangleNet.Geometry.Vertex> vertices = new List <TriangleNet.Geometry.Vertex>(); List <ISegment> segments = new List <ISegment>(); foreach (TriangleNet.Topology.DCEL.Vertex v in sv.Vertices) { TriangleNet.Geometry.Vertex new_v = new TriangleNet.Geometry.Vertex(v.x, v.y); id2vert[v.id] = new_v; vertices.Add(new_v); } foreach (Edge e in sv.Edges) { TriangleNet.Geometry.Vertex v1 = id2vert[e.P0]; TriangleNet.Geometry.Vertex v2 = id2vert[e.P1]; segments.Add(new Segment(v1, v2)); } CreateMap(vertices, segments, maxz, yOffset); }
/** * Reposition the current center points to be at the average position of their corners. * Doing this makes the point distribution more even, setting it up to create a better mesh. * Can be performed recursively, each iteration yielding smaller and smaller improvements. */ private static List <Vector2> performLloydRelaxation(List <Vector2> currentPoints, List <Vector2> cornerPoints) { VoronoiBase voronoi = Triangulator.generateVoronoi(currentPoints); List <TriangleNet.Topology.DCEL.Face> faces = voronoi.Faces; List <Vector2> points = new List <Vector2>(currentPoints.Count + cornerPoints.Count); points.AddRange(cornerPoints); foreach (Face face in faces) { float x = 0, y = 0; IEnumerable <HalfEdge> halfEdges = face.EnumerateEdges(); float count = 0; foreach (var halfEdge in halfEdges) { count++; x += (float)halfEdge.Origin.X; y += (float)halfEdge.Origin.Y; } points.Add(new Vector2(x / count, y / count)); } return(points); }
private bool CreateVoronoi() { if (mesh == null) { return(false); } if (mesh.IsPolygon) { try { this.voronoi = new BoundedVoronoi(mesh); } catch (Exception ex) { if (!meshControlView.ParamConformDelChecked) { DarkMessageBox.Show("Exception - Bounded Voronoi", Settings.VoronoiString, MessageBoxButtons.OK); } else { DarkMessageBox.Show("Exception - Bounded Voronoi", ex.Message, MessageBoxButtons.OK); } return(false); } } else { this.voronoi = new StandardVoronoi(mesh); } // HACK: List<Vertex> -> ICollection<Point> ? Nope, no way. // Vertex[] -> ICollection<Point> ? Well, ok. renderManager.Set(voronoi.Vertices.ToArray(), voronoi.Edges, false); return(true); }
public Map Generate(Vector2 dimensions, int generations, PolygonList.FaceType faceType, EnvironmentConstructionScript.FunctionType functionType, float height, AnimationCurve heightMap, int regionCount, int relaxationCount, float radius) { data = new Map(); data.generations = generations; this.height = height; this.heigtMap = heightMap; islandShape = new EnvironmentConstructionScript(generations, dimensions.x, dimensions.y, functionType); rectangle = new Rectangle(0, 0, dimensions.x, dimensions.y); if (faceType == PolygonList.FaceType.Hexagon || faceType == PolygonList.FaceType.Square) { relaxationCount = 0; // relaxation is used to create accurate polygon size } // no specific value for rectangle - able to resize in the editor Polygon polygon = PolygonList.Create(dimensions, generations, faceType, regionCount, radius); VoronoiBase voronoi = GenerateVoronoi(ref polygon, relaxationCount); Build(polygon, voronoi); ImproveBorders(); // Determine the elevations and water at Voronoi corners. Elevation.AssignCorner(ref data, islandShape, faceType == PolygonList.FaceType.Hexagon || faceType == PolygonList.FaceType.Square); // Determine polygon and corner type: ocean, coast, land. CheckingScript.AssignSeaSealandAndLand(ref data); // Rescale elevations so that the highest is 1.0, and they're // distributed well. Lower elevations will be more common // than higher elevations. List <Characteristics.Corner> corners = LandCorners(data.corners); Elevation.Readjust(ref corners); // elevations assigned to water corners foreach (var q in data.corners) { if (q.sea || q.sealine) { q.built = 0.0f; } } // Polygon elevations are the average of their corners Elevation.AllocatePolygon(ref data); // Determine humidity at corners, starting at rivers // and lakes, but not oceans. Then redistribute // humidity to cover the entire range evenly from 0.0 // to 1.0. Then assign polygon humidity as the average // of the corner humidity. Humidity.AccountEdge(ref data); Humidity.Redistribute(ref corners); Humidity.AssignPolygon(ref data); CheckingScript.AssignHabitat(ref data); return(data); }
private void Build(Polygon polygon, VoronoiBase voronoi) { Dictionary <Point, Characteristics.Center> centerLoopup = new Dictionary <Point, Characteristics.Center>(); foreach (var point in polygon.Points) { Characteristics.Center center = new Characteristics.Center { id = data.centers.Count, pos = point.ToVector(), neighbours = new List <Characteristics.Center>(), borders = new List <Characteristics.Border>(), corners = new List <Characteristics.Corner>() }; data.centers.Add(center); centerLoopup[point] = center; } foreach (var face in voronoi.Faces) { face.BorderLooping(halfEdge => { Point voronoiEdge1 = halfEdge.Origin; Point voronoiEdge2 = halfEdge.Twin.Origin; Point delaunayEdge1 = polygon.Points[halfEdge.Face.ID]; Point delaunayEdge2 = polygon.Points[halfEdge.Twin.Face.ID]; Characteristics.Border border = new Characteristics.Border { id = data.borders.Count, midPoint = new Vector2((float)(voronoiEdge1.X + voronoiEdge2.X) / 2, (float)(voronoiEdge1.Y + voronoiEdge2.Y) / 2), v0 = MakeCorner(voronoiEdge1), v1 = MakeCorner(voronoiEdge2), d0 = centerLoopup[delaunayEdge1], d1 = centerLoopup[delaunayEdge2] }; if (border.d0 != null) { border.d0.borders.Add(border); } if (border.d1 != null) { border.d1.borders.Add(border); } if (border.v0 != null) { border.v0.protrudes.Add(border); } if (border.v1 != null) { border.v1.protrudes.Add(border); } data.borders.Add(border); if (border.d0 != null && border.d1 != null) { AddToCenterList(border.d0.neighbours, border.d1); AddToCenterList(border.d1.neighbours, border.d0); } if (border.v0 != null && border.v1 != null) { AddToCornerList(border.v0.adjacent, border.v1); AddToCornerList(border.v1.adjacent, border.v0); } if (border.d0 != null) { AddToCornerList(border.d0.corners, border.v0); AddToCornerList(border.d0.corners, border.v1); } if (border.d1 != null) { AddToCornerList(border.d1.corners, border.v0); AddToCornerList(border.d1.corners, border.v1); } if (border.v0 != null) { AddToCenterList(border.v0.touches, border.d0); AddToCenterList(border.v0.touches, border.d1); } if (border.v1 != null) { AddToCenterList(border.v1.touches, border.d0); AddToCenterList(border.v1.touches, border.d1); } }); } }
public Map Generate(Vector2 dimensions, int seed, PointSelector.FaceType faceType, IslandShape.FunctionType functionType, float height, AnimationCurve heightMap, int regionCount, int relaxationCount, float radius) { data = new Map(); data.seed = seed; this.height = height; this.heigtMap = heightMap; islandShape = new IslandShape(seed, dimensions.x, dimensions.y, functionType); rectangle = new Rectangle(0, 0, dimensions.x, dimensions.y); if (faceType == PointSelector.FaceType.Hexagon || faceType == PointSelector.FaceType.Square) { relaxationCount = 0; } Polygon polygon = PointSelector.Generate(dimensions, seed, faceType, regionCount, radius); VoronoiBase voronoi = GenerateVoronoi(ref polygon, relaxationCount); Build(polygon, voronoi); ImproveCorners(); // Determine the elevations and water at Voronoi corners. Elevation.AssignCorner(ref data, islandShape, faceType == PointSelector.FaceType.Hexagon || faceType == PointSelector.FaceType.Square); // Determine polygon and corner type: ocean, coast, land. Biomes.AssignOceanCoastAndLand(ref data); // Rescale elevations so that the highest is 1.0, and they're // distributed well. We want lower elevations to be more common // than higher elevations, in proportions approximately matching // concentric rings. That is, the lowest elevation is the // largest ring around the island, and therefore should more // land area than the highest elevation, which is the very // center of a perfectly circular island. List <Graph.Corner> corners = LandCorners(data.corners); Elevation.Redistribute(ref corners); // Assign elevations to non-land corners foreach (var q in data.corners) { if (q.ocean || q.coast) { q.elevation = 0.0f; } } // Polygon elevations are the average of their corners Elevation.AssignPolygon(ref data); // Determine moisture at corners, starting at rivers // and lakes, but not oceans. Then redistribute // moisture to cover the entire range evenly from 0.0 // to 1.0. Then assign polygon moisture as the average // of the corner moisture. Moisture.AssignCorner(ref data); Moisture.Redistribute(ref corners); Moisture.AssignPolygon(ref data); Biomes.AssignBiomes(ref data); return(data); }
private void Build(Polygon polygon, VoronoiBase voronoi) { Dictionary <Point, Graph.Center> centerLoopup = new Dictionary <Point, Graph.Center>(); foreach (var point in polygon.Points) { Graph.Center center = new Graph.Center { id = data.centers.Count, pos = point.ToVector(), neighbours = new List <Graph.Center>(), borders = new List <Graph.Edge>(), corners = new List <Graph.Corner>() }; data.centers.Add(center); centerLoopup[point] = center; } foreach (var face in voronoi.Faces) { face.LoopEdges(halfEdge => { Point voronoiEdge1 = halfEdge.Origin; Point voronoiEdge2 = halfEdge.Twin.Origin; Point delaunayEdge1 = polygon.Points[halfEdge.Face.ID]; Point delaunayEdge2 = polygon.Points[halfEdge.Twin.Face.ID]; Graph.Edge edge = new Graph.Edge { id = data.edges.Count, midPoint = new Vector2((float)(voronoiEdge1.X + voronoiEdge2.X) / 2, (float)(voronoiEdge1.Y + voronoiEdge2.Y) / 2), v0 = MakeCorner(voronoiEdge1), v1 = MakeCorner(voronoiEdge2), d0 = centerLoopup[delaunayEdge1], d1 = centerLoopup[delaunayEdge2] }; if (edge.d0 != null) { edge.d0.borders.Add(edge); } if (edge.d1 != null) { edge.d1.borders.Add(edge); } if (edge.v0 != null) { edge.v0.protrudes.Add(edge); } if (edge.v1 != null) { edge.v1.protrudes.Add(edge); } data.edges.Add(edge); // Centers point to centers. if (edge.d0 != null && edge.d1 != null) { AddToCenterList(edge.d0.neighbours, edge.d1); AddToCenterList(edge.d1.neighbours, edge.d0); } // Corners point to corners if (edge.v0 != null && edge.v1 != null) { AddToCornerList(edge.v0.adjacent, edge.v1); AddToCornerList(edge.v1.adjacent, edge.v0); } // Centers point to corners if (edge.d0 != null) { AddToCornerList(edge.d0.corners, edge.v0); AddToCornerList(edge.d0.corners, edge.v1); } if (edge.d1 != null) { AddToCornerList(edge.d1.corners, edge.v0); AddToCornerList(edge.d1.corners, edge.v1); } // Corners point to centers if (edge.v0 != null) { AddToCenterList(edge.v0.touches, edge.d0); AddToCenterList(edge.v0.touches, edge.d1); } if (edge.v1 != null) { AddToCenterList(edge.v1.touches, edge.d0); AddToCenterList(edge.v1.touches, edge.d1); } }); } }