343 lines
11 KiB
C#
343 lines
11 KiB
C#
using Simulator.Data;
|
|
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
public class InitializePopupWindow : MonoBehaviour
|
|
{
|
|
[Header("Prefabs / Roots")]
|
|
[SerializeField] private InitializeLayerView layerPrefab;
|
|
[SerializeField] private InitializePopupCellButton cellPrefab;
|
|
[SerializeField] private Transform layersParent;
|
|
|
|
[Header("Right Panel Controls")]
|
|
[SerializeField] private TMP_Dropdown prefabDropdown;
|
|
[SerializeField] private TMP_InputField countInput; // 숫자 입력
|
|
[SerializeField] private Button applyButton;
|
|
[SerializeField] private Button deleteButton;
|
|
|
|
[Header("Z Layer Toggles (max 2 selected)")]
|
|
[SerializeField] private Toggle z1Toggle;
|
|
[SerializeField] private Toggle z2Toggle;
|
|
// z가 더 늘 수 있으면 Toggle 리스트로 확장 권장(지금은 최대 2층 요구에 맞춰 간단히)
|
|
|
|
[Header("Table")]
|
|
[SerializeField] private Transform tableContent; // row parent
|
|
[SerializeField] private InitializeRow rowPrefab;
|
|
|
|
private ASRSDataClass asrs;
|
|
|
|
// grid lookup
|
|
private readonly Dictionary<(int x, int y, int z), InitializePopupCellButton> cellMap = new();
|
|
|
|
// occupied lookup: each cell -> entry
|
|
private readonly Dictionary<(int x, int y, int z), InitializeEntry> occupied = new();
|
|
|
|
// selection
|
|
private bool dragging;
|
|
private (int x, int y) dragStart;
|
|
private (int x, int y) dragEnd;
|
|
|
|
// highlight diff (성능)
|
|
private readonly HashSet<(int x, int y, int z)> lastHighlighted = new();
|
|
private readonly HashSet<(int x, int y, int z)> newHighlighted = new();
|
|
|
|
// table selection
|
|
private InitializeEntry selectedRowEntry;
|
|
|
|
private void Awake()
|
|
{
|
|
applyButton.onClick.AddListener(ApplySelection);
|
|
deleteButton.onClick.AddListener(DeleteSelectedRow);
|
|
|
|
z1Toggle.onValueChanged.AddListener(_ => OnZToggleChanged());
|
|
z2Toggle.onValueChanged.AddListener(_ => OnZToggleChanged());
|
|
}
|
|
|
|
public void Open(ASRSDataClass asrsData, int sizeX, int sizeY, int sizeZ)
|
|
{
|
|
asrs = asrsData;
|
|
asrs.initialize ??= new List<InitializeEntry>();
|
|
|
|
BuildPrefabDropdown();
|
|
BuildLayerGrids(sizeX, sizeY, sizeZ);
|
|
|
|
RebuildOccupiedIndex();
|
|
RefreshAllCellsFromOccupied();
|
|
RefreshTableFromInitialize();
|
|
|
|
// 초기 상태: 1번 랙만 체크
|
|
z1Toggle.isOn = true;
|
|
z2Toggle.isOn = false;
|
|
OnZToggleChanged();
|
|
|
|
dragging = false;
|
|
deleteButton.interactable = false;
|
|
}
|
|
|
|
private void BuildPrefabDropdown()
|
|
{
|
|
prefabDropdown.ClearOptions();
|
|
var options = new List<string>(PrefabManager.Instance.prefabDict.Keys);
|
|
prefabDropdown.AddOptions(options);
|
|
}
|
|
|
|
private void BuildLayerGrids(int sizeX, int sizeY, int sizeZ)
|
|
{
|
|
// 기존 UI 삭제
|
|
foreach (Transform child in layersParent) Destroy(child.gameObject);
|
|
cellMap.Clear();
|
|
|
|
for (int z = 1; z <= sizeZ; z++)
|
|
{
|
|
var layer = Instantiate(layerPrefab, layersParent);
|
|
layer.SetLayer(z);
|
|
|
|
// y는 화면 상단이 큰 값(이미지처럼)
|
|
for (int y = sizeY; y >= 1; y--)
|
|
for (int x = 1; x <= sizeX; x++)
|
|
{
|
|
var cell = Instantiate(cellPrefab, layer.GridRoot);
|
|
|
|
// Pointer 이벤트 기반 드래그 지원용 컴포넌트 부착
|
|
var trigger = cell.gameObject.GetComponent<InitializeCellPointer>();
|
|
if (trigger == null) trigger = cell.gameObject.AddComponent<InitializeCellPointer>();
|
|
|
|
trigger.Bind(
|
|
cell,
|
|
onDown: OnCellPointerDown,
|
|
onEnter: OnCellPointerEnter,
|
|
onUp: OnCellPointerUp
|
|
);
|
|
|
|
cellMap[(x, y, z)] = cell;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Pointer / Drag Handlers
|
|
private void OnCellPointerDown(InitializePopupCellButton cell)
|
|
{
|
|
dragging = true;
|
|
dragStart = (cell.X, cell.Y);
|
|
dragEnd = (cell.X, cell.Y);
|
|
UpdateHighlights();
|
|
}
|
|
|
|
private void OnCellPointerEnter(InitializePopupCellButton cell)
|
|
{
|
|
if (!dragging) return;
|
|
dragEnd = (cell.X, cell.Y);
|
|
UpdateHighlights();
|
|
}
|
|
|
|
private void OnCellPointerUp()
|
|
{
|
|
if (!dragging) return;
|
|
dragging = false;
|
|
// 드래그 확정 상태 유지(하이라이트 유지)
|
|
UpdateHighlights();
|
|
}
|
|
#endregion
|
|
|
|
private void OnZToggleChanged()
|
|
{
|
|
// 최대 2개만 허용(현재 토글이 2개라 사실상 자동 충족)
|
|
// z가 늘면: 체크 수가 2 초과 시 방금 체크한 토글을 되돌리는 식으로 처리
|
|
|
|
UpdateHighlights(); // z 선택 바뀌면 하이라이트도 즉시 반영
|
|
}
|
|
|
|
private List<int> GetSelectedZLayers()
|
|
{
|
|
var list = new List<int>(2);
|
|
if (z1Toggle.isOn) list.Add(1);
|
|
if (z2Toggle.isOn) list.Add(2);
|
|
return list;
|
|
}
|
|
|
|
private void UpdateHighlights()
|
|
{
|
|
// 선택된 XY rect 계산
|
|
int minX = Mathf.Min(dragStart.x, dragEnd.x);
|
|
int maxX = Mathf.Max(dragStart.x, dragEnd.x);
|
|
int minY = Mathf.Min(dragStart.y, dragEnd.y);
|
|
int maxY = Mathf.Max(dragStart.y, dragEnd.y);
|
|
|
|
var zs = GetSelectedZLayers();
|
|
newHighlighted.Clear();
|
|
|
|
// 새 하이라이트 집합 구성
|
|
foreach (int z in zs)
|
|
{
|
|
for (int y = minY; y <= maxY; y++)
|
|
for (int x = minX; x <= maxX; x++)
|
|
{
|
|
newHighlighted.Add((x, y, z));
|
|
}
|
|
}
|
|
|
|
// diff 적용(전체 리셋 방지)
|
|
foreach (var key in lastHighlighted)
|
|
{
|
|
if (!newHighlighted.Contains(key) && cellMap.TryGetValue(key, out var cell))
|
|
cell.SetHighlight(false);
|
|
}
|
|
|
|
foreach (var key in newHighlighted)
|
|
{
|
|
if (!lastHighlighted.Contains(key) && cellMap.TryGetValue(key, out var cell))
|
|
cell.SetHighlight(true);
|
|
}
|
|
|
|
lastHighlighted.Clear();
|
|
foreach (var k in newHighlighted) lastHighlighted.Add(k);
|
|
}
|
|
|
|
#region Apply / Delete
|
|
private void ApplySelection()
|
|
{
|
|
var zs = GetSelectedZLayers();
|
|
if (zs.Count == 0) return;
|
|
|
|
// prefab
|
|
string prefab = prefabDropdown.options[prefabDropdown.value].text;
|
|
|
|
// count
|
|
int count = 1;
|
|
if (!int.TryParse(countInput.text, out count)) count = 1;
|
|
count = Mathf.Max(1, count);
|
|
|
|
// XY rect
|
|
int minX = Mathf.Min(dragStart.x, dragEnd.x);
|
|
int maxX = Mathf.Max(dragStart.x, dragEnd.x);
|
|
int minY = Mathf.Min(dragStart.y, dragEnd.y);
|
|
int maxY = Mathf.Max(dragStart.y, dragEnd.y);
|
|
|
|
// 1) 중복 검사(선택된 z 각각)
|
|
foreach (int z in zs)
|
|
{
|
|
if (HasOverlap(minX, minY, maxX, maxY, z))
|
|
{
|
|
// 여기서 경고 UI 띄우시면 됩니다(Toast/Popup)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 2) initialize 추가
|
|
// z가 2개이고 연속(1,2)인 경우: 하나의 entry로 z 범위를 담는 것도 가능
|
|
// 단, 삭제/표시가 더 쉬운 구조는 “z마다 entry 하나씩”입니다(권장).
|
|
foreach (int z in zs)
|
|
{
|
|
var entry = new InitializeEntry
|
|
{
|
|
prefab = prefab,
|
|
count = count,
|
|
from_position = new Position { x = minX, y = maxY, z = z }, // y축 방향은 프로젝트 좌표계에 맞춰 조정
|
|
to_position = new Position { x = maxX, y = minY, z = z },
|
|
};
|
|
|
|
asrs.initialize.Add(entry);
|
|
}
|
|
|
|
// 3) 점유 인덱스/그리드/테이블 갱신
|
|
RebuildOccupiedIndex();
|
|
RefreshAllCellsFromOccupied();
|
|
RefreshTableFromInitialize();
|
|
|
|
// 4) 모델 반영(패치 저장 필요 시 여기서)
|
|
// SaveChange(asrs, asrs.initialize, "initialize");
|
|
}
|
|
|
|
private bool HasOverlap(int minX, int minY, int maxX, int maxY, int z)
|
|
{
|
|
for (int y = minY; y <= maxY; y++)
|
|
for (int x = minX; x <= maxX; x++)
|
|
{
|
|
if (occupied.ContainsKey((x, y, z)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void DeleteSelectedRow()
|
|
{
|
|
if (selectedRowEntry == null) return;
|
|
|
|
asrs.initialize.Remove(selectedRowEntry);
|
|
selectedRowEntry = null;
|
|
deleteButton.interactable = false;
|
|
|
|
RebuildOccupiedIndex();
|
|
RefreshAllCellsFromOccupied();
|
|
RefreshTableFromInitialize();
|
|
|
|
// SaveChange(asrs, asrs.initialize, "initialize");
|
|
}
|
|
#endregion
|
|
|
|
#region Occupied / Refresh
|
|
private void RebuildOccupiedIndex()
|
|
{
|
|
occupied.Clear();
|
|
if (asrs.initialize == null) return;
|
|
|
|
foreach (var e in asrs.initialize)
|
|
{
|
|
if (e?.from_position == null || e?.to_position == null) continue;
|
|
|
|
// normalize
|
|
int minX = (int)Mathf.Min(e.from_position.x, e.to_position.x);
|
|
int maxX = (int)Mathf.Max(e.from_position.x, e.to_position.x);
|
|
int minY = (int)Mathf.Min(e.from_position.y, e.to_position.y);
|
|
int maxY = (int)Mathf.Max(e.from_position.y, e.to_position.y);
|
|
int minZ = (int)Mathf.Min(e.from_position.z, e.to_position.z);
|
|
int maxZ = (int)Mathf.Max(e.from_position.z, e.to_position.z);
|
|
|
|
for (int z = minZ; z <= maxZ; z++)
|
|
for (int y = minY; y <= maxY; y++)
|
|
for (int x = minX; x <= maxX; x++)
|
|
{
|
|
occupied[(x, y, z)] = e;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RefreshAllCellsFromOccupied()
|
|
{
|
|
foreach (var kv in cellMap)
|
|
{
|
|
var key = kv.Key;
|
|
var cell = kv.Value;
|
|
|
|
if (occupied.TryGetValue(key, out var entry))
|
|
cell.SetOccupied(entry.prefab);
|
|
else
|
|
cell.SetEmpty();
|
|
}
|
|
}
|
|
|
|
private void RefreshTableFromInitialize()
|
|
{
|
|
foreach (Transform child in tableContent) Destroy(child.gameObject);
|
|
|
|
if (asrs.initialize == null || asrs.initialize.Count == 0)
|
|
return;
|
|
|
|
foreach (var e in asrs.initialize)
|
|
{
|
|
var row = Instantiate(rowPrefab, tableContent);
|
|
row.Bind(e, onSelect: OnRowSelected);
|
|
}
|
|
}
|
|
|
|
private void OnRowSelected(InitializeEntry entry, bool selected)
|
|
{
|
|
selectedRowEntry = selected ? entry : null;
|
|
deleteButton.interactable = selectedRowEntry != null;
|
|
|
|
// 선택된 row의 범위를 하이라이트로 보여주고 싶으면 여기서 lastHighlighted를 해당 entry 범위로 재구성하면 됩니다.
|
|
}
|
|
#endregion
|
|
} |