public SpriterObject Clone()
        {
            var so = new SpriterObject(FlatRedBallServices.GlobalContentManager, false)
            {
                Animations = new Dictionary<string, SpriterObjectAnimation>()
            };

            var allObjects =
                Animations.Select(a => a.Value)
                    .SelectMany(v => v.KeyFrames)
                    .SelectMany(kf => kf.Values)
                    .Select(kfv => kfv.Key)
                    .GroupBy(k => k.Name)
                    .Select(g => g.First().Clone<PositionedObject>())
                    .ToList();

            foreach (var animationPair in Animations)
            {
                var keyframes = new List<KeyFrame>();
                animationPair.Value.KeyFrames.ForEach(kf =>
                    {
                        var keyFrame = new KeyFrame
                            {
                                Time = kf.Time,
                                Values = new Dictionary<PositionedObject, KeyFrameValues>(kf.Values.Count)
                            };

                        foreach (var kfPair in kf.Values)
                        {
                            var parent = kfPair.Value.Parent == null || kfPair.Value.Parent.Name == null
                                ? null
                                : allObjects.First(k => k.Name == kfPair.Value.Parent.Name);

                            var kfv = new KeyFrameValues
                            {
                                Alpha = kfPair.Value.Alpha,
                                Parent = parent,
                                RelativePosition = kfPair.Value.RelativePosition,
                                RelativeRotation = kfPair.Value.RelativeRotation,
                                RelativeScaleX = kfPair.Value.RelativeScaleX,
                                RelativeScaleY = kfPair.Value.RelativeScaleY,
                                Spin = kfPair.Value.Spin,
                                Texture = kfPair.Value.Texture
                            };

                            keyFrame.Values[allObjects.First(k => k.Name == kfPair.Key.Name)] = kfv;
                        }
                        keyframes.Add(keyFrame);
                    });
                so.Animations[animationPair.Key] = new SpriterObjectAnimation(animationPair.Value.Name,
                                                                              animationPair.Value.Looping,
                                                                              animationPair.Value.TotalTime,
                                                                              keyframes);
            }
            so.ObjectList = so.Animations.Select(a => a.Value)
                    .SelectMany(v => v.KeyFrames)
                    .SelectMany(kf => kf.Values)
                    .Select(kfv => kfv.Key)
                    .GroupBy(k => k.Name)
                    .Select(g => g.First())
                    .ToList();

            return so;
        }
        private static KeyFramePivotScaledValues GetKeyFrameValues(Key timelineKey, SpriterDataFolderFile file, IDictionary<string, Texture2D> textures, string folderFileId, KeyObjectRef objectRef)
        {
            var pivotValue = new KeyFrameValues
                {
                    RelativePosition = new Vector3(timelineKey.Object.X,
                                           timelineKey.Object.Y,
                                           0.0f),
                    RelativeRotation = new Vector3
                        {
                            Z = timelineKey.Object.Angle
                        },
                    Spin = timelineKey.Spin
                };
            int width = file.Width;
            int height = file.Height;

            var pivotY = 1.0f;
            var pivotX = 0.0f;

            if (timelineKey.Object.PivotX.HasValue)
            {
                pivotX = timelineKey.Object.PivotX.Value;
            }
            else if (file.PivotX.HasValue)
            {
                pivotX = file.PivotX.Value;
            }

            if (timelineKey.Object.PivotY.HasValue)
            {
                pivotY = timelineKey.Object.PivotY.Value;
            }
            else if (file.PivotY.HasValue)
            {
                pivotY = file.PivotY.Value;
            }

            var ScaledSpriteValue = new KeyFrameValues
                {
                    Texture = textures[folderFileId],
                    RelativeScaleX = timelineKey.Object.ScaleX,
                    RelativeScaleY = timelineKey.Object.ScaleY,
                    RelativePosition = GetPivotedRelativePosition(width, height, pivotX,
                                                         pivotY, objectRef.ZIndex),
                    Alpha = timelineKey.Object.Alpha
                };
            return new KeyFramePivotScaledValues { Pivot = pivotValue, ScaledSprite = ScaledSpriteValue };
        }
        private KeyFramePivotScaledValues GetKeyFrameValues(Key timelineKey, ObjectInfo box, KeyObjectRef objectRef)
        {
            var pivotValue = new KeyFrameValues
                {
                    RelativePosition = new Vector3(timelineKey.Object.X,
                                           timelineKey.Object.Y,
                                           0.0f),
                    RelativeRotation = new Vector3
                        {
                            Z = timelineKey.Object.Angle
                        },
                    Spin = timelineKey.Spin
                };
            var width = (int)box.Width;
            var height = (int)box.Height;

            float pivotX = timelineKey.Object.PivotX.HasValue ? timelineKey.Object.PivotX.Value : box.PivotX;

            float pivotY = timelineKey.Object.PivotY.HasValue ? timelineKey.Object.PivotY.Value : box.PivotY;

            var scaledPolygonValue = new KeyFrameValues
                {
                    RelativeScaleX = timelineKey.Object.ScaleX,
                    RelativeScaleY = timelineKey.Object.ScaleY,
                    RelativePosition = GetPivotedRelativePosition(width, height, pivotX,
                                                         pivotY, objectRef.ZIndex)
                };
            return new KeyFramePivotScaledValues { Pivot = pivotValue, ScaledPolygon = scaledPolygonValue };
        }
        public void KeyFrameValuesScaleDefaultsTo1()
        {
            var kfv = new KeyFrameValues();

            Assert.IsTrue(Math.Abs(kfv.RelativeScaleX - 1.0f) < Single.Epsilon);
            Assert.IsTrue(Math.Abs(kfv.RelativeScaleY - 1.0f) < Single.Epsilon);
        }