/
GeekNerd
/
ServerModelingProject
Обзор
Документация
Войти
/
GeekNerd
/
ServerModelingProject
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Assets/Scripts/OptimizationLogic/Core/AisleValidator.cs
281 строка
10 KB
Gorney-Alex
Init
30 май 2026, 19:05
30 май 2026, 19:05
3cdcc49
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using DataClasses; using DataClasses.Algorithm; using UnityEngine; namespace OptimizationLogic.Core { public sealed class AisleValidator { public HashSet<int> ComputeAccessibleRacks(RoomConfig room, IReadOnlyList<RackGene> genes, IReadOnlyList<RackData> models) { HashSet<int> accessible = new(); if (room == null || genes == null || models == null) return accessible; if (room.PolygonVertices == null || room.PolygonVertices.Count < 3) return accessible; float step = room.GridStep > 0f ? room.GridStep : 0.6f; float dmin = room.MinAisleWidth > 0f ? room.MinAisleWidth : 1.0f; Rect bbox = Geometry2D.ComputeBoundingBox(room.PolygonVertices); int cols = Mathf.Max(2, Mathf.CeilToInt(bbox.width / step) + 3); int rows = Mathf.Max(2, Mathf.CeilToInt(bbox.height / step) + 3); Vector2 origin = new Vector2(bbox.xMin - step, bbox.yMin - step); bool[] staticBlocked = new bool[rows * cols]; // Precompute static obstacles + outside polygon for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { Vector2 p = origin + new Vector2(c * step, r * step); bool isCellBlocked = !Geometry2D.IsPointInPolygon(p, room.PolygonVertices) || IsPointInStaticObstacle(room, p); staticBlocked[r * cols + c] = isCellBlocked; } } // Precompute exit cells (close to polygon boundary) bool[] exit = new bool[rows * cols]; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { int id = r * cols + c; if (staticBlocked[id]) continue; Vector2 p = origin + new Vector2(c * step, r * step); if (DistanceToPolygonEdges(p, room.PolygonVertices) <= step * 1.1f) exit[id] = true; } } // Dynamic blocked: racks bool[] blocked = (bool[])staticBlocked.Clone(); for (int i = 0; i < genes.Count; i++) { RackGene g = genes[i]; if (!g.IsPlaced) continue; if (g.ModelIndex < 0 || g.ModelIndex >= models.Count) continue; Rect foot = PlacementValidator.GetFootprint(g, models); MarkRect(blocked, rows, cols, origin, step, foot); } // Clearance dilation to approximate width >= dmin int radCells = Mathf.CeilToInt((dmin * 0.5f) / step); if (radCells > 0) blocked = Dilate(blocked, rows, cols, radCells); // BFS per rack for (int i = 0; i < genes.Count; i++) { RackGene g = genes[i]; if (!g.IsPlaced) continue; if (g.ModelIndex < 0 || g.ModelIndex >= models.Count) continue; Rect foot = PlacementValidator.GetFootprint(g, models); Vector2 front = Geometry2D.FrontVectorFromRotation(g.Rotation); float halfDepth = Mathf.Max(models[g.ModelIndex].Length, models[g.ModelIndex].Width) * 0.5f; Vector2 startWorld = new Vector2(g.PositionX, g.PositionZ) + front * (halfDepth + step); if (!TryWorldToCell(startWorld, origin, step, rows, cols, out int start)) continue; if (blocked[start]) { // Try a small neighborhood for a walkable start bool found = false; for (int dr = -1; dr <= 1 && !found; dr++) for (int dc = -1; dc <= 1 && !found; dc++) { int rr = (start / cols) + dr; int cc = (start % cols) + dc; if (rr < 0 || rr >= rows || cc < 0 || cc >= cols) continue; int nid = rr * cols + cc; if (!blocked[nid]) { start = nid; found = true; } } if (!found) continue; } if (BfsToExit(start, blocked, exit, rows, cols)) accessible.Add(i); } return accessible; } private static bool BfsToExit(int start, bool[] blocked, bool[] exit, int rows, int cols) { int n = rows * cols; bool[] visited = new bool[n]; int[] queue = new int[n]; int qh = 0, qt = 0; visited[start] = true; queue[qt++] = start; while (qh < qt) { int v = queue[qh++]; if (exit[v]) return true; int r = v / cols; int c = v % cols; TryEnqueue(r - 1, c); TryEnqueue(r + 1, c); TryEnqueue(r, c - 1); TryEnqueue(r, c + 1); } return false; void TryEnqueue(int rr, int cc) { if (rr < 0 || rr >= rows || cc < 0 || cc >= cols) return; int id = rr * cols + cc; if (visited[id] || blocked[id]) return; visited[id] = true; queue[qt++] = id; } } private static bool[] Dilate(bool[] blocked, int rows, int cols, int radius) { bool[] outArr = (bool[])blocked.Clone(); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { if (!blocked[r * cols + c]) continue; for (int dr = -radius; dr <= radius; dr++) { int rr = r + dr; if (rr < 0 || rr >= rows) continue; for (int dc = -radius; dc <= radius; dc++) { int cc = c + dc; if (cc < 0 || cc >= cols) continue; outArr[rr * cols + cc] = true; } } } } return outArr; } private static void MarkRect(bool[] blocked, int rows, int cols, Vector2 origin, float step, Rect rect) { int minC = Mathf.FloorToInt((rect.xMin - origin.x) / step); int maxC = Mathf.CeilToInt((rect.xMax - origin.x) / step); int minR = Mathf.FloorToInt((rect.yMin - origin.y) / step); int maxR = Mathf.CeilToInt((rect.yMax - origin.y) / step); minC = Mathf.Clamp(minC, 0, cols - 1); maxC = Mathf.Clamp(maxC, 0, cols - 1); minR = Mathf.Clamp(minR, 0, rows - 1); maxR = Mathf.Clamp(maxR, 0, rows - 1); for (int r = minR; r <= maxR; r++) { for (int c = minC; c <= maxC; c++) { Vector2 p = origin + new Vector2(c * step, r * step); if (rect.Contains(p)) blocked[r * cols + c] = true; } } } private static bool TryWorldToCell(Vector2 p, Vector2 origin, float step, int rows, int cols, out int id) { int c = Mathf.RoundToInt((p.x - origin.x) / step); int r = Mathf.RoundToInt((p.y - origin.y) / step); if (r < 0 || r >= rows || c < 0 || c >= cols) { id = -1; return false; } id = r * cols + c; return true; } private static float DistanceToPolygonEdges(Vector2 p, IReadOnlyList<Vector2> poly) { float best = float.PositiveInfinity; for (int i = 0; i < poly.Count; i++) { Vector2 a = poly[i]; Vector2 b = poly[(i + 1) % poly.Count]; float d = Geometry2D.DistancePointToSegment(p, a, b); if (d < best) best = d; } return best; } private static bool IsPointInStaticObstacle(RoomConfig room, Vector2 p) { // Columns if (room.Columns != null) { for (int i = 0; i < room.Columns.Count; i++) { ColumnData col = room.Columns[i]; if (col == null) continue; if (col.IsCircular) { float r = col.GetCircularRadius(); if (Vector2.Distance(p, col.Center) <= r) return true; } else { Vector2 size = col.Size; Rect rect = new Rect(col.Center.x - size.x * 0.5f, col.Center.y - size.y * 0.5f, size.x, size.y); if (rect.Contains(p)) return true; } } } // Forbidden zones if (room.ForbiddenZones != null) { for (int i = 0; i < room.ForbiddenZones.Count; i++) { ForbiddenZoneData f = room.ForbiddenZones[i]; OrientedRect zone = new OrientedRect(f.Center, f.Size, f.Rotation); if (PointInOrientedRect(p, zone)) return true; } } // Partitions if (room.Partitions != null) { for (int i = 0; i < room.Partitions.Count; i++) { PartitionData part = room.Partitions[i]; float dist = Geometry2D.DistancePointToSegment(p, part.Start, part.End); if (dist <= part.Thickness * 0.5f) return true; } } return false; } private static bool PointInOrientedRect(Vector2 p, OrientedRect rect) { Vector2 d = p - rect.Center; float x = Vector2.Dot(d, rect.Right); float y = Vector2.Dot(d, rect.Up); Vector2 e = rect.Extents; return Mathf.Abs(x) <= e.x && Mathf.Abs(y) <= e.y; } } }