12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEditorInternal;
- using UnityEngine.U2D.Sprites;
- namespace UnityEditor.U2D.Sprites
- {
- internal interface IShapeEditorFactory
- {
- ShapeEditor CreateShapeEditor();
- }
- internal class ShapeEditorFactory : IShapeEditorFactory
- {
- public ShapeEditor CreateShapeEditor()
- {
- return new ShapeEditor(new GUIUtilitySystem(), new EventSystem());
- }
- }
- internal class ShapeEditor
- {
- public delegate float DistanceToControl(Vector3 pos, Quaternion rotation, float handleSize);
- internal enum SelectionType { Normal, Additive, Subtractive }
- internal enum Tool { Edit, Create, Break }
- internal enum TangentMode { Linear, Continuous, Broken }
- // --- To use this class in your editor, you need to implement these: (see ShapeEditorTests for example)
- // --- Data
- public Func<int, Vector3> GetPointPosition = i => Vector3.zero;
- public Action<int, Vector3> SetPointPosition = (i, p) => {};
- public Func<int, Vector3> GetPointLTangent = i => Vector3.zero;
- public Action<int, Vector3> SetPointLTangent = (i, p) => {};
- public Func<int, Vector3> GetPointRTangent = i => Vector3.zero;
- public Action<int, Vector3> SetPointRTangent = (i, p) => {};
- public Func<int, TangentMode> GetTangentMode = i => TangentMode.Linear;
- public Action<int, TangentMode> SetTangentMode = (i, m) => {};
- public Action<int, Vector3> InsertPointAt = (i, p) => {};
- public Action<int> RemovePointAt = i => {};
- public Func<int> GetPointsCount = () => 0;
- // --- Transforms
- public Func<Vector2, Vector3> ScreenToLocal = i => i;
- public Func<Vector3, Vector2> LocalToScreen = i => i;
- public Func<Matrix4x4> LocalToWorldMatrix = () => Matrix4x4.identity;
- // --- Distance functions
- public Func<DistanceToControl> DistanceToRectangle = () => HandleUtility.DistanceToRectangle;
- public Func<DistanceToControl> DistanceToDiamond = () => HandleUtility.DistanceToDiamond;
- public Func<DistanceToControl> DistanceToCircle = () => DistanceToCircleInternal;
- // --- Other
- public Action Repaint = () => {};
- public Action RecordUndo = () => {};
- public Func<Vector3, Vector3> Snap = i => i;
- public Action<Bounds> Frame = b => {};
- public Action<int> OnPointClick = i => {};
- public Func<bool> OpenEnded = () => false;
- public Func<float> GetHandleSize = () => 5f;
- // --- END
- public ITexture2D lineTexture { get; set; }
- public int activePoint { get; set; }
- public HashSet<int> selectedPoints { get { return m_Selection.indices; } }
- public bool inEditMode { get; set; }
- public int activeEdge { get { return m_ActiveEdge; } set { m_ActiveEdge = value; } }
- // Shape editor needs to reset its state on next OnSceneGUI. Reset can't be called immediately elsewhere, because we need to also reset local sceneview state like GUIUtility.keyboardControl
- public bool delayedReset { set { m_DelayedReset = value; } }
- private ShapeEditorSelection m_Selection;
- private Vector2 m_MousePositionLastMouseDown;
- private int m_ActivePointOnLastMouseDown = -1;
- private int m_NewPointIndex = -1;
- private Vector3 m_EdgeDragStartMousePosition;
- private Vector3 m_EdgeDragStartP0;
- private Vector3 m_EdgeDragStartP1;
- private bool m_NewPointDragFinished;
- private int m_ActiveEdge = -1;
- private bool m_DelayedReset = false;
- private HashSet<ShapeEditor> m_ShapeEditorListeners = new HashSet<ShapeEditor>();
- private ShapeEditorRectSelectionTool m_RectSelectionTool;
- private int m_MouseClosestEdge = -1;
- private float m_MouseClosestEdgeDist = float.MaxValue;
- private int m_ShapeEditorRegisteredTo = 0;
- private int m_ShapeEditorUpdateDone = 0;
- private static Color handleOutlineColor { get; set; }
- private static Color handleFillColor { get; set; }
- private Quaternion handleMatrixrotation { get { return Quaternion.LookRotation(handles.matrix.GetColumn(2), handles.matrix.GetColumn(1)); } }
- private IGUIUtility guiUtility { get; set; }
- private IEventSystem eventSystem { get; set; }
- private IEvent currentEvent { get; set; }
- private IGL glSystem { get; set; }
- private IHandles handles { get; set; }
- private Dictionary<DrawBatchDataKey, List<Vector3>> m_DrawBatch;
- private Vector3[][] m_EdgePoints;
- enum ColorEnum
- {
- EUnselected,
- EUnselectedHovered,
- ESelected,
- ESelectedHovered
- }
- private static readonly Color[] k_OutlineColor = new[]
- {
- Color.gray,
- Color.white,
- new Color(34f / 255f, 171f / 255f, 1f), // #22abff
- Color.white
- };
- static readonly Color[] k_FillColor = new[]
- {
- Color.white,
- new Color(131f / 255f, 220f / 255f, 1f), // #83dcff
- new Color(34f / 255f, 171f / 255f, 1f), // #22abff
- new Color(34f / 255f, 171f / 255f, 1f) // #22abff
- };
- private static readonly Color k_TangentColor = new Color(34f / 255f, 171f / 255f, 1f); // #22abff
- private static readonly Color k_TangentColorAlternative = new Color(131f / 255f, 220f / 255f, 1f); // #83dcff
- private const float k_EdgeHoverDistance = 9f;
- private const float k_EdgeWidth = 2f;
- private const float k_ActiveEdgeWidth = 6f;
- private const float k_MinExistingPointDistanceForInsert = 20f;
- private readonly int k_CreatorID;
- private readonly int k_EdgeID;
- private readonly int k_RightTangentID;
- private readonly int k_LeftTangentID;
- private const int k_BezierPatch = 40;
- class DrawBatchDataKey
- {
- public Color color { get; private set; }
- public int glMode { get; private set; }
- private int m_Hash;
- public DrawBatchDataKey(Color c , int mode)
- {
- color = c;
- glMode = mode;
- m_Hash = glMode ^ (color.GetHashCode() << 2);
- }
- public override int GetHashCode()
- {
- return m_Hash;
- }
- public override bool Equals(object obj)
- {
- var key = obj as DrawBatchDataKey;
- if (ReferenceEquals(key, null))
- return false;
- return m_Hash == obj.GetHashCode();
- }
- }
- public ShapeEditor(IGUIUtility gu, IEventSystem es)
- {
- m_Selection = new ShapeEditorSelection(this);
- guiUtility = gu;
- eventSystem = es;
- k_CreatorID = guiUtility.GetPermanentControlID();
- k_EdgeID = guiUtility.GetPermanentControlID();
- k_RightTangentID = guiUtility.GetPermanentControlID();
- k_LeftTangentID = guiUtility.GetPermanentControlID();
- glSystem = GLSystem.GetSystem();
- handles = HandlesSystem.GetSystem();
- }
- public void SetRectSelectionTool(ShapeEditorRectSelectionTool sers)
- {
- if (m_RectSelectionTool != null)
- {
- m_RectSelectionTool.RectSelect -= SelectPointsInRect;
- m_RectSelectionTool.ClearSelection -= ClearSelectedPoints;
- }
- m_RectSelectionTool = sers;
- m_RectSelectionTool.RectSelect += SelectPointsInRect;
- m_RectSelectionTool.ClearSelection += ClearSelectedPoints;
- }
- public void OnDisable()
- {
- m_RectSelectionTool.RectSelect -= SelectPointsInRect;
- m_RectSelectionTool.ClearSelection -= ClearSelectedPoints;
- m_RectSelectionTool = null;
- }
- private void PrepareDrawBatch()
- {
- if (currentEvent.type == EventType.Repaint)
- {
- m_DrawBatch = new Dictionary<DrawBatchDataKey, List<Vector3>>();
- }
- }
- private void DrawBatch()
- {
- if (currentEvent.type == EventType.Repaint)
- {
- HandleUtility.ApplyWireMaterial();
- glSystem.PushMatrix();
- glSystem.MultMatrix(handles.matrix);
- foreach (var drawBatch in m_DrawBatch)
- {
- glSystem.Begin(drawBatch.Key.glMode);
- glSystem.Color(drawBatch.Key.color);
- foreach (var t in drawBatch.Value)
- {
- glSystem.Vertex(t);
- }
- glSystem.End();
- }
- glSystem.PopMatrix();
- }
- }
- List<Vector3> GetDrawBatchList(DrawBatchDataKey key)
- {
- List<Vector3> data = null;
- if (!m_DrawBatch.ContainsKey(key))
- {
- m_DrawBatch.Add(key, new List<Vector3>());
- }
- data = m_DrawBatch[key];
- return data;
- }
- public void OnGUI()
- {
- DelayedResetIfNecessary();
- currentEvent = eventSystem.current;
- if (currentEvent.type == EventType.MouseDown)
- StoreMouseDownState();
- var oldColor = handles.color;
- var oldMatrix = handles.matrix;
- handles.matrix = LocalToWorldMatrix();
- PrepareDrawBatch();
- Edges();
- if (inEditMode)
- {
- Framing();
- Tangents();
- Points();
- }
- DrawBatch();
- handles.color = oldColor;
- handles.matrix = oldMatrix;
- OnShapeEditorUpdateDone();
- foreach (var se in m_ShapeEditorListeners)
- se.OnShapeEditorUpdateDone();
- }
- private void Framing()
- {
- if (currentEvent.commandName == EventCommandNames.FrameSelected && m_Selection.Count > 0)
- {
- switch (currentEvent.type)
- {
- case EventType.ExecuteCommand:
- Bounds bounds = new Bounds(GetPointPosition(selectedPoints.First()), Vector3.zero);
- foreach (var index in selectedPoints)
- bounds.Encapsulate(GetPointPosition(index));
- Frame(bounds);
- goto case EventType.ValidateCommand;
- case EventType.ValidateCommand:
- currentEvent.Use();
- break;
- }
- }
- }
- void PrepareEdgePointList()
- {
- if (m_EdgePoints == null)
- {
- var total = this.GetPointsCount();
- int loopCount = OpenEnded() ? total - 1 : total;
- m_EdgePoints = new Vector3[loopCount][];
- int index = mod(total - 1, loopCount);
- for (int loop = mod(index + 1, total); loop < total; index = loop, ++loop)
- {
- var position0 = this.GetPointPosition(index);
- var position1 = this.GetPointPosition(loop);
- if (GetTangentMode(index) == TangentMode.Linear && GetTangentMode(loop) == TangentMode.Linear)
- {
- m_EdgePoints[index] = new[] { position0, position1 };
- }
- else
- {
- var tangent0 = GetPointRTangent(index) + position0;
- var tangent1 = GetPointLTangent(loop) + position1;
- m_EdgePoints[index] = handles.MakeBezierPoints(position0, position1, tangent0, tangent1, k_BezierPatch);
- }
- }
- }
- }
- float DistancePointEdge(Vector3 point, Vector3[] edge)
- {
- float result = float.MaxValue;
- int index = edge.Length - 1;
- for (int nextIndex = 0; nextIndex < edge.Length; index = nextIndex, nextIndex++)
- {
- float dist = HandleUtility.DistancePointLine(point, edge[index], edge[nextIndex]);
- if (dist < result)
- result = dist;
- }
- return result;
- }
- private float GetMouseClosestEdgeDistance()
- {
- var mousePosition = ScreenToLocal(eventSystem.current.mousePosition);
- var total = this.GetPointsCount();
- if (m_MouseClosestEdge == -1 && total > 0)
- {
- PrepareEdgePointList();
- m_MouseClosestEdgeDist = float.MaxValue;
- int loopCount = OpenEnded() ? total - 1 : total;
- for (int loop = 0; loop < loopCount; loop++)
- {
- var dist = DistancePointEdge(mousePosition, m_EdgePoints[loop]);
- if (dist < m_MouseClosestEdgeDist)
- {
- m_MouseClosestEdge = loop;
- m_MouseClosestEdgeDist = dist;
- }
- }
- }
- if (guiUtility.hotControl == k_CreatorID || guiUtility.hotControl == k_EdgeID)
- return float.MinValue;
- return m_MouseClosestEdgeDist;
- }
- public void Edges()
- {
- var otherClosestEdgeDistance = float.MaxValue;
- if (m_ShapeEditorListeners.Count > 0)
- otherClosestEdgeDistance = (from se in m_ShapeEditorListeners select se.GetMouseClosestEdgeDistance()).Max();
- float edgeDistance = GetMouseClosestEdgeDistance();
- bool closestEdgeHighlight = EdgeDragModifiersActive() && edgeDistance < k_EdgeHoverDistance && edgeDistance < otherClosestEdgeDistance;
- if (currentEvent.type == EventType.Repaint)
- {
- Color handlesOldColor = handles.color;
- PrepareEdgePointList();
- var total = this.GetPointsCount();
- int loopCount = OpenEnded() ? total - 1 : total;
- for (int loop = 0; loop < loopCount; loop++)
- {
- Color edgeColor = loop == m_ActiveEdge ? Color.yellow : Color.white;
- float edgeWidth = loop == m_ActiveEdge || (m_MouseClosestEdge == loop && closestEdgeHighlight) ? k_ActiveEdgeWidth : k_EdgeWidth;
- handles.color = edgeColor;
- handles.DrawAAPolyLine(lineTexture, edgeWidth, m_EdgePoints[loop]);
- }
- handles.color = handlesOldColor;
- }
- if (inEditMode)
- {
- if (otherClosestEdgeDistance > edgeDistance)
- {
- var farEnoughFromExistingPoints = MouseDistanceToPoint(FindClosestPointToMouse()) > k_MinExistingPointDistanceForInsert;
- var farEnoughtFromActiveTangents = MouseDistanceToClosestTangent() > k_MinExistingPointDistanceForInsert;
- var farEnoughFromExisting = farEnoughFromExistingPoints && farEnoughtFromActiveTangents;
- var hoveringEdge = m_MouseClosestEdgeDist < k_EdgeHoverDistance;
- var handleEvent = hoveringEdge && farEnoughFromExisting && !m_RectSelectionTool.isSelecting;
- if (GUIUtility.hotControl == k_EdgeID || EdgeDragModifiersActive() && handleEvent)
- HandleEdgeDragging(m_MouseClosestEdge);
- else if (GUIUtility.hotControl == k_CreatorID || (currentEvent.modifiers == EventModifiers.None && handleEvent))
- HandlePointInsertToEdge(m_MouseClosestEdge, m_MouseClosestEdgeDist);
- }
- }
- if (guiUtility.hotControl != k_CreatorID && m_NewPointIndex != -1)
- {
- m_NewPointDragFinished = true;
- guiUtility.keyboardControl = 0;
- m_NewPointIndex = -1;
- }
- if (guiUtility.hotControl != k_EdgeID && m_ActiveEdge != -1)
- {
- m_ActiveEdge = -1;
- }
- }
- public void Tangents()
- {
- if (activePoint < 0 || m_Selection.Count > 1 || GetTangentMode(activePoint) == TangentMode.Linear)
- return;
- var evt = eventSystem.current;
- var p = this.GetPointPosition(activePoint);
- var lt = this.GetPointLTangent(activePoint);
- var rt = this.GetPointRTangent(activePoint);
- var isHot = (guiUtility.hotControl == k_RightTangentID || guiUtility.hotControl == k_LeftTangentID);
- var allZero = lt.sqrMagnitude == 0 && rt.sqrMagnitude == 0;
- if (isHot || !allZero)
- {
- var m = this.GetTangentMode(activePoint);
- var mouseDown = evt.GetTypeForControl(k_RightTangentID) == EventType.MouseDown || evt.GetTypeForControl(k_LeftTangentID) == EventType.MouseDown;
- var mouseUp = evt.GetTypeForControl(k_RightTangentID) == EventType.MouseUp || evt.GetTypeForControl(k_LeftTangentID) == EventType.MouseUp;
- var nlt = DoTangent(p, p + lt, k_LeftTangentID, activePoint, k_TangentColor);
- var nrt = DoTangent(p, p + rt, k_RightTangentID, activePoint, GetTangentMode(activePoint) == TangentMode.Broken ? k_TangentColorAlternative : k_TangentColor);
- var changed = (nlt != lt || nrt != rt);
- allZero = nlt.sqrMagnitude == 0 && nrt.sqrMagnitude == 0;
- // if indeed we are dragging one of the handles and we just shift+mousedown, toggle the point. this happens even without change!
- if (isHot && mouseDown)
- {
- var nm = ((int)m + 1) % 3;
- m = (TangentMode)nm;
- //m = m == PointMode.Continuous ? PointMode.Broken : PointMode.Continuous;
- this.SetTangentMode(activePoint, m);
- }
- // make it broken when both tangents are zero
- if (mouseUp && allZero)
- {
- this.SetTangentMode(activePoint, TangentMode.Linear);
- changed = true;
- }
- // only do something when it's changed
- if (changed)
- {
- RecordUndo();
- this.SetPointLTangent(activePoint, nlt);
- this.SetPointRTangent(activePoint, nrt);
- RefreshTangents(activePoint, guiUtility.hotControl == k_RightTangentID);
- Repaint();
- }
- }
- }
- public void Points()
- {
- var wantsDelete =
- (UnityEngine.Event.current.type == EventType.ExecuteCommand || UnityEngine.Event.current.type == EventType.ValidateCommand)
- && (UnityEngine.Event.current.commandName == EventCommandNames.SoftDelete || UnityEngine.Event.current.commandName == EventCommandNames.Delete);
- for (int i = 0; i < this.GetPointsCount(); i++)
- {
- if (i == m_NewPointIndex)
- continue;
- var p0 = this.GetPointPosition(i);
- var id = guiUtility.GetControlID(5353, FocusType.Keyboard);
- var mouseDown = currentEvent.GetTypeForControl(id) == EventType.MouseDown;
- var mouseUp = currentEvent.GetTypeForControl(id) == EventType.MouseUp;
- EditorGUI.BeginChangeCheck();
- if (currentEvent.type == EventType.Repaint)
- {
- ColorEnum c = GetColorForPoint(i, id);
- handleOutlineColor = k_OutlineColor[(int)c];
- handleFillColor = k_FillColor[(int)c];
- }
- var np = p0;
- var hotControlBefore = guiUtility.hotControl;
- if (!currentEvent.alt || guiUtility.hotControl == id)
- np = DoSlider(id, p0, Vector3.up, Vector3.right, GetHandleSizeForPoint(i), GetCapForPoint(i));
- else if (currentEvent.type == EventType.Repaint)
- GetCapForPoint(i)(id, p0, Quaternion.LookRotation(Vector3.forward, Vector3.up), GetHandleSizeForPoint(i), currentEvent.type);
- var hotcontrolAfter = guiUtility.hotControl;
- if (mouseUp && hotControlBefore == id && hotcontrolAfter == 0 && (currentEvent.mousePosition == m_MousePositionLastMouseDown) && !currentEvent.shift)
- HandlePointClick(i);
- if (EditorGUI.EndChangeCheck())
- {
- RecordUndo();
- np = Snap(np);
- MoveSelections(np - p0);
- }
- if (guiUtility.hotControl == id && mouseDown && !m_Selection.Contains(i))
- {
- SelectPoint(i, currentEvent.shift ? SelectionType.Additive : SelectionType.Normal);
- Repaint();
- }
- if (m_NewPointDragFinished && activePoint == i && id != -1)
- {
- guiUtility.keyboardControl = id;
- m_NewPointDragFinished = false;
- }
- }
- if (wantsDelete)
- {
- if (currentEvent.type == EventType.ValidateCommand)
- {
- currentEvent.Use();
- }
- else if (currentEvent.type == EventType.ExecuteCommand)
- {
- RecordUndo();
- DeleteSelections();
- currentEvent.Use();
- }
- }
- }
- public void HandlePointInsertToEdge(int closestEdge, float closestEdgeDist)
- {
- var pointCreatorIsHot = GUIUtility.hotControl == k_CreatorID;
- Vector3 position = pointCreatorIsHot ? GetPointPosition(m_NewPointIndex) : FindClosestPointOnEdge(closestEdge, ScreenToLocal(currentEvent.mousePosition), 100);
- EditorGUI.BeginChangeCheck();
- handleFillColor = k_FillColor[(int)ColorEnum.ESelectedHovered];
- handleOutlineColor = k_OutlineColor[(int)ColorEnum.ESelectedHovered];
- if (!pointCreatorIsHot)
- {
- handleFillColor = handleFillColor.AlphaMultiplied(0.5f);
- handleOutlineColor = handleOutlineColor.AlphaMultiplied(0.5f);
- }
- int hotControlBefore = GUIUtility.hotControl;
- var newPosition = DoSlider(k_CreatorID, position, Vector3.up, Vector3.right, GetHandleSizeForPoint(closestEdge), RectCap);
- if (hotControlBefore != k_CreatorID && GUIUtility.hotControl == k_CreatorID)
- {
- RecordUndo();
- m_NewPointIndex = NextIndex(closestEdge, GetPointsCount());
- InsertPointAt(m_NewPointIndex, newPosition);
- SelectPoint(m_NewPointIndex, SelectionType.Normal);
- }
- else if (EditorGUI.EndChangeCheck())
- {
- RecordUndo();
- newPosition = Snap(newPosition);
- MoveSelections(newPosition - position);
- }
- }
- private void HandleEdgeDragging(int closestEdge)
- {
- switch (currentEvent.type)
- {
- case EventType.MouseDown:
- m_ActiveEdge = closestEdge;
- m_EdgeDragStartP0 = GetPointPosition(m_ActiveEdge);
- m_EdgeDragStartP1 = GetPointPosition(NextIndex(m_ActiveEdge, GetPointsCount()));
- if (currentEvent.shift)
- {
- RecordUndo();
- InsertPointAt(m_ActiveEdge + 1, m_EdgeDragStartP0);
- InsertPointAt(m_ActiveEdge + 2, m_EdgeDragStartP1);
- m_ActiveEdge++;
- }
- m_EdgeDragStartMousePosition = ScreenToLocal(currentEvent.mousePosition);
- GUIUtility.hotControl = k_EdgeID;
- currentEvent.Use();
- break;
- case EventType.MouseDrag:
- // This can happen when MouseDown event happen when dragging a point instead of line
- if (GUIUtility.hotControl == k_EdgeID)
- {
- RecordUndo();
- Vector3 mousePos = ScreenToLocal(currentEvent.mousePosition);
- Vector3 delta = mousePos - m_EdgeDragStartMousePosition;
- Vector3 position = GetPointPosition(m_ActiveEdge);
- Vector3 newPosition = m_EdgeDragStartP0 + delta;
- newPosition = Snap(newPosition);
- Vector3 snappedDelta = newPosition - position;
- var i0 = m_ActiveEdge;
- var i1 = NextIndex(m_ActiveEdge, GetPointsCount());
- SetPointPosition(m_ActiveEdge, GetPointPosition(i0) + snappedDelta);
- SetPointPosition(i1, GetPointPosition(i1) + snappedDelta);
- currentEvent.Use();
- }
- break;
- case EventType.MouseUp:
- if (GUIUtility.hotControl == k_EdgeID)
- {
- m_ActiveEdge = -1;
- GUIUtility.hotControl = 0;
- currentEvent.Use();
- }
- break;
- }
- }
- Vector3 DoTangent(Vector3 p0, Vector3 t0, int cid, int pointIndex, Color color)
- {
- var handleSize = GetHandleSizeForPoint(pointIndex);
- var tangentSize = GetTangentSizeForPoint(pointIndex);
- handles.color = color;
- float distance = HandleUtility.DistanceToCircle(t0, tangentSize);
- if (lineTexture != null)
- handles.DrawAAPolyLine(lineTexture, new Vector3[] {p0, t0});
- else
- handles.DrawLine(p0, t0);
- handleOutlineColor = distance > 0f ? color : k_OutlineColor[(int)ColorEnum.ESelectedHovered];
- handleFillColor = color;
- var delta = DoSlider(cid, t0, Vector3.up, Vector3.right, tangentSize, GetCapForTangent(pointIndex)) - p0;
- return delta.magnitude < handleSize ? Vector3.zero : delta;
- }
- public void HandlePointClick(int pointIndex)
- {
- if (m_Selection.Count > 1)
- {
- m_Selection.SelectPoint(pointIndex, SelectionType.Normal);
- }
- else if (!currentEvent.control && !currentEvent.shift && m_ActivePointOnLastMouseDown == activePoint)
- {
- OnPointClick(pointIndex);
- }
- }
- public void CycleTangentMode()
- {
- TangentMode oldMode = GetTangentMode(activePoint);
- TangentMode newMode = GetNextTangentMode(oldMode);
- SetTangentMode(activePoint, newMode);
- RefreshTangentsAfterModeChange(activePoint, oldMode, newMode);
- }
- public static TangentMode GetNextTangentMode(TangentMode current)
- {
- return (TangentMode)((((int)current) + 1) % Enum.GetValues(typeof(TangentMode)).Length);
- }
- public void RefreshTangentsAfterModeChange(int pointIndex, TangentMode oldMode, TangentMode newMode)
- {
- if (oldMode != TangentMode.Linear && newMode == TangentMode.Linear)
- {
- SetPointLTangent(pointIndex, Vector3.zero);
- SetPointRTangent(pointIndex, Vector3.zero);
- }
- if (newMode == TangentMode.Continuous)
- {
- if (oldMode == TangentMode.Broken)
- {
- SetPointRTangent(pointIndex, GetPointLTangent(pointIndex) * -1f);
- }
- if (oldMode == TangentMode.Linear)
- {
- FromAllZeroToTangents(pointIndex);
- }
- }
- }
- private ColorEnum GetColorForPoint(int pointIndex, int handleID)
- {
- bool hovered = MouseDistanceToPoint(pointIndex) <= 0f;
- bool selected = m_Selection.Contains(pointIndex);
- if (hovered && selected || GUIUtility.hotControl == handleID)
- return ColorEnum.ESelectedHovered;
- if (hovered)
- return ColorEnum.EUnselectedHovered;
- if (selected)
- return ColorEnum.ESelected;
- return ColorEnum.EUnselected;
- }
- private void FromAllZeroToTangents(int pointIndex)
- {
- Vector3 p0 = GetPointPosition(pointIndex);
- int prevPointIndex = pointIndex > 0 ? pointIndex - 1 : GetPointsCount() - 1;
- Vector3 lt = (GetPointPosition(prevPointIndex) - p0) * .33f;
- Vector3 rt = -lt;
- const float maxScreenLen = 100f;
- float ltScreenLen = (LocalToScreen(p0) - LocalToScreen(p0 + lt)).magnitude;
- float rtScreenLen = (LocalToScreen(p0) - LocalToScreen(p0 + rt)).magnitude;
- lt *= Mathf.Min(maxScreenLen / ltScreenLen, 1f);
- rt *= Mathf.Min(maxScreenLen / rtScreenLen, 1f);
- this.SetPointLTangent(pointIndex, lt);
- this.SetPointRTangent(pointIndex, rt);
- }
- private Handles.CapFunction GetCapForTangent(int index)
- {
- if (GetTangentMode(index) == TangentMode.Continuous)
- return CircleCap;
- return DiamondCap;
- }
- private DistanceToControl GetDistanceFuncForTangent(int index)
- {
- if (GetTangentMode(index) == TangentMode.Continuous)
- return DistanceToCircle();
- return HandleUtility.DistanceToDiamond;
- }
- private Handles.CapFunction GetCapForPoint(int index)
- {
- switch (GetTangentMode(index))
- {
- case TangentMode.Broken:
- return DiamondCap;
- case TangentMode.Continuous:
- return CircleCap;
- case TangentMode.Linear:
- return RectCap;
- }
- return DiamondCap;
- }
- private static float DistanceToCircleInternal(Vector3 position, Quaternion rotation, float size)
- {
- return HandleUtility.DistanceToCircle(position, size);
- }
- private float GetHandleSizeForPoint(int index)
- {
- return Camera.current != null ? HandleUtility.GetHandleSize(GetPointPosition(index)) * 0.075f : GetHandleSize();
- }
- private float GetTangentSizeForPoint(int index)
- {
- return GetHandleSizeForPoint(index) * 0.8f;
- }
- private void RefreshTangents(int index, bool rightIsActive)
- {
- var m = GetTangentMode(index);
- var lt = GetPointLTangent(index);
- var rt = GetPointRTangent(index);
- // mirror the change on the other tangent for continuous
- if (m == TangentMode.Continuous)
- {
- if (rightIsActive)
- {
- lt = -rt;
- var mag = lt.magnitude;
- lt = lt.normalized * mag;
- }
- else
- {
- rt = -lt;
- var mag = rt.magnitude;
- rt = rt.normalized * mag;
- }
- }
- this.SetPointLTangent(activePoint, lt);
- this.SetPointRTangent(activePoint, rt);
- }
- private void StoreMouseDownState()
- {
- m_MousePositionLastMouseDown = currentEvent.mousePosition;
- m_ActivePointOnLastMouseDown = activePoint;
- }
- private void DelayedResetIfNecessary()
- {
- if (m_DelayedReset)
- {
- guiUtility.hotControl = 0;
- guiUtility.keyboardControl = 0;
- m_Selection.Clear();
- activePoint = -1;
- m_DelayedReset = false;
- }
- }
- public Vector3 FindClosestPointOnEdge(int edgeIndex, Vector3 position, int iterations)
- {
- float step = 1f / iterations;
- float closestDistance = float.MaxValue;
- float closestDistanceIndex = edgeIndex;
- for (float a = 0f; a <= 1f; a += step)
- {
- Vector3 pos = GetPositionByIndex(edgeIndex + a);
- float distance = (position - pos).sqrMagnitude;
- if (distance < closestDistance)
- {
- closestDistance = distance;
- closestDistanceIndex = edgeIndex + a;
- }
- }
- return GetPositionByIndex(closestDistanceIndex);
- }
- private Vector3 GetPositionByIndex(float index)
- {
- int indexA = Mathf.FloorToInt(index);
- int indexB = NextIndex(indexA, GetPointsCount());
- float subPosition = index - indexA;
- return GetPoint(GetPointPosition(indexA), GetPointPosition(indexB), GetPointRTangent(indexA), GetPointLTangent(indexB), subPosition);
- }
- private static Vector3 GetPoint(Vector3 startPosition, Vector3 endPosition, Vector3 startTangent, Vector3 endTangent, float t)
- {
- t = Mathf.Clamp01(t);
- float a = 1f - t;
- return a * a * a * startPosition + 3f * a * a * t * (startPosition + startTangent) + 3f * a * t * t * (endPosition + endTangent) + t * t * t * endPosition;
- }
- private int FindClosestPointToMouse()
- {
- Vector3 mousePos = ScreenToLocal(currentEvent.mousePosition);
- return FindClosestPointIndex(mousePos);
- }
- private float MouseDistanceToClosestTangent()
- {
- if (activePoint < 0)
- return float.MaxValue;
- var lt = GetPointLTangent(activePoint);
- var rt = GetPointRTangent(activePoint);
- if (lt.sqrMagnitude == 0f && rt.sqrMagnitude == 0f)
- return float.MaxValue;
- var p = GetPointPosition(activePoint);
- var tangentSize = GetTangentSizeForPoint(activePoint);
- return Mathf.Min(
- HandleUtility.DistanceToRectangle(p + lt, Quaternion.identity, tangentSize),
- HandleUtility.DistanceToRectangle(p + rt, Quaternion.identity, tangentSize)
- );
- }
- private int FindClosestPointIndex(Vector3 position)
- {
- float closestDistance = float.MaxValue;
- int closestIndex = -1;
- for (int i = 0; i < this.GetPointsCount(); i++)
- {
- var p0 = this.GetPointPosition(i);
- var distance = (p0 - position).sqrMagnitude;
- if (distance < closestDistance)
- {
- closestIndex = i;
- closestDistance = distance;
- }
- }
- return closestIndex;
- }
- private DistanceToControl GetDistanceFuncForPoint(int index)
- {
- switch (GetTangentMode(index))
- {
- case TangentMode.Broken:
- return DistanceToDiamond();
- case TangentMode.Continuous:
- return DistanceToCircle();
- case TangentMode.Linear:
- return DistanceToRectangle();
- }
- return DistanceToRectangle();
- }
- private float MouseDistanceToPoint(int index)
- {
- switch (GetTangentMode(index))
- {
- case TangentMode.Broken:
- return HandleUtility.DistanceToDiamond(GetPointPosition(index), Quaternion.identity, GetHandleSizeForPoint(index));
- case TangentMode.Linear:
- return HandleUtility.DistanceToRectangle(GetPointPosition(index), Quaternion.identity, GetHandleSizeForPoint(index));
- case TangentMode.Continuous:
- return HandleUtility.DistanceToCircle(GetPointPosition(index), GetHandleSizeForPoint(index));
- }
- return float.MaxValue;
- }
- private bool EdgeDragModifiersActive()
- {
- return currentEvent.modifiers == EventModifiers.Control;
- }
- private static Vector3 DoSlider(int id, Vector3 position, Vector3 slide1, Vector3 slide2, float s, Handles.CapFunction cap)
- {
- return Slider2D.Do(id, position, Vector3.zero, Vector3.Cross(slide1, slide2), slide1, slide2, s, cap, Vector2.zero, false);
- }
- public void RectCap(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
- {
- if (eventType == EventType.Layout || eventType == EventType.MouseMove)
- {
- HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
- }
- else if (eventType == EventType.Repaint)
- {
- Vector3 normal = handles.matrix.GetColumn(2);
- Vector3 projectedUp = (ProjectPointOnPlane(normal, position, position + Vector3.up) - position).normalized;
- Quaternion localRotation = Quaternion.LookRotation(handles.matrix.GetColumn(2), projectedUp);
- Vector3 sideways = localRotation * Vector3.right * size;
- Vector3 up = localRotation * Vector3.up * size;
- List<Vector3> list = GetDrawBatchList(new DrawBatchDataKey(handleFillColor, GL.TRIANGLES));
- list.Add(position + sideways + up);
- list.Add(position + sideways - up);
- list.Add(position - sideways - up);
- list.Add(position - sideways - up);
- list.Add(position - sideways + up);
- list.Add(position + sideways + up);
- list = GetDrawBatchList(new DrawBatchDataKey(handleOutlineColor, GL.LINES));
- list.Add(position + sideways + up);
- list.Add(position + sideways - up);
- list.Add(position + sideways - up);
- list.Add(position - sideways - up);
- list.Add(position - sideways - up);
- list.Add(position - sideways + up);
- list.Add(position - sideways + up);
- list.Add(position + sideways + up);
- }
- }
- public void CircleCap(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
- {
- if (eventType == EventType.Layout || eventType == EventType.MouseMove)
- {
- HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
- }
- else if (eventType == EventType.Repaint)
- {
- Vector3 forward = handleMatrixrotation * rotation * Vector3.forward;
- Vector3 tangent = Vector3.Cross(forward, Vector3.up);
- if (tangent.sqrMagnitude < .001f)
- tangent = Vector3.Cross(forward, Vector3.right);
- Vector3[] points = new Vector3[60];
- handles.SetDiscSectionPoints(points, position, forward, tangent, 360f, size);
- List<Vector3> list = GetDrawBatchList(new DrawBatchDataKey(handleFillColor, GL.TRIANGLES));
- for (int i = 1; i < points.Length; i++)
- {
- list.Add(position);
- list.Add(points[i]);
- list.Add(points[i - 1]);
- }
- list = GetDrawBatchList(new DrawBatchDataKey(handleOutlineColor, GL.LINES));
- for (int i = 0; i < points.Length - 1; i++)
- {
- list.Add(points[i]);
- list.Add(points[i + 1]);
- }
- }
- }
- public void DiamondCap(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
- {
- if (eventType == EventType.Layout || eventType == EventType.MouseMove)
- {
- HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(position, size * 0.5f));
- }
- else if (eventType == EventType.Repaint)
- {
- Vector3 normal = handles.matrix.GetColumn(2);
- Vector3 projectedUp = (ProjectPointOnPlane(normal, position, position + Vector3.up) - position).normalized;
- Quaternion localRotation = Quaternion.LookRotation(handles.matrix.GetColumn(2), projectedUp);
- Vector3 sideways = localRotation * Vector3.right * size * 1.25f;
- Vector3 up = localRotation * Vector3.up * size * 1.25f;
- List<Vector3> list = GetDrawBatchList(new DrawBatchDataKey(handleFillColor, GL.TRIANGLES));
- list.Add(position - up);
- list.Add(position + sideways);
- list.Add(position - sideways);
- list.Add(position - sideways);
- list.Add(position + up);
- list.Add(position + sideways);
- list = GetDrawBatchList(new DrawBatchDataKey(handleOutlineColor, GL.LINES));
- list.Add(position + sideways);
- list.Add(position - up);
- list.Add(position - up);
- list.Add(position - sideways);
- list.Add(position - sideways);
- list.Add(position + up);
- list.Add(position + up);
- list.Add(position + sideways);
- }
- }
- private static int NextIndex(int index, int total)
- {
- return mod(index + 1, total);
- }
- private static int PreviousIndex(int index, int total)
- {
- return mod(index - 1, total);
- }
- private static int mod(int x, int m)
- {
- int r = x % m;
- return r < 0 ? r + m : r;
- }
- private static Vector3 ProjectPointOnPlane(Vector3 planeNormal, Vector3 planePoint, Vector3 point)
- {
- planeNormal.Normalize();
- var distance = -Vector3.Dot(planeNormal.normalized, (point - planePoint));
- return point + planeNormal * distance;
- }
- public void RegisterToShapeEditor(ShapeEditor se)
- {
- ++m_ShapeEditorRegisteredTo;
- se.m_ShapeEditorListeners.Add(this);
- }
- public void UnregisterFromShapeEditor(ShapeEditor se)
- {
- --m_ShapeEditorRegisteredTo;
- se.m_ShapeEditorListeners.Remove(this);
- }
- private void OnShapeEditorUpdateDone()
- {
- // When all the ShapeEditor we are interested in
- // has completed their update, we reset our internal values
- ++m_ShapeEditorUpdateDone;
- if (m_ShapeEditorUpdateDone >= m_ShapeEditorRegisteredTo)
- {
- m_ShapeEditorUpdateDone = 0;
- m_MouseClosestEdge = -1;
- m_MouseClosestEdgeDist = float.MaxValue;
- m_EdgePoints = null;
- }
- }
- private void ClearSelectedPoints()
- {
- selectedPoints.Clear();
- activePoint = -1;
- }
- private void SelectPointsInRect(Rect r, SelectionType st)
- {
- var localRect = EditorGUIExt.FromToRect(ScreenToLocal(r.min), ScreenToLocal(r.max));
- m_Selection.RectSelect(localRect, st);
- }
- private void DeleteSelections()
- {
- foreach (var se in m_ShapeEditorListeners)
- se.m_Selection.DeleteSelection();
- m_Selection.DeleteSelection();
- }
- private void MoveSelections(Vector2 distance)
- {
- foreach (var se in m_ShapeEditorListeners)
- se.m_Selection.MoveSelection(distance);
- m_Selection.MoveSelection(distance);
- }
- private void SelectPoint(int index, SelectionType st)
- {
- if (st == SelectionType.Normal)
- {
- foreach (var se in m_ShapeEditorListeners)
- se.ClearSelectedPoints();
- }
- m_Selection.SelectPoint(index, st);
- }
- }
- }
|