public void OnSceneGUI()
            {
                MultiSpline ms = (MultiSpline)target;

                if (!ms || ms.m_Type == GenType.Uninitialized || s_CurrentlyEditing != ms)
                {
                    return;
                }

                InitializeStyles();
                ResetUndo();

                switch (ms.m_Type)
                {
                case GenType.Simple:
                    SceneGUI_Simple(ms);
                    break;

                case GenType.Linear:
                    SceneGUI_Linear(ms);
                    break;

                case GenType.CSpline:
                    SceneGUI_CSpline(ms);
                    break;

                case GenType.Cardinal:
                    SceneGUI_Cardinal(ms);
                    break;
                }
            }
        static private void ConvertTo(MultiSpline inSpline, GenType inType)
        {
            switch (inType)
            {
            case GenType.Simple:
                ConvertToSimple(inSpline);
                break;

            case GenType.Linear:
                ConvertToLinear(inSpline);
                break;

            case GenType.CSpline:
                ConvertToCSpline(inSpline);
                break;

            case GenType.Cardinal:
                ConvertToCardinal(inSpline);
                break;

            default:
                Debug.LogError("[MultiSpline] Type " + inType.ToString() + " not yet supported in editor.");
                return;
            }

            inSpline.m_Type  = inType;
            inSpline.m_Dirty = true;
        }
        static private void RenderGizmo_CSplinePreview(MultiSpline inSpline, Transform inTransform)
        {
            int vertCount = inSpline.m_Vertices.Length;

            if (!inSpline.m_Looped)
            {
                --vertCount;
            }

            for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
            {
                if (i < vertCount)
                {
                    RenderGizmo_PathPreview(inSpline, (float)i / vertCount, (float)(i + 1) / vertCount, 24);
                }

                RenderGizmo_PathNode(inTransform, inSpline.m_Vertices[i].Point, i == 0);

                if (inSpline.m_Looped || i > 0)
                {
                    RenderGizmo_TangentLine(inTransform, inSpline.m_Vertices[i].Point, -inSpline.m_Vertices[i].InTangent, false);
                    RenderGizmo_TangentPoint(inTransform, inSpline.m_Vertices[i].Point, -inSpline.m_Vertices[i].InTangent, false);
                }

                if (i < vertCount)
                {
                    RenderGizmo_TangentLine(inTransform, inSpline.m_Vertices[i].Point, inSpline.m_Vertices[i].OutTangent, true);
                    RenderGizmo_TangentPoint(inTransform, inSpline.m_Vertices[i].Point, inSpline.m_Vertices[i].OutTangent, true);
                }
            }
        }
            private void InspectorGUI_Cardinal(MultiSpline inSpline)
            {
                HeaderText("Catmull-Rom / Cardinal Spline", 200);

                ToggleField("Looped", ref inSpline.m_Looped);

                SliderField("Tension", ref inSpline.m_CRTension, -3, 1);
            }
            private void InspectorGUI_Simple(MultiSpline inSpline)
            {
                HeaderText("Simple Spline");

                Vector3Field("Start Position", ref inSpline.m_Vertices[0].Point);
                Vector3Field("End Position", ref inSpline.m_Vertices[1].Point);
                Vector3Field("Control", ref inSpline.m_ControlPointA);
            }
            private void OnDisable()
            {
                if (s_CurrentlyEditing == target)
                {
                    s_CurrentlyEditing = null;
                }

                Undo.undoRedoPerformed += MarkSplineAsDirty;
            }
            private void MarkSplineAsDirty()
            {
                MultiSpline ms = (MultiSpline)target;

                if (ms != null)
                {
                    ms.m_Dirty = true;
                }
            }
        static private void RenderGizmo_SimplePreview(MultiSpline inSpline, Transform inTransform)
        {
            RenderGizmo_PathPreview(inSpline, 0, 1, 24);

            RenderGizmo_PathNode(inTransform, inSpline.m_Vertices[0].Point, true);
            RenderGizmo_PathNode(inTransform, inSpline.m_Vertices[1].Point, false);
            RenderGizmo_ControlPoint(inTransform, inSpline.m_ControlPointA);
            RenderGizmo_ControlLine(inTransform, inSpline.m_Vertices[0].Point, inSpline.m_ControlPointA);
            RenderGizmo_ControlLine(inTransform, inSpline.m_Vertices[1].Point, inSpline.m_ControlPointA);
        }
        static private void DeleteVertex(MultiSpline inSpline, int inIndex)
        {
            if (inSpline.m_Vertices == null || inSpline.m_Vertices.Length < 3 || inSpline.m_Type == GenType.Uninitialized || inSpline.m_Type == GenType.Simple)
            {
                return;
            }

            UnityEditor.ArrayUtility.RemoveAt(ref inSpline.m_Vertices, inIndex);
            inSpline.m_Dirty = true;
        }
        static private void ConvertToCardinal(MultiSpline inSpline)
        {
            switch (inSpline.m_Type)
            {
            case GenType.Uninitialized:
            default:
            {
                // Default values
                inSpline.m_CRTension = 0;
                inSpline.m_Looped    = false;

                Array.Resize(ref inSpline.m_Vertices, 4);
                inSpline.m_Vertices[0].Point = new Vector3(-1, -1, 0);
                inSpline.m_Vertices[1].Point = new Vector3(-1, 0, 0);
                inSpline.m_Vertices[2].Point = new Vector3(1, 0, 0);
                inSpline.m_Vertices[3].Point = new Vector3(1, -1, 0);
                break;
            }

            case GenType.Simple:
            {
                inSpline.m_Looped = false;
                Vector3 start   = inSpline.m_Vertices[0].Point;
                Vector3 end     = inSpline.m_Vertices[1].Point;
                Vector3 control = inSpline.m_ControlPointA;

                Vector3 startTangent = 0.5f * (start - control);
                Vector3 endTangent   = 0.5f * (end - control);

                Array.Resize(ref inSpline.m_Vertices, 2);
                inSpline.m_ControlPointA     = start + startTangent;
                inSpline.m_Vertices[0].Point = start;
                inSpline.m_Vertices[1].Point = end;
                inSpline.m_ControlPointB     = end + endTangent;
                break;
            }

            case GenType.Linear:
            case GenType.CSpline:
            {
                // Points are identical, just need to set control points properly
                inSpline.m_ControlPointA = inSpline.m_Vertices[0].Point;
                inSpline.m_ControlPointB = inSpline.m_Vertices[inSpline.m_Vertices.Length - 1].Point;
                break;
            }

            case GenType.Cardinal:
            {
                // Converts directly
                return;
            }
            }
        }
        static private void RenderGizmo_PathPreview(MultiSpline inSpline, float inStart = 0, float inEnd = 1, int inSegments = 64)
        {
            Transform tr         = inSpline.transform;
            int       numSamples = Spline.Sample(inSpline.m_TransformWrapper, s_CachedSamples, inStart, inEnd, 0, inSegments);

            for (int i = 0; i < numSamples - 1; ++i)
            {
                Vector3 start = s_CachedSamples[i];
                Vector3 next  = s_CachedSamples[i + 1];

                Gizmos.color = Color.Lerp(GizmoConfig.LineColorNormalStart, GizmoConfig.LineColorNormalEnd, inStart + (inEnd - inStart) * ((float)i / (numSamples - 1)));
                Gizmos.DrawLine(start, next);
            }
        }
            private void SceneGUI_Cardinal(MultiSpline inSpline)
            {
                Transform tr = inSpline.transform;

                if (Tools.current == Tool.Move)
                {
                    for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
                    {
                        EditorGUI.BeginChangeCheck();
                        Vector3 pos = Handles.PositionHandle(
                            tr.TransformPoint(inSpline.m_Vertices[i].Point),
                            Quaternion.identity);
                        if (EditorGUI.EndChangeCheck())
                        {
                            MarkDirty("Changed node position");
                            inSpline.m_Vertices[i].Point = tr.InverseTransformPoint(pos);
                        }
                    }

                    if (!inSpline.m_Looped)
                    {
                        // Control point A
                        {
                            EditorGUI.BeginChangeCheck();
                            Vector3 pos = Handles.PositionHandle(
                                tr.TransformPoint(inSpline.m_ControlPointA),
                                Quaternion.identity);
                            if (EditorGUI.EndChangeCheck())
                            {
                                MarkDirty("Changed control point");
                                inSpline.m_ControlPointA = tr.InverseTransformPoint(pos);
                            }
                        }

                        // Control point B
                        {
                            EditorGUI.BeginChangeCheck();
                            Vector3 pos = Handles.PositionHandle(
                                tr.TransformPoint(inSpline.m_ControlPointB),
                                Quaternion.identity);
                            if (EditorGUI.EndChangeCheck())
                            {
                                MarkDirty("Changed control point");
                                inSpline.m_ControlPointB = tr.InverseTransformPoint(pos);
                            }
                        }
                    }
                }
            }
        static private void RenderGizmo_LinearPreview(MultiSpline inSpline, Transform inTransform)
        {
            int vertCount = inSpline.m_Vertices.Length;

            if (!inSpline.m_Looped)
            {
                --vertCount;
            }

            RenderGizmo_PathPreview(inSpline, 0, 1, 1 + vertCount * 8);

            for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
            {
                RenderGizmo_PathNode(inTransform, inSpline.m_Vertices[i].Point, i == 0);
            }
        }
        static private void ConvertToLinear(MultiSpline inSpline)
        {
            switch (inSpline.m_Type)
            {
            case GenType.Uninitialized:
            default:
            {
                // Default values
                inSpline.m_Looped = false;
                Array.Resize(ref inSpline.m_Vertices, 2);
                inSpline.m_Vertices[0] = new CSplineVertex(Vector3.left);
                inSpline.m_Vertices[1] = new CSplineVertex(Vector3.right);
                break;
            }

            case GenType.Simple:
            {
                // Swap control and end points for linear order
                Vector3 control = inSpline.m_ControlPointA;
                Array.Resize(ref inSpline.m_Vertices, 3);
                inSpline.m_Vertices[2] = inSpline.m_Vertices[1];
                inSpline.m_Vertices[1] = new CSplineVertex(control);

                // Default to not closed
                inSpline.m_Looped = false;
                break;
            }

            case GenType.Linear:
            {
                // Already linear, no need to do anything
                return;
            }

            case GenType.CSpline:
            case GenType.Cardinal:
            {
                // Copy CSpline points over
                Array.Resize(ref inSpline.m_Vertices, inSpline.m_Vertices.Length);
                for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
                {
                    inSpline.m_Vertices[i] = inSpline.m_Vertices[i];
                }
                break;
            }
            }
        }
        static private void RenderGizmo_CardinalPreview(MultiSpline inSpline, Transform inTransform)
        {
            for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
            {
                RenderGizmo_PathPreview(inSpline, (float)i / inSpline.m_Vertices.Length, (float)(i + 1) / inSpline.m_Vertices.Length, 24);
                RenderGizmo_PathNode(inTransform, inSpline.m_Vertices[i].Point, i == 0);
            }

            if (!inSpline.m_Looped)
            {
                RenderGizmo_ControlLine(inTransform, inSpline.m_Vertices[0].Point, inSpline.m_ControlPointA);
                RenderGizmo_ControlPoint(inTransform, inSpline.m_ControlPointA);

                RenderGizmo_ControlLine(inTransform, inSpline.m_Vertices[inSpline.m_Vertices.Length - 1].Point, inSpline.m_ControlPointB);
                RenderGizmo_ControlPoint(inTransform, inSpline.m_ControlPointB);
            }
        }
        static private void InsertVertexAfter(MultiSpline inSpline, int inIndex)
        {
            if (inSpline.m_Vertices == null || inSpline.m_Type == GenType.Uninitialized || inSpline.m_Type == GenType.Simple)
            {
                return;
            }

            int   currentIndex = inIndex;
            float lerp         = 0.5f;
            bool  bAddToEnd    = false;

            int segCount = inSpline.m_Vertices.Length;

            if (!inSpline.m_Looped)
            {
                --segCount;
            }

            if (currentIndex == inSpline.m_Vertices.Length - 1)
            {
                bAddToEnd = true;
                if (!inSpline.m_Looped)
                {
                    --currentIndex;
                    lerp = 1.5f;
                }
            }

            float   realLerp = (float)(currentIndex + lerp) / segCount;
            Vector3 next     = inSpline.GetPoint(realLerp);
            Vector3 dir      = inSpline.GetDirection(realLerp);

            CSplineVertex vert;

            vert.Point     = next;
            vert.InTangent = vert.OutTangent = inSpline.m_Type == GenType.CSpline ? dir : Vector3.zero;

            if (bAddToEnd)
            {
                UnityEditor.ArrayUtility.Add(ref inSpline.m_Vertices, vert);
            }
            else
            {
                UnityEditor.ArrayUtility.Insert(ref inSpline.m_Vertices, currentIndex + 1, vert);
            }
        }
            private void SceneGUI_Simple(MultiSpline inSpline)
            {
                Transform tr = inSpline.transform;

                if (Tools.current == Tool.Move)
                {
                    // Starting position
                    {
                        EditorGUI.BeginChangeCheck();
                        Vector3 newStartPos = Handles.PositionHandle(
                            tr.TransformPoint(inSpline.m_Vertices[0].Point),
                            Quaternion.identity);
                        if (EditorGUI.EndChangeCheck())
                        {
                            MarkDirty("Changed starting position");
                            inSpline.m_Vertices[0].Point = tr.InverseTransformPoint(newStartPos);
                        }
                    }

                    // Ending position
                    {
                        EditorGUI.BeginChangeCheck();
                        Vector3 newEndPos = Handles.PositionHandle(
                            tr.TransformPoint(inSpline.m_Vertices[1].Point),
                            Quaternion.identity);
                        if (EditorGUI.EndChangeCheck())
                        {
                            MarkDirty("Changed ending position");
                            inSpline.m_Vertices[1].Point = tr.InverseTransformPoint(newEndPos);
                        }
                    }

                    // Control position
                    {
                        EditorGUI.BeginChangeCheck();
                        Vector3 newControlPos = Handles.PositionHandle(
                            tr.TransformPoint(inSpline.m_ControlPointA),
                            Quaternion.identity);
                        if (EditorGUI.EndChangeCheck())
                        {
                            MarkDirty("Changed control position");
                            inSpline.m_ControlPointA = tr.InverseTransformPoint(newControlPos);
                        }
                    }
                }
            }
            private void SceneGUI_Linear(MultiSpline inSpline)
            {
                Transform tr = inSpline.transform;

                if (Tools.current == Tool.Move)
                {
                    for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
                    {
                        EditorGUI.BeginChangeCheck();
                        Vector3 pos = Handles.PositionHandle(
                            tr.TransformPoint(inSpline.m_Vertices[i].Point),
                            Quaternion.identity);
                        if (EditorGUI.EndChangeCheck())
                        {
                            MarkDirty("Changed node position");
                            inSpline.m_Vertices[i].Point = tr.InverseTransformPoint(pos);
                        }
                    }
                }
            }
        static private void RecenterVertices(MultiSpline inSpline, Vector3 inZero)
        {
            // Center the vertices
            if (inSpline.m_Vertices != null)
            {
                Vector3 currentCenter = Vector3.zero;
                for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
                {
                    currentCenter += inSpline.m_Vertices[i].Point;
                }
                currentCenter /= inSpline.m_Vertices.Length;

                Vector3 offset = inZero - currentCenter;
                for (int i = 0; i < inSpline.m_Vertices.Length; ++i)
                {
                    inSpline.m_Vertices[i].Point += offset;
                }

                inSpline.m_ControlPointA += offset;
                inSpline.m_ControlPointB += offset;
            }
        }
        static private void InsertVertexBefore(MultiSpline inSpline, int inIndex)
        {
            if (inSpline.m_Vertices == null || inSpline.m_Type == GenType.Uninitialized || inSpline.m_Type == GenType.Simple)
            {
                return;
            }

            int   currentIndex = inIndex;
            float lerp         = -0.5f;

            int segCount = inSpline.m_Vertices.Length;

            if (!inSpline.m_Looped)
            {
                --segCount;
            }

            if (currentIndex == 0)
            {
                if (!inSpline.m_Looped)
                {
                    ++currentIndex;
                    lerp = -1.5f;
                }
            }

            float   realLerp = (float)(currentIndex + lerp) / segCount;
            Vector3 next     = inSpline.GetPoint(realLerp);
            Vector3 dir      = inSpline.GetDirection(realLerp);

            CSplineVertex vert;

            vert.Point     = next;
            vert.InTangent = vert.OutTangent = inSpline.m_Type == GenType.CSpline ? dir : Vector3.zero;

            UnityEditor.ArrayUtility.Insert(ref inSpline.m_Vertices, currentIndex, vert);
            inSpline.m_Dirty = true;
        }
 static private void ConvertToCSpline(MultiSpline inSpline)
 {
     //TODO(Alex): Implement
 }
            public override void OnInspectorGUI()
            {
                MultiSpline ms = (MultiSpline)target;

                if (!ms)
                {
                    return;
                }

                InitializeStyles();
                ResetUndo();

                // Editing controls
                if (ms.m_Type != GenType.Uninitialized)
                {
                    HeaderText("Edit Controls");

                    if (s_CurrentlyEditing == ms)
                    {
                        if (GUILayout.Button("Stop Editing", s_ActivatedButton))
                        {
                            s_CurrentlyEditing = null;
                        }
                    }
                    else
                    {
                        if (GUILayout.Button("Edit", EditorStyles.miniButton))
                        {
                            s_CurrentlyEditing = ms;
                        }
                    }
                }

                using (new EnableGUI(s_CurrentlyEditing == ms || ms.m_Type == GenType.Uninitialized))
                {
                    HeaderText("Spline Types");

                    if (ms.m_Type == GenType.Uninitialized)
                    {
                        EditorGUILayout.HelpBox("This spline has not been initialized. Select one of the following types before starting.", MessageType.Warning);
                    }

                    EditorGUILayout.BeginHorizontal();
                    {
                        if (GUILayout.Button("Simple Spline", ms.m_Type == GenType.Simple ? s_ActivatedButton : EditorStyles.miniButton))
                        {
                            MarkDirty("Converted to Simple");
                            ConvertTo(ms, GenType.Simple);
                            s_CurrentlyEditing = ms;
                        }
                        if (GUILayout.Button("Linear Spline", ms.m_Type == GenType.Linear ? s_ActivatedButton : EditorStyles.miniButton))
                        {
                            MarkDirty("Converted to Linear");
                            ConvertTo(ms, GenType.Linear);
                            s_CurrentlyEditing = ms;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.BeginHorizontal();
                    {
                        if (GUILayout.Button("C-Spline", ms.m_Type == GenType.CSpline ? s_ActivatedButton : EditorStyles.miniButton))
                        {
                            MarkDirty("Converted to CSpline");
                            ConvertTo(ms, GenType.CSpline);
                            s_CurrentlyEditing = ms;
                        }
                        if (GUILayout.Button("Catmull-Rom / Cardinal", ms.m_Type == GenType.Cardinal ? s_ActivatedButton : EditorStyles.miniButton))
                        {
                            MarkDirty("Converted to Cardinal");
                            ConvertTo(ms, GenType.Cardinal);
                            s_CurrentlyEditing = ms;
                        }
                    }
                    EditorGUILayout.EndHorizontal();

                    if (ms.m_Type != GenType.Uninitialized)
                    {
                        GUILayout.Space(8);

                        switch (ms.m_Type)
                        {
                        case GenType.Simple:
                            InspectorGUI_Simple(ms);
                            break;

                        case GenType.Linear:
                            InspectorGUI_Linear(ms);
                            break;

                        case GenType.CSpline:
                            InspectorGUI_CSpline(ms);
                            break;

                        case GenType.Cardinal:
                            InspectorGUI_Cardinal(ms);
                            break;
                        }

                        GUILayout.Space(8);

                        HeaderText("Utilities", 80);

                        if (GUILayout.Button("Re-center Vertices"))
                        {
                            MarkDirty("Recenter Vertices");
                            RecenterVertices(ms, Vector3.zero);
                        }
                    }
                }
            }
            private void InspectorGUI_Linear(MultiSpline inSpline)
            {
                HeaderText("Linear Spline");

                ToggleField("Looped", ref inSpline.m_Looped);
            }
            private void InspectorGUI_CSpline(MultiSpline inSpline)
            {
                HeaderText("C-Spline", 80);

                ToggleField("Looped", ref inSpline.m_Looped);
            }
        static private void ConvertToSimple(MultiSpline inSpline)
        {
            Vector3 start, end, control;

            switch (inSpline.m_Type)
            {
            case GenType.Uninitialized:
            default:
            {
                // initialize with default values
                inSpline.m_Looped = false;
                start             = Vector3.left;
                end     = Vector3.right;
                control = Vector3.up;
                break;
            }

            case GenType.Simple:
            {
                // Already a simple spline
                return;
            }

            case GenType.Linear:
            {
                // take start and end
                int endIdx = inSpline.m_Vertices.Length - 1;
                start = inSpline.m_Vertices[0].Point;
                end   = inSpline.m_Vertices[endIdx].Point;

                // average the mid vertices to get control point
                if (endIdx > 1)
                {
                    Vector3 vSum = Vector3.zero;
                    for (int i = 1; i < endIdx; ++i)
                    {
                        vSum += inSpline.m_Vertices[i].Point;
                    }
                    vSum.x /= (endIdx - 1);
                    vSum.y /= (endIdx - 1);
                    vSum.z /= (endIdx - 1);
                    control = vSum;
                }
                else
                {
                    control = (start + end) * 0.5f;
                }

                break;
            }

            case GenType.CSpline:
            case GenType.Cardinal:
            {
                // take start and end
                int endIdx = inSpline.m_Vertices.Length - 1;
                start = inSpline.m_Vertices[0].Point;
                end   = inSpline.m_Vertices[endIdx].Point;

                // Average the out/in control points
                control = ((start - inSpline.m_Vertices[0].OutTangent)
                           + (end + inSpline.m_Vertices[endIdx].InTangent)) * 0.5f;

                break;
            }
            }

            Array.Resize(ref inSpline.m_Vertices, 2);
            inSpline.m_Vertices[0]   = new CSplineVertex(start);
            inSpline.m_Vertices[1]   = new CSplineVertex(end);
            inSpline.m_ControlPointA = control;
            inSpline.m_Looped        = false;
        }