瀏覽代碼

Timelines are done.

Stephen Gowen 7 年之前
父節點
當前提交
3038a90cc5
共有 30 個文件被更改,包括 1072 次插入833 次删除
  1. 3 0
      spine-cpp/spine-cpp/include/spine/Bone.h
  2. 3 0
      spine-cpp/spine-cpp/include/spine/BoneData.h
  3. 15 46
      spine-cpp/spine-cpp/include/spine/EventTimeline.h
  4. 1 0
      spine-cpp/spine-cpp/include/spine/IkConstraint.h
  5. 1 0
      spine-cpp/spine-cpp/include/spine/IkConstraintData.h
  6. 3 57
      spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h
  7. 3 0
      spine-cpp/spine-cpp/include/spine/PathConstraint.h
  8. 3 0
      spine-cpp/spine-cpp/include/spine/PathConstraintData.h
  9. 10 65
      spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h
  10. 3 49
      spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h
  11. 0 35
      spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h
  12. 3 63
      spine-cpp/spine-cpp/include/spine/ScaleTimeline.h
  13. 2 46
      spine-cpp/spine-cpp/include/spine/ShearTimeline.h
  14. 1 0
      spine-cpp/spine-cpp/include/spine/TransformConstraint.h
  15. 1 0
      spine-cpp/spine-cpp/include/spine/TransformConstraintData.h
  16. 5 79
      spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h
  17. 11 77
      spine-cpp/spine-cpp/include/spine/TranslateTimeline.h
  18. 15 134
      spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h
  19. 60 2
      spine-cpp/spine-cpp/include/spine/Vector.h
  20. 66 1
      spine-cpp/spine-cpp/src/spine/EventTimeline.cpp
  21. 69 1
      spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp
  22. 73 1
      spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp
  23. 52 2
      spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp
  24. 45 1
      spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp
  25. 81 1
      spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp
  26. 59 1
      spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp
  27. 169 169
      spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp
  28. 85 1
      spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp
  29. 73 1
      spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp
  30. 157 1
      spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp

+ 3 - 0
spine-cpp/spine-cpp/include/spine/Bone.h

@@ -57,6 +57,9 @@ namespace Spine
         friend class Skeleton;
         friend class RegionAttachment;
         friend class PointAttachment;
+        friend class ScaleTimeline;
+        friend class ShearTimeline;
+        friend class TranslateTimeline;
         
     public:
         static void setYDown(bool inValue);

+ 3 - 0
spine-cpp/spine-cpp/include/spine/BoneData.h

@@ -40,6 +40,9 @@ namespace Spine
     class BoneData
     {
         friend class RotateTimeline;
+        friend class ScaleTimeline;
+        friend class ShearTimeline;
+        friend class TranslateTimeline;
         
     public:
         BoneData(int index, std::string name, BoneData* parent = NULL);

+ 15 - 46
spine-cpp/spine-cpp/include/spine/EventTimeline.h

@@ -39,56 +39,25 @@ namespace Spine
     {
         RTTI_DECL;
         
+    public:
+        EventTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
         
-//        internal float[] frames;
-//        private Event[] events;
-//        
-//        public float[] Frames { return frames; } set { frames = inValue; } // time, ...
-//        public Event[] Events { return events; } set { events = inValue; }
-//        public int FrameCount { return frames.Length; }
-//
-//        
-//        public EventTimeline (int frameCount) {
-//            frames = new float[frameCount];
-//            events = new Event[frameCount];
-//        }
-//        
-//        /// Sets the time and value of the specified keyframe.
-//        public void setFrame (int frameIndex, Event e) {
-//            frames[frameIndex] = e.Time;
-//            events[frameIndex] = e;
-//        }
-//        
-//        /// Fires events for frames &gt; lastTime and &lt;= time.
-//        public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            if (firedEvents == NULL) return;
-//            float[] frames = _frames;
-//            int frameCount = frames.Length;
-//            
-//            if (lastTime > time) { // Fire events after last time for looped animations.
-//                Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, pose, direction);
-//                lastTime = -1f;
-//            } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
-//                return;
-//            if (time < frames[0]) return; // Time is before first frame.
-//            
-//            int frame;
-//            if (lastTime < frames[0])
-//                frame = 0;
-//            else {
-//                frame = Animation::binarySearch(frames, lastTime);
-//                float frameTime = frames[frame];
-//                while (frame > 0) { // Fire multiple events with the same frame.
-//                    if (frames[frame - 1] != frameTime) break;
-//                    frame--;
-//                }
-//            }
-//            for (; frame < frameCount && time >= frames[frame]; frame++)
-//                firedEvents.push_back(events[frame]);
-//        }
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frameIndex, Event* e);
+        
+        Vector<float> getFrames();
+        void setFrames(Vector<float>& inValue);
+        Vector<Event*>& getEvents();
+        void setEvents(Vector<Event*>& inValue);
+        int getFrameCount();
+        
+    private:
+        Vector<float> _frames;
+        Vector<Event*> _events;
     };
 }
 

+ 1 - 0
spine-cpp/spine-cpp/include/spine/IkConstraint.h

@@ -44,6 +44,7 @@ namespace Spine
     class IkConstraint : public Constraint
     {
         friend class Skeleton;
+        friend class IkConstraintTimeline;
         
         RTTI_DECL;
         

+ 1 - 0
spine-cpp/spine-cpp/include/spine/IkConstraintData.h

@@ -43,6 +43,7 @@ namespace Spine
     {
         friend class IkConstraint;
         friend class Skeleton;
+        friend class IkConstraintTimeline;
         
     public:
         IkConstraintData(std::string name);

+ 3 - 57
spine-cpp/spine-cpp/include/spine/IkConstraintTimeline.h

@@ -48,69 +48,15 @@ namespace Spine
         
         virtual int getPropertyId();
         
+        /// Sets the time, mix and bend direction of the specified keyframe.
+        void setFrame (int frameIndex, float time, float mix, int bendDirection);
+        
     private:
         static const int PREV_TIME, PREV_MIX, PREV_BEND_DIRECTION;
         static const int MIX, BEND_DIRECTION;
         
         Vector<float> _frames;
         int _ikConstraintIndex;
-        
-//        public int IkConstraintIndex { return ikConstraintIndex; } set { ikConstraintIndex = inValue; }
-//        public float[] Frames { return frames; } set { frames = inValue; } // time, mix, bendDirection, ...
-//
-//
-//        
-//        /// Sets the time, mix and bend direction of the specified keyframe.
-//        public void setFrame (int frameIndex, float time, float mix, int bendDirection) {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + MIX] = mix;
-//            frames[frameIndex + BEND_DIRECTION] = bendDirection;
-//        }
-//        
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        constraint.mix = constraint.data.mix;
-//                        constraint.bendDirection = constraint.data.bendDirection;
-//                        return;
-//                    case MixPose_Current:
-//                        constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
-//                        constraint.bendDirection = constraint.data.bendDirection;
-//                        return;
-//                }
-//                return;
-//            }
-//            
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                if (pose == MixPose_Setup) {
-//                    constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
-//                    constraint.bendDirection = direction == MixDirection_Out ? constraint.data.bendDirection
-//                    : (int)frames[frames.Length + PREV_BEND_DIRECTION];
-//                } else {
-//                    constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
-//                    if (direction == MixDirection_In) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
-//                }
-//                return;
-//            }
-//            
-//            // Interpolate between the previous frame and the current frame.
-//            int frame = Animation::binarySearch(frames, time, ENTRIES);
-//            float mix = frames[frame + PREV_MIX];
-//            float frameTime = frames[frame];
-//            float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//            
-//            if (pose == MixPose_Setup) {
-//                constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
-//                constraint.bendDirection = direction == MixDirection_Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION];
-//            } else {
-//                constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
-//                if (direction == MixDirection_In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
-//            }
-//        }
     };
 }
 

+ 3 - 0
spine-cpp/spine-cpp/include/spine/PathConstraint.h

@@ -46,6 +46,9 @@ namespace Spine
     class PathConstraint : public Constraint
     {
         friend class Skeleton;
+        friend class PathConstraintMixTimeline;
+        friend class PathConstraintPositionTimeline;
+        friend class PathConstraintSpacingTimeline;
         
         RTTI_DECL;
         

+ 3 - 0
spine-cpp/spine-cpp/include/spine/PathConstraintData.h

@@ -47,6 +47,9 @@ namespace Spine
     {
         friend class PathConstraint;
         friend class Skeleton;
+        friend class PathConstraintMixTimeline;
+        friend class PathConstraintPositionTimeline;
+        friend class PathConstraintSpacingTimeline;
         
     public:
         PathConstraintData(std::string name);

+ 10 - 65
spine-cpp/spine-cpp/include/spine/PathConstraintMixTimeline.h

@@ -40,78 +40,23 @@ namespace Spine
         RTTI_DECL;
         
     public:
+        static const int ENTRIES;
+        
+        PathConstraintMixTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
         
     private:
+        static const int PREV_TIME, PREV_ROTATE, PREV_TRANSLATE;
+        static const int ROTATE, TRANSLATE;
+        
+        Vector<float> _frames;
         int _pathConstraintIndex;
         
-//        public const int ENTRIES = 3;
-//        private const int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1;
-//        private const int ROTATE = 1, TRANSLATE = 2;
-//
-//        internal float[] frames;
-//        
-//        public int PathConstraintIndex { return pathConstraintIndex; } set { pathConstraintIndex = inValue; }
-//        public float[] Frames { return frames; } set { frames = inValue; } // time, rotate mix, translate mix, ...
-//
-//        
-//        public PathConstraintMixTimeline (int frameCount)
-//        : base(frameCount) {
-//            frames = new float[frameCount * ENTRIES];
-//        }
-//        
-//        /// Sets the time and mixes of the specified keyframe.
-//        public void setFrame (int frameIndex, float time, float rotateMix, float translateMix) {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + ROTATE] = rotateMix;
-//            frames[frameIndex + TRANSLATE] = translateMix;
-//        }
-//        
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        constraint.rotateMix = constraint.data.rotateMix;
-//                        constraint.translateMix = constraint.data.translateMix;
-//                        return;
-//                    case MixPose_Current:
-//                        constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha;
-//                        constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//            
-//            float rotate, translate;
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                rotate = frames[frames.Length + PREV_ROTATE];
-//                translate = frames[frames.Length + PREV_TRANSLATE];
-//            } else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                rotate = frames[frame + PREV_ROTATE];
-//                translate = frames[frame + PREV_TRANSLATE];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//                
-//                rotate += (frames[frame + ROTATE] - rotate) * percent;
-//                translate += (frames[frame + TRANSLATE] - translate) * percent;
-//            }
-//            
-//            if (pose == MixPose_Setup) {
-//                constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
-//                constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
-//            } else {
-//                constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
-//                constraint.translateMix += (translate - constraint.translateMix) * alpha;
-//            }
-//        }
+        /// Sets the time and mixes of the specified keyframe.
+        void setFrame(int frameIndex, float time, float rotateMix, float translateMix);
     };
 }
 

+ 3 - 49
spine-cpp/spine-cpp/include/spine/PathConstraintPositionTimeline.h

@@ -48,61 +48,15 @@ namespace Spine
         
         virtual int getPropertyId();
         
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frameIndex, float time, float value);
+        
     protected:
         static const int PREV_TIME, PREV_VALUE;
         static const int VALUE;
         
         Vector<float> _frames;
         int _pathConstraintIndex;
