/
GeekNerd
/
ServerModelingProject
Обзор
Документация
Войти
/
GeekNerd
/
ServerModelingProject
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Assets/Scripts/RoomBuilding/PolygonDrawingService.cs
292 строки
9 KB
Gorney-Alex
Init
30 май 2026, 19:05
30 май 2026, 19:05
3cdcc49
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using DataClasses; using UnityEngine; namespace RoomBuilding { public enum DrawingState { Idle, Drawing, Closed } public class PolygonDrawingService { private readonly PolygonValidator _validator; private readonly RoomBuildingSettings _settings; private readonly CracData _baseCracData; private readonly List<Vector2> _vertices = new(); private readonly List<DoorData> _doors = new(); private readonly List<ColumnData> _columns = new(); private readonly List<CracData> _cracs = new(); public IReadOnlyList<Vector2> Vertices => _vertices; public IReadOnlyList<DoorData> Doors => _doors; public IReadOnlyList<ColumnData> Columns => _columns; public IReadOnlyList<CracData> Cracs => _cracs; public DrawingState State { get; private set; } = DrawingState.Idle; public float PreviewColumnRadius { get; private set; } = 0.25f; private bool _isColumnMode; public bool IsColumnMode { get => _isColumnMode; set { if (_isColumnMode == value) return; _isColumnMode = value; if (!_isColumnMode) { PreviewCursorWorld = null; } OnColumnsChanged?.Invoke(); } } public Vector2? PreviewCursorWorld { get; private set; } public event Action OnVerticesChanged; public event Action OnDoorsChanged; public event Action OnColumnsChanged; public event Action OnCracsChanged; public event Action<DrawingState> OnStateChanged; public event Action<string> OnError; public event Action<RoomConfig> OnRoomBuilt; public PolygonDrawingService(PolygonValidator validator, RoomBuildingSettings settings, CracData baseCracData) { _validator = validator; _settings = settings; _baseCracData = baseCracData ?? new CracData(); } public void HandleDotClick(Vector2 worldPoint) { if (State == DrawingState.Closed) return; if (_vertices.Count >= 3 && IsFirstVertex(worldPoint)) { ClosePolygon(); return; } var snapped = SnapGrid.Snap(worldPoint, _settings.GridSize, _settings.SnapThreshold); _vertices.Add(snapped); SetState(DrawingState.Drawing); OnVerticesChanged?.Invoke(); } public void HandleCursorMove(Vector2 worldPoint) { if (State != DrawingState.Drawing && !IsColumnMode) return; PreviewCursorWorld = worldPoint; OnVerticesChanged?.Invoke(); if (IsColumnMode) { OnColumnsChanged?.Invoke(); } } public void RemoveLastVertex() { if (_vertices.Count == 0) return; _vertices.RemoveAt(_vertices.Count - 1); SetState(_vertices.Count == 0 ? DrawingState.Idle : DrawingState.Drawing); OnVerticesChanged?.Invoke(); } public void Clear() { _vertices.Clear(); _doors.Clear(); _columns.Clear(); _cracs.Clear(); PreviewCursorWorld = null; IsColumnMode = false; SetState(DrawingState.Idle); OnVerticesChanged?.Invoke(); OnDoorsChanged?.Invoke(); OnColumnsChanged?.Invoke(); OnCracsChanged?.Invoke(); } public void AddColumn(Vector2 worldPoint) { _columns.Add(new ColumnData { Center = worldPoint, IsCircular = true, Radius = PreviewColumnRadius, RadiusOrHalfWidth = PreviewColumnRadius }); OnColumnsChanged?.Invoke(); } public void AddCrac(Vector2 worldPoint) { if (State != DrawingState.Closed) { OnError?.Invoke("CRAC can only be placed after the polygon is closed."); return; } _cracs.Add(new CracData { Name = _baseCracData.Name, Center = worldPoint, Radius = _baseCracData.Radius, Color = _baseCracData.Color, VolumetricFlow = _baseCracData.VolumetricFlow, SupplyTemperature = _baseCracData.SupplyTemperature, }); OnCracsChanged?.Invoke(); } public void RemoveColumn(int index) { if (index < 0 || index >= _columns.Count) return; _columns.RemoveAt(index); OnColumnsChanged?.Invoke(); } public void ChangePreviewRadius(float delta) { PreviewColumnRadius = Mathf.Clamp(PreviewColumnRadius + delta, 0.1f, 2.0f); OnColumnsChanged?.Invoke(); } public void AddDoor(int edgeIndex, float width = 1.2f) { if (State != DrawingState.Closed) { OnError?.Invoke("Doors can only be placed after the polygon is closed."); return; } if (edgeIndex < 0 || edgeIndex >= _vertices.Count) { OnError?.Invoke($"Invalid edge index: {edgeIndex}"); return; } var a = _vertices[edgeIndex]; var b = _vertices[(edgeIndex + 1) % _vertices.Count]; var length = Vector2.Distance(a, b); if (width >= length) { OnError?.Invoke("Door width exceeds edge length."); return; } var offset = (length - width) * 0.5f; _doors.Add(new DoorData { EdgeIndex = edgeIndex, OffsetAlongEdge = offset, Width = width, Color = Color.yellow, Thickness = 6f }); OnDoorsChanged?.Invoke(); } public void RemoveDoor(int index) { if (index < 0 || index >= _doors.Count) return; _doors.RemoveAt(index); OnDoorsChanged?.Invoke(); } public bool TryFinish() { if (State != DrawingState.Closed) { OnError?.Invoke("Polygon must be closed before finishing."); return false; } if (!_validator.Validate(_vertices, out var error)) { OnError?.Invoke(error); return false; } var config = new RoomConfig { PolygonVertices = new List<Vector2>(_vertices), Height = _settings.RoomHeight, BaseTemperature = _settings.BaseTemperature, MinAisleWidth = _settings.MinAisleWidth, Doors = new List<DoorData>(_doors), Columns = new List<ColumnData>(_columns), Cracs = new List<CracData>(_cracs) }; OnRoomBuilt?.Invoke(config); return true; } public int FindNearestEdgeIndex(Vector2 worldPoint, float maxDistMeters = 0.5f) { if (_vertices.Count < 2) return -1; int best = -1; float bestDist = maxDistMeters; for (int i = 0; i < _vertices.Count; i++) { var a = _vertices[i]; var b = _vertices[(i + 1) % _vertices.Count]; var d = PointToSegmentDistance(worldPoint, a, b); if (d < bestDist) { bestDist = d; best = i; } } return best; } private void ClosePolygon() { PreviewCursorWorld = null; SetState(DrawingState.Closed); OnVerticesChanged?.Invoke(); } private bool IsFirstVertex(Vector2 worldPoint) { if (_vertices.Count == 0) return false; var snapped = SnapGrid.Snap(worldPoint, _settings.GridSize, _settings.SnapThreshold); return Vector2.Distance(snapped, _vertices[0]) <= _settings.SnapThreshold; } private void SetState(DrawingState newState) { if (State == newState) return; State = newState; OnStateChanged?.Invoke(newState); } private static float PointToSegmentDistance(Vector2 p, Vector2 a, Vector2 b) { var ab = b - a; var ap = p - a; var t = Mathf.Clamp01(Vector2.Dot(ap, ab) / ab.sqrMagnitude); return Vector2.Distance(p, a + t * ab); } } }