// update cache (optimized)
        public override void updateCache()
        {
            // destroy cache
            destroyCache(); //undo handled inside
            // create new cache
            _clearCache();  //undo handled inside
            // sort keys
            sortKeys();     //undo handled inside

            for (int i = 0; i < keys.Count; i++)
            {
                AMScaleAction a = ScriptableObject.CreateInstance <AMScaleAction>();
                a.startFrame = keys[i].frame;
                if (keys.Count > (i + 1))
                {
                    a.endFrame = keys[i + 1].frame;
                }
                else
                {
                    a.endFrame = -1;
                }

                a.obj        = obj;
                a.startScale = (keys[i] as AMScaleKey).scale;
                if (a.endFrame != -1)
                {
                    a.endScale = (keys[i + 1] as AMScaleKey).scale;
                }

                a.easeType   = (keys[i] as AMScaleKey).easeType;
                a.customEase = new List <float>(keys[i].customEase);
                // add to cache
                cache.Add(a);
            }
        }
        // preview a frame in the scene view
        public override void previewFrame(float frame, AMTrack extraTrack = null)
        {
            if (!obj)
            {
                return;
            }
            if (cache.Count <= 1)
            {
                return;
            }
            // if before first frame
            if (frame <= (float)cache[0].startFrame)
            {
                obj.localScale = (cache[0] as AMScaleAction).startScale;
                return;
            }
            // if beyond last frame
            if (frame >= (float)(cache[cache.Count - 2] as AMScaleAction).endFrame)
            {
                obj.localScale = (cache[cache.Count - 2] as AMScaleAction).endScale;
                return;
            }
            // if lies on curve
            for (int i = 0; i <= cache.Count - 2; ++i)
            {
                AMScaleAction action = cache[i] as AMScaleAction;
                if (((int)frame < action.startFrame) || ((int)frame > action.endFrame))
                {
                    continue;
                }

                float _value;
                float framePositionInPath = frame - (float)action.startFrame;
                if (framePositionInPath < 0f)
                {
                    framePositionInPath = 0f;
                }

                AMTween.EasingFunction ease;
                AnimationCurve         curve = null;

                if (action.hasCustomEase())
                {
                    ease  = AMTween.customEase;
                    curve = action.easeCurve;
                }
                else
                {
                    ease = AMTween.GetEasingFunction((AMTween.EaseType)action.easeType);
                }

                _value = ease(0f, 1f, framePositionInPath / action.getNumberOfFrames(), curve);

                obj.localScale = Vector3.Lerp(action.startScale, action.endScale, _value);
                return;
            }
        }