private void BuildPatches() { var points = GetVoronoiPoints(NumPatches, Center).ToList(); var v = Voronoi.Build(points); for (var i = 0; i < 3; i++) { var toRelax = v.Points.Take(3).ToList(); toRelax.Add(v.Points[NumPatches]); v = Voronoi.Relax(v, toRelax); } v = Voronoi.Relax(v); var comparer = new Vector2LengthComparer(); v.Points.Sort(comparer); var regions = v.Partitioning(); Patches.Clear(); foreach (var region in regions) { Patches.Add(Patch.FromRegion(this, region)); } var patchesInTown = Patches.OrderBy(p => (Center - p.Center).Length).Take(NumPatches).ToList(); // Find random patch at the outside of town to place water if (Options.Water) { var firstWaterPatch = patchesInTown.Where(p => p.GetAllNeighbours().Any(n => !n.WithinCity)) .OrderBy(p => Rnd.NextDouble()).First(); firstWaterPatch.Water = true; var waterDirection = Vector2.Normalize(firstWaterPatch.Center - Center); var toCheck = new List <Patch> { firstWaterPatch }; while (toCheck.Any()) { var checking = toCheck[0]; toCheck.RemoveAt(0); var waterPatches = checking.GetAllNeighbours().Except(patchesInTown) .Where(n => Math.Abs((Center - n.Center).AngleComparedTo(waterDirection)) < Math.PI / 4) .Where(n => !n.Water).ToList(); foreach (var waterPatch in waterPatches) { waterPatch.Water = true; toCheck.Add(waterPatch); } } } patchesInTown = Patches.Where(p => !p.Water).OrderBy(p => (Center - p.Center).Length).Take(NumPatches) .ToList(); foreach (var patch in patchesInTown) { patch.WithinCity = true; patch.WithinWalls = true; } Castle = new Castle(patchesInTown.Last()); Market = patchesInTown.First(); var circumference = FindCircumference(patchesInTown); var smoothAmount = Math.Min(1f, 40f / circumference.Vertices.Count); if (Options.Walls) { SmoothPatches(circumference, smoothAmount); } if (Options.Water) { var waterCircumference = FindCircumference(Patches.Where(p => p.Water)); SmoothPatches(waterCircumference, 0.2f); WaterBorder.Clear(); WaterBorder.AddRange(waterCircumference.Vertices); } }
public Town(TownOptions options) { Options = options; Rnd.Seed = options.Seed.Value; var ok = false; while (!ok) { NumPatches = options.Patches; Roads = new List <List <Vector2> >(); Streets = new List <List <Vector2> >(); Gates = new List <Vector2>(); Center = new Vector2(Width / 2f * (1 + 0.1 * Rnd.NextDouble() - 0.1 * Rnd.NextDouble()), Height / 2f * (1 + 0.1 * Rnd.NextDouble() - 0.1 * Rnd.NextDouble())); Patches = new List <Patch>(); WaterBorder = new List <Vector2>(); try { BuildPatches(); OptimizePatches(); BuildWalls(); BuildRoads(); PopulateTown(); ok = Roads.Count > 1; } catch (Exception exception) { Console.WriteLine(exception); } } }
private static IEnumerable <Polygon> CreateAlleys(Polygon block, float minArea, float gridChaos, float sizeChaos, Func <Polygon, float> emptyProbabilityFunc, bool split, int levels = 0) { Vector2 point = Vector2.Zero; var length = float.MinValue; block.ForEachEdge((p1, p2, index) => { var len = (p1 - p2).Length; if (len > length) { length = len; point = p1; } }); var spread = 0.8f * gridChaos; var ratio = (1 - spread) / 2 + (float)Rnd.NextDouble() * spread; // Trying to keep buildings rectangular even in chaotic wards var angleSpread = (float)(Math.PI / 6 * gridChaos * (block.Area() < minArea * 4 ? 0 : 1)); var angle = ((float)Rnd.NextDouble() - 0.5f) * angleSpread; var halves = block.Bisect(point, ratio, angle, split ? Alley : 0f); var buildings = new List <Polygon>(); foreach (var half in halves) { if (half.Area() < minArea * Math.Pow(2, 4 * sizeChaos * (Rnd.NextDouble() - 0.5f)) || levels > 5) { if (!Rnd.NextBool(emptyProbabilityFunc(half))) { buildings.Add(half); } } else { buildings.AddRange(CreateAlleys(half, minArea, gridChaos, sizeChaos, emptyProbabilityFunc, half.Area() > minArea / (Rnd.NextDouble() * Rnd.NextDouble()), levels + 1)); } } return(buildings); }