-        
-        
-//
-//
-//
-//        
-//        public int PathConstraintIndex { return pathConstraintIndex; } set { pathConstraintIndex = inValue; }
-//        public float[] Frames { return frames; } set { frames = inValue; } // time, position, ...
-//        
-//        /// Sets the time and value of the specified keyframe.
-//        public void setFrame (int frameIndex, float time, float value) {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + VALUE] = inValue;
-//        }
-//        
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        constraint.position = constraint.data.position;
-//                        return;
-//                    case MixPose_Current:
-//                        constraint.position += (constraint.data.position - constraint.position) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//            
-//            float position;
-//            if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame.
-//                position = frames[frames.Length + PREV_VALUE];
-//            else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                position = frames[frame + PREV_VALUE];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//                
-//                position += (frames[frame + VALUE] - position) * percent;
-//            }
-//            if (pose == MixPose_Setup)
-//                constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
-//            else
-//                constraint.position += (position - constraint.position) * alpha;
-//        }
     };
 }
 

+ 0 - 35
spine-cpp/spine-cpp/include/spine/PathConstraintSpacingTimeline.h

@@ -45,41 +45,6 @@ namespace Spine
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
-        
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        constraint.spacing = constraint.data.spacing;
-//                        return;
-//                    case MixPose_Current:
-//                        constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float spacing;
-//            if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame.
-//                spacing = frames[frames.Length + PREV_VALUE];
-//            else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                spacing = frames[frame + PREV_VALUE];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                spacing += (frames[frame + VALUE] - spacing) * percent;
-//            }
-//
-//            if (pose == MixPose_Setup)
-//                constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
-//            else
-//                constraint.spacing += (spacing - constraint.spacing) * alpha;
-//        }
     };
 }
 

+ 3 - 63
spine-cpp/spine-cpp/include/spine/ScaleTimeline.h

@@ -39,72 +39,12 @@ namespace Spine
     {
         RTTI_DECL;
         
+    public:
+        ScaleTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
-        
-//        public ScaleTimeline (int frameCount)
-//        : base(frameCount) {
-//        }
-//
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            Bone bone = skeleton.bones.Items[boneIndex];
-//
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        bone.scaleX = bone.data.scaleX;
-//                        bone.scaleY = bone.data.scaleY;
-//                        return;
-//                    case MixPose_Current:
-//                        bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
-//                        bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float x, y;
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                x = frames[frames.Length + PREV_X] * bone.data.scaleX;
-//                y = frames[frames.Length + PREV_Y] * bone.data.scaleY;
-//            } else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                x = frames[frame + PREV_X];
-//                y = frames[frame + PREV_Y];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX;
-//                y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY;
-//            }
-//            if (alpha == 1) {
-//                bone.scaleX = x;
-//                bone.scaleY = y;
-//            } else {
-//                float bx, by;
-//                if (pose == MixPose_Setup) {
-//                    bx = bone.data.scaleX;
-//                    by = bone.data.scaleY;
-//                } else {
-//                    bx = bone.scaleX;
-//                    by = bone.scaleY;
-//                }
-//                // Mixing out uses sign of setup or current pose, else use sign of key.
-//                if (direction == MixDirection_Out) {
-//                    x = (x >= 0 ? x : -x) * (bx >= 0 ? 1 : -1);
-//                    y = (y >= 0 ? y : -y) * (by >= 0 ? 1 : -1);
-//                } else {
-//                    bx = (bx >= 0 ? bx : -bx) * (x >= 0 ? 1 : -1);
-//                    by = (by >= 0 ? by : -by) * (y >= 0 ? 1 : -1);
-//                }
-//                bone.scaleX = bx + (x - bx) * alpha;
-//                bone.scaleY = by + (y - by) * alpha;
-//            }
-//        }
     };
 }
 

+ 2 - 46
spine-cpp/spine-cpp/include/spine/ShearTimeline.h

@@ -40,55 +40,11 @@ namespace Spine
         RTTI_DECL;
         
     public:
+        ShearTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
-        
-//        public ShearTimeline (int frameCount)
-//        : base(frameCount) {
-//        }
-//
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            Bone bone = skeleton.bones.Items[boneIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        bone.shearX = bone.data.shearX;
-//                        bone.shearY = bone.data.shearY;
-//                        return;
-//                    case MixPose_Current:
-//                        bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
-//                        bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float x, y;
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                x = frames[frames.Length + PREV_X];
-//                y = frames[frames.Length + PREV_Y];
-//            } else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                x = frames[frame + PREV_X];
-//                y = frames[frame + PREV_Y];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                x = x + (frames[frame + X] - x) * percent;
-//                y = y + (frames[frame + Y] - y) * percent;
-//            }
-//            if (pose == MixPose_Setup) {
-//                bone.shearX = bone.data.shearX + x * alpha;
-//                bone.shearY = bone.data.shearY + y * alpha;
-//            } else {
-//                bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
-//                bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
-//            }
-//        }
     };
 }
 

+ 1 - 0
spine-cpp/spine-cpp/include/spine/TransformConstraint.h

