/// <summary>Sets the bones and constraints to their setup pose values.</summary>
        public void SetBonesToSetupPose()
        {
            ExposedList <Bone> bones = this.bones;

            for (int i = 0, n = bones.Count; i < n; i++)
            {
                bones.Items[i].SetToSetupPose();
            }

            ExposedList <IkConstraint> ikConstraints = this.ikConstraints;

            for (int i = 0, n = ikConstraints.Count; i < n; i++)
            {
                IkConstraint constraint = ikConstraints.Items[i];
                constraint.bendDirection = constraint.data.bendDirection;
                constraint.mix           = constraint.data.mix;
            }

            ExposedList <TransformConstraint> transformConstraints = this.transformConstraints;

            for (int i = 0, n = transformConstraints.Count; i < n; i++)
            {
                TransformConstraint     constraint = transformConstraints.Items[i];
                TransformConstraintData data       = constraint.data;
                constraint.rotateMix    = data.rotateMix;
                constraint.translateMix = data.translateMix;
                constraint.scaleMix     = data.scaleMix;
                constraint.shearMix     = data.shearMix;
            }
        }
        /** @return May be null. */
        public TransformConstraint FindTransformConstraint(String constraintName)
        {
            if (constraintName == null)
            {
                throw new ArgumentNullException("constraintName cannot be null.");
            }
            ExposedList <TransformConstraint> transformConstraints = this.transformConstraints;

            for (int i = 0, n = transformConstraints.Count; i < n; i++)
            {
                TransformConstraint transformConstraint = transformConstraints.Items[i];
                if (transformConstraint.data.name == constraintName)
                {
                    return(transformConstraint);
                }
            }
            return(null);
        }
        /// <summary>Caches information about bones and constraints. Must be called if bones or constraints are added
        /// or removed.</summary>
        public void UpdateCache()
        {
            ExposedList <Bone>                bones                = this.bones;
            ExposedList <IUpdatable>          updateCache          = this.updateCache;
            ExposedList <IkConstraint>        ikConstraints        = this.ikConstraints;
            ExposedList <TransformConstraint> transformConstraints = this.transformConstraints;
            int ikConstraintsCount        = ikConstraints.Count;
            int transformConstraintsCount = transformConstraints.Count;

            updateCache.Clear();
            for (int i = 0, n = bones.Count; i < n; i++)
            {
                Bone bone = bones.Items[i];
                updateCache.Add(bone);
                for (int ii = 0; ii < ikConstraintsCount; ii++)
                {
                    IkConstraint ikConstraint = ikConstraints.Items[ii];
                    if (bone == ikConstraint.bones.Items[ikConstraint.bones.Count - 1])
                    {
                        updateCache.Add(ikConstraint);
                        break;
                    }
                }
            }

            for (int i = 0; i < transformConstraintsCount; i++)
            {
                TransformConstraint transformConstraint = transformConstraints.Items[i];
                for (int ii = updateCache.Count - 1; i >= 0; ii--)
                {
                    if (updateCache.Items[ii] == transformConstraint.bone)
                    {
                        updateCache.Insert(ii + 1, transformConstraint);
                        break;
                    }
                }
            }
        }
        override public void Apply(Skeleton skeleton, float lastTime, float time, ExposedList <Event> firedEvents, float alpha)
        {
            float[] frames = this.frames;
            if (time < frames[0])
            {
                return;                               // Time is before first frame.
            }
            TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];

            if (time >= frames[frames.Length - 5])               // Time is after last frame.
            {
                int i = frames.Length - 1;
                constraint.rotateMix    += (frames[i - 3] - constraint.rotateMix) * alpha;
                constraint.translateMix += (frames[i - 2] - constraint.translateMix) * alpha;
                constraint.scaleMix     += (frames[i - 1] - constraint.scaleMix) * alpha;
                constraint.shearMix     += (frames[i] - constraint.shearMix) * alpha;
                return;
            }

            // Interpolate between the previous frame and the current frame.
            int   frame     = Animation.binarySearch(frames, time, 5);
            float frameTime = frames[frame];
            float percent   = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);

            percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));

            float rotate    = frames[frame + PREV_ROTATE_MIX];
            float translate = frames[frame + PREV_TRANSLATE_MIX];
            float scale     = frames[frame + PREV_SCALE_MIX];
            float shear     = frames[frame + PREV_SHEAR_MIX];

            constraint.rotateMix    += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
            constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) * alpha;
            constraint.scaleMix     += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha;
            constraint.shearMix     += (shear + (frames[frame + SHEAR_MIX] - shear) * percent - constraint.shearMix) * alpha;
        }