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 s_SplineElementsCompareSet = new HashSet(); static readonly List s_SplineElementsBuffer = new List(); static readonly HashSet s_PreRectSelectionElements = new HashSet(); public void OnGUI(IReadOnlyList 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 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 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 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 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); } } }