public void AppendIdentity()
            {
                var toAdd = new TransformOperation();

                toAdd.Type = TransformOperation.OperationType.Identity;

                _operations.Add(toAdd);
            }
            public void AppendMatrix(Matrix matrix)
            {
                var toAdd = new TransformOperation();

                toAdd.Type   = TransformOperation.OperationType.Matrix;
                toAdd.Matrix = matrix;

                _operations.Add(toAdd);
            }
            public void AppendRotate(double angle)
            {
                var toAdd = new TransformOperation();

                toAdd.Type = TransformOperation.OperationType.Rotate;
                toAdd.Data.Rotate.Angle = angle;

                toAdd.Bake();

                _operations.Add(toAdd);
            }
        private static bool TryInterpolate(TransformOperations from, TransformOperations to, double progress, ref TransformOperations result)
        {
            bool fromIdentity = from.IsIdentity;
            bool toIdentity   = to.IsIdentity;

            if (fromIdentity && toIdentity)
            {
                return(true);
            }

            int matchingPrefixLength = ComputeMatchingPrefixLength(from, to);
            int fromSize             = fromIdentity ? 0 : from._operations.Count;
            int toSize        = toIdentity ? 0 : to._operations.Count;
            int numOperations = Math.Max(fromSize, toSize);

            var builder = new Builder(matchingPrefixLength);

            for (int i = 0; i < matchingPrefixLength; i++)
            {
                TransformOperation interpolated = new TransformOperation
                {
                    Type = TransformOperation.OperationType.Identity
                };

                if (!TransformOperation.TryInterpolate(
                        i >= fromSize ? default(TransformOperation?) : from._operations[i],
                        i >= toSize ? default(TransformOperation?) : to._operations[i],
                        progress,
                        ref interpolated))
                {
                    return(false);
                }

                builder.Append(interpolated);
            }

            if (matchingPrefixLength < numOperations)
            {
                if (!ComputeDecomposedTransform(from, matchingPrefixLength, out Matrix.Decomposed fromDecomposed) ||
                    !ComputeDecomposedTransform(to, matchingPrefixLength, out Matrix.Decomposed toDecomposed))
                {
                    return(false);
                }

                var transform = InterpolationUtilities.InterpolateDecomposedTransforms(ref fromDecomposed, ref toDecomposed, progress);

                builder.AppendMatrix(InterpolationUtilities.ComposeTransform(transform));
            }

            result = builder.Build();

            return(true);
        }
        private Matrix ApplyTransforms(int startOffset = 0)
        {
            Matrix matrix = Matrix.Identity;

            for (var i = startOffset; i < _operations.Count; i++)
            {
                TransformOperation operation = _operations[i];
                matrix *= operation.Matrix;
            }

            return(matrix);
        }
            public void AppendSkew(double x, double y)
            {
                var toAdd = new TransformOperation();

                toAdd.Type        = TransformOperation.OperationType.Skew;
                toAdd.Data.Skew.X = x;
                toAdd.Data.Skew.Y = y;

                toAdd.Bake();

                _operations.Add(toAdd);
            }
            public void AppendTranslate(double x, double y)
            {
                var toAdd = new TransformOperation();

                toAdd.Type             = TransformOperation.OperationType.Translate;
                toAdd.Data.Translate.X = x;
                toAdd.Data.Translate.Y = y;

                toAdd.Bake();

                _operations.Add(toAdd);
            }
        /// <summary>
        /// Attempts to interpolate between two transform operations.
        /// </summary>
        /// <param name="from">Source operation.</param>
        /// <param name="to">Target operation.</param>
        /// <param name="progress">Interpolation progress.</param>
        /// <param name="result">Interpolation result that will be filled in when operation was successful.</param>
        /// <remarks>
        /// Based upon https://www.w3.org/TR/css-transforms-1/#interpolation-of-transform-functions.
        /// </remarks>
        public static bool TryInterpolate(TransformOperation?from, TransformOperation?to, double progress,
                                          ref TransformOperation result)
        {
            bool fromIdentity = IsOperationIdentity(ref from);
            bool toIdentity   = IsOperationIdentity(ref to);

            if (fromIdentity && toIdentity)
            {
                return(true);
            }

            // ReSharper disable PossibleInvalidOperationException
            TransformOperation fromValue = fromIdentity ? Identity : from.Value;
            TransformOperation toValue   = toIdentity ? Identity : to.Value;
            // ReSharper restore PossibleInvalidOperationException

            var interpolationType = toIdentity ? fromValue.Type : toValue.Type;

            result.Type = interpolationType;

            switch (interpolationType)
            {
            case OperationType.Translate:
            {
                double fromX = fromIdentity ? 0 : fromValue.Data.Translate.X;
                double fromY = fromIdentity ? 0 : fromValue.Data.Translate.Y;

                double toX = toIdentity ? 0 : toValue.Data.Translate.X;
                double toY = toIdentity ? 0 : toValue.Data.Translate.Y;

                result.Data.Translate.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress);
                result.Data.Translate.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress);

                result.Bake();

                break;
            }

            case OperationType.Rotate:
            {
                double fromAngle = fromIdentity ? 0 : fromValue.Data.Rotate.Angle;

                double toAngle = toIdentity ? 0 : toValue.Data.Rotate.Angle;

                result.Data.Rotate.Angle = InterpolationUtilities.InterpolateScalars(fromAngle, toAngle, progress);

                result.Bake();

                break;
            }

            case OperationType.Scale:
            {
                double fromX = fromIdentity ? 1 : fromValue.Data.Scale.X;
                double fromY = fromIdentity ? 1 : fromValue.Data.Scale.Y;

                double toX = toIdentity ? 1 : toValue.Data.Scale.X;
                double toY = toIdentity ? 1 : toValue.Data.Scale.Y;

                result.Data.Scale.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress);
                result.Data.Scale.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress);

                result.Bake();

                break;
            }

            case OperationType.Skew:
            {
                double fromX = fromIdentity ? 0 : fromValue.Data.Skew.X;
                double fromY = fromIdentity ? 0 : fromValue.Data.Skew.Y;

                double toX = toIdentity ? 0 : toValue.Data.Skew.X;
                double toY = toIdentity ? 0 : toValue.Data.Skew.Y;

                result.Data.Skew.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress);
                result.Data.Skew.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress);

                result.Bake();

                break;
            }

            case OperationType.Matrix:
            {
                var fromMatrix = fromIdentity ? Matrix.Identity : fromValue.Matrix;
                var toMatrix   = toIdentity ? Matrix.Identity : toValue.Matrix;

                if (!Matrix.TryDecomposeTransform(fromMatrix, out Matrix.Decomposed fromDecomposed) ||
                    !Matrix.TryDecomposeTransform(toMatrix, out Matrix.Decomposed toDecomposed))
                {
                    return(false);
                }

                var interpolated =
                    InterpolationUtilities.InterpolateDecomposedTransforms(
                        ref fromDecomposed, ref toDecomposed,
                        progress);

                result.Matrix = InterpolationUtilities.ComposeTransform(interpolated);

                break;
            }

            case OperationType.Identity:
            {
                // Do nothing.
                break;
            }
            }

            return(true);
        }
 public void Append(TransformOperation toAdd)
 {
     _operations.Add(toAdd);
 }