public static List <CustomVector> PlaceAt(CustomVector point, StructureLayoutDef building, CustomVector[][] grid) { RectUtils.HeightWidthFromLayout(building, out int height, out int width); List <CustomVector> doors = GetDoorsInLayout(building); List <CustomVector> doorsAdjusted = new List <CustomVector>(); for (int i = (int)point.X; i < width + point.X; i++) { for (int j = (int)point.Y; j < height + point.Y; j++) { CellType type = doors.FindAll(d => i == d.X + point.X && j == d.Y + point.Y).Any() ? CellType.DOOR : CellType.BUILDING; if (type == CellType.DOOR) { doorsAdjusted.Add(new CustomVector(i, j)); } if (grid[i][j] != null) { grid[i][j].Type = type; } else { grid[i][j] = new CustomVector(i, j, type: type); } } } return(doorsAdjusted); }
/// <summary> /// Draw X road /// </summary> private static void DrawXMainRoad(CustomVector[][] grid, int mapWidth, int mapHeight, int borderDist, Random r) { CustomVector v1 = new CustomVector(0, r.Next(borderDist, mapHeight - borderDist)); CustomVector v2 = new CustomVector(mapWidth - 1, r.Next(borderDist, mapHeight - borderDist)); List <CustomVector> all = AStar.Run(v1, v2, grid, true); all.Add(v1); double y; for (int i = 0; i < all.Count; i++) { CustomVector v = all[i]; y = i + 1 < all.Count ? all[i + 1].Y : v.Y; grid[(int)v.X][(int)v.Y].Type = CellType.MAINROAD; if (v.Y == y) { grid[(int)v.X][(int)v.Y - 1].Type = CellType.MAINROAD; grid[(int)v.X][(int)v.Y + 1].Type = CellType.MAINROAD; } else if (v.Y < y) { grid[(int)v.X][(int)v.Y - 1].Type = CellType.MAINROAD; grid[(int)v.X][(int)v.Y + 1].Type = CellType.MAINROAD; grid[(int)v.X][(int)v.Y + 2].Type = CellType.MAINROAD; } else if (v.Y > y) { grid[(int)v.X][(int)v.Y - 2].Type = CellType.MAINROAD; grid[(int)v.X][(int)v.Y - 1].Type = CellType.MAINROAD; grid[(int)v.X][(int)v.Y + 1].Type = CellType.MAINROAD; } } }
/// <summary> /// Draw Y road /// </summary> private static void DrawYMainRoad(CustomVector[][] grid, int mapWidth, int mapHeight, int borderDist, Random r) { CustomVector v1 = new CustomVector(r.Next(borderDist, mapWidth - borderDist), 0); CustomVector v2 = new CustomVector(r.Next(borderDist, mapWidth - borderDist), mapHeight - 1); List <CustomVector> all = AStar.Run(v1, v2, grid, true); all.Add(v1); double x; for (int i = 0; i < all.Count; i++) { CustomVector v = all[i]; x = i + 1 < all.Count ? all[i + 1].X : v.X; grid[(int)v.X][(int)v.Y].Type = CellType.MAINROAD; if (v.X == x) { grid[(int)v.X - 1][(int)v.Y].Type = CellType.MAINROAD; grid[(int)v.X + 1][(int)v.Y].Type = CellType.MAINROAD; } else if (v.X < x) { grid[(int)v.X - 1][(int)v.Y].Type = CellType.MAINROAD; grid[(int)v.X + 1][(int)v.Y].Type = CellType.MAINROAD; grid[(int)v.X + 2][(int)v.Y].Type = CellType.MAINROAD; } else if (v.X > x) { grid[(int)v.X - 2][(int)v.Y].Type = CellType.MAINROAD; grid[(int)v.X - 1][(int)v.Y].Type = CellType.MAINROAD; grid[(int)v.X + 1][(int)v.Y].Type = CellType.MAINROAD; } } }
public static bool CanPlaceAt(CustomVector point, StructureLayoutDef building, CustomVector[][] grid) { bool result = true; RectUtils.HeightWidthFromLayout(building, out int height, out int width); for (int i = (int)point.X - 1; i < width + point.X + 1 && result; i++) { for (int j = (int)(point.Y - 1); j < height + point.Y + 1 && result; j++) { if (IsInBound(i, j, grid.Length, grid[0].Length)) { if (grid[i][j] != null && grid[i][j].Type != CellType.NONE) { return(false); } } else { return(false); } } } return(result); }
private static bool InsideCircle(CustomVector center, CustomVector tile, float radius) { float dx = (float)(center.X - tile.X), dy = (float)(center.Y - tile.Y); float distance_squared = dx * dx + dy * dy; return(distance_squared <= radius * radius); }
public static List <CustomVector> Run(CustomVector start, CustomVector end, CustomVector[][] grid, bool neig8) { gridCols = grid.Count(); gridRows = grid[0].Count(); List <CustomVector> Path = new List <CustomVector>(); List <CustomVector> OpenList = new List <CustomVector>(); List <CustomVector> ClosedList = new List <CustomVector>(); List <CustomVector> adjacencies; CustomVector current = start; OpenList.Add(start); while (OpenList.Count > 0 && !ClosedList.Exists(v => v.X == end.X && v.Y == end.Y)) { current = OpenList[0]; OpenList.Remove(current); ClosedList.Add(current); adjacencies = GetAdjacent(current, grid, neig8); foreach (CustomVector n in adjacencies) { if (!ClosedList.Contains(n) && n.Type != CellType.WATER && n.Type != CellType.BUILDING) { if (!OpenList.Contains(n)) { n.Parent = current; n.DistanceToTarget = (float)(Math.Abs(n.X - end.X) + Math.Abs(n.Y - end.Y)); n.Cost = n.Weight + n.Parent.Cost; OpenList.Add(n); OpenList = OpenList.OrderBy(v => v.F).ToList(); } } } } if (!ClosedList.Exists(v => v.X == end.X && v.Y == end.Y)) { KLog.Message($"AStar error: end point not found in ClosedList. Closed list count {ClosedList.Count}. Path wanted from {start} to {end}."); return(null); } CustomVector temp = ClosedList[ClosedList.IndexOf(current)]; if (temp == null) { KLog.Message("AStar error: temp is null."); return(null); } while (temp != start && temp != null) { Path.Add(temp); temp = temp.Parent; } return(Path); }
private static bool InsideCircles(CustomVector tile, float radius, List <CustomVector> allPoints) { foreach (CustomVector item in allPoints) { if (InsideCircle(item, tile, radius)) { return(false); } } return(true); }
private bool AwayFromAllField(List <CustomVector> allFields, int distance, CustomVector point) { foreach (CustomVector customVector in allFields) { if (customVector.DistanceToPow(point) < Math.Pow(distance, 2)) { return(false); } } return(true); }
public CustomVector(double x, double y, CellType type = CellType.NONE, float weight = 1f, ThingDef plant = null) { X = x; Y = y; this.Type = type; Parent = null; DistanceToTarget = -1f; Cost = 1f; Weight = weight; PlantType = plant; }
public static List <CustomVector> GetAdjacent(CustomVector n, CustomVector[][] grid, bool neig8) { List <CustomVector> temp = new List <CustomVector>(); int row = (int)n.Y; int col = (int)n.X; if (row + 1 < gridRows) { temp.Add(grid[col][row + 1]); } if (row - 1 >= 0) { temp.Add(grid[col][row - 1]); } if (col - 1 >= 0) { temp.Add(grid[col - 1][row]); } if (col + 1 < gridCols) { temp.Add(grid[col + 1][row]); } if (neig8) { if (col - 1 >= 0 && row - 1 >= 0) { temp.Add(grid[col - 1][row - 1]); } if (col + 1 < gridCols && row - 1 >= 0) { temp.Add(grid[col + 1][row - 1]); } if (col - 1 >= 0 && row + 1 < gridRows) { temp.Add(grid[col - 1][row + 1]); } if (col + 1 < gridCols && row + 1 < gridRows) { temp.Add(grid[col + 1][row + 1]); } } return(temp); }
public Triangle(CustomVector point1, CustomVector point2, CustomVector point3) { Vertices[0] = point1; if (!IsCounterClockwise(point1, point2, point3)) { Vertices[1] = point3; Vertices[2] = point2; } else { Vertices[1] = point2; Vertices[2] = point3; } Vertices[0].AdjacentTriangles.Add(this); Vertices[1].AdjacentTriangles.Add(this); Vertices[2].AdjacentTriangles.Add(this); UpdateCircumcircle(); }
/// https://bl.ocks.org/mbostock/raw/dbb02448b0f93e4c82c3/?raw=true public static List <CustomVector> Run(int maxTries, int Width, int Height, Random r, CustomVector[][] grid) { List <CustomVector> points = new List <CustomVector>(); List <CustomVector> active = new List <CustomVector>(); /* Initial random point */ CustomVector p0 = new CustomVector(0, 0); grid[(int)p0.X][(int)p0.Y] = p0; points.Add(p0); active.Add(p0); while (active.Count > 0) { int random_index = r.Next(active.Count); CustomVector p = active.ElementAt(random_index); for (int tries = 1; tries <= maxTries; tries++) { /* Pick a random angle */ int theta = r.Next(361); /* Pick a random radius between r and 1.5r */ int new_radius = r.Next(CGO.radius, (int)(1.5f * CGO.radius)); /* Find X & Y coordinates relative to point p */ int pnewx = (int)(p.X + new_radius * Math.Cos(ConvertToRadians(theta))); int pnewy = (int)(p.Y + new_radius * Math.Sin(ConvertToRadians(theta))); CustomVector pnew = new CustomVector(pnewx, pnewy); if (IsInBound(pnew, Width, Height, CGO.radius) && InsideCircles(pnew, CGO.radius, points)) { points.Add(pnew); active.Add(pnew); break; } else if (tries == maxTries) { active.RemoveAt(random_index); } } } return(points); }
private static bool IsInBound(CustomVector p, int gridWidth, int gridHeight, int radius) { if (p.X < 0) { return(false); } if (p.X >= gridWidth - radius) { return(false); } if (p.Y < 0) { return(false); } if (p.Y >= gridHeight - radius) { return(false); } return(true); }
public static IEnumerable <Triangle> Run(IEnumerable <CustomVector> points, double maxX, double maxY) { CustomVector point0 = new CustomVector(0, 0); CustomVector point1 = new CustomVector(0, maxY); CustomVector point2 = new CustomVector(maxX, maxY); CustomVector point3 = new CustomVector(maxX, 0); Triangle triangle1 = new Triangle(point0, point1, point2); Triangle triangle2 = new Triangle(point0, point2, point3); border = new List <Triangle>() { triangle1, triangle2 }; HashSet <Triangle> triangulation = new HashSet <Triangle>(border); foreach (CustomVector point in points) { ISet <Triangle> badTriangles = FindBadTriangles(point, triangulation); List <Edge> polygon = FindHoleBoundaries(badTriangles); foreach (Triangle triangle in badTriangles) { foreach (CustomVector vertex in triangle.Vertices) { vertex.AdjacentTriangles.Remove(triangle); } } triangulation.RemoveWhere(o => badTriangles.Contains(o)); foreach (Edge edge in polygon.Where(possibleEdge => possibleEdge.Point1 != point && possibleEdge.Point2 != point)) { Triangle triangle = new Triangle(point, edge.Point1, edge.Point2); triangulation.Add(triangle); } } triangulation.RemoveWhere(t => t.Vertices.ToList().Find(v => v.X == 0 || v.Y == 0 || v.X == maxX || v.Y == maxY) != null); return(triangulation); }
private void UpdateCircumcircle() { // https://codefound.wordpress.com/2013/02/21/how-to-compute-a-circumcircle/#more-58 CustomVector p0 = Vertices[0]; CustomVector p1 = Vertices[1]; CustomVector p2 = Vertices[2]; double dA = p0.X * p0.X + p0.Y * p0.Y; double dB = p1.X * p1.X + p1.Y * p1.Y; double dC = p2.X * p2.X + p2.Y * p2.Y; double aux1 = dA * (p2.Y - p1.Y) + dB * (p0.Y - p2.Y) + dC * (p1.Y - p0.Y); double aux2 = -(dA * (p2.X - p1.X) + dB * (p0.X - p2.X) + dC * (p1.X - p0.X)); double div = 2 * (p0.X * (p2.Y - p1.Y) + p1.X * (p0.Y - p2.Y) + p2.X * (p1.Y - p0.Y)); if (div == 0) { Log.Error(new DivideByZeroException().ToString()); } CustomVector center = new CustomVector(aux1 / div, aux2 / div); Circumcenter = center; RadiusSquared = (center.X - p0.X) * (center.X - p0.X) + (center.Y - p0.Y) * (center.Y - p0.Y); }
public bool IsPointInsideCircumcircle(CustomVector point) { double d_squared = (point.X - Circumcenter.X) * (point.X - Circumcenter.X) + (point.Y - Circumcenter.Y) * (point.Y - Circumcenter.Y); return(d_squared < RadiusSquared); }
private bool IsCounterClockwise(CustomVector point1, CustomVector point2, CustomVector point3) { double result = (point2.X - point1.X) * (point3.Y - point1.Y) - (point3.X - point1.X) * (point2.Y - point1.Y); return(result > 0); }
private static ISet <Triangle> FindBadTriangles(CustomVector point, HashSet <Triangle> triangles) { IEnumerable <Triangle> badTriangles = triangles.Where(o => o.IsPointInsideCircumcircle(point)); return(new HashSet <Triangle>(badTriangles)); }
public Edge(CustomVector point1, CustomVector point2) { Point1 = point1; Point2 = point2; }
/// <summary> /// Generate a grid, add the preexisting vanilla road if needed. Add main roads. Fill the potential point list, add buildings on grid and fill the origin/layout dictionnary /// </summary> /// <param name="seed">Seed for Random</param> /// <param name="sld">SettlementLayoutDef to get all StructureLayoutDef authorized</param> /// <param name="map">The map</param> /// <returns></returns> public static CustomVector[][] GenerateGrid(int seed, SettlementLayoutDef sld, Map map) { CGO.currentGenStepMoreInfo = "Generating grid and main road"; int mapWidth = Math.Min(map.Size.x, sld.settlementSize.x), mapHeight = Math.Min(map.Size.z, sld.settlementSize.z); // Search for the smallest sized allowed structure. It will be the radius between potential building spawn points CGO.radius = 9999; for (int i = 0; i < sld.allowedStructures.Count; i++) { foreach (StructureLayoutDef item in DefDatabase <StructureLayoutDef> .AllDefsListForReading.FindAll(s => s.tags.Contains(sld.allowedStructuresConverted[i].structureLayoutTag))) { RectUtils.HeightWidthFromLayout(item, out int height, out int width); if (height < CGO.radius) { CGO.radius = height; } if (width < CGO.radius) { CGO.radius = width; } } } CGO.radius += 2; // Add to radius to ensure no building touch one another // Initialize the grid used for generation Random r = new Random(seed); CustomVector[][] grid = new CustomVector[mapWidth][]; for (int i = 0; i < mapWidth; i++) { grid[i] = new CustomVector[mapHeight]; for (int j = 0; j < mapHeight; j++) { grid[i][j] = new CustomVector(i, j); } } // Exclude non bridgeable cells for (int i = 0; i < mapWidth; i++) { for (int j = 0; j < mapHeight; j++) { TerrainDef t = map.terrainGrid.TerrainAt(new IntVec3(CGO.offset.x + i, 0, CGO.offset.y + j)); if (t.HasTag("Water") && (t.affordances == null || !t.affordances.Contains(TerrainAffordanceDefOf.Bridgeable))) { grid[i][j].Type = CellType.WATER; } } } // Main roads CGO.usePathCostReduction = false; // No path cost reduction for main road, for them to be distinct DrawXMainRoad(grid, mapWidth, mapHeight, 15, r); DrawYMainRoad(grid, mapWidth, mapHeight, 15, r); // If bigger sized settlement, add more main roads for (int i = 0; i < mapWidth / 100; i++) { if (i == 0) { DrawXMainRoad(grid, mapWidth, mapHeight, mapWidth / 2, r); DrawYMainRoad(grid, mapWidth, mapHeight, mapHeight / 2, r); } else { DrawXMainRoad(grid, mapWidth, mapHeight, 50, r); DrawYMainRoad(grid, mapWidth, mapHeight, 50, r); } } // Get vanilla world road type and use it if present if (CGO.preRoadTypes?.Count > 0) { for (int i = 0; i < mapWidth; i++) { for (int j = 0; j < mapHeight; j++) { TerrainDef t = map.terrainGrid.TerrainAt(new IntVec3(CGO.offset.x + i, 0, CGO.offset.y + j)); if (CGO.preRoadTypes.Contains(t)) { grid[i][j].Type = CellType.MAINROAD; // Add vanilla generated road to the grid } } } } CGO.usePathCostReduction = true; // Renable path cost reduction CGO.vectors = PoissonDiskSampling.Run(50, mapWidth, mapHeight, r, grid); // Get all possible points with radius CGO.doors = BuildingPlacement.Run(sld, grid, 50); // Place buildings on grid and add them/their origin into vectStruct return(grid); }
public double DistanceTo(CustomVector otherVector) { return(Math.Sqrt(Math.Pow(X - otherVector.X, 2) + Math.Pow(Y - otherVector.Y, 2))); }