@@ -44,6 +44,7 @@ namespace Spine
     class TransformConstraint : public Constraint
     {
         friend class Skeleton;
+        friend class TransformConstraintTimeline;
         
         RTTI_DECL;
         

+ 1 - 0
spine-cpp/spine-cpp/include/spine/TransformConstraintData.h

@@ -43,6 +43,7 @@ namespace Spine
     {
         friend class TransformConstraint;
         friend class Skeleton;
+        friend class TransformConstraintTimeline;
         
     public:
         TransformConstraintData(std::string name);

+ 5 - 79
spine-cpp/spine-cpp/include/spine/TransformConstraintTimeline.h

@@ -42,94 +42,20 @@ namespace Spine
     public:
         static const int ENTRIES;
         
+        TransformConstraintTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
         
+        void setFrame(int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix);
+        
     private:
         static const int PREV_TIME, PREV_ROTATE, PREV_TRANSLATE, PREV_SCALE, PREV_SHEAR;
         static const int ROTATE, TRANSLATE, SCALE, SHEAR;
         
+        Vector<float> _frames;
         int _transformConstraintIndex;
-        
-//        internal float[] frames;
-//
-//        public int TransformConstraintIndex { return transformConstraintIndex; } set { transformConstraintIndex = inValue; }
-//        public float[] Frames { return frames; } set { frames = inValue; } // time, rotate mix, translate mix, scale mix, shear mix, ...
-//
-//
-//        public TransformConstraintTimeline (int frameCount)
-//        : base(frameCount) {
-//            frames = new float[frameCount * ENTRIES];
-//        }
-//
-//        public void setFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + ROTATE] = rotateMix;
-//            frames[frameIndex + TRANSLATE] = translateMix;
-//            frames[frameIndex + SCALE] = scaleMix;
-//            frames[frameIndex + SHEAR] = shearMix;
-//        }
-//
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) {
-//                var data = constraint.data;
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        constraint.rotateMix = data.rotateMix;
-//                        constraint.translateMix = data.translateMix;
-//                        constraint.scaleMix = data.scaleMix;
-//                        constraint.shearMix = data.shearMix;
-//                        return;
-//                    case MixPose_Current:
-//                        constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha;
-//                        constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha;
-//                        constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha;
-//                        constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float rotate, translate, scale, shear;
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                int i = frames.Length;
-//                rotate = frames[i + PREV_ROTATE];
-//                translate = frames[i + PREV_TRANSLATE];
-//                scale = frames[i + PREV_SCALE];
-//                shear = frames[i + PREV_SHEAR];
-//            } else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                rotate = frames[frame + PREV_ROTATE];
-//                translate = frames[frame + PREV_TRANSLATE];
-//                scale = frames[frame + PREV_SCALE];
-//                shear = frames[frame + PREV_SHEAR];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                rotate += (frames[frame + ROTATE] - rotate) * percent;
-//                translate += (frames[frame + TRANSLATE] - translate) * percent;
-//                scale += (frames[frame + SCALE] - scale) * percent;
-//                shear += (frames[frame + SHEAR] - shear) * percent;
-//            }
-//            if (pose == MixPose_Setup) {
-//                TransformConstraintData data = constraint.data;
-//                constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
-//                constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
-//                constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
-//                constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
-//            } else {
-//                constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
-//                constraint.translateMix += (translate - constraint.translateMix) * alpha;
-//                constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
-//                constraint.shearMix += (shear - constraint.shearMix) * alpha;
-//            }
-//        }
     };
 }
 

+ 11 - 77
spine-cpp/spine-cpp/include/spine/TranslateTimeline.h

@@ -43,89 +43,23 @@ namespace Spine
         RTTI_DECL;
         
     public:
+        static const int ENTRIES;
+        
+        TranslateTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
         
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frameIndex, float time, float x, float y);
+        
     protected:
-        int _boneIndex;
+        static const int PREV_TIME, PREV_X, PREV_Y;
+        static const int X, Y;
         
-//        public const int ENTRIES = 3;
-//        protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
-//        protected const int X = 1, Y = 2;
-//
-//        internal float[] frames;
-//
-//        public int getBoneIndex { return boneIndex; } set { boneIndex = inValue; }
-//        public Vector<float> getFrames { return frames; } set { frames = inValue; } // time, value, value, ...
-//
-//
-//        public TranslateTimeline(int frameCount) : CurveTimeline(frameCount)
-//        {
-//            frames = new float[frameCount * ENTRIES];
-//        }
-//
-//        /// Sets the time and value of the specified keyframe.
-//        public void setFrame(int frameIndex, float time, float x, float y)
-//        {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + X] = x;
-//            frames[frameIndex + Y] = y;
-//        }
-//
-//        override public void apply(Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction)
-//        {
-//            Bone bone = skeleton.bones.Items[boneIndex];
-//
-//            float[] frames = _frames;
-//            if (time < frames[0])
-//            {
-//                switch (pose)
-//                {
-//                    case MixPose_Setup:
-//                        bone.x = bone.data.x;
-//                        bone.y = bone.data.y;
-//                        return;
-//                    case MixPose_Current:
-//                        bone.x += (bone.data.x - bone.x) * alpha;
-//                        bone.y += (bone.data.y - bone.y) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float x, y;
-//            if (time >= frames[frames.Length - ENTRIES])
-//            {
-//                // Time is after last frame.
-//                x = frames[frames.Length + PREV_X];
-//                y = frames[frames.Length + PREV_Y];
-//            }
-//            else
-//            {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                x = frames[frame + PREV_X];
-//                y = frames[frame + PREV_Y];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                x += (frames[frame + X] - x) * percent;
-//                y += (frames[frame + Y] - y) * percent;
-//            }
-//
-//            if (pose == MixPose_Setup)
-//            {
-//                bone.x = bone.data.x + x * alpha;
-//                bone.y = bone.data.y + y * alpha;
-//            }
-//            else
-//            {
-//                bone.x += (bone.data.x + x - bone.x) * alpha;
-//                bone.y += (bone.data.y + y - bone.y) * alpha;
-//            }
-//        }
+        Vector<float> _frames;
+        int _boneIndex;
     };
 }
 

+ 15 - 134
spine-cpp/spine-cpp/include/spine/TwoColorTimeline.h

@@ -40,146 +40,27 @@ namespace Spine
         RTTI_DECL;
         
     public:
+        static const int ENTRIES;
+        
+        TwoColorTimeline(int frameCount);
+        
         virtual void apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction);
         
         virtual int getPropertyId();
         
+        /// Sets the time and value of the specified keyframe.
+        void setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2);
+        
+        int getSlotIndex();
+        void setSlotIndex(int inValue);
+        
     private:
-        int _slotIndex;
+        static const int PREV_TIME, PREV_R, PREV_G, PREV_B, PREV_A;
+        static const int PREV_R2, PREV_G2, PREV_B2;
+        static const int R, G, B, A, R2, G2, B2;
         
-//        public const int ENTRIES = 8;
-//        protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4;
-//        protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1;
-//        protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7;
-//
-//        internal float[] frames; // time, r, g, b, a, r2, g2, b2, ...
-//        public float[] Frames { return frames; }
-//
-//        public int SlotIndex {
-//            get { return slotIndex; }
-//            set {
-//                if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
-//                    slotIndex = inValue;
-//            }
-//        }
-//
-//
-//        public TwoColorTimeline (int frameCount) :
-//        base(frameCount) {
-//            frames = new float[frameCount * ENTRIES];
-//        }
-//
-//        /// Sets the time and value of the specified keyframe.
-//        public void setFrame (int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
-//            frameIndex *= ENTRIES;
-//            frames[frameIndex] = time;
-//            frames[frameIndex + R] = r;
-//            frames[frameIndex + G] = g;
-//            frames[frameIndex + B] = b;
-//            frames[frameIndex + A] = a;
-//            frames[frameIndex + R2] = r2;
-//            frames[frameIndex + G2] = g2;
-//            frames[frameIndex + B2] = b2;
-//        }
-//
-//        override public void Apply (Skeleton skeleton, float lastTime, float time, Vector<Event> firedEvents, float alpha, MixPose pose, MixDirection direction) {
-//            Slot slot = skeleton.slots.Items[slotIndex];
-//            float[] frames = _frames;
-//            if (time < frames[0]) { // Time is before first frame.
-//                var slotData = slot.data;
-//                switch (pose) {
-//                    case MixPose_Setup:
-//                        //    slot.color.set(slot.data.color);
-//                        //    slot.darkColor.set(slot.data.darkColor);
-//                        slot.r = slotData.r;
-//                        slot.g = slotData.g;
-//                        slot.b = slotData.b;
-//                        slot.a = slotData.a;
-//                        slot.r2 = slotData.r2;
-//                        slot.g2 = slotData.g2;
-//                        slot.b2 = slotData.b2;
-//                        return;
-//                    case MixPose_Current:
-//                        slot.r += (slot.r - slotData.r) * alpha;
-//                        slot.g += (slot.g - slotData.g) * alpha;
-//                        slot.b += (slot.b - slotData.b) * alpha;
-//                        slot.a += (slot.a - slotData.a) * alpha;
-//                        slot.r2 += (slot.r2 - slotData.r2) * alpha;
-//                        slot.g2 += (slot.g2 - slotData.g2) * alpha;
-//                        slot.b2 += (slot.b2 - slotData.b2) * alpha;
-//                        return;
-//                }
-//                return;
-//            }
-//
-//            float r, g, b, a, r2, g2, b2;
-//            if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
-//                int i = frames.Length;
-//                r = frames[i + PREV_R];
-//                g = frames[i + PREV_G];
-//                b = frames[i + PREV_B];
-//                a = frames[i + PREV_A];
-//                r2 = frames[i + PREV_R2];
-//                g2 = frames[i + PREV_G2];
-//                b2 = frames[i + PREV_B2];
-//            } else {
-//                // Interpolate between the previous frame and the current frame.
-//                int frame = Animation::binarySearch(frames, time, ENTRIES);
-//                r = frames[frame + PREV_R];
-//                g = frames[frame + PREV_G];
-//                b = frames[frame + PREV_B];
-//                a = frames[frame + PREV_A];
-//                r2 = frames[frame + PREV_R2];
-//                g2 = frames[frame + PREV_G2];
-//                b2 = frames[frame + PREV_B2];
-//                float frameTime = frames[frame];
-//                float percent = GetCurvePercent(frame / ENTRIES - 1,
-//                                                1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
-//
-//                r += (frames[frame + R] - r) * percent;
-//                g += (frames[frame + G] - g) * percent;
-//                b += (frames[frame + B] - b) * percent;
-//                a += (frames[frame + A] - a) * percent;
-//                r2 += (frames[frame + R2] - r2) * percent;
-//                g2 += (frames[frame + G2] - g2) * percent;
-//                b2 += (frames[frame + B2] - b2) * percent;
-//            }
-//            if (alpha == 1) {
-//                slot.r = r;
-//                slot.g = g;
-//                slot.b = b;
-//                slot.a = a;
-//                slot.r2 = r2;
-//                slot.g2 = g2;
-//                slot.b2 = b2;
-//            } else {
-//                float br, bg, bb, ba, br2, bg2, bb2;
-//                if (pose == MixPose_Setup) {
-//                    br = slot.data.r;
-//                    bg = slot.data.g;
-//                    bb = slot.data.b;
-//                    ba = slot.data.a;
-//                    br2 = slot.data.r2;
-//                    bg2 = slot.data.g2;
-//                    bb2 = slot.data.b2;
-//                } else {
-//                    br = slot.r;
-//                    bg = slot.g;
-//                    bb = slot.b;
-//                    ba = slot.a;
-//                    br2 = slot.r2;
-//                    bg2 = slot.g2;
-//                    bb2 = slot.b2;
-//                }
-//                slot.r = br + ((r - br) * alpha);
-//                slot.g = bg + ((g - bg) * alpha);
-//                slot.b = bb + ((b - bb) * alpha);
-//                slot.a = ba + ((a - ba) * alpha);
-//                slot.r2 = br2 + ((r2 - br2) * alpha);
-//                slot.g2 = bg2 + ((g2 - bg2) * alpha);
-//                slot.b2 = bb2 + ((b2 - bb2) * alpha);
-//            }
-//        }
+        Vector<float> _frames; // time, r, g, b, a, r2, g2, b2, ...
+        int _slotIndex;
     };
 }
 

