From a10eea6e4ce647061813519d5b0ea496f29495b9 Mon Sep 17 00:00:00 2001
From: leonard Wu <364452445@qq.com>
Date: 星期四, 09 八月 2018 09:47:08 +0800
Subject: [PATCH] 同步最新svn内容

---
 Assets/Plugins/PostProcessing/Editor/Utils/CurveEditor.cs | 1694 +++++++++++++++++++++++++++++-----------------------------
 1 files changed, 847 insertions(+), 847 deletions(-)

diff --git a/Assets/Plugins/PostProcessing/Editor/Utils/CurveEditor.cs b/Assets/Plugins/PostProcessing/Editor/Utils/CurveEditor.cs
index ee8f1c1..f0021d6 100644
--- a/Assets/Plugins/PostProcessing/Editor/Utils/CurveEditor.cs
+++ b/Assets/Plugins/PostProcessing/Editor/Utils/CurveEditor.cs
@@ -1,847 +1,847 @@
-using System;
-using System.Collections.Generic;
-using UnityEngine;
-
-namespace UnityEditor.PostProcessing
-{
-    public sealed class CurveEditor
-    {
-        #region Enums
-
-        enum EditMode
-        {
-            None,
-            Moving,
-            TangentEdit
-        }
-
-        enum Tangent
-        {
-            In,
-            Out
-        }
-        #endregion
-
-        #region Structs
-        public struct Settings
-        {
-            public Rect bounds;
-            public RectOffset padding;
-            public Color selectionColor;
-            public float curvePickingDistance;
-            public float keyTimeClampingDistance;
-
-            public static Settings defaultSettings
-            {
-                get
-                {
-                    return new Settings
-                    {
-                        bounds = new Rect(0f, 0f, 1f, 1f),
-                        padding = new RectOffset(10, 10, 10, 10),
-                        selectionColor = Color.yellow,
-                        curvePickingDistance = 6f,
-                        keyTimeClampingDistance = 1e-4f
-                    };
-                }
-            }
-        }
-
-        public struct CurveState
-        {
-            public bool visible;
-            public bool editable;
-            public uint minPointCount;
-            public float zeroKeyConstantValue;
-            public Color color;
-            public float width;
-            public float handleWidth;
-            public bool showNonEditableHandles;
-            public bool onlyShowHandlesOnSelection;
-            public bool loopInBounds;
-
-            public static CurveState defaultState
-            {
-                get
-                {
-                    return new CurveState
-                    {
-                        visible = true,
-                        editable = true,
-                        minPointCount = 2,
-                        zeroKeyConstantValue = 0f,
-                        color = Color.white,
-                        width = 2f,
-                        handleWidth = 2f,
-                        showNonEditableHandles = true,
-                        onlyShowHandlesOnSelection = false,
-                        loopInBounds = false
-                    };
-                }
-            }
-        }
-
-        public struct Selection
-        {
-            public SerializedProperty curve;
-            public int keyframeIndex;
-            public Keyframe? keyframe;
-
-            public Selection(SerializedProperty curve, int keyframeIndex, Keyframe? keyframe)
-            {
-                this.curve = curve;
-                this.keyframeIndex = keyframeIndex;
-                this.keyframe = keyframe;
-            }
-        }
-
-        internal struct MenuAction
-        {
-            internal SerializedProperty curve;
-            internal int index;
-            internal Vector3 position;
-
-            internal MenuAction(SerializedProperty curve)
-            {
-                this.curve = curve;
-                this.index = -1;
-                this.position = Vector3.zero;
-            }
-
-            internal MenuAction(SerializedProperty curve, int index)
-            {
-                this.curve = curve;
-                this.index = index;
-                this.position = Vector3.zero;
-            }
-
-            internal MenuAction(SerializedProperty curve, Vector3 position)
-            {
-                this.curve = curve;
-                this.index = -1;
-                this.position = position;
-            }
-        }
-        #endregion
-
-        #region Fields & properties
-        public Settings settings { get; private set; }
-
-        Dictionary<SerializedProperty, CurveState> m_Curves;
-        Rect m_CurveArea;
-
-        SerializedProperty m_SelectedCurve;
-        int m_SelectedKeyframeIndex = -1;
-
-        EditMode m_EditMode = EditMode.None;
-        Tangent m_TangentEditMode;
-
-        bool m_Dirty;
-        #endregion
-
-        #region Constructors & destructors
-        public CurveEditor()
-            : this(Settings.defaultSettings)
-        {}
-
-        public CurveEditor(Settings settings)
-        {
-            this.settings = settings;
-            m_Curves = new Dictionary<SerializedProperty, CurveState>();
-        }
-
-        #endregion
-
-        #region Public API
-        public void Add(params SerializedProperty[] curves)
-        {
-            foreach (var curve in curves)
-                Add(curve, CurveState.defaultState);
-        }
-
-        public void Add(SerializedProperty curve)
-        {
-            Add(curve, CurveState.defaultState);
-        }
-
-        public void Add(SerializedProperty curve, CurveState state)
-        {
-            // Make sure the property is in fact an AnimationCurve
-            var animCurve = curve.animationCurveValue;
-            if (animCurve == null)
-                throw new ArgumentException("curve");
-
-            if (m_Curves.ContainsKey(curve))
-                Debug.LogWarning("Curve has already been added to the editor");
-
-            m_Curves.Add(curve, state);
-        }
-
-        public void Remove(SerializedProperty curve)
-        {
-            m_Curves.Remove(curve);
-        }
-
-        public void RemoveAll()
-        {
-            m_Curves.Clear();
-        }
-
-        public CurveState GetCurveState(SerializedProperty curve)
-        {
-            CurveState state;
-            if (!m_Curves.TryGetValue(curve, out state))
-                throw new KeyNotFoundException("curve");
-
-            return state;
-        }
-
-        public void SetCurveState(SerializedProperty curve, CurveState state)
-        {
-            if (!m_Curves.ContainsKey(curve))
-                throw new KeyNotFoundException("curve");
-
-            m_Curves[curve] = state;
-        }
-
-        public Selection GetSelection()
-        {
-            Keyframe? key = null;
-            if (m_SelectedKeyframeIndex > -1)
-            {
-                var curve = m_SelectedCurve.animationCurveValue;
-
-                if (m_SelectedKeyframeIndex >= curve.length)
-                    m_SelectedKeyframeIndex = -1;
-                else
-                    key = curve[m_SelectedKeyframeIndex];
-            }
-
-            return new Selection(m_SelectedCurve, m_SelectedKeyframeIndex, key);
-        }
-
-        public void SetKeyframe(SerializedProperty curve, int keyframeIndex, Keyframe keyframe)
-        {
-            var animCurve = curve.animationCurveValue;
-            SetKeyframe(animCurve, keyframeIndex, keyframe);
-            SaveCurve(curve, animCurve);
-        }
-
-        public bool OnGUI(Rect rect)
-        {
-            if (Event.current.type == EventType.Repaint)
-                m_Dirty = false;
-
-            GUI.BeginClip(rect);
-            {
-                var area = new Rect(Vector2.zero, rect.size);
-                m_CurveArea = settings.padding.Remove(area);
-
-                foreach (var curve in m_Curves)
-                    OnCurveGUI(area, curve.Key, curve.Value);
-
-                OnGeneralUI(area);
-            }
-            GUI.EndClip();
-
-            return m_Dirty;
-        }
-
-        #endregion
-
-        #region UI & events
-
-        void OnCurveGUI(Rect rect, SerializedProperty curve, CurveState state)
-        {
-            // Discard invisible curves
-            if (!state.visible)
-                return;
-
-            var animCurve = curve.animationCurveValue;
-            var keys = animCurve.keys;
-            var length = keys.Length;
-
-            // Curve drawing
-            // Slightly dim non-editable curves
-            var color = state.color;
-            if (!state.editable)
-                color.a *= 0.5f;
-
-            Handles.color = color;
-            var bounds = settings.bounds;
-
-            if (length == 0)
-            {
-                var p1 = CurveToCanvas(new Vector3(bounds.xMin, state.zeroKeyConstantValue));
-                var p2 = CurveToCanvas(new Vector3(bounds.xMax, state.zeroKeyConstantValue));
-                Handles.DrawAAPolyLine(state.width, p1, p2);
-            }
-            else if (length == 1)
-            {
-                var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value));
-                var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[0].value));
-                Handles.DrawAAPolyLine(state.width, p1, p2);
-            }
-            else
-            {
-                var prevKey = keys[0];
-                for (int k = 1; k < length; k++)
-                {
-                    var key = keys[k];
-                    var pts = BezierSegment(prevKey, key);
-
-                    if (float.IsInfinity(prevKey.outTangent) || float.IsInfinity(key.inTangent))
-                    {
-                        var s = HardSegment(prevKey, key);
-                        Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
-                    }
-                    else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
-
-                    prevKey = key;
-                }
-
-                // Curve extents & loops
-                if (keys[0].time > bounds.xMin)
-                {
-                    if (state.loopInBounds)
-                    {
-                        var p1 = keys[length - 1];
-                        p1.time -= settings.bounds.width;
-                        var p2 = keys[0];
-                        var pts = BezierSegment(p1, p2);
-
-                        if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent))
-                        {
-                            var s = HardSegment(p1, p2);
-                            Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
-                        }
-                        else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
-                    }
-                    else
-                    {
-                        var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value));
-                        var p2 = CurveToCanvas(keys[0]);
-                        Handles.DrawAAPolyLine(state.width, p1, p2);
-                    }
-                }
-
-                if (keys[length - 1].time < bounds.xMax)
-                {
-                    if (state.loopInBounds)
-                    {
-                        var p1 = keys[length - 1];
-                        var p2 = keys[0];
-                        p2.time += settings.bounds.width;
-                        var pts = BezierSegment(p1, p2);
-
-                        if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent))
-                        {
-                            var s = HardSegment(p1, p2);
-                            Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
-                        }
-                        else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
-                    }
-                    else
-                    {
-                        var p1 = CurveToCanvas(keys[length - 1]);
-                        var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[length - 1].value));
-                        Handles.DrawAAPolyLine(state.width, p1, p2);
-                    }
-                }
-            }
-
-            // Make sure selection is correct (undo can break it)
-            bool isCurrentlySelectedCurve = curve == m_SelectedCurve;
-
-            if (isCurrentlySelectedCurve && m_SelectedKeyframeIndex >= length)
-                m_SelectedKeyframeIndex = -1;
-
-            // Handles & keys
-            for (int k = 0; k < length; k++)
-            {
-                bool isCurrentlySelectedKeyframe = k == m_SelectedKeyframeIndex;
-                var e = Event.current;
-
-                var pos = CurveToCanvas(keys[k]);
-                var hitRect = new Rect(pos.x - 8f, pos.y - 8f, 16f, 16f);
-                var offset = isCurrentlySelectedCurve
-                    ? new RectOffset(5, 5, 5, 5)
-                    : new RectOffset(6, 6, 6, 6);
-
-                var outTangent = pos + CurveTangentToCanvas(keys[k].outTangent).normalized * 40f;
-                var inTangent = pos - CurveTangentToCanvas(keys[k].inTangent).normalized * 40f;
-                var inTangentHitRect = new Rect(inTangent.x - 7f, inTangent.y - 7f, 14f, 14f);
-                var outTangentHitrect = new Rect(outTangent.x - 7f, outTangent.y - 7f, 14f, 14f);
-
-                // Draw
-                if (state.showNonEditableHandles)
-                {
-                    if (e.type == EventType.repaint)
-                    {
-                        var selectedColor = (isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
-                            ? settings.selectionColor
-                            : state.color;
-
-                        // Keyframe
-                        EditorGUI.DrawRect(offset.Remove(hitRect), selectedColor);
-
-                        // Tangents
-                        if (isCurrentlySelectedCurve && (!state.onlyShowHandlesOnSelection || (state.onlyShowHandlesOnSelection && isCurrentlySelectedKeyframe)))
-                        {
-                            Handles.color = selectedColor;
-
-                            if (k > 0 || state.loopInBounds)
-                            {
-                                Handles.DrawAAPolyLine(state.handleWidth, pos, inTangent);
-                                EditorGUI.DrawRect(offset.Remove(inTangentHitRect), selectedColor);
-                            }
-
-                            if (k < length - 1 || state.loopInBounds)
-                            {
-                                Handles.DrawAAPolyLine(state.handleWidth, pos, outTangent);
-                                EditorGUI.DrawRect(offset.Remove(outTangentHitrect), selectedColor);
-                            }
-                        }
-                    }
-                }
-
-                // Events
-                if (state.editable)
-                {
-                    // Keyframe move
-                    if (m_EditMode == EditMode.Moving && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
-                    {
-                        EditMoveKeyframe(animCurve, keys, k);
-                    }
-
-                    // Tangent editing
-                    if (m_EditMode == EditMode.TangentEdit && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
-                    {
-                        bool alreadyBroken = !(Mathf.Approximately(keys[k].inTangent, keys[k].outTangent) || (float.IsInfinity(keys[k].inTangent) && float.IsInfinity(keys[k].outTangent)));
-                        EditMoveTangent(animCurve, keys, k, m_TangentEditMode, e.shift || !(alreadyBroken || e.control));
-                    }
-
-                    // Keyframe selection & context menu
-                    if (e.type == EventType.mouseDown && rect.Contains(e.mousePosition))
-                    {
-                        if (hitRect.Contains(e.mousePosition))
-                        {
-                            if (e.button == 0)
-                            {
-                                SelectKeyframe(curve, k);
-                                m_EditMode = EditMode.Moving;
-                                e.Use();
-                            }
-                            else if (e.button == 1)
-                            {
-                                // Keyframe context menu
-                                var menu = new GenericMenu();
-                                menu.AddItem(new GUIContent("Delete Key"), false, (x) =>
-                                {
-                                    var action = (MenuAction)x;
-                                    var curveValue = action.curve.animationCurveValue;
-                                    action.curve.serializedObject.Update();
-                                    RemoveKeyframe(curveValue, action.index);
-                                    m_SelectedKeyframeIndex = -1;
-                                    SaveCurve(action.curve, curveValue);
-                                    action.curve.serializedObject.ApplyModifiedProperties();
-                                }, new MenuAction(curve, k));
-                                menu.ShowAsContext();
-                                e.Use();
-                            }
-                        }
-                    }
-
-                    // Tangent selection & edit mode
-                    if (e.type == EventType.mouseDown && rect.Contains(e.mousePosition))
-                    {
-                        if (inTangentHitRect.Contains(e.mousePosition) && (k > 0 || state.loopInBounds))
-                        {
-                            SelectKeyframe(curve, k);
-                            m_EditMode = EditMode.TangentEdit;
-                            m_TangentEditMode = Tangent.In;
-                            e.Use();
-                        }
-                        else if (outTangentHitrect.Contains(e.mousePosition) && (k < length - 1 || state.loopInBounds))
-                        {
-                            SelectKeyframe(curve, k);
-                            m_EditMode = EditMode.TangentEdit;
-                            m_TangentEditMode = Tangent.Out;
-                            e.Use();
-                        }
-                    }
-
-                    // Mouse up - clean up states
-                    if (e.rawType == EventType.MouseUp && m_EditMode != EditMode.None)
-                    {
-                        m_EditMode = EditMode.None;
-                    }
-
-                    // Set cursors
-                    {
-                        EditorGUIUtility.AddCursorRect(hitRect, MouseCursor.MoveArrow);
-
-                        if (k > 0 || state.loopInBounds)
-                            EditorGUIUtility.AddCursorRect(inTangentHitRect, MouseCursor.RotateArrow);
-
-                        if (k < length - 1 || state.loopInBounds)
-                            EditorGUIUtility.AddCursorRect(outTangentHitrect, MouseCursor.RotateArrow);
-                    }
-                }
-            }
-
-            Handles.color = Color.white;
-            SaveCurve(curve, animCurve);
-        }
-
-        void OnGeneralUI(Rect rect)
-        {
-            var e = Event.current;
-
-            // Selection
-            if (e.type == EventType.mouseDown)
-            {
-                GUI.FocusControl(null);
-                m_SelectedCurve = null;
-                m_SelectedKeyframeIndex = -1;
-                bool used = false;
-
-                var hit = CanvasToCurve(e.mousePosition);
-                float curvePickValue = CurveToCanvas(hit).y;
-
-                // Try and select a curve
-                foreach (var curve in m_Curves)
-                {
-                    if (!curve.Value.editable || !curve.Value.visible)
-                        continue;
-
-                    var prop = curve.Key;
-                    var state = curve.Value;
-                    var animCurve = prop.animationCurveValue;
-                    float hitY = animCurve.length == 0
-                        ? state.zeroKeyConstantValue
-                        : animCurve.Evaluate(hit.x);
-
-                    var curvePos = CurveToCanvas(new Vector3(hit.x, hitY));
-
-                    if (Mathf.Abs(curvePos.y - curvePickValue) < settings.curvePickingDistance)
-                    {
-                        m_SelectedCurve = prop;
-
-                        if (e.clickCount == 2 && e.button == 0)
-                        {
-                            // Create a keyframe on double-click on this curve
-                            EditCreateKeyframe(animCurve, hit, true, state.zeroKeyConstantValue);
-                            SaveCurve(prop, animCurve);
-                        }
-                        else if (e.button == 1)
-                        {
-                            // Curve context menu
-                            var menu = new GenericMenu();
-                            menu.AddItem(new GUIContent("Add Key"), false, (x) =>
-                            {
-                                var action = (MenuAction)x;
-                                var curveValue = action.curve.animationCurveValue;
-                                action.curve.serializedObject.Update();
-                                EditCreateKeyframe(curveValue, hit, true, 0f);
-                                SaveCurve(action.curve, curveValue);
-                                action.curve.serializedObject.ApplyModifiedProperties();
-                            }, new MenuAction(prop, hit));
-                            menu.ShowAsContext();
-                            e.Use();
-                            used = true;
-                        }
-                    }
-                }
-
-                if (e.clickCount == 2 && e.button == 0 && m_SelectedCurve == null)
-                {
-                    // Create a keyframe on every curve on double-click
-                    foreach (var curve in m_Curves)
-                    {
-                        if (!curve.Value.editable || !curve.Value.visible)
-                            continue;
-
-                        var prop = curve.Key;
-                        var state = curve.Value;
-                        var animCurve = prop.animationCurveValue;
-                        EditCreateKeyframe(animCurve, hit, e.alt, state.zeroKeyConstantValue);
-                        SaveCurve(prop, animCurve);
-                    }
-                }
-                else if (!used && e.button == 1)
-                {
-                    // Global context menu
-                    var menu = new GenericMenu();
-                    menu.AddItem(new GUIContent("Add Key At Position"), false, () => ContextMenuAddKey(hit, false));
-                    menu.AddItem(new GUIContent("Add Key On Curves"), false, () => ContextMenuAddKey(hit, true));
-                    menu.ShowAsContext();
-                }
-
-                e.Use();
-            }
-
-            // Delete selected key(s)
-            if (e.type == EventType.keyDown && (e.keyCode == KeyCode.Delete || e.keyCode == KeyCode.Backspace))
-            {
-                if (m_SelectedKeyframeIndex != -1 && m_SelectedCurve != null)
-                {
-                    var animCurve = m_SelectedCurve.animationCurveValue;
-                    var length = animCurve.length;
-
-                    if (m_Curves[m_SelectedCurve].minPointCount < length && length >= 0)
-                    {
-                        EditDeleteKeyframe(animCurve, m_SelectedKeyframeIndex);
-                        m_SelectedKeyframeIndex = -1;
-                        SaveCurve(m_SelectedCurve, animCurve);
-                    }
-
-                    e.Use();
-                }
-            }
-        }
-
-        void SaveCurve(SerializedProperty prop, AnimationCurve curve)
-        {
-            prop.animationCurveValue = curve;
-        }
-
-        void Invalidate()
-        {
-            m_Dirty = true;
-        }
-
-        #endregion
-
-        #region Keyframe manipulations
-
-        void SelectKeyframe(SerializedProperty curve, int keyframeIndex)
-        {
-            m_SelectedKeyframeIndex = keyframeIndex;
-            m_SelectedCurve = curve;
-            Invalidate();
-        }
-
-        void ContextMenuAddKey(Vector3 hit, bool createOnCurve)
-        {
-            SerializedObject serializedObject = null;
-
-            foreach (var curve in m_Curves)
-            {
-                if (!curve.Value.editable || !curve.Value.visible)
-                    continue;
-
-                var prop = curve.Key;
-                var state = curve.Value;
-
-                if (serializedObject == null)
-                {
-                    serializedObject = prop.serializedObject;
-                    serializedObject.Update();
-                }
-
-                var animCurve = prop.animationCurveValue;
-                EditCreateKeyframe(animCurve, hit, createOnCurve, state.zeroKeyConstantValue);
-                SaveCurve(prop, animCurve);
-            }
-
-            if (serializedObject != null)
-                serializedObject.ApplyModifiedProperties();
-
-            Invalidate();
-        }
-
-        void EditCreateKeyframe(AnimationCurve curve, Vector3 position, bool createOnCurve, float zeroKeyConstantValue)
-        {
-            float tangent = EvaluateTangent(curve, position.x);
-
-            if (createOnCurve)
-            {
-                position.y = curve.length == 0
-                    ? zeroKeyConstantValue
-                    : curve.Evaluate(position.x);
-            }
-
-            AddKeyframe(curve, new Keyframe(position.x, position.y, tangent, tangent));
-        }
-
-        void EditDeleteKeyframe(AnimationCurve curve, int keyframeIndex)
-        {
-            RemoveKeyframe(curve, keyframeIndex);
-        }
-
-        void AddKeyframe(AnimationCurve curve, Keyframe newValue)
-        {
-            curve.AddKey(newValue);
-            Invalidate();
-        }
-
-        void RemoveKeyframe(AnimationCurve curve, int keyframeIndex)
-        {
-            curve.RemoveKey(keyframeIndex);
-            Invalidate();
-        }
-
-        void SetKeyframe(AnimationCurve curve, int keyframeIndex, Keyframe newValue)
-        {
-            var keys = curve.keys;
-
-            if (keyframeIndex > 0)
-                newValue.time = Mathf.Max(keys[keyframeIndex - 1].time + settings.keyTimeClampingDistance, newValue.time);
-
-            if (keyframeIndex < keys.Length - 1)
-                newValue.time = Mathf.Min(keys[keyframeIndex + 1].time - settings.keyTimeClampingDistance, newValue.time);
-
-            curve.MoveKey(keyframeIndex, newValue);
-            Invalidate();
-        }
-
-        void EditMoveKeyframe(AnimationCurve curve, Keyframe[] keys, int keyframeIndex)
-        {
-            var key = CanvasToCurve(Event.current.mousePosition);
-            float inTgt = keys[keyframeIndex].inTangent;
-            float outTgt = keys[keyframeIndex].outTangent;
-            SetKeyframe(curve, keyframeIndex, new Keyframe(key.x, key.y, inTgt, outTgt));
-        }
-
-        void EditMoveTangent(AnimationCurve curve, Keyframe[] keys, int keyframeIndex, Tangent targetTangent, bool linkTangents)
-        {
-            var pos = CanvasToCurve(Event.current.mousePosition);
-
-            float time = keys[keyframeIndex].time;
-            float value = keys[keyframeIndex].value;
-
-            pos -= new Vector3(time, value);
-
-            if (targetTangent == Tangent.In && pos.x > 0f)
-                pos.x = 0f;
-
-            if (targetTangent == Tangent.Out && pos.x < 0f)
-                pos.x = 0f;
-
-            float tangent;
-
-            if (Mathf.Approximately(pos.x, 0f))
-                tangent = pos.y < 0f ? float.PositiveInfinity : float.NegativeInfinity;
-            else
-                tangent = pos.y / pos.x;
-
-            float inTangent = keys[keyframeIndex].inTangent;
-            float outTangent = keys[keyframeIndex].outTangent;
-
-            if (targetTangent == Tangent.In || linkTangents)
-                inTangent = tangent;
-            if (targetTangent == Tangent.Out || linkTangents)
-                outTangent = tangent;
-
-            SetKeyframe(curve, keyframeIndex, new Keyframe(time, value, inTangent, outTangent));
-        }
-
-        #endregion
-
-        #region Maths utilities
-
-        Vector3 CurveToCanvas(Keyframe keyframe)
-        {
-            return CurveToCanvas(new Vector3(keyframe.time, keyframe.value));
-        }
-
-        Vector3 CurveToCanvas(Vector3 position)
-        {
-            var bounds = settings.bounds;
-            var output = new Vector3((position.x - bounds.x) / (bounds.xMax - bounds.x), (position.y - bounds.y) / (bounds.yMax - bounds.y));
-            output.x = output.x * (m_CurveArea.xMax - m_CurveArea.xMin) + m_CurveArea.xMin;
-            output.y = (1f - output.y) * (m_CurveArea.yMax - m_CurveArea.yMin) + m_CurveArea.yMin;
-            return output;
-        }
-
-        Vector3 CanvasToCurve(Vector3 position)
-        {
-            var bounds = settings.bounds;
-            var output = position;
-            output.x = (output.x - m_CurveArea.xMin) / (m_CurveArea.xMax - m_CurveArea.xMin);
-            output.y = (output.y - m_CurveArea.yMin) / (m_CurveArea.yMax - m_CurveArea.yMin);
-            output.x = Mathf.Lerp(bounds.x, bounds.xMax, output.x);
-            output.y = Mathf.Lerp(bounds.yMax, bounds.y, output.y);
-            return output;
-        }
-
-        Vector3 CurveTangentToCanvas(float tangent)
-        {
-            if (!float.IsInfinity(tangent))
-            {
-                var bounds = settings.bounds;
-                float ratio = (m_CurveArea.width / m_CurveArea.height) / ((bounds.xMax - bounds.x) / (bounds.yMax - bounds.y));
-                return new Vector3(1f, -tangent / ratio).normalized;
-            }
-
-            return float.IsPositiveInfinity(tangent) ? Vector3.up : Vector3.down;
-        }
-
-        Vector3[] BezierSegment(Keyframe start, Keyframe end)
-        {
-            var segment = new Vector3[4];
-
-            segment[0] = CurveToCanvas(new Vector3(start.time, start.value));
-            segment[3] = CurveToCanvas(new Vector3(end.time, end.value));
-
-            float middle  = start.time + ((end.time - start.time) * 0.333333f);
-            float middle2 = start.time + ((end.time - start.time) * 0.666666f);
-
-            segment[1] = CurveToCanvas(new Vector3(middle, ProjectTangent(start.time, start.value, start.outTangent, middle)));
-            segment[2] = CurveToCanvas(new Vector3(middle2, ProjectTangent(end.time, end.value, end.inTangent, middle2)));
-
-            return segment;
-        }
-
-        Vector3[] HardSegment(Keyframe start, Keyframe end)
-        {
-            var segment = new Vector3[3];
-
-            segment[0] = CurveToCanvas(start);
-            segment[1] = CurveToCanvas(new Vector3(end.time, start.value));
-            segment[2] = CurveToCanvas(end);
-
-            return segment;
-        }
-
-        float ProjectTangent(float inPosition, float inValue, float inTangent, float projPosition)
-        {
-            return inValue + ((projPosition - inPosition) * inTangent);
-        }
-
-        float EvaluateTangent(AnimationCurve curve, float time)
-        {
-            int prev = -1, next = 0;
-            for (int i = 0; i < curve.keys.Length; i++)
-            {
-                if (time > curve.keys[i].time)
-                {
-                    prev = i;
-                    next = i + 1;
-                }
-                else break;
-            }
-
-            if (next == 0)
-                return 0f;
-
-            if (prev == curve.keys.Length - 1)
-                return 0f;
-
-            const float kD = 1e-3f;
-            float tp = Mathf.Max(time - kD, curve.keys[prev].time);
-            float tn = Mathf.Min(time + kD, curve.keys[next].time);
-
-            float vp = curve.Evaluate(tp);
-            float vn = curve.Evaluate(tn);
-
-            if (Mathf.Approximately(tn, tp))
-                return (vn - vp > 0f) ? float.PositiveInfinity : float.NegativeInfinity;
-
-            return (vn - vp) / (tn - tp);
-        }
-
-        #endregion
-    }
-}
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace UnityEditor.PostProcessing
+{
+    public sealed class CurveEditor
+    {
+        #region Enums
+
+        enum EditMode
+        {
+            None,
+            Moving,
+            TangentEdit
+        }
+
+        enum Tangent
+        {
+            In,
+            Out
+        }
+        #endregion
+
+        #region Structs
+        public struct Settings
+        {
+            public Rect bounds;
+            public RectOffset padding;
+            public Color selectionColor;
+            public float curvePickingDistance;
+            public float keyTimeClampingDistance;
+
+            public static Settings defaultSettings
+            {
+                get
+                {
+                    return new Settings
+                    {
+                        bounds = new Rect(0f, 0f, 1f, 1f),
+                        padding = new RectOffset(10, 10, 10, 10),
+                        selectionColor = Color.yellow,
+                        curvePickingDistance = 6f,
+                        keyTimeClampingDistance = 1e-4f
+                    };
+                }
+            }
+        }
+
+        public struct CurveState
+        {
+            public bool visible;
+            public bool editable;
+            public uint minPointCount;
+            public float zeroKeyConstantValue;
+            public Color color;
+            public float width;
+            public float handleWidth;
+            public bool showNonEditableHandles;
+            public bool onlyShowHandlesOnSelection;
+            public bool loopInBounds;
+
+            public static CurveState defaultState
+            {
+                get
+                {
+                    return new CurveState
+                    {
+                        visible = true,
+                        editable = true,
+                        minPointCount = 2,
+                        zeroKeyConstantValue = 0f,
+                        color = Color.white,
+                        width = 2f,
+                        handleWidth = 2f,
+                        showNonEditableHandles = true,
+                        onlyShowHandlesOnSelection = false,
+                        loopInBounds = false
+                    };
+                }
+            }
+        }
+
+        public struct Selection
+        {
+            public SerializedProperty curve;
+            public int keyframeIndex;
+            public Keyframe? keyframe;
+
+            public Selection(SerializedProperty curve, int keyframeIndex, Keyframe? keyframe)
+            {
+                this.curve = curve;
+                this.keyframeIndex = keyframeIndex;
+                this.keyframe = keyframe;
+            }
+        }
+
+        internal struct MenuAction
+        {
+            internal SerializedProperty curve;
+            internal int index;
+            internal Vector3 position;
+
+            internal MenuAction(SerializedProperty curve)
+            {
+                this.curve = curve;
+                this.index = -1;
+                this.position = Vector3.zero;
+            }
+
+            internal MenuAction(SerializedProperty curve, int index)
+            {
+                this.curve = curve;
+                this.index = index;
+                this.position = Vector3.zero;
+            }
+
+            internal MenuAction(SerializedProperty curve, Vector3 position)
+            {
+                this.curve = curve;
+                this.index = -1;
+                this.position = position;
+            }
+        }
+        #endregion
+
+        #region Fields & properties
+        public Settings settings { get; private set; }
+
+        Dictionary<SerializedProperty, CurveState> m_Curves;
+        Rect m_CurveArea;
+
+        SerializedProperty m_SelectedCurve;
+        int m_SelectedKeyframeIndex = -1;
+
+        EditMode m_EditMode = EditMode.None;
+        Tangent m_TangentEditMode;
+
+        bool m_Dirty;
+        #endregion
+
+        #region Constructors & destructors
+        public CurveEditor()
+            : this(Settings.defaultSettings)
+        {}
+
+        public CurveEditor(Settings settings)
+        {
+            this.settings = settings;
+            m_Curves = new Dictionary<SerializedProperty, CurveState>();
+        }
+
+        #endregion
+
+        #region Public API
+        public void Add(params SerializedProperty[] curves)
+        {
+            foreach (var curve in curves)
+                Add(curve, CurveState.defaultState);
+        }
+
+        public void Add(SerializedProperty curve)
+        {
+            Add(curve, CurveState.defaultState);
+        }
+
+        public void Add(SerializedProperty curve, CurveState state)
+        {
+            // Make sure the property is in fact an AnimationCurve
+            var animCurve = curve.animationCurveValue;
+            if (animCurve == null)
+                throw new ArgumentException("curve");
+
+            if (m_Curves.ContainsKey(curve))
+                Debug.LogWarning("Curve has already been added to the editor");
+
+            m_Curves.Add(curve, state);
+        }
+
+        public void Remove(SerializedProperty curve)
+        {
+            m_Curves.Remove(curve);
+        }
+
+        public void RemoveAll()
+        {
+            m_Curves.Clear();
+        }
+
+        public CurveState GetCurveState(SerializedProperty curve)
+        {
+            CurveState state;
+            if (!m_Curves.TryGetValue(curve, out state))
+                throw new KeyNotFoundException("curve");
+
+            return state;
+        }
+
+        public void SetCurveState(SerializedProperty curve, CurveState state)
+        {
+            if (!m_Curves.ContainsKey(curve))
+                throw new KeyNotFoundException("curve");
+
+            m_Curves[curve] = state;
+        }
+
+        public Selection GetSelection()
+        {
+            Keyframe? key = null;
+            if (m_SelectedKeyframeIndex > -1)
+            {
+                var curve = m_SelectedCurve.animationCurveValue;
+
+                if (m_SelectedKeyframeIndex >= curve.length)
+                    m_SelectedKeyframeIndex = -1;
+                else
+                    key = curve[m_SelectedKeyframeIndex];
+            }
+
+            return new Selection(m_SelectedCurve, m_SelectedKeyframeIndex, key);
+        }
+
+        public void SetKeyframe(SerializedProperty curve, int keyframeIndex, Keyframe keyframe)
+        {
+            var animCurve = curve.animationCurveValue;
+            SetKeyframe(animCurve, keyframeIndex, keyframe);
+            SaveCurve(curve, animCurve);
+        }
+
+        public bool OnGUI(Rect rect)
+        {
+            if (Event.current.type == EventType.Repaint)
+                m_Dirty = false;
+
+            GUI.BeginClip(rect);
+            {
+                var area = new Rect(Vector2.zero, rect.size);
+                m_CurveArea = settings.padding.Remove(area);
+
+                foreach (var curve in m_Curves)
+                    OnCurveGUI(area, curve.Key, curve.Value);
+
+                OnGeneralUI(area);
+            }
+            GUI.EndClip();
+
+            return m_Dirty;
+        }
+
+        #endregion
+
+        #region UI & events
+
+        void OnCurveGUI(Rect rect, SerializedProperty curve, CurveState state)
+        {
+            // Discard invisible curves
+            if (!state.visible)
+                return;
+
+            var animCurve = curve.animationCurveValue;
+            var keys = animCurve.keys;
+            var length = keys.Length;
+
+            // Curve drawing
+            // Slightly dim non-editable curves
+            var color = state.color;
+            if (!state.editable)
+                color.a *= 0.5f;
+
+            Handles.color = color;
+            var bounds = settings.bounds;
+
+            if (length == 0)
+            {
+                var p1 = CurveToCanvas(new Vector3(bounds.xMin, state.zeroKeyConstantValue));
+                var p2 = CurveToCanvas(new Vector3(bounds.xMax, state.zeroKeyConstantValue));
+                Handles.DrawAAPolyLine(state.width, p1, p2);
+            }
+            else if (length == 1)
+            {
+                var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value));
+                var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[0].value));
+                Handles.DrawAAPolyLine(state.width, p1, p2);
+            }
+            else
+            {
+                var prevKey = keys[0];
+                for (int k = 1; k < length; k++)
+                {
+                    var key = keys[k];
+                    var pts = BezierSegment(prevKey, key);
+
+                    if (float.IsInfinity(prevKey.outTangent) || float.IsInfinity(key.inTangent))
+                    {
+                        var s = HardSegment(prevKey, key);
+                        Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
+                    }
+                    else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
+
+                    prevKey = key;
+                }
+
+                // Curve extents & loops
+                if (keys[0].time > bounds.xMin)
+                {
+                    if (state.loopInBounds)
+                    {
+                        var p1 = keys[length - 1];
+                        p1.time -= settings.bounds.width;
+                        var p2 = keys[0];
+                        var pts = BezierSegment(p1, p2);
+
+                        if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent))
+                        {
+                            var s = HardSegment(p1, p2);
+                            Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
+                        }
+                        else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
+                    }
+                    else
+                    {
+                        var p1 = CurveToCanvas(new Vector3(bounds.xMin, keys[0].value));
+                        var p2 = CurveToCanvas(keys[0]);
+                        Handles.DrawAAPolyLine(state.width, p1, p2);
+                    }
+                }
+
+                if (keys[length - 1].time < bounds.xMax)
+                {
+                    if (state.loopInBounds)
+                    {
+                        var p1 = keys[length - 1];
+                        var p2 = keys[0];
+                        p2.time += settings.bounds.width;
+                        var pts = BezierSegment(p1, p2);
+
+                        if (float.IsInfinity(p1.outTangent) || float.IsInfinity(p2.inTangent))
+                        {
+                            var s = HardSegment(p1, p2);
+                            Handles.DrawAAPolyLine(state.width, s[0], s[1], s[2]);
+                        }
+                        else Handles.DrawBezier(pts[0], pts[3], pts[1], pts[2], color, null, state.width);
+                    }
+                    else
+                    {
+                        var p1 = CurveToCanvas(keys[length - 1]);
+                        var p2 = CurveToCanvas(new Vector3(bounds.xMax, keys[length - 1].value));
+                        Handles.DrawAAPolyLine(state.width, p1, p2);
+                    }
+                }
+            }
+
+            // Make sure selection is correct (undo can break it)
+            bool isCurrentlySelectedCurve = curve == m_SelectedCurve;
+
+            if (isCurrentlySelectedCurve && m_SelectedKeyframeIndex >= length)
+                m_SelectedKeyframeIndex = -1;
+
+            // Handles & keys
+            for (int k = 0; k < length; k++)
+            {
+                bool isCurrentlySelectedKeyframe = k == m_SelectedKeyframeIndex;
+                var e = Event.current;
+
+                var pos = CurveToCanvas(keys[k]);
+                var hitRect = new Rect(pos.x - 8f, pos.y - 8f, 16f, 16f);
+                var offset = isCurrentlySelectedCurve
+                    ? new RectOffset(5, 5, 5, 5)
+                    : new RectOffset(6, 6, 6, 6);
+
+                var outTangent = pos + CurveTangentToCanvas(keys[k].outTangent).normalized * 40f;
+                var inTangent = pos - CurveTangentToCanvas(keys[k].inTangent).normalized * 40f;
+                var inTangentHitRect = new Rect(inTangent.x - 7f, inTangent.y - 7f, 14f, 14f);
+                var outTangentHitrect = new Rect(outTangent.x - 7f, outTangent.y - 7f, 14f, 14f);
+
+                // Draw
+                if (state.showNonEditableHandles)
+                {
+                    if (e.type == EventType.repaint)
+                    {
+                        var selectedColor = (isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
+                            ? settings.selectionColor
+                            : state.color;
+
+                        // Keyframe
+                        EditorGUI.DrawRect(offset.Remove(hitRect), selectedColor);
+
+                        // Tangents
+                        if (isCurrentlySelectedCurve && (!state.onlyShowHandlesOnSelection || (state.onlyShowHandlesOnSelection && isCurrentlySelectedKeyframe)))
+                        {
+                            Handles.color = selectedColor;
+
+                            if (k > 0 || state.loopInBounds)
+                            {
+                                Handles.DrawAAPolyLine(state.handleWidth, pos, inTangent);
+                                EditorGUI.DrawRect(offset.Remove(inTangentHitRect), selectedColor);
+                            }
+
+                            if (k < length - 1 || state.loopInBounds)
+                            {
+                                Handles.DrawAAPolyLine(state.handleWidth, pos, outTangent);
+                                EditorGUI.DrawRect(offset.Remove(outTangentHitrect), selectedColor);
+                            }
+                        }
+                    }
+                }
+
+                // Events
+                if (state.editable)
+                {
+                    // Keyframe move
+                    if (m_EditMode == EditMode.Moving && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
+                    {
+                        EditMoveKeyframe(animCurve, keys, k);
+                    }
+
+                    // Tangent editing
+                    if (m_EditMode == EditMode.TangentEdit && e.type == EventType.MouseDrag && isCurrentlySelectedCurve && isCurrentlySelectedKeyframe)
+                    {
+                        bool alreadyBroken = !(Mathf.Approximately(keys[k].inTangent, keys[k].outTangent) || (float.IsInfinity(keys[k].inTangent) && float.IsInfinity(keys[k].outTangent)));
+                        EditMoveTangent(animCurve, keys, k, m_TangentEditMode, e.shift || !(alreadyBroken || e.control));
+                    }
+
+                    // Keyframe selection & context menu
+                    if (e.type == EventType.mouseDown && rect.Contains(e.mousePosition))
+                    {
+                        if (hitRect.Contains(e.mousePosition))
+                        {
+                            if (e.button == 0)
+                            {
+                                SelectKeyframe(curve, k);
+                                m_EditMode = EditMode.Moving;
+                                e.Use();
+                            }
+                            else if (e.button == 1)
+                            {
+                                // Keyframe context menu
+                                var menu = new GenericMenu();
+                                menu.AddItem(new GUIContent("Delete Key"), false, (x) =>
+                                {
+                                    var action = (MenuAction)x;
+                                    var curveValue = action.curve.animationCurveValue;
+                                    action.curve.serializedObject.Update();
+                                    RemoveKeyframe(curveValue, action.index);
+                                    m_SelectedKeyframeIndex = -1;
+                                    SaveCurve(action.curve, curveValue);
+                                    action.curve.serializedObject.ApplyModifiedProperties();
+                                }, new MenuAction(curve, k));
+                                menu.ShowAsContext();
+                                e.Use();
+                            }
+                        }
+                    }
+
+                    // Tangent selection & edit mode
+                    if (e.type == EventType.mouseDown && rect.Contains(e.mousePosition))
+                    {
+                        if (inTangentHitRect.Contains(e.mousePosition) && (k > 0 || state.loopInBounds))
+                        {
+                            SelectKeyframe(curve, k);
+                            m_EditMode = EditMode.TangentEdit;
+                            m_TangentEditMode = Tangent.In;
+                            e.Use();
+                        }
+                        else if (outTangentHitrect.Contains(e.mousePosition) && (k < length - 1 || state.loopInBounds))
+                        {
+                            SelectKeyframe(curve, k);
+                            m_EditMode = EditMode.TangentEdit;
+                            m_TangentEditMode = Tangent.Out;
+                            e.Use();
+                        }
+                    }
+
+                    // Mouse up - clean up states
+                    if (e.rawType == EventType.MouseUp && m_EditMode != EditMode.None)
+                    {
+                        m_EditMode = EditMode.None;
+                    }
+
+                    // Set cursors
+                    {
+                        EditorGUIUtility.AddCursorRect(hitRect, MouseCursor.MoveArrow);
+
+                        if (k > 0 || state.loopInBounds)
+                            EditorGUIUtility.AddCursorRect(inTangentHitRect, MouseCursor.RotateArrow);
+
+                        if (k < length - 1 || state.loopInBounds)
+                            EditorGUIUtility.AddCursorRect(outTangentHitrect, MouseCursor.RotateArrow);
+                    }
+                }
+            }
+
+            Handles.color = Color.white;
+            SaveCurve(curve, animCurve);
+        }
+
+        void OnGeneralUI(Rect rect)
+        {
+            var e = Event.current;
+
+            // Selection
+            if (e.type == EventType.mouseDown)
+            {
+                GUI.FocusControl(null);
+                m_SelectedCurve = null;
+                m_SelectedKeyframeIndex = -1;
+                bool used = false;
+
+                var hit = CanvasToCurve(e.mousePosition);
+                float curvePickValue = CurveToCanvas(hit).y;
+
+                // Try and select a curve
+                foreach (var curve in m_Curves)
+                {
+                    if (!curve.Value.editable || !curve.Value.visible)
+                        continue;
+
+                    var prop = curve.Key;
+                    var state = curve.Value;
+                    var animCurve = prop.animationCurveValue;
+                    float hitY = animCurve.length == 0
+                        ? state.zeroKeyConstantValue
+                        : animCurve.Evaluate(hit.x);
+
+                    var curvePos = CurveToCanvas(new Vector3(hit.x, hitY));
+
+                    if (Mathf.Abs(curvePos.y - curvePickValue) < settings.curvePickingDistance)
+                    {
+                        m_SelectedCurve = prop;
+
+                        if (e.clickCount == 2 && e.button == 0)
+                        {
+                            // Create a keyframe on double-click on this curve
+                            EditCreateKeyframe(animCurve, hit, true, state.zeroKeyConstantValue);
+                            SaveCurve(prop, animCurve);
+                        }
+                        else if (e.button == 1)
+                        {
+                            // Curve context menu
+                            var menu = new GenericMenu();
+                            menu.AddItem(new GUIContent("Add Key"), false, (x) =>
+                            {
+                                var action = (MenuAction)x;
+                                var curveValue = action.curve.animationCurveValue;
+                                action.curve.serializedObject.Update();
+                                EditCreateKeyframe(curveValue, hit, true, 0f);
+                                SaveCurve(action.curve, curveValue);
+                                action.curve.serializedObject.ApplyModifiedProperties();
+                            }, new MenuAction(prop, hit));
+                            menu.ShowAsContext();
+                            e.Use();
+                            used = true;
+                        }
+                    }
+                }
+
+                if (e.clickCount == 2 && e.button == 0 && m_SelectedCurve == null)
+                {
+                    // Create a keyframe on every curve on double-click
+                    foreach (var curve in m_Curves)
+                    {
+                        if (!curve.Value.editable || !curve.Value.visible)
+                            continue;
+
+                        var prop = curve.Key;
+                        var state = curve.Value;
+                        var animCurve = prop.animationCurveValue;
+                        EditCreateKeyframe(animCurve, hit, e.alt, state.zeroKeyConstantValue);
+                        SaveCurve(prop, animCurve);
+                    }
+                }
+                else if (!used && e.button == 1)
+                {
+                    // Global context menu
+                    var menu = new GenericMenu();
+                    menu.AddItem(new GUIContent("Add Key At Position"), false, () => ContextMenuAddKey(hit, false));
+                    menu.AddItem(new GUIContent("Add Key On Curves"), false, () => ContextMenuAddKey(hit, true));
+                    menu.ShowAsContext();
+                }
+
+                e.Use();
+            }
+
+            // Delete selected key(s)
+            if (e.type == EventType.keyDown && (e.keyCode == KeyCode.Delete || e.keyCode == KeyCode.Backspace))
+            {
+                if (m_SelectedKeyframeIndex != -1 && m_SelectedCurve != null)
+                {
+                    var animCurve = m_SelectedCurve.animationCurveValue;
+                    var length = animCurve.length;
+
+                    if (m_Curves[m_SelectedCurve].minPointCount < length && length >= 0)
+                    {
+                        EditDeleteKeyframe(animCurve, m_SelectedKeyframeIndex);
+                        m_SelectedKeyframeIndex = -1;
+                        SaveCurve(m_SelectedCurve, animCurve);
+                    }
+
+                    e.Use();
+                }
+            }
+        }
+
+        void SaveCurve(SerializedProperty prop, AnimationCurve curve)
+        {
+            prop.animationCurveValue = curve;
+        }
+
+        void Invalidate()
+        {
+            m_Dirty = true;
+        }
+
+        #endregion
+
+        #region Keyframe manipulations
+
+        void SelectKeyframe(SerializedProperty curve, int keyframeIndex)
+        {
+            m_SelectedKeyframeIndex = keyframeIndex;
+            m_SelectedCurve = curve;
+            Invalidate();
+        }
+
+        void ContextMenuAddKey(Vector3 hit, bool createOnCurve)
+        {
+            SerializedObject serializedObject = null;
+
+            foreach (var curve in m_Curves)
+            {
+                if (!curve.Value.editable || !curve.Value.visible)
+                    continue;
+
+                var prop = curve.Key;
+                var state = curve.Value;
+
+                if (serializedObject == null)
+                {
+                    serializedObject = prop.serializedObject;
+                    serializedObject.Update();
+                }
+
+                var animCurve = prop.animationCurveValue;
+                EditCreateKeyframe(animCurve, hit, createOnCurve, state.zeroKeyConstantValue);
+                SaveCurve(prop, animCurve);
+            }
+
+            if (serializedObject != null)
+                serializedObject.ApplyModifiedProperties();
+
+            Invalidate();
+        }
+
+        void EditCreateKeyframe(AnimationCurve curve, Vector3 position, bool createOnCurve, float zeroKeyConstantValue)
+        {
+            float tangent = EvaluateTangent(curve, position.x);
+
+            if (createOnCurve)
+            {
+                position.y = curve.length == 0
+                    ? zeroKeyConstantValue
+                    : curve.Evaluate(position.x);
+            }
+
+            AddKeyframe(curve, new Keyframe(position.x, position.y, tangent, tangent));
+        }
+
+        void EditDeleteKeyframe(AnimationCurve curve, int keyframeIndex)
+        {
+            RemoveKeyframe(curve, keyframeIndex);
+        }
+
+        void AddKeyframe(AnimationCurve curve, Keyframe newValue)
+        {
+            curve.AddKey(newValue);
+            Invalidate();
+        }
+
+        void RemoveKeyframe(AnimationCurve curve, int keyframeIndex)
+        {
+            curve.RemoveKey(keyframeIndex);
+            Invalidate();
+        }
+
+        void SetKeyframe(AnimationCurve curve, int keyframeIndex, Keyframe newValue)
+        {
+            var keys = curve.keys;
+
+            if (keyframeIndex > 0)
+                newValue.time = Mathf.Max(keys[keyframeIndex - 1].time + settings.keyTimeClampingDistance, newValue.time);
+
+            if (keyframeIndex < keys.Length - 1)
+                newValue.time = Mathf.Min(keys[keyframeIndex + 1].time - settings.keyTimeClampingDistance, newValue.time);
+
+            curve.MoveKey(keyframeIndex, newValue);
+            Invalidate();
+        }
+
+        void EditMoveKeyframe(AnimationCurve curve, Keyframe[] keys, int keyframeIndex)
+        {
+            var key = CanvasToCurve(Event.current.mousePosition);
+            float inTgt = keys[keyframeIndex].inTangent;
+            float outTgt = keys[keyframeIndex].outTangent;
+            SetKeyframe(curve, keyframeIndex, new Keyframe(key.x, key.y, inTgt, outTgt));
+        }
+
+        void EditMoveTangent(AnimationCurve curve, Keyframe[] keys, int keyframeIndex, Tangent targetTangent, bool linkTangents)
+        {
+            var pos = CanvasToCurve(Event.current.mousePosition);
+
+            float time = keys[keyframeIndex].time;
+            float value = keys[keyframeIndex].value;
+
+            pos -= new Vector3(time, value);
+
+            if (targetTangent == Tangent.In && pos.x > 0f)
+                pos.x = 0f;
+
+            if (targetTangent == Tangent.Out && pos.x < 0f)
+                pos.x = 0f;
+
+            float tangent;
+
+            if (Mathf.Approximately(pos.x, 0f))
+                tangent = pos.y < 0f ? float.PositiveInfinity : float.NegativeInfinity;
+            else
+                tangent = pos.y / pos.x;
+
+            float inTangent = keys[keyframeIndex].inTangent;
+            float outTangent = keys[keyframeIndex].outTangent;
+
+            if (targetTangent == Tangent.In || linkTangents)
+                inTangent = tangent;
+            if (targetTangent == Tangent.Out || linkTangents)
+                outTangent = tangent;
+
+            SetKeyframe(curve, keyframeIndex, new Keyframe(time, value, inTangent, outTangent));
+        }
+
+        #endregion
+
+        #region Maths utilities
+
+        Vector3 CurveToCanvas(Keyframe keyframe)
+        {
+            return CurveToCanvas(new Vector3(keyframe.time, keyframe.value));
+        }
+
+        Vector3 CurveToCanvas(Vector3 position)
+        {
+            var bounds = settings.bounds;
+            var output = new Vector3((position.x - bounds.x) / (bounds.xMax - bounds.x), (position.y - bounds.y) / (bounds.yMax - bounds.y));
+            output.x = output.x * (m_CurveArea.xMax - m_CurveArea.xMin) + m_CurveArea.xMin;
+            output.y = (1f - output.y) * (m_CurveArea.yMax - m_CurveArea.yMin) + m_CurveArea.yMin;
+            return output;
+        }
+
+        Vector3 CanvasToCurve(Vector3 position)
+        {
+            var bounds = settings.bounds;
+            var output = position;
+            output.x = (output.x - m_CurveArea.xMin) / (m_CurveArea.xMax - m_CurveArea.xMin);
+            output.y = (output.y - m_CurveArea.yMin) / (m_CurveArea.yMax - m_CurveArea.yMin);
+            output.x = Mathf.Lerp(bounds.x, bounds.xMax, output.x);
+            output.y = Mathf.Lerp(bounds.yMax, bounds.y, output.y);
+            return output;
+        }
+
+        Vector3 CurveTangentToCanvas(float tangent)
+        {
+            if (!float.IsInfinity(tangent))
+            {
+                var bounds = settings.bounds;
+                float ratio = (m_CurveArea.width / m_CurveArea.height) / ((bounds.xMax - bounds.x) / (bounds.yMax - bounds.y));
+                return new Vector3(1f, -tangent / ratio).normalized;
+            }
+
+            return float.IsPositiveInfinity(tangent) ? Vector3.up : Vector3.down;
+        }
+
+        Vector3[] BezierSegment(Keyframe start, Keyframe end)
+        {
+            var segment = new Vector3[4];
+
+            segment[0] = CurveToCanvas(new Vector3(start.time, start.value));
+            segment[3] = CurveToCanvas(new Vector3(end.time, end.value));
+
+            float middle  = start.time + ((end.time - start.time) * 0.333333f);
+            float middle2 = start.time + ((end.time - start.time) * 0.666666f);
+
+            segment[1] = CurveToCanvas(new Vector3(middle, ProjectTangent(start.time, start.value, start.outTangent, middle)));
+            segment[2] = CurveToCanvas(new Vector3(middle2, ProjectTangent(end.time, end.value, end.inTangent, middle2)));
+
+            return segment;
+        }
+
+        Vector3[] HardSegment(Keyframe start, Keyframe end)
+        {
+            var segment = new Vector3[3];
+
+            segment[0] = CurveToCanvas(start);
+            segment[1] = CurveToCanvas(new Vector3(end.time, start.value));
+            segment[2] = CurveToCanvas(end);
+
+            return segment;
+        }
+
+        float ProjectTangent(float inPosition, float inValue, float inTangent, float projPosition)
+        {
+            return inValue + ((projPosition - inPosition) * inTangent);
+        }
+
+        float EvaluateTangent(AnimationCurve curve, float time)
+        {
+            int prev = -1, next = 0;
+            for (int i = 0; i < curve.keys.Length; i++)
+            {
+                if (time > curve.keys[i].time)
+                {
+                    prev = i;
+                    next = i + 1;
+                }
+                else break;
+            }
+
+            if (next == 0)
+                return 0f;
+
+            if (prev == curve.keys.Length - 1)
+                return 0f;
+
+            const float kD = 1e-3f;
+            float tp = Mathf.Max(time - kD, curve.keys[prev].time);
+            float tn = Mathf.Min(time + kD, curve.keys[next].time);
+
+            float vp = curve.Evaluate(tp);
+            float vn = curve.Evaluate(tn);
+
+            if (Mathf.Approximately(tn, tp))
+                return (vn - vp > 0f) ? float.PositiveInfinity : float.NegativeInfinity;
+
+            return (vn - vp) / (tn - tp);
+        }
+
+        #endregion
+    }
+}

--
Gitblit v1.8.0