private void subdivide(Vector2 A, Vector2 B, Vector2 C, Vector2 D, List <Vector2> points, float minLength) { if (Vector2.Distance(A, C) < minLength || Vector2.Distance(B, D) < minLength) { return; } // Subdivide the quadrilateral float p = Random.Range(0.2f, 0.8f); // vertical (along A-D and B-C) float q = Random.Range(0.2f, 0.8f); // horizontal (along A-B and D-C) // Midpoints Vector2 E = Vector2Extensions.Interpolate(A, D, p); Vector2 F = Vector2Extensions.Interpolate(B, C, p); Vector2 G = Vector2Extensions.Interpolate(A, B, q); Vector2 I = Vector2Extensions.Interpolate(D, C, q); // Central point Vector2 H = Vector2Extensions.Interpolate(E, F, q); // Divide the quad into subquads, but meet at H float s = 1 - Random.Range(-0.4f, 0.4f); float t = 1 - Random.Range(-0.4f, 0.4f); subdivide(A, Vector2Extensions.Interpolate(G, B, s), H, Vector2Extensions.Interpolate(E, D, t), points, minLength); points.Add(H); subdivide(H, Vector2Extensions.Interpolate(F, C, s), C, Vector2Extensions.Interpolate(I, D, t), points, minLength); }
// Build noisy line paths for each of the Voronoi edges. There are // two noisy line paths for each edge, each covering half the // distance: path0 is from v0 to the midpoint and path1 is from v1 // to the midpoint. When drawing the polygons, one or the other // must be drawn in reverse order. public void BuildNoisyEdges(Map map) { foreach (Center p in map.Graph.centers) { foreach (Edge edge in p.borders) { if (edge.d0 != null && edge.d1 != null && edge.v0 != null && edge.v1 != null && !path0.ContainsKey(edge.index)) { float f = NOISY_LINE_TRADEOFF; Vector2 t = Vector2Extensions.Interpolate(edge.v0.point, edge.d0.point, f); Vector2 q = Vector2Extensions.Interpolate(edge.v0.point, edge.d1.point, f); Vector2 r = Vector2Extensions.Interpolate(edge.v1.point, edge.d0.point, f); Vector2 s = Vector2Extensions.Interpolate(edge.v1.point, edge.d1.point, f); float minLength = 10 * SizeScale; if (edge.d0.biome != edge.d1.biome) { minLength = 3 * SizeScale; } if (edge.d0.ocean && edge.d1.ocean) { minLength = 100 * SizeScale; } if (edge.d0.coast || edge.d1.coast) { minLength = 1 * SizeScale; } if (edge.river > 0) { minLength = 1 * SizeScale; } path0[edge.index] = buildNoisyLineSegments(edge.v0.point, t, edge.midpoint, q, minLength); path1[edge.index] = buildNoisyLineSegments(edge.v1.point, s, edge.midpoint, r, minLength); } } } }
private void BuildGraph(IEnumerable <Vector2> points, Delaunay.Voronoi voronoi) { // Build graph data structure in 'edges', 'centers', 'corners', // based on information in the Voronoi results: point.neighbors // will be a list of neighboring points of the same type (corner // or center); point.edges will be a list of edges that include // that point. Each edge connects to four points: the Voronoi edge // edge.{v0,v1} and its dual Delaunay triangle edge edge.{d0,d1}. // For boundary polygons, the Delaunay edge will have one null // point, and the Voronoi edge may be null. var libedges = voronoi.Edges(); var centerLookup = new Dictionary <Vector2?, Center>(); // Build Center objects for each of the points, and a lookup map // to find those Center objects again as we build the graph foreach (var point in points) { var p = new Center { index = centers.Count, point = point }; centers.Add(p); centerLookup[point] = p; } // Workaround for Voronoi lib bug: we need to call region() // before Edges or neighboringSites are available foreach (var p in centers) { voronoi.Region(p.point); } foreach (var libedge in libedges) { var dedge = libedge.DelaunayLine(); var vedge = libedge.VoronoiEdge(); // Fill the graph data. Make an Edge object corresponding to // the edge from the voronoi library. var edge = new Edge { index = edges.Count, river = 0, // Edges point to corners. Edges point to centers. v0 = MakeCorner(vedge.p0), v1 = MakeCorner(vedge.p1), d0 = centerLookup[dedge.p0], d1 = centerLookup[dedge.p1] }; if (vedge.p0.HasValue && vedge.p1.HasValue) { edge.midpoint = Vector2Extensions.Interpolate(vedge.p0.Value, vedge.p1.Value, 0.5f); } edges.Add(edge); // Centers point to edges. Corners point to edges. 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); } // Centers point to centers. if (edge.d0 != null && edge.d1 != null) { AddToCenterList(edge.d0.neighbors, edge.d1); AddToCenterList(edge.d1.neighbors, 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); } } // TODO: use edges to determine these var topLeft = centers.OrderBy(p => p.point.x + p.point.y).First(); AddCorner(topLeft, 0, 0); var bottomRight = centers.OrderByDescending(p => p.point.x + p.point.y).First(); AddCorner(bottomRight, Width, Height); var topRight = centers.OrderByDescending(p => Width - p.point.x + p.point.y).First(); AddCorner(topRight, 0, Height); var bottomLeft = centers.OrderByDescending(p => p.point.x + Height - p.point.y).First(); AddCorner(bottomLeft, Width, 0); // required for polygon fill foreach (var center in centers) { center.corners.Sort(ClockwiseComparison(center)); } }