+ 60 - 2
spine-cpp/spine-cpp/include/spine/Vector.h

@@ -48,18 +48,53 @@ namespace Spine
             // Empty
         }
         
-        Vector(const Vector& inArray) : _size(inArray._size), _capacity(inArray._capacity), _buffer(NULL)
+        Vector(const Vector& inVector) : _size(inVector._size), _capacity(inVector._capacity), _buffer(NULL)
         {
             if (_capacity > 0)
             {
                 _buffer = allocate(_capacity);
                 for (size_t i = 0; i < _size; ++i)
                 {
-                    construct(_buffer + i, inArray._buffer[i]);
+                    construct(_buffer + i, inVector._buffer[i]);
                 }
             }
         }
         
+        Vector(Vector& inVector) : _size(inVector._size), _capacity(inVector._capacity), _buffer(NULL)
+        {
+            if (_capacity > 0)
+            {
+                _buffer = allocate(_capacity);
+                for (size_t i = 0; i < _size; ++i)
+                {
+                    construct(_buffer + i, inVector._buffer[i]);
+                }
+            }
+        }
+        
+        Vector& operator=(Vector& inVector)
+        {
+            if (this != &inVector)
+            {
+                clear();
+                deallocate(_buffer);
+                
+                _size = inVector._size;
+                _capacity = inVector._capacity;
+                
+                if (_capacity > 0)
+                {
+                    _buffer = allocate(_capacity);
+                    for (size_t i = 0; i < _size; ++i)
+                    {
+                        construct(_buffer + i, inVector._buffer[i]);
+                    }
+                }
+            }
+            
+            return *this;
+        }
+        
         ~Vector()
         {
             clear();
@@ -179,6 +214,29 @@ namespace Spine
             return &_buffer[_size];
         }
         
+        friend bool operator==(Vector<T>& lhs, Vector<T>& rhs)
+        {
+            if (lhs.size() != rhs.size())
+            {
+                return false;
+            }
+            
+            for (int i = 0, n = static_cast<int>(lhs.size()); i < n; ++i)
+            {
+                if (lhs[i] != rhs[i])
+                {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        
+        friend bool operator!=(Vector<T>& lhs, Vector<T>& rhs)
+        {
+            return !(lhs == rhs);
+        }
+        
     private:
         size_t _size;
         size_t _capacity;

+ 66 - 1
spine-cpp/spine-cpp/src/spine/EventTimeline.cpp

@@ -37,18 +37,83 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/Event.h>
 
 namespace Spine
 {
     RTTI_IMPL(EventTimeline, Timeline);
     
+    EventTimeline::EventTimeline(int frameCount) : Timeline()
+    {
+        _frames.reserve(frameCount);
+        _events.reserve(frameCount);
+    }
+    
     void EventTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        if (events.size() == 0)
+        {
+            return;
+        }
+        
+        int frameCount = static_cast<int>(_frames.size());
+        
+        if (lastTime > time)
+        {
+            // Fire events after last time for looped animations.
+            apply(skeleton, lastTime, std::numeric_limits<int>::max(), events, alpha, pose, direction);
+            lastTime = -1.0f;
+        }
+        else if (lastTime >= _frames[frameCount - 1]) // Last time is after last frame.
+        {
+            return;
+        }
+       
+        if (time < _frames[0])
+        {
+            return; // Time is before first frame.
+        }
+        
+        int frame;
+        if (lastTime < _frames[0])
+        {
+            frame = 0;
+        }
+        else
+        {
+            frame = Animation::binarySearch(_frames, lastTime);
+            float frameTime = _frames[frame];
+            while (frame > 0)
+            {
+                // Fire multiple events with the same frame.
+                if (_frames[frame - 1] != frameTime)
+                {
+                    break;
+                }
+                frame--;
+            }
+        }
+        
+        for (; frame < frameCount && time >= _frames[frame]; ++frame)
+        {
+            events.push_back(_events[frame]);
+        }
     }
     
     int EventTimeline::getPropertyId()
     {
         return ((int)TimelineType_Event << 24);
     }
+    
+    void EventTimeline::setFrame(int frameIndex, Event* e)
+    {
+        _frames[frameIndex] = e->getTime();
+        _events[frameIndex] = e;
+    }
+    
+    Vector<float> EventTimeline::getFrames() { return _frames; }
+    void EventTimeline::setFrames(Vector<float>& inValue) { _frames = inValue; } // time, ...
+    Vector<Event*>& EventTimeline::getEvents() { return _events; }
+    void EventTimeline::setEvents(Vector<Event*>& inValue) { _events = inValue; }
+    int EventTimeline::getFrameCount() { return static_cast<int>(_frames.size()); }
 }

+ 69 - 1
spine-cpp/spine-cpp/src/spine/IkConstraintTimeline.cpp

@@ -37,6 +37,8 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/IkConstraint.h>
+#include <spine/IkConstraintData.h>
 
 namespace Spine
 {
@@ -56,11 +58,77 @@ namespace Spine
     
     void IkConstraintTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        IkConstraint* constraintP = skeleton._ikConstraints[_ikConstraintIndex];
+        IkConstraint& constraint = *constraintP;
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    constraint._mix = constraint._data._mix;
+                    constraint._bendDirection = constraint._data._bendDirection;
+                    return;
+                case MixPose_Current:
+                    constraint._mix += (constraint._data._mix - constraint._mix) * alpha;
+                    constraint._bendDirection = constraint._data._bendDirection;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            if (pose == MixPose_Setup)
+            {
+                constraint._mix = constraint._data._mix + (_frames[_frames.size() + PREV_MIX] - constraint._data._mix) * alpha;
+                constraint._bendDirection = direction == MixDirection_Out ? constraint._data._bendDirection
+                : (int)_frames[_frames.size() + PREV_BEND_DIRECTION];
+            }
+            else
+            {
+                constraint._mix += (_frames[_frames.size() + PREV_MIX] - constraint._mix) * alpha;
+                if (direction == MixDirection_In)
+                {
+                    constraint._bendDirection = (int)_frames[_frames.size() + PREV_BEND_DIRECTION];
+                }
+            }
+            return;
+        }
+        
+        // Interpolate between the previous frame and the current frame.
+        int frame = Animation::binarySearch(_frames, time, ENTRIES);
+        float mix = _frames[frame + PREV_MIX];
+        float frameTime = _frames[frame];
+        float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+        
+        if (pose == MixPose_Setup)
+        {
+            constraint._mix = constraint._data._mix + (mix + (_frames[frame + MIX] - mix) * percent - constraint._data._mix) * alpha;
+            constraint._bendDirection = direction == MixDirection_Out ? constraint._data._bendDirection : (int)_frames[frame + PREV_BEND_DIRECTION];
+        }
+        else
+        {
+            constraint._mix += (mix + (_frames[frame + MIX] - mix) * percent - constraint._mix) * alpha;
+            if (direction == MixDirection_In)
+            {
+                constraint._bendDirection = (int)_frames[frame + PREV_BEND_DIRECTION];
+            }
+        }
     }
     
     int IkConstraintTimeline::getPropertyId()
     {
         return ((int)TimelineType_IkConstraint << 24) + _ikConstraintIndex;
     }
+    
+    void IkConstraintTimeline::setFrame(int frameIndex, float time, float mix, int bendDirection)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + MIX] = mix;
+        _frames[frameIndex + BEND_DIRECTION] = bendDirection;
+    }
 }

