RoboticArms/Library/PackageCache/com.unity.splines@d3e1e500c9a0/Editor/Controls/SplineElementRectSelector.cs
2025-11-17 15:16:36 +07:00

229 lines
8.9 KiB
C#

using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
class SplineElementRectSelector
{
enum Mode
{
None,
Replace,
Add,
Subtract
}
static class Styles
{
public static readonly GUIStyle selectionRect = GUI.skin.FindStyle("selectionRect");
}
Rect m_Rect;
Vector2 m_StartPos;
Mode m_Mode;
Mode m_InitialMode;
static readonly HashSet<ISelectableElement> s_SplineElementsCompareSet = new HashSet<ISelectableElement>();
static readonly List<ISelectableElement> s_SplineElementsBuffer = new List<ISelectableElement>();
static readonly HashSet<ISelectableElement> s_PreRectSelectionElements = new HashSet<ISelectableElement>();
public void OnGUI(IReadOnlyList<SplineInfo> splines)
{
int id = GUIUtility.GetControlID(FocusType.Passive);
Event evt = Event.current;
switch (evt.GetTypeForControl(id))
{
case EventType.Layout:
case EventType.MouseMove:
HandleUtility.AddDefaultControl(id);
if (m_Mode != Mode.None)
{
// If we've started rect select in Add or Subtract modes, then if we were in a Replace
// mode just before (i.e. the shift or action has been released temporarily),
// we need to bring back the pre rect selection elements into current selection.
if (m_InitialMode != Mode.Replace && RefreshSelectionMode())
{
SplineSelection.Clear();
s_SplineElementsCompareSet.Clear();
if (m_Mode != Mode.Replace)
{
foreach (var element in s_PreRectSelectionElements)
SplineSelection.Add(element);
}
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
UpdateSelection(m_Rect, splines);
}
}
break;
case EventType.Repaint:
if (GUIUtility.hotControl == id && m_Rect.size != Vector2.zero)
{
Handles.BeginGUI();
Styles.selectionRect.Draw(m_Rect, GUIContent.none, false, false, false, false);
Handles.EndGUI();
}
break;
case EventType.MouseDown:
if (SplineHandles.ViewToolActive())
return;
if (HandleUtility.nearestControl == id && evt.button == 0)
{
m_StartPos = evt.mousePosition;
m_Rect = new Rect(Vector3.zero, Vector2.zero);
BeginSelection(splines);
GUIUtility.hotControl = id;
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id)
{
m_Rect = GetRectFromPoints(m_StartPos, evt.mousePosition);
evt.Use();
UpdateSelection(m_Rect, splines);
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == id)
{
GUIUtility.hotControl = 0;
evt.Use();
EndSelection(m_Rect, splines);
}
break;
}
}
void BeginSelection(IReadOnlyList<SplineInfo> splines)
{
RefreshSelectionMode();
m_InitialMode = m_Mode;
s_SplineElementsCompareSet.Clear();
s_SplineElementsBuffer.Clear();
if (m_Mode == Mode.Replace)
{
SplineSelection.Clear();
s_PreRectSelectionElements.Clear();
}
else
SplineSelection.GetElements(splines, s_PreRectSelectionElements);
}
void UpdateSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
{
//Get all elements in rect
s_SplineElementsBuffer.Clear();
for (int i = 0; i < splines.Count; ++i)
{
var splineData = splines[i];
for (int j = 0; j < splineData.Spline.Count; ++j)
if(!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(splineData))
GetElementSelection(rect, splineData, j, s_SplineElementsBuffer);
}
foreach (var splineElement in s_SplineElementsBuffer)
{
//Compare current frame buffer with last frame's to find new additions/removals
var wasInRectLastFrame = s_SplineElementsCompareSet.Remove(splineElement);
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
var canAdd = m_Mode == Mode.Replace ? true : !s_PreRectSelectionElements.Contains(splineElement);
if (!wasInRectLastFrame && canAdd)
SplineSelection.Add(splineElement);
}
else if (m_Mode == Mode.Subtract && !wasInRectLastFrame)
{
SplineSelection.Remove(splineElement);
}
}
//Remaining spline elements from last frame are removed from selection (or added if mode is subtract)
foreach (var splineElement in s_SplineElementsCompareSet)
{
if (m_Mode == Mode.Replace || m_Mode == Mode.Add)
{
// If we're in Add mode, don't remove elements that were in select prior to rect selection
if (m_Mode == Mode.Add && s_PreRectSelectionElements.Contains(splineElement))
continue;
SplineSelection.Remove(splineElement);
}
else if (m_Mode == Mode.Subtract && s_PreRectSelectionElements.Contains(splineElement))
SplineSelection.Add(splineElement);
}
//Move current elements buffer to hash set for next frame compare
s_SplineElementsCompareSet.Clear();
foreach (var splineElement in s_SplineElementsBuffer)
s_SplineElementsCompareSet.Add(splineElement);
}
bool RefreshSelectionMode()
{
var modeBefore = m_Mode;
if (Event.current.shift)
m_Mode = Mode.Add;
else if (EditorGUI.actionKey)
m_Mode = Mode.Subtract;
else
m_Mode = Mode.Replace;
// Return true if the mode has changed
return m_Mode != modeBefore;
}
void GetElementSelection(Rect rect, SplineInfo splineInfo, int index, List<ISelectableElement> results)
{
var knot = splineInfo.Spline[index];
var localToWorld = splineInfo.LocalToWorld;
var worldKnot = knot.Transform(localToWorld);
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position);
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(new SelectableKnot(splineInfo, index));
var tangentIn = new SelectableTangent(splineInfo, index, BezierTangent.In);
if (SplineSelectionUtility.IsSelectable(tangentIn))
{
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentIn));
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(tangentIn);
}
var tangentOut = new SelectableTangent(splineInfo, index, BezierTangent.Out);
if (SplineSelectionUtility.IsSelectable(tangentOut))
{
screenSpace = HandleUtility.WorldToGUIPointWithDepth(worldKnot.Position + math.rotate(worldKnot.Rotation, worldKnot.TangentOut));
if (screenSpace.z > 0 && rect.Contains(screenSpace))
results.Add(tangentOut);
}
}
void EndSelection(Rect rect, IReadOnlyList<SplineInfo> splines)
{
m_Mode = m_InitialMode = Mode.None;
}
static Rect GetRectFromPoints(Vector2 a, Vector2 b)
{
Vector2 min = new Vector2(Mathf.Min(a.x, b.x), Mathf.Min(a.y, b.y));
Vector2 max = new Vector2(Mathf.Max(a.x, b.x), Mathf.Max(a.y, b.y));
return new Rect(min, max - min);
}
}
}