216 lines
8.3 KiB
C#
216 lines
8.3 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
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<ISplineElement> s_SplineElementsCompareSet = new HashSet<ISplineElement>();
|
|
static readonly List<ISplineElement> s_SplineElementsBuffer = new List<ISplineElement>();
|
|
static readonly HashSet<ISplineElement> s_PreRectSelectionElements = new HashSet<ISplineElement>();
|
|
|
|
public void OnGUI(IReadOnlyList<IEditableSpline> paths)
|
|
{
|
|
int id = GUIUtility.GetControlID(FocusType.Passive);
|
|
Event evt = Event.current;
|
|
|
|
switch (evt.GetTypeForControl(id))
|
|
{
|
|
case EventType.Layout:
|
|
if (!Tools.viewToolActive)
|
|
{
|
|
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, paths);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (HandleUtility.nearestControl == id && evt.button == 0)
|
|
{
|
|
m_StartPos = evt.mousePosition;
|
|
m_Rect = new Rect(Vector3.zero, Vector2.zero);
|
|
|
|
BeginSelection(paths);
|
|
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, paths);
|
|
}
|
|
break;
|
|
|
|
case EventType.MouseUp:
|
|
if (GUIUtility.hotControl == id)
|
|
{
|
|
GUIUtility.hotControl = 0;
|
|
evt.Use();
|
|
|
|
EndSelection(m_Rect, paths);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected virtual void BeginSelection(IReadOnlyList<IEditableSpline> paths)
|
|
{
|
|
RefreshSelectionMode();
|
|
m_InitialMode = m_Mode;
|
|
|
|
s_SplineElementsCompareSet.Clear();
|
|
s_SplineElementsBuffer.Clear();
|
|
if (m_Mode == Mode.Replace)
|
|
{
|
|
SplineSelection.Clear();
|
|
s_PreRectSelectionElements.Clear();
|
|
}
|
|
else
|
|
SplineSelection.GetSelectedElements(s_PreRectSelectionElements);
|
|
}
|
|
|
|
protected virtual void UpdateSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
|
|
{
|
|
//Get all elements in rect
|
|
s_SplineElementsBuffer.Clear();
|
|
for (int i = 0; i < paths.Count; ++i)
|
|
{
|
|
IEditableSpline spline = paths[i];
|
|
for (int j = 0; j < spline.knotCount; ++j)
|
|
GetElementSelection(rect, spline, 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, IEditableSpline spline, int index, List<ISplineElement> results)
|
|
{
|
|
var knot = spline.GetKnot(index);
|
|
Vector3 screenSpace = HandleUtility.WorldToGUIPointWithDepth(knot.position);
|
|
|
|
if (screenSpace.z > 0 && rect.Contains(screenSpace))
|
|
results.Add(knot);
|
|
|
|
for(int tangentIndex = 0; tangentIndex < knot.tangentCount; tangentIndex++)
|
|
{
|
|
var tangent = knot.GetTangent(tangentIndex);
|
|
screenSpace = HandleUtility.WorldToGUIPointWithDepth(tangent.position);
|
|
|
|
if (SplineSelectionUtility.IsSelectable(spline, index, tangent) && screenSpace.z > 0 && rect.Contains(screenSpace))
|
|
results.Add(tangent);
|
|
}
|
|
}
|
|
|
|
protected virtual void EndSelection(Rect rect, IReadOnlyList<IEditableSpline> paths)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|