+ 73 - 1
spine-cpp/spine-cpp/src/spine/PathConstraintMixTimeline.cpp

@@ -37,18 +37,90 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/PathConstraint.h>
+#include <spine/PathConstraintData.h>
 
 namespace Spine
 {
     RTTI_IMPL(PathConstraintMixTimeline, CurveTimeline);
     
+    const int PathConstraintMixTimeline::ENTRIES = 3;
+    const int PathConstraintMixTimeline::PREV_TIME = -3;
+    const int PathConstraintMixTimeline::PREV_ROTATE = -2;
+    const int PathConstraintMixTimeline::PREV_TRANSLATE = -1;
+    const int PathConstraintMixTimeline::ROTATE = 1;
+    const int PathConstraintMixTimeline::TRANSLATE = 2;
+    
+    PathConstraintMixTimeline::PathConstraintMixTimeline(int frameCount) : CurveTimeline(frameCount), _pathConstraintIndex(0)
+    {
+        _frames.reserve(frameCount * ENTRIES);
+    }
+    
     void PathConstraintMixTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex];
+        PathConstraint& constraint = *constraintP;
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    constraint._rotateMix = constraint._data._rotateMix;
+                    constraint._translateMix = constraint._data._translateMix;
+                    return;
+                case MixPose_Current:
+                    constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha;
+                    constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float rotate, translate;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            rotate = _frames[_frames.size() + PREV_ROTATE];
+            translate = _frames[_frames.size() + PREV_TRANSLATE];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            rotate = _frames[frame + PREV_ROTATE];
+            translate = _frames[frame + PREV_TRANSLATE];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            rotate += (_frames[frame + ROTATE] - rotate) * percent;
+            translate += (_frames[frame + TRANSLATE] - translate) * percent;
+        }
+        
+        if (pose == MixPose_Setup)
+        {
+            constraint._rotateMix = constraint._data._rotateMix + (rotate - constraint._data._rotateMix) * alpha;
+            constraint._translateMix = constraint._data._translateMix + (translate - constraint._data._translateMix) * alpha;
+        }
+        else
+        {
+            constraint._rotateMix += (rotate - constraint._rotateMix) * alpha;
+            constraint._translateMix += (translate - constraint._translateMix) * alpha;
+        }
     }
     
     int PathConstraintMixTimeline::getPropertyId()
     {
         return ((int)TimelineType_PathConstraintMix << 24) + _pathConstraintIndex;
     }
+    
+    void PathConstraintMixTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + ROTATE] = rotateMix;
+        _frames[frameIndex + TRANSLATE] = translateMix;
+    }
 }

+ 52 - 2
spine-cpp/spine-cpp/src/spine/PathConstraintPositionTimeline.cpp

@@ -37,6 +37,8 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/PathConstraint.h>
+#include <spine/PathConstraintData.h>
 
 namespace Spine
 {
@@ -47,18 +49,66 @@ namespace Spine
     const int PathConstraintPositionTimeline::PREV_VALUE = -1;
     const int PathConstraintPositionTimeline::VALUE = 1;
     
-    PathConstraintPositionTimeline::PathConstraintPositionTimeline(int frameCount) : CurveTimeline(frameCount)
+    PathConstraintPositionTimeline::PathConstraintPositionTimeline(int frameCount) : CurveTimeline(frameCount), _pathConstraintIndex(0)
     {
         _frames.reserve(frameCount * ENTRIES);
     }
     
     void PathConstraintPositionTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex];
+        PathConstraint& constraint = *constraintP;
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    constraint._position = constraint._data._position;
+                    return;
+                case MixPose_Current:
+                    constraint._position += (constraint._data._position - constraint._position) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float position;
+        if (time >= _frames[_frames.size() - ENTRIES]) // Time is after last frame.
+        {
+            position = _frames[_frames.size() + PREV_VALUE];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            position = _frames[frame + PREV_VALUE];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            position += (_frames[frame + VALUE] - position) * percent;
+        }
+        if (pose == MixPose_Setup)
+        {
+            constraint._position = constraint._data._position + (position - constraint._data._position) * alpha;
+        }
+        else
+        {
+            constraint._position += (position - constraint._position) * alpha;
+        }
     }
     
     int PathConstraintPositionTimeline::getPropertyId()
     {
         return ((int)TimelineType_PathConstraintPosition << 24) + _pathConstraintIndex;
     }
+    
+    void PathConstraintPositionTimeline::setFrame(int frameIndex, float time, float value)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + VALUE] = value;
+    }
 }

+ 45 - 1
spine-cpp/spine-cpp/src/spine/PathConstraintSpacingTimeline.cpp

@@ -37,6 +37,8 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/PathConstraint.h>
+#include <spine/PathConstraintData.h>
 
 namespace Spine
 {
@@ -49,7 +51,49 @@ namespace Spine
     
     void PathConstraintSpacingTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        PathConstraint* constraintP = skeleton._pathConstraints[_pathConstraintIndex];
+        PathConstraint& constraint = *constraintP;
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    constraint._spacing = constraint._data._spacing;
+                    return;
+                case MixPose_Current:
+                    constraint._spacing += (constraint._data._spacing - constraint._spacing) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float spacing;
+        if (time >= _frames[_frames.size() - ENTRIES]) // Time is after last frame.
+        {
+            spacing = _frames[_frames.size() + PREV_VALUE];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            spacing = _frames[frame + PREV_VALUE];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            spacing += (_frames[frame + VALUE] - spacing) * percent;
+        }
+        
+        if (pose == MixPose_Setup)
+        {
+            constraint._spacing = constraint._data._spacing + (spacing - constraint._data._spacing) * alpha;
+        }
+        else
+        {
+            constraint._spacing += (spacing - constraint._spacing) * alpha;
+        }
     }
     
     int PathConstraintSpacingTimeline::getPropertyId()

+ 81 - 1
spine-cpp/spine-cpp/src/spine/ScaleTimeline.cpp

@@ -37,14 +37,94 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/Bone.h>
+#include <spine/BoneData.h>
 
 namespace Spine
 {
     RTTI_IMPL(ScaleTimeline, TranslateTimeline);
     
+    ScaleTimeline::ScaleTimeline(int frameCount) : TranslateTimeline(frameCount)
+    {
+        // Empty
+    }
+    
     void ScaleTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        Bone* boneP = skeleton._bones[_boneIndex];
+        Bone& bone = *boneP;
+        
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    bone._scaleX = bone._data._scaleX;
+                    bone._scaleY = bone._data._scaleY;
+                    return;
+                case MixPose_Current:
+                    bone._scaleX += (bone._data._scaleX - bone._scaleX) * alpha;
+                    bone._scaleY += (bone._data._scaleY - bone._scaleY) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float x, y;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            x = _frames[_frames.size() + PREV_X] * bone._data._scaleX;
+            y = _frames[_frames.size() + PREV_Y] * bone._data._scaleY;
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            x = _frames[frame + PREV_X];
+            y = _frames[frame + PREV_Y];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            x = (x + (_frames[frame + X] - x) * percent) * bone._data._scaleX;
+            y = (y + (_frames[frame + Y] - y) * percent) * bone._data._scaleY;
+        }
+        
+        if (alpha == 1)
+        {
+            bone._scaleX = x;
+            bone._scaleY = y;
+        }
+        else
+        {
+            float bx, by;
+            if (pose == MixPose_Setup)
+            {
+                bx = bone._data._scaleX;
+                by = bone._data._scaleY;
+            }
+            else
+            {
+                bx = bone._scaleX;
+                by = bone._scaleY;
+            }
+            // Mixing out uses sign of setup or current pose, else use sign of key.
+            if (direction == MixDirection_Out)
+            {
+                x = (x >= 0 ? x : -x) * (bx >= 0 ? 1 : -1);
+                y = (y >= 0 ? y : -y) * (by >= 0 ? 1 : -1);
+            }
+            else
+            {
+                bx = (bx >= 0 ? bx : -bx) * (x >= 0 ? 1 : -1);
+                by = (by >= 0 ? by : -by) * (y >= 0 ? 1 : -1);
+            }
+            bone._scaleX = bx + (x - bx) * alpha;
+            bone._scaleY = by + (y - by) * alpha;
+        }
     }
     
     int ScaleTimeline::getPropertyId()

+ 59 - 1
spine-cpp/spine-cpp/src/spine/ShearTimeline.cpp

