123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEngine.Timeline;
- namespace UnityEditor.Timeline
- {
- enum ManipulateEdges
- {
- Left,
- Right,
- Both
- }
- class SnapEngine
- {
- static readonly float k_MagnetInfluenceInPixels = 10.0f;
- class SnapInfo
- {
- public double time { get; set; }
- public bool showSnapHint { get; set; }
- public bool IsInInfluenceZone(double currentTime, WindowState state)
- {
- var pos = state.TimeToPixel(currentTime);
- var magnetPos = state.TimeToPixel(time);
- return Math.Abs(pos - magnetPos) < k_MagnetInfluenceInPixels;
- }
- }
- struct TimeBoundaries
- {
- public TimeBoundaries(double l, double r)
- {
- left = l;
- right = r;
- }
- public readonly double left;
- public readonly double right;
- public TimeBoundaries Translate(double d)
- {
- return new TimeBoundaries(left + d, right + d);
- }
- }
- public static bool displayDebugLayout;
- readonly IAttractable m_Attractable;
- readonly IAttractionHandler m_AttractionHandler;
- readonly ManipulateEdges m_ManipulateEdges;
- readonly WindowState m_State;
- double m_GrabbedTime;
- TimeBoundaries m_GrabbedTimes;
- TimeBoundaries m_CurrentTimes;
- readonly List<SnapInfo> m_Magnets = new List<SnapInfo>();
- bool m_SnapEnabled;
- public SnapEngine(IAttractable attractable, IAttractionHandler attractionHandler, ManipulateEdges manipulateEdges, WindowState state,
- Vector2 mousePosition, IEnumerable<ISnappable> snappables = null)
- {
- m_Attractable = attractable;
- m_ManipulateEdges = manipulateEdges;
- m_AttractionHandler = attractionHandler;
- m_State = state;
- m_CurrentTimes = m_GrabbedTimes = new TimeBoundaries(m_Attractable.start, m_Attractable.end);
- m_GrabbedTime = m_State.PixelToTime(mousePosition.x);
- // Add Time zero as Magnet
- AddMagnet(0.0, true, state);
- // Add current Time as Magnet
- // case1157280 only add current time as magnet if visible
- if (TimelineWindow.instance.currentMode.ShouldShowTimeCursor(m_State))
- AddMagnet(state.editSequence.time, true, state);
- if (state.IsEditingASubTimeline())
- {
- // Add start and end of evaluable range as Magnets
- // This includes the case where the master timeline has a fixed length
- var range = state.editSequence.GetEvaluableRange();
- AddMagnet(range.start, true, state);
- AddMagnet(range.end, true, state);
- }
- else if (state.masterSequence.asset.durationMode == TimelineAsset.DurationMode.FixedLength)
- {
- // Add end sequence Time as Magnet
- AddMagnet(state.masterSequence.asset.duration, true, state);
- }
- if (snappables == null)
- snappables = GetVisibleSnappables(m_State);
- foreach (var snappable in snappables)
- {
- if (!attractable.ShouldSnapTo(snappable))
- continue;
- var edges = snappable.SnappableEdgesFor(attractable, manipulateEdges);
- foreach (var edge in edges)
- AddMagnet(edge.time, edge.showSnapHint, state);
- }
- }
- public static IEnumerable<ISnappable> GetVisibleSnappables(WindowState state)
- {
- Rect rect = TimelineWindow.instance.state.timeAreaRect;
- rect.height = float.MaxValue;
- return state.spacePartitioner.GetItemsInArea<ISnappable>(rect).ToArray();
- }
- void AddMagnet(double magnetTime, bool showSnapHint, WindowState state)
- {
- var magnet = m_Magnets.FirstOrDefault(m => m.time.Equals(magnetTime));
- if (magnet == null)
- {
- if (IsMagnetInShownArea(magnetTime, state))
- m_Magnets.Add(new SnapInfo { time = magnetTime, showSnapHint = showSnapHint });
- }
- else
- {
- magnet.showSnapHint |= showSnapHint;
- }
- }
- static bool IsMagnetInShownArea(double time, WindowState state)
- {
- var shownArea = state.timeAreaShownRange;
- return time >= shownArea.x && time <= shownArea.y;
- }
- SnapInfo GetMagnetAt(double time)
- {
- return m_Magnets.FirstOrDefault(m => m.time.Equals(time));
- }
- SnapInfo ClosestMagnet(double time)
- {
- SnapInfo candidate = null;
- var min = double.MaxValue;
- foreach (var magnetInfo in m_Magnets)
- {
- var m = Math.Abs(magnetInfo.time - time);
- if (m < min)
- {
- candidate = magnetInfo;
- min = m;
- }
- }
- if (candidate != null && candidate.IsInInfluenceZone(time, m_State))
- return candidate;
- return null;
- }
- public void Snap(Vector2 currentMousePosition, EventModifiers modifiers)
- {
- var d = m_State.PixelToTime(currentMousePosition.x) - m_GrabbedTime;
- m_CurrentTimes = m_GrabbedTimes.Translate(d);
- bool isLeft = m_ManipulateEdges == ManipulateEdges.Left || m_ManipulateEdges == ManipulateEdges.Both;
- bool isRight = m_ManipulateEdges == ManipulateEdges.Right || m_ManipulateEdges == ManipulateEdges.Both;
- bool attracted = false;
- m_SnapEnabled = modifiers == ManipulatorsUtils.actionModifier ? !m_State.edgeSnaps : m_State.edgeSnaps;
- if (m_SnapEnabled)
- {
- SnapInfo leftActiveMagnet = null;
- SnapInfo rightActiveMagnet = null;
- if (isLeft)
- leftActiveMagnet = ClosestMagnet(m_CurrentTimes.left);
- if (isRight)
- rightActiveMagnet = ClosestMagnet(m_CurrentTimes.right);
- if (leftActiveMagnet != null || rightActiveMagnet != null)
- {
- attracted = true;
- bool leftAttraction = false;
- if (rightActiveMagnet == null)
- {
- // Attracted by a left magnet only.
- leftAttraction = true;
- }
- else
- {
- if (leftActiveMagnet != null)
- {
- // Attracted by both magnets, choose the closest one.
- var leftDistance = Math.Abs(leftActiveMagnet.time - m_CurrentTimes.left);
- var rightDistance = Math.Abs(rightActiveMagnet.time - m_CurrentTimes.right);
- leftAttraction = leftDistance <= rightDistance;
- }
- // else, Attracted by right magnet only
- }
- if (leftAttraction)
- {
- m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Left, leftActiveMagnet.time);
- }
- else
- {
- m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.Right, rightActiveMagnet.time);
- }
- }
- }
- if (!attracted)
- {
- var time = isLeft ? m_CurrentTimes.left : m_CurrentTimes.right;
- time = m_State.SnapToFrameIfRequired(time);
- m_AttractionHandler.OnAttractedEdge(m_Attractable, m_ManipulateEdges, AttractedEdge.None, time);
- }
- }
- public void OnGUI(bool showLeft = true, bool showRight = true)
- {
- if (displayDebugLayout)
- {
- // Display Magnet influence zone
- foreach (var m in m_Magnets)
- {
- var window = TimelineWindow.instance;
- var rect = new Rect(m_State.TimeToPixel(m.time) - k_MagnetInfluenceInPixels, window.state.timeAreaRect.yMax, 2f * k_MagnetInfluenceInPixels, m_State.windowHeight);
- EditorGUI.DrawRect(rect, new Color(1f, 0f, 0f, 0.4f));
- }
- // Display Cursor position
- var mousePos = Event.current.mousePosition;
- var time = m_State.PixelToTime(mousePos.x);
- var p = new Vector2(m_State.TimeToPixel(time), TimelineWindow.instance.state.timeAreaRect.yMax);
- var s = new Vector2(1f, m_State.windowHeight);
- EditorGUI.DrawRect(new Rect(p, s), Color.blue);
- p = new Vector2(m_State.TimeToPixel(m_GrabbedTime), TimelineWindow.instance.state.timeAreaRect.yMax);
- s = new Vector2(1f, m_State.windowHeight);
- EditorGUI.DrawRect(new Rect(p, s), Color.red);
- p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.left), TimelineWindow.instance.state.timeAreaRect.yMax);
- s = new Vector2(1f, m_State.windowHeight);
- EditorGUI.DrawRect(new Rect(p, s), Color.yellow);
- p = new Vector2(m_State.TimeToPixel(m_CurrentTimes.right), TimelineWindow.instance.state.timeAreaRect.yMax);
- EditorGUI.DrawRect(new Rect(p, s), Color.yellow);
- }
- if (m_SnapEnabled)
- {
- if (showLeft)
- DrawMagnetLineAt(m_Attractable.start);
- if (showRight)
- DrawMagnetLineAt(m_Attractable.end);
- }
- }
- void DrawMagnetLineAt(double time)
- {
- var magnet = GetMagnetAt(time);
- if (magnet != null && magnet.showSnapHint)
- Graphics.DrawLineAtTime(m_State, magnet.time, Color.white);
- }
- }
- }
|