/
GeekNerd
/
ServerModelingProject
Обзор
Документация
Войти
/
GeekNerd
/
ServerModelingProject
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Assets/Scripts/OptimizationLogic/Core/PlacementValidator.cs
202 строки
8 KB
Gorney-Alex
Init
30 май 2026, 19:05
30 май 2026, 19:05
3cdcc49
Код
Авторство
О чём код?
using System.Collections.Generic; using DataClasses; using DataClasses.Algorithm; using UnityEngine; namespace OptimizationLogic.Core { /// <summary> /// Validates rack placement against room polygon, obstacles, collisions and minimum aisle width. /// No physics/colliders used. /// </summary> public sealed class PlacementValidator { private const float DoorEvacuationDepth = 1.5f; public bool IsPlacementValid(RoomConfig room, IReadOnlyList<RackGene> placed, RackGene candidate, IReadOnlyList<RackData> models) { if (room == null || models == null) return false; if (!candidate.IsPlaced) return true; if (candidate.ModelIndex < 0 || candidate.ModelIndex >= models.Count) return false; Rect candRect = GetFootprint(candidate, models); // 1) Footprint corners inside polygon if (!IsFootprintInsidePolygon(room.PolygonVertices, candRect)) return false; // 2) No intersection with obstacles if (IntersectsObstacles(room, candRect)) return false; // 3) No rack-rack intersections if (placed != null) { for (int i = 0; i < placed.Count; i++) { RackGene other = placed[i]; if (!other.IsPlaced) continue; if (other.ModelIndex < 0 || other.ModelIndex >= models.Count) continue; Rect otherRect = GetFootprint(other, models); if (Geometry2D.Intersects(candRect, otherRect)) return false; } } // 4) Minimum aisle width float dmin = room.MinAisleWidth > 0f ? room.MinAisleWidth : 1.0f; if (placed != null) { for (int i = 0; i < placed.Count; i++) { RackGene other = placed[i]; if (!other.IsPlaced) continue; if (other.ModelIndex < 0 || other.ModelIndex >= models.Count) continue; // Aisle width is enforced only within a single zone. // Between zones the separation is guaranteed by zone partitioning. if (other.ZoneId != candidate.ZoneId) continue; Rect otherRect = GetFootprint(other, models); float dist = Geometry2D.DistanceBetweenRects(candRect, otherRect); if (dist < dmin - 1e-6f) return false; } } return true; } public static Rect GetFootprint(RackGene gene, IReadOnlyList<RackData> models) { if (models == null || gene.ModelIndex < 0 || gene.ModelIndex >= models.Count) return new Rect(gene.PositionX, gene.PositionZ, 0f, 0f); RackData model = models[gene.ModelIndex]; bool rotated = Mathf.Abs(Mathf.Repeat(gene.Rotation, 180f) - 90f) < 0.01f; float width = rotated ? model.Length : model.Width; float length = rotated ? model.Width : model.Length; return new Rect(gene.PositionX - width * 0.5f, gene.PositionZ - length * 0.5f, width, length); } private static bool IsFootprintInsidePolygon(IReadOnlyList<Vector2> polygon, Rect footprint) { if (polygon == null || polygon.Count < 3) return false; Vector2 c1 = new(footprint.xMin, footprint.yMin); Vector2 c2 = new(footprint.xMax, footprint.yMin); Vector2 c3 = new(footprint.xMax, footprint.yMax); Vector2 c4 = new(footprint.xMin, footprint.yMax); return Geometry2D.IsPointInPolygon(c1, polygon) && Geometry2D.IsPointInPolygon(c2, polygon) && Geometry2D.IsPointInPolygon(c3, polygon) && Geometry2D.IsPointInPolygon(c4, polygon); } private static bool IntersectsObstacles(RoomConfig room, Rect candidateFootprint) { // Forbidden zones (OBB) 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 (Geometry2D.Intersects(candidateFootprint, zone)) return true; } } // 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(); float d = Geometry2D.DistanceRectToCircle(candidateFootprint, col.Center); if (d < r - 1e-6f) 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 (Geometry2D.Intersects(candidateFootprint, rect)) return true; } } } // Partitions: treat as oriented rectangles with thickness if (room.Partitions != null) { for (int i = 0; i < room.Partitions.Count; i++) { PartitionData p = room.Partitions[i]; Vector2 ab = p.End - p.Start; float len = ab.magnitude; if (len <= 1e-6f) continue; Vector2 center = (p.Start + p.End) * 0.5f; float rot = Mathf.Atan2(ab.y, ab.x) * Mathf.Rad2Deg; OrientedRect wall = new OrientedRect(center, new Vector2(p.Thickness, len), rot); if (Geometry2D.Intersects(candidateFootprint, wall)) return true; } } // Door evacuation zones List<OrientedRect> evacZones = BuildDoorEvacuationZones(room); for (int i = 0; i < evacZones.Count; i++) { if (Geometry2D.Intersects(candidateFootprint, evacZones[i])) return true; } return false; } private static List<OrientedRect> BuildDoorEvacuationZones(RoomConfig room) { List<OrientedRect> zones = new(); if (room.Doors == null || room.Doors.Count == 0) return zones; if (room.PolygonVertices == null || room.PolygonVertices.Count < 3) return zones; float area = Geometry2D.SignedArea(room.PolygonVertices); bool ccw = area > 0f; for (int i = 0; i < room.Doors.Count; i++) { DoorData door = room.Doors[i]; int ei = door.EdgeIndex; if (ei < 0 || ei >= room.PolygonVertices.Count) continue; Vector2 a = room.PolygonVertices[ei]; Vector2 b = room.PolygonVertices[(ei + 1) % room.PolygonVertices.Count]; Vector2 dir = (b - a); float edgeLen = dir.magnitude; if (edgeLen <= 1e-6f) continue; dir /= edgeLen; // Interior is on the left side for CCW polygons, right side for CW. Vector2 left = new Vector2(-dir.y, dir.x); Vector2 inward = ccw ? left : -left; float w = Mathf.Max(0f, door.Width); float offset = Mathf.Clamp(door.OffsetAlongEdge, 0f, Mathf.Max(0f, edgeLen - w)); Vector2 doorCenterOnEdge = a + dir * (offset + w * 0.5f); Vector2 center = doorCenterOnEdge + inward * (DoorEvacuationDepth * 0.5f); float rot = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; OrientedRect zone = new OrientedRect(center, new Vector2(w, DoorEvacuationDepth), rot); zones.Add(zone); } return zones; } } }