@@ -37,14 +37,72 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/Bone.h>
+#include <spine/BoneData.h>
 
 namespace Spine
 {
     RTTI_IMPL(ShearTimeline, TranslateTimeline);
     
+    ShearTimeline::ShearTimeline(int frameCount) : TranslateTimeline(frameCount)
+    {
+        // Empty
+    }
+    
     void ShearTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        Bone* boneP = skeleton._bones[_boneIndex];
+        Bone& bone = *boneP;
+        
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    bone._shearX = bone._data._shearX;
+                    bone._shearY = bone._data._shearY;
+                    return;
+                case MixPose_Current:
+                    bone._shearX += (bone._data._shearX - bone._shearX) * alpha;
+                    bone._shearY += (bone._data._shearY - bone._shearY) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float x, y;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            x = _frames[_frames.size() + PREV_X];
+            y = _frames[_frames.size() + PREV_Y];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            x = _frames[frame + PREV_X];
+            y = _frames[frame + PREV_Y];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            x = x + (_frames[frame + X] - x) * percent;
+            y = y + (_frames[frame + Y] - y) * percent;
+        }
+        
+        if (pose == MixPose_Setup)
+        {
+            bone._shearX = bone._data._shearX + x * alpha;
+            bone._shearY = bone._data._shearY + y * alpha;
+        }
+        else
+        {
+            bone._shearX += (bone._data._shearX + x - bone._shearX) * alpha;
+            bone._shearY += (bone._data._shearY + y - bone._shearY) * alpha;
+        }
     }
     
     int ShearTimeline::getPropertyId()

+ 169 - 169
spine-cpp/spine-cpp/src/spine/SkeletonClipping.cpp

@@ -56,7 +56,9 @@ namespace Spine
         _clippingPolygon.reserve(n);
         clip->computeWorldVertices(slot, 0, n, _clippingPolygon, 0, 2);
         makeClockwise(_clippingPolygon);
-        _clippingPolygons = _triangulator.decompose(_clippingPolygon, _triangulator.triangulate(_clippingPolygon));
+        Vector< Vector<float>* > clippingPolygons = _triangulator.decompose(_clippingPolygon, _triangulator.triangulate(_clippingPolygon));
+        
+        _clippingPolygons = clippingPolygons;
         
         for (Vector<float>** i = _clippingPolygons.begin(); i != _clippingPolygons.end(); ++i)
         {
@@ -97,14 +99,13 @@ namespace Spine
         Vector<float>& clipOutput = _clipOutput, clippedVertices = _clippedVertices;
         Vector<int>& clippedTriangles = _clippedTriangles;
         Vector< Vector<float>* >& polygons = _clippingPolygons;
-        int polygonsCount = _clippingPolygons.size();
+        int polygonsCount = static_cast<int>(_clippingPolygons.size());
 
         int index = 0;
         clippedVertices.clear();
         _clippedUVs.clear();
         clippedTriangles.clear();
         
-        //outer:
         for (int i = 0; i < trianglesLength; i += 3)
         {
             int vertexOffset = triangles[i] << 1;
@@ -121,73 +122,72 @@ namespace Spine
 
             for (int p = 0; p < polygonsCount; p++)
             {
-                int s = clippedVertices.size();
-//                if (clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput))
-//                {
-//                    int clipOutputLength = clipOutput.Count;
-//                    if (clipOutputLength == 0)
-//                    {
-//                        continue;
-//                    }
-//                    float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
-//                    float d = 1 / (d0 * d2 + d1 * (y1 - y3));
-//
-//                    int clipOutputCount = clipOutputLength >> 1;
-//                    float[] clipOutputItems = clipOutput.Items;
-//                    float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items;
-//                    float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items;
-//                    for (int ii = 0; ii < clipOutputLength; ii += 2)
-//                    {
-//                        float x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
-//                        clippedVerticesItems[s] = x;
-//                        clippedVerticesItems[s + 1] = y;
-//                        float c0 = x - x3, c1 = y - y3;
-//                        float a = (d0 * c0 + d1 * c1) * d;
-//                        float b = (d4 * c0 + d2 * c1) * d;
-//                        float c = 1 - a - b;
-//                        clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
-//                        clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
-//                        s += 2;
-//                    }
-//
-//                    s = clippedTriangles.Count;
-//                    int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items;
-//                    clipOutputCount--;
-//                    for (int ii = 1; ii < clipOutputCount; ii++)
-//                    {
-//                        clippedTrianglesItems[s] = index;
-//                        clippedTrianglesItems[s + 1] = index + ii;
-//                        clippedTrianglesItems[s + 2] = index + ii + 1;
-//                        s += 3;
-//                    }
-//                    index += clipOutputCount + 1;
-//                }
-//                else
-//                {
-//                    float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items;
-//                    float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items;
-//                    clippedVerticesItems[s] = x1;
-//                    clippedVerticesItems[s + 1] = y1;
-//                    clippedVerticesItems[s + 2] = x2;
-//                    clippedVerticesItems[s + 3] = y2;
-//                    clippedVerticesItems[s + 4] = x3;
-//                    clippedVerticesItems[s + 5] = y3;
-//
-//                    clippedUVsItems[s] = u1;
-//                    clippedUVsItems[s + 1] = v1;
-//                    clippedUVsItems[s + 2] = u2;
-//                    clippedUVsItems[s + 3] = v2;
-//                    clippedUVsItems[s + 4] = u3;
-//                    clippedUVsItems[s + 5] = v3;
-//
-//                    s = clippedTriangles.Count;
-//                    int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items;
-//                    clippedTrianglesItems[s] = index;
-//                    clippedTrianglesItems[s + 1] = index + 1;
-//                    clippedTrianglesItems[s + 2] = index + 2;
-//                    index += 3;
-//                    break; //continue outer;
-//                }
+                int s = static_cast<int>(clippedVertices.size());
+                if (clip(x1, y1, x2, y2, x3, y3, *polygons[p], clipOutput))
+                {
+                    int clipOutputLength = static_cast<int>(clipOutput.size());
+                    if (clipOutputLength == 0)
+                    {
+                        continue;
+                    }
+                    float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
+                    float d = 1 / (d0 * d2 + d1 * (y1 - y3));
+
+                    int clipOutputCount = clipOutputLength >> 1;
+                    clippedVertices.reserve(s + clipOutputCount * 2);
+                    _clippedUVs.reserve(s + clipOutputCount * 2);
+                    for (int ii = 0; ii < clipOutputLength; ii += 2)
+                    {
+                        float x = clipOutput[ii], y = clipOutput[ii + 1];
+                        clippedVertices[s] = x;
+                        clippedVertices[s + 1] = y;
+                        float c0 = x - x3, c1 = y - y3;
+                        float a = (d0 * c0 + d1 * c1) * d;
+                        float b = (d4 * c0 + d2 * c1) * d;
+                        float c = 1 - a - b;
+                        _clippedUVs[s] = u1 * a + u2 * b + u3 * c;
+                        _clippedUVs[s + 1] = v1 * a + v2 * b + v3 * c;
+                        s += 2;
+                    }
+
+                    s = static_cast<int>(clippedTriangles.size());
+                    clippedTriangles.reserve(s + 3 * (clipOutputCount - 2));
+                    clipOutputCount--;
+                    for (int ii = 1; ii < clipOutputCount; ii++)
+                    {
+                        clippedTriangles[s] = index;
+                        clippedTriangles[s + 1] = index + ii;
+                        clippedTriangles[s + 2] = index + ii + 1;
+                        s += 3;
+                    }
+                    index += clipOutputCount + 1;
+                }
+                else
+                {
+                    clippedVertices.reserve(s + 3 * 2);
+                    _clippedUVs.reserve(s + 3 * 2);
+                    clippedVertices[s] = x1;
+                    clippedVertices[s + 1] = y1;
+                    clippedVertices[s + 2] = x2;
+                    clippedVertices[s + 3] = y2;
+                    clippedVertices[s + 4] = x3;
+                    clippedVertices[s + 5] = y3;
+
+                    _clippedUVs[s] = u1;
+                    _clippedUVs[s + 1] = v1;
+                    _clippedUVs[s + 2] = u2;
+                    _clippedUVs[s + 3] = v2;
+                    _clippedUVs[s + 4] = u3;
+                    _clippedUVs[s + 5] = v3;
+
+                    s = static_cast<int>(clippedTriangles.size());
+                    clippedTriangles.reserve(s + 3);
+                    clippedTriangles[s] = index;
+                    clippedTriangles[s + 1] = index + 1;
+                    clippedTriangles[s + 2] = index + 2;
+                    index += 3;
+                    break;
+                }
             }
         }
     }
