Singularity/Library/PackageCache/com.unity.2d.spriteshape@7.0.7/Editor/AngleRangeController.cs

401 lines
12 KiB
C#
Raw Normal View History

2024-05-06 14:45:45 -04:00
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;
namespace UnityEditor.U2D.SpriteShape
{
public interface IAngleRangeCache
{
List<AngleRange> angleRanges { get; }
int selectedIndex { get; set; }
float previewAngle { get; set; }
void RegisterUndo(string name);
}
public class AngleRangeController
{
public event Action selectionChanged = () => { };
public IAngleRangeCache cache { get; set; }
public IAngleRangeView view { get; set; }
public float angleOffset { get; set; }
public float radius { get; set; }
public Rect rect { get; set; }
public bool snap { get; set; }
public Color gradientMin { get; set; }
public Color gradientMid { get; set; }
public Color gradientMax { get; set; }
public AngleRange selectedAngleRange
{
get
{
Debug.Assert(cache != null);
AngleRange angleRange = null;
if (cache.selectedIndex >= 0 && cache.selectedIndex < cache.angleRanges.Count)
return cache.angleRanges[cache.selectedIndex];
return angleRange;
}
}
private AngleRange hoveredAngleRange
{
get
{
Debug.Assert(cache != null);
Debug.Assert(view != null);
AngleRange angleRange = null;
if (view.hoveredRangeIndex >= 0 && view.hoveredRangeIndex < cache.angleRanges.Count)
return cache.angleRanges[view.hoveredRangeIndex];
return angleRange;
}
}
public void OnGUI()
{
Debug.Assert(cache != null);
view.SetupLayout(rect, angleOffset, radius);
DoAngleRanges();
HandleSelectAngleRange();
HandleCreateRange();
HandlePreviewSelector();
HandleRemoveRange();
}
private void DoAngleRanges()
{
var removeInvalid = false;
if (view.IsActionTriggering(AngleRangeAction.ModifyRange))
cache.RegisterUndo("Modify Range");
if (view.IsActionFinishing(AngleRangeAction.ModifyRange))
removeInvalid = true;
var index = 0;
foreach (var angleRange in cache.angleRanges)
{
var start = angleRange.start;
var end = angleRange.end;
var isSelected = selectedAngleRange != null && selectedAngleRange == angleRange;
if (view.DoAngleRange(index, rect, radius, angleOffset, ref start, ref end, snap, !isSelected, gradientMin, gradientMid, gradientMax))
SetRange(angleRange, start, end);
++index;
}
if (removeInvalid)
RemoveInvalidRanges();
}
public void RemoveInvalidRanges()
{
var toDelete = new List<AngleRange>();
foreach (var angleRange in cache.angleRanges)
{
var start = angleRange.start;
var end = angleRange.end;
if (start >= end)
toDelete.Add(angleRange);
}
foreach (var angleRange in toDelete)
cache.angleRanges.Remove(angleRange);
if (toDelete.Count > 0)
{
SetSelectedIndexFromPreviewAngle();
view.Repaint();
}
}
private void HandleSelectAngleRange()
{
int newSelected;
if (view.DoSelectAngleRange(cache.selectedIndex, out newSelected))
{
cache.RegisterUndo("Select Angle Range");
cache.previewAngle = view.GetAngleFromPosition(rect, angleOffset);
SelectIndex(newSelected);
}
if (view.IsActionActive(AngleRangeAction.SelectRange))
{
if (hoveredAngleRange == null)
return;
view.DrawAngleRangeOutline(rect, hoveredAngleRange.start, hoveredAngleRange.end, angleOffset, radius);
}
}
private void HandleCreateRange()
{
if (!view.IsActionActive(AngleRangeAction.CreateRange))
return;
var angle = view.GetAngleFromPosition(rect, angleOffset);
var start = 0f;
var end = 0f;
var canCreate = GetNewRangeBounds(angle, out start, out end);
if (canCreate && view.DoCreateRange(rect, radius, angleOffset, start, end))
{
CreateRangeAtAngle(angle);
cache.previewAngle = angle;
SetSelectedIndexFromPreviewAngle();
}
}
private void HandlePreviewSelector()
{
if (view.IsActionTriggering(AngleRangeAction.ModifySelector))
cache.RegisterUndo("Set Preview Angle");
float newAngle;
if (view.DoSelector(rect, angleOffset, radius, cache.previewAngle, out newAngle))
{
if (selectedAngleRange == null)
newAngle = Mathf.Repeat(newAngle + 180f, 360f) - 180f;
cache.previewAngle = newAngle;
SetSelectedIndexFromPreviewAngle();
}
}
private void SetSelectedIndexFromPreviewAngle()
{
var index = SpriteShapeEditorUtility.GetRangeIndexFromAngle(cache.angleRanges, cache.previewAngle);
SelectIndex(index);
}
private void SelectIndex(int index)
{
view.RequestFocusIndex(index);
if (cache.selectedIndex == index)
return;
cache.selectedIndex = index;
selectionChanged();
}
private void ClampPreviewAngle(float start, float end, float prevStart, float prevEnd)
{
var angle = cache.previewAngle;
if (prevStart < start)
{
var a1 = Mathf.Repeat(angle - prevStart, 360f);
var a2 = Mathf.Repeat(angle - start, 360f);
if (a1 < a2)
angle = Mathf.Min(start, end);
}
else if (prevEnd > end)
{
var a1 = Mathf.Repeat(prevEnd - angle, 360f);
var a2 = Mathf.Repeat(end - angle, 360f);
if (a1 < a2)
angle = Mathf.Max(start, end);
}
cache.previewAngle = angle;
}
private void HandleRemoveRange()
{
if (view.DoRemoveRange())
{
cache.RegisterUndo("Remove Range");
cache.angleRanges.RemoveAt(cache.selectedIndex);
SelectIndex(-1);
}
}
public void CreateRange()
{
CreateRangeAtAngle(cache.previewAngle);
}
private void CreateRangeAtAngle(float angle)
{
var start = 0f;
var end = 0f;
if (GetNewRangeBounds(angle, out start, out end))
{
cache.RegisterUndo("Create Range");
var angleRange = new AngleRange();
angleRange.start = start;
angleRange.end = end;
cache.angleRanges.Add(angleRange);
ValidateRange(angleRange);
SetSelectedIndexFromPreviewAngle();
}
}
public void SetRange(AngleRange angleRange, float start, float end)
{
var prevStart = angleRange.start;
var prevEnd = angleRange.end;
angleRange.start = start;
angleRange.end = end;
ValidateRange(angleRange, prevStart, prevEnd);
if (angleRange == selectedAngleRange)
ClampPreviewAngle(start, end, prevStart, prevEnd);
}
private bool GetNewRangeBounds(float angle, out float emptyRangeStart, out float emptyRangeEnd)
{
angle = Mathf.Repeat(angle + 180f, 360f) - 180f;
emptyRangeStart = float.MinValue;
emptyRangeEnd = float.MaxValue;
if (GetAngleRangeAt(angle) != null)
return false;
FindMinMax(out emptyRangeEnd, out emptyRangeStart);
if (angle < emptyRangeStart)
emptyRangeStart -= 360f;
if (angle > emptyRangeEnd)
emptyRangeEnd += 360f;
foreach (var angleRange in cache.angleRanges)
{
var start = angleRange.start;
var end = angleRange.end;
if (angle > end)
emptyRangeStart = Mathf.Max(emptyRangeStart, end);
if (angle < start)
emptyRangeEnd = Mathf.Min(emptyRangeEnd, start);
}
var rangeLength = emptyRangeEnd - emptyRangeStart;
if (rangeLength > 90f)
{
emptyRangeStart = Mathf.Max(angle - 45f, emptyRangeStart);
emptyRangeEnd = Mathf.Min(angle + 45f, emptyRangeEnd);
}
return true;
}
private AngleRange GetAngleRangeAt(float angle)
{
foreach (var angleRange in cache.angleRanges)
{
var start = angleRange.start;
var end = angleRange.end;
var range = end - start;
var angle2 = Mathf.Repeat(angle - start, 360f);
if (angle2 >= 0f && angle2 <= range)
return angleRange;
}
return null;
}
private void FindMinMax(out float min, out float max)
{
min = float.MaxValue;
max = float.MinValue;
foreach (var angleRange in cache.angleRanges)
{
min = Mathf.Min(angleRange.start, min);
max = Mathf.Max(angleRange.end, max);
}
}
private void ValidateRange(AngleRange range)
{
ValidateRange(range, range.start, range.end);
}
private void ValidateRange(AngleRange angleRange, float prevStart, float prevEnd)
{
var start = angleRange.start;
var end = angleRange.end;
foreach (var otherRange in cache.angleRanges)
{
var otherStart = otherRange.start;
var otherEnd = otherRange.end;
if (otherRange == angleRange)
{
if ((start > 180f && end > 180f) || (start < -180f && end < -180f))
{
start = Mathf.Repeat(start + 180f, 360f) - 180f;
end = Mathf.Repeat(end + 180f, 360f) - 180f;
}
otherStart = start + 360f;
otherEnd = end - 360f;
}
ValidateRangeStartEnd(ref start, ref end, prevStart, prevEnd, otherStart, otherEnd);
}
angleRange.start = start;
angleRange.end = end;
}
private void ValidateRangeStartEnd(ref float start, ref float end, float prevStart, float prevEnd, float otherStart, float otherEnd)
{
var min = Mathf.Min(start, otherStart);
var max = Mathf.Max(end, otherEnd);
start -= min;
end -= min;
otherStart -= min;
otherEnd -= min;
prevStart -= min;
prevEnd -= min;
if (prevEnd != end)
end = Mathf.Clamp(end, start, otherStart >= start ? otherStart : 360f);
start += min - max;
end += min - max;
otherStart += min - max;
otherEnd += min - max;
prevStart += min - max;
prevEnd += min - max;
if (prevStart != start)
start = Mathf.Clamp(start, otherEnd <= end ? otherEnd : -360f, end);
start += max;
end += max;
}
}
}