@@ -201,105 +201,105 @@ namespace Spine
     {
         Vector<float> originalOutput = output;
         bool clipped = false;
-//
-//        // Avoid copy at the end.
-//        Vector<float> input = NULL;
-//        if (clippingArea.Count % 4 >= 2)
-//        {
-//            input = output;
-//            output = scratch;
-//        }
-//        else
-//        {
-//            input = scratch;
-//        }
-//
-//        input.Clear();
-//        input.push_back(x1);
-//        input.push_back(y1);
-//        input.push_back(x2);
-//        input.push_back(y2);
-//        input.push_back(x3);
-//        input.push_back(y3);
-//        input.push_back(x1);
-//        input.push_back(y1);
-//        output.Clear();
-//
-//        Vector<float> clippingVertices = clippingArea.Items;
-//        int clippingVerticesLast = clippingArea.Count - 4;
-//        for (int i = 0; ; i += 2)
-//        {
-//            float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
-//            float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
-//            float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
-//
-//            Vector<float> inputVertices = input.Items;
-//            int inputVerticesLength = input.Count - 2, outputStart = output.Count;
-//            for (int ii = 0; ii < inputVerticesLength; ii += 2)
-//            {
-//                float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
-//                float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
-//                bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
-//                if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0)
-//                {
-//                    if (side2)
-//                    {
-//                        // v1 inside, v2 inside
-//                        output.push_back(inputX2);
-//                        output.push_back(inputY2);
-//                        continue;
-//                    }
-//                    // v1 inside, v2 outside
-//                    float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
-//                    float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
-//                    output.push_back(edgeX + (edgeX2 - edgeX) * ua);
-//                    output.push_back(edgeY + (edgeY2 - edgeY) * ua);
-//                }
-//                else if (side2)
-//                {
-//                    // v1 outside, v2 inside
-//                    float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
-//                    float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
-//                    output.push_back(edgeX + (edgeX2 - edgeX) * ua);
-//                    output.push_back(edgeY + (edgeY2 - edgeY) * ua);
-//                    output.push_back(inputX2);
-//                    output.push_back(inputY2);
-//                }
-//                clipped = true;
-//            }
-//
-//            if (outputStart == output.Count)
-//            {
-//                // All edges outside.
-//                originalOutput.Clear();
-//                return true;
-//            }
-//
-//            output.push_back(output.Items[0]);
-//            output.push_back(output.Items[1]);
-//
-//            if (i == clippingVerticesLast)
-//            {
-//                break;
-//            }
-//            var temp = output;
-//            output = input;
-//            output.Clear();
-//            input = temp;
-//        }
-//
-//        if (originalOutput != output)
-//        {
-//            originalOutput.Clear();
-//            for (int i = 0, n = output.Count - 2; i < n; i++)
-//            {
-//                originalOutput.push_back(output.Items[i]);
-//            }
-//        }
-//        else
-//        {
-//            originalOutput.Resize(originalOutput.Count - 2);
-//        }
+
+        // Avoid copy at the end.
+        Vector<float> input;
+        if (clippingArea.size() % 4 >= 2)
+        {
+            input = output;
+            output = _scratch;
+        }
+        else
+        {
+            input = _scratch;
+        }
+
+        input.clear();
+        input.push_back(x1);
+        input.push_back(y1);
+        input.push_back(x2);
+        input.push_back(y2);
+        input.push_back(x3);
+        input.push_back(y3);
+        input.push_back(x1);
+        input.push_back(y1);
+        output.clear();
+
+        Vector<float> clippingVertices = clippingArea;
+        int clippingVerticesLast = static_cast<int>(clippingArea.size()) - 4;
+        for (int i = 0; ; i += 2)
+        {
+            float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
+            float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
+            float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
+
+            Vector<float> inputVertices = input;
+            int inputVerticesLength = static_cast<int>(input.size()) - 2, outputStart = static_cast<int>(output.size());
+            for (int ii = 0; ii < inputVerticesLength; ii += 2)
+            {
+                float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
+                float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
+                bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
+                if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0)
+                {
+                    if (side2)
+                    {
+                        // v1 inside, v2 inside
+                        output.push_back(inputX2);
+                        output.push_back(inputY2);
+                        continue;
+                    }
+                    // v1 inside, v2 outside
+                    float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
+                    float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
+                    output.push_back(edgeX + (edgeX2 - edgeX) * ua);
+                    output.push_back(edgeY + (edgeY2 - edgeY) * ua);
+                }
+                else if (side2)
+                {
+                    // v1 outside, v2 inside
+                    float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
+                    float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
+                    output.push_back(edgeX + (edgeX2 - edgeX) * ua);
+                    output.push_back(edgeY + (edgeY2 - edgeY) * ua);
+                    output.push_back(inputX2);
+                    output.push_back(inputY2);
+                }
+                clipped = true;
+            }
+
+            if (outputStart == output.size())
+            {
+                // All edges outside.
+                originalOutput.clear();
+                return true;
+            }
+
+            output.push_back(output[0]);
+            output.push_back(output[1]);
+
+            if (i == clippingVerticesLast)
+            {
+                break;
+            }
+            Vector<float> temp = output;
+            output = input;
+            output.clear();
+            input = temp;
+        }
+
+        if (originalOutput != output)
+        {
+            originalOutput.clear();
+            for (int i = 0, n = static_cast<int>(output.size()) - 2; i < n; ++i)
+            {
+                originalOutput.push_back(output[i]);
+            }
+        }
+        else
+        {
+            originalOutput.reserve(originalOutput.size() - 2);
+        }
 
         return clipped;
     }

+ 85 - 1
spine-cpp/spine-cpp/src/spine/TransformConstraintTimeline.cpp

@@ -37,6 +37,8 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/TransformConstraint.h>
+#include <spine/TransformConstraintData.h>
 
 namespace Spine
 {
@@ -53,13 +55,95 @@ namespace Spine
     const int TransformConstraintTimeline::SCALE = 3;
     const int TransformConstraintTimeline::SHEAR = 4;
     
+    TransformConstraintTimeline::TransformConstraintTimeline(int frameCount) : CurveTimeline(frameCount), _transformConstraintIndex(0)
+    {
+        _frames.reserve(frameCount * ENTRIES);
+    }
+    
     void TransformConstraintTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        TransformConstraint* constraintP = skeleton._transformConstraints[_transformConstraintIndex];
+        TransformConstraint& constraint = *constraintP;
+        
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    constraint._rotateMix = constraint._data._rotateMix;
+                    constraint._translateMix = constraint._data._translateMix;
+                    constraint._scaleMix = constraint._data._scaleMix;
+                    constraint._shearMix = constraint._data._shearMix;
+                    return;
+                case MixPose_Current:
+                    constraint._rotateMix += (constraint._data._rotateMix - constraint._rotateMix) * alpha;
+                    constraint._translateMix += (constraint._data._translateMix - constraint._translateMix) * alpha;
+                    constraint._scaleMix += (constraint._data._scaleMix - constraint._scaleMix) * alpha;
+                    constraint._shearMix += (constraint._data._shearMix - constraint._shearMix) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float rotate, translate, scale, shear;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            int i = static_cast<int>(_frames.size());
+            rotate = _frames[i + PREV_ROTATE];
+            translate = _frames[i + PREV_TRANSLATE];
+            scale = _frames[i + PREV_SCALE];
+            shear = _frames[i + PREV_SHEAR];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            rotate = _frames[frame + PREV_ROTATE];
+            translate = _frames[frame + PREV_TRANSLATE];
+            scale = _frames[frame + PREV_SCALE];
+            shear = _frames[frame + PREV_SHEAR];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            rotate += (_frames[frame + ROTATE] - rotate) * percent;
+            translate += (_frames[frame + TRANSLATE] - translate) * percent;
+            scale += (_frames[frame + SCALE] - scale) * percent;
+            shear += (_frames[frame + SHEAR] - shear) * percent;
+        }
+        
+        if (pose == MixPose_Setup)
+        {
+            TransformConstraintData& data = constraint._data;
+            constraint._rotateMix = data._rotateMix + (rotate - data._rotateMix) * alpha;
+            constraint._translateMix = data._translateMix + (translate - data._translateMix) * alpha;
+            constraint._scaleMix = data._scaleMix + (scale - data._scaleMix) * alpha;
+            constraint._shearMix = data._shearMix + (shear - data._shearMix) * alpha;
+        }
+        else
+        {
+            constraint._rotateMix += (rotate - constraint._rotateMix) * alpha;
+            constraint._translateMix += (translate - constraint._translateMix) * alpha;
+            constraint._scaleMix += (scale - constraint._scaleMix) * alpha;
+            constraint._shearMix += (shear - constraint._shearMix) * alpha;
+        }
     }
     
     int TransformConstraintTimeline::getPropertyId()
     {
         return ((int)TimelineType_TransformConstraint << 24) + _transformConstraintIndex;
     }
+    
+    void TransformConstraintTimeline::setFrame(int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + ROTATE] = rotateMix;
+        _frames[frameIndex + TRANSLATE] = translateMix;
+        _frames[frameIndex + SCALE] = scaleMix;
+        _frames[frameIndex + SHEAR] = shearMix;
+    }
 }

+ 73 - 1
spine-cpp/spine-cpp/src/spine/TranslateTimeline.cpp

@@ -37,18 +37,90 @@
 #include <spine/TimelineType.h>
 #include <spine/Slot.h>
 #include <spine/SlotData.h>
+#include <spine/Bone.h>
+#include <spine/BoneData.h>
 
 namespace Spine
 {
     RTTI_IMPL(TranslateTimeline, CurveTimeline);
     
+    const int TranslateTimeline::ENTRIES = 3;
+    const int TranslateTimeline::PREV_TIME = -3;
+    const int TranslateTimeline::PREV_X = -2;
+    const int TranslateTimeline::PREV_Y = -1;
+    const int TranslateTimeline::X = 1;
+    const int TranslateTimeline::Y = 2;
+    
+    TranslateTimeline::TranslateTimeline(int frameCount) : CurveTimeline(frameCount), _boneIndex(0)
+    {
+        _frames.reserve(frameCount * ENTRIES);
+    }
+    
     void TranslateTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        Bone* boneP = skeleton._bones[_boneIndex];
+        Bone& bone = *boneP;
+        
+        if (time < _frames[0])
+        {
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    bone._x = bone._data._x;
+                    bone._y = bone._data._y;
+                    return;
+                case MixPose_Current:
+                    bone._x += (bone._data._x - bone._x) * alpha;
+                    bone._y += (bone._data._y - bone._y) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float x, y;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            x = _frames[_frames.size() + PREV_X];
+            y = _frames[_frames.size() + PREV_Y];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            x = _frames[frame + PREV_X];
+            y = _frames[frame + PREV_Y];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            x += (_frames[frame + X] - x) * percent;
+            y += (_frames[frame + Y] - y) * percent;
+        }
+        
+        if (pose == MixPose_Setup)
+        {
+            bone._x = bone._data._x + x * alpha;
+            bone._y = bone._data._y + y * alpha;
+        }
+        else
+        {
+            bone._x += (bone._data._x + x - bone._x) * alpha;
+            bone._y += (bone._data._y + y - bone._y) * alpha;
+        }
     }
     
     int TranslateTimeline::getPropertyId()
     {
         return ((int)TimelineType_Translate << 24) + _boneIndex;
     }
+    
+    void TranslateTimeline::setFrame(int frameIndex, float time, float x, float y)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + X] = x;
+        _frames[frameIndex + Y] = y;
+    }
 }

+ 157 - 1
spine-cpp/spine-cpp/src/spine/TwoColorTimeline.cpp

@@ -42,13 +42,169 @@ namespace Spine
 {
     RTTI_IMPL(TwoColorTimeline, CurveTimeline);
     
+    const int TwoColorTimeline::ENTRIES = 8;
+    const int TwoColorTimeline::PREV_TIME = -8;
+    const int TwoColorTimeline::PREV_R = -7;
+    const int TwoColorTimeline::PREV_G = -6;
+    const int TwoColorTimeline::PREV_B = -5;
+    const int TwoColorTimeline::PREV_A = -4;
+    const int TwoColorTimeline::PREV_R2 = -3;
+    const int TwoColorTimeline::PREV_G2 = -2;
+    const int TwoColorTimeline::PREV_B2 = -1;
+    const int TwoColorTimeline::R = 1;
+    const int TwoColorTimeline::G = 2;
+    const int TwoColorTimeline::B = 3;
+    const int TwoColorTimeline::A = 4;
+    const int TwoColorTimeline::R2 = 5;
+    const int TwoColorTimeline::G2 = 6;
+    const int TwoColorTimeline::B2 = 7;
+    
+    TwoColorTimeline::TwoColorTimeline(int frameCount) : CurveTimeline(frameCount)
+    {
+        _frames.reserve(frameCount * ENTRIES);
+    }
+    
     void TwoColorTimeline::apply(Skeleton& skeleton, float lastTime, float time, Vector<Event*>& events, float alpha, MixPose pose, MixDirection direction)
     {
-        // TODO
+        Slot* slotP = skeleton._slots[_slotIndex];
+        Slot& slot = *slotP;
+        
+        if (time < _frames[0])
+        {
+            // Time is before first frame.
+            switch (pose)
+            {
+                case MixPose_Setup:
+                    slot._r = slot._data._r;
+                    slot._g = slot._data._g;
+                    slot._b = slot._data._b;
+                    slot._a = slot._data._a;
+                    slot._r2 = slot._data._r2;
+                    slot._g2 = slot._data._g2;
+                    slot._b2 = slot._data._b2;
+                    return;
+                case MixPose_Current:
+                    slot._r += (slot._r - slot._data._r) * alpha;
+                    slot._g += (slot._g - slot._data._g) * alpha;
+                    slot._b += (slot._b - slot._data._b) * alpha;
+                    slot._a += (slot._a - slot._data._a) * alpha;
+                    slot._r2 += (slot._r2 - slot._data._r2) * alpha;
+                    slot._g2 += (slot._g2 - slot._data._g2) * alpha;
+                    slot._b2 += (slot._b2 - slot._data._b2) * alpha;
+                    return;
+                case MixPose_CurrentLayered:
+                default:
+                    return;
+            }
+        }
+        
+        float r, g, b, a, r2, g2, b2;
+        if (time >= _frames[_frames.size() - ENTRIES])
+        {
+            // Time is after last frame.
+            int i = static_cast<int>(_frames.size());
+            r = _frames[i + PREV_R];
+            g = _frames[i + PREV_G];
+            b = _frames[i + PREV_B];
+            a = _frames[i + PREV_A];
+            r2 = _frames[i + PREV_R2];
+            g2 = _frames[i + PREV_G2];
+            b2 = _frames[i + PREV_B2];
+        }
+        else
+        {
+            // Interpolate between the previous frame and the current frame.
+            int frame = Animation::binarySearch(_frames, time, ENTRIES);
+            r = _frames[frame + PREV_R];
+            g = _frames[frame + PREV_G];
+            b = _frames[frame + PREV_B];
+            a = _frames[frame + PREV_A];
+            r2 = _frames[frame + PREV_R2];
+            g2 = _frames[frame + PREV_G2];
+            b2 = _frames[frame + PREV_B2];
+            float frameTime = _frames[frame];
+            float percent = getCurvePercent(frame / ENTRIES - 1,
+                                            1 - (time - frameTime) / (_frames[frame + PREV_TIME] - frameTime));
+            
+            r += (_frames[frame + R] - r) * percent;
+            g += (_frames[frame + G] - g) * percent;
+            b += (_frames[frame + B] - b) * percent;
+            a += (_frames[frame + A] - a) * percent;
+            r2 += (_frames[frame + R2] - r2) * percent;
+            g2 += (_frames[frame + G2] - g2) * percent;
+            b2 += (_frames[frame + B2] - b2) * percent;
+        }
+        
+        if (alpha == 1)
+        {
+            slot._r = r;
+            slot._g = g;
+            slot._b = b;
+            slot._a = a;
+            slot._r2 = r2;
+            slot._g2 = g2;
+            slot._b2 = b2;
+        }
+        else
+        {
+            float br, bg, bb, ba, br2, bg2, bb2;
+            if (pose == MixPose_Setup)
+            {
+                br = slot._data._r;
+                bg = slot._data._g;
+                bb = slot._data._b;
+                ba = slot._data._a;
+                br2 = slot._data._r2;
+                bg2 = slot._data._g2;
+                bb2 = slot._data._b2;
+            }
+            else
+            {
+                br = slot._r;
+                bg = slot._g;
+                bb = slot._b;
+                ba = slot._a;
+                br2 = slot._r2;
+                bg2 = slot._g2;
+                bb2 = slot._b2;
+            }
+            
+            slot._r = br + ((r - br) * alpha);
+            slot._g = bg + ((g - bg) * alpha);
+            slot._b = bb + ((b - bb) * alpha);
+            slot._a = ba + ((a - ba) * alpha);
+            slot._r2 = br2 + ((r2 - br2) * alpha);
+            slot._g2 = bg2 + ((g2 - bg2) * alpha);
+            slot._b2 = bb2 + ((b2 - bb2) * alpha);
+        }
     }
     
     int TwoColorTimeline::getPropertyId()
     {
         return ((int)TimelineType_TwoColor << 24) + _slotIndex;
     }
+    
+    void TwoColorTimeline::setFrame(int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2)
+    {
+        frameIndex *= ENTRIES;
+        _frames[frameIndex] = time;
+        _frames[frameIndex + R] = r;
+        _frames[frameIndex + G] = g;
+        _frames[frameIndex + B] = b;
+        _frames[frameIndex + A] = a;
+        _frames[frameIndex + R2] = r2;
+        _frames[frameIndex + G2] = g2;
+        _frames[frameIndex + B2] = b2;
+    }
+    
+    int TwoColorTimeline::getSlotIndex()
+    {
+        return _slotIndex;
+    }
+    
+    void TwoColorTimeline::setSlotIndex(int inValue)
+    {
+        assert(inValue >= 0);
+        _slotIndex = inValue;
+    }
 }