diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/CompositionExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/CompositionExtensions.cs index 521d69ea2a9..3dea1f5c27c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/CompositionExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/CompositionExtensions.cs @@ -263,5 +263,46 @@ private static ExpressionAnimation CreateExpressionAnimationFromNode(Compositor return expressionNode.ExpressionAnimation; } + + internal static float EvaluateSubchannel(this ExpressionNode node, string subchannel) => (node, subchannel) switch + { + (Vector2Node n, "X") => n.Evaluate().X, + (Vector2Node n, "Y") => n.Evaluate().Y, + + (Vector3Node n, "X") => n.Evaluate().X, + (Vector3Node n, "Y") => n.Evaluate().Y, + (Vector3Node n, "Z") => n.Evaluate().Z, + + (Vector4Node n, "X") => n.Evaluate().X, + (Vector4Node n, "Y") => n.Evaluate().Y, + (Vector4Node n, "Z") => n.Evaluate().Z, + (Vector4Node n, "W") => n.Evaluate().W, + + (Matrix3x2Node n, "Channel11") => n.Evaluate().M11, + (Matrix3x2Node n, "Channel12") => n.Evaluate().M12, + (Matrix3x2Node n, "Channel21") => n.Evaluate().M21, + (Matrix3x2Node n, "Channel22") => n.Evaluate().M22, + (Matrix3x2Node n, "Channel31") => n.Evaluate().M31, + (Matrix3x2Node n, "Channel32") => n.Evaluate().M32, + + (Matrix4x4Node n, "Channel11") => n.Evaluate().M11, + (Matrix4x4Node n, "Channel12") => n.Evaluate().M12, + (Matrix4x4Node n, "Channel13") => n.Evaluate().M13, + (Matrix4x4Node n, "Channel14") => n.Evaluate().M14, + (Matrix4x4Node n, "Channel21") => n.Evaluate().M21, + (Matrix4x4Node n, "Channel22") => n.Evaluate().M22, + (Matrix4x4Node n, "Channel23") => n.Evaluate().M23, + (Matrix4x4Node n, "Channel24") => n.Evaluate().M24, + (Matrix4x4Node n, "Channel31") => n.Evaluate().M31, + (Matrix4x4Node n, "Channel32") => n.Evaluate().M32, + (Matrix4x4Node n, "Channel33") => n.Evaluate().M33, + (Matrix4x4Node n, "Channel34") => n.Evaluate().M34, + (Matrix4x4Node n, "Channel41") => n.Evaluate().M41, + (Matrix4x4Node n, "Channel42") => n.Evaluate().M42, + (Matrix4x4Node n, "Channel43") => n.Evaluate().M43, + (Matrix4x4Node n, "Channel44") => n.Evaluate().M44, + + _ => 0 + }; } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/BooleanNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/BooleanNode.cs index 6e49f5e9417..625cf7dbbae 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/BooleanNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/BooleanNode.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { // Ignore warning: 'BooleanNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() @@ -127,6 +129,67 @@ protected internal override string GetValue() } private bool _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public bool Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.Equals: + return Equals(Children[0], Children[1]); + case ExpressionNodeType.NotEquals: + return !Equals(Children[0], Children[1]); + case ExpressionNodeType.And: + return (Children[0] as BooleanNode).Evaluate() && (Children[1] as BooleanNode).Evaluate(); + case ExpressionNodeType.Or: + return (Children[0] as BooleanNode).Evaluate() || (Children[1] as BooleanNode).Evaluate(); + case ExpressionNodeType.LessThan: + return (Children[0] as ScalarNode).Evaluate() < (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.LessThanEquals: + return (Children[0] as ScalarNode).Evaluate() <= (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.GreaterThan: + return (Children[0] as ScalarNode).Evaluate() > (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.GreaterThanEquals: + return (Children[0] as ScalarNode).Evaluate() >= (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Not: + return !(Children[0] as BooleanNode).Evaluate(); + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + switch (PropertyName) + { + default: + reference.Properties.TryGetBoolean(PropertyName, out var referencedProperty); + return referencedProperty; + } + + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as BooleanNode).Evaluate() : + (Children[2] as BooleanNode).Evaluate(); + default: + throw new NotImplementedException(); + } + + bool Equals(ExpressionNode e1, ExpressionNode e2) => (e1, e2) switch + { + (BooleanNode n1, BooleanNode n2) => n1.Evaluate() == n2.Evaluate(), + (ScalarNode n1, ScalarNode n2) => n1.Evaluate() == n2.Evaluate(), + (Vector2Node n1, Vector2Node n2) => n1.Evaluate() == n2.Evaluate(), + (Vector3Node n1, Vector3Node n2) => n1.Evaluate() == n2.Evaluate(), + (Vector4Node n1, Vector4Node n2) => n1.Evaluate() == n2.Evaluate(), + (ColorNode n1, ColorNode n2) => n1.Evaluate() == n2.Evaluate(), + (QuaternionNode n1, QuaternionNode n2) => n1.Evaluate() == n2.Evaluate(), + (Matrix3x2Node n1, Matrix3x2Node n2) => n1.Evaluate() == n2.Evaluate(), + (Matrix4x4Node n1, Matrix4x4Node n2) => n1.Evaluate() == n2.Evaluate(), + _ => false + }; + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ColorNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ColorNode.cs index 553beee5005..c442df610fd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ColorNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ColorNode.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Windows.UI; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -97,6 +99,40 @@ protected internal override string GetValue() } private Color _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Color Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(CompositionColorBrush.Color) => (reference as CompositionColorBrush).Color, + _ => GetProperty() + }; + + Color GetProperty() + { + reference.Properties.TryGetColor(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as ColorNode).Evaluate() : + (Children[2] as ColorNode).Evaluate(); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix3x2Node.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix3x2Node.cs index 4fe297e34f8..87155ba3073 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix3x2Node.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix3x2Node.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions @@ -336,6 +337,57 @@ protected internal override string GetValue() } private Matrix3x2 _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Matrix3x2 Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + reference.Properties.TryGetMatrix3x2(PropertyName, out var referencedProperty); + return referencedProperty; + case ExpressionNodeType.Add: + return + (Children[0] as Matrix3x2Node).Evaluate() + + (Children[1] as Matrix3x2Node).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as Matrix3x2Node).Evaluate() - + (Children[1] as Matrix3x2Node).Evaluate(); + case ExpressionNodeType.Negate: + return + -(Children[0] as Matrix3x2Node).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (Matrix3x2Node v1, Matrix3x2Node v2) => v1.Evaluate() * v2.Evaluate(), + (Matrix3x2Node v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, Matrix3x2Node v2) => v2.Evaluate() * s1.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as Matrix3x2Node).Evaluate() : + (Children[2] as Matrix3x2Node).Evaluate(); + case ExpressionNodeType.Swizzle: + return new Matrix3x2( + Children[0].EvaluateSubchannel(Subchannels[0]), + Children[0].EvaluateSubchannel(Subchannels[1]), + Children[0].EvaluateSubchannel(Subchannels[2]), + Children[0].EvaluateSubchannel(Subchannels[3]), + Children[0].EvaluateSubchannel(Subchannels[4]), + Children[0].EvaluateSubchannel(Subchannels[5])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix4x4Node.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix4x4Node.cs index b75ec095e87..33bd6650465 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix4x4Node.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Matrix4x4Node.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -410,6 +412,77 @@ protected internal override string GetValue() } private Matrix4x4 _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Matrix4x4 Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(Visual.TransformMatrix) => (reference as Visual).TransformMatrix, + _ => GetProperty() + }; + + Matrix4x4 GetProperty() + { + reference.Properties.TryGetMatrix4x4(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Add: + return + (Children[0] as Matrix4x4Node).Evaluate() + + (Children[1] as Matrix4x4Node).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as Matrix4x4Node).Evaluate() - + (Children[1] as Matrix4x4Node).Evaluate(); + case ExpressionNodeType.Negate: + return + -(Children[0] as Matrix4x4Node).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (Matrix4x4Node v1, Matrix4x4Node v2) => v1.Evaluate() * v2.Evaluate(), + (Matrix4x4Node v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, Matrix4x4Node v2) => v2.Evaluate() * s1.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as Matrix4x4Node).Evaluate() : + (Children[2] as Matrix4x4Node).Evaluate(); + case ExpressionNodeType.Swizzle: + return new Matrix4x4( + Children[0].EvaluateSubchannel(Subchannels[0]), + Children[0].EvaluateSubchannel(Subchannels[1]), + Children[0].EvaluateSubchannel(Subchannels[2]), + Children[0].EvaluateSubchannel(Subchannels[3]), + Children[0].EvaluateSubchannel(Subchannels[4]), + Children[0].EvaluateSubchannel(Subchannels[5]), + Children[0].EvaluateSubchannel(Subchannels[6]), + Children[0].EvaluateSubchannel(Subchannels[7]), + Children[0].EvaluateSubchannel(Subchannels[8]), + Children[0].EvaluateSubchannel(Subchannels[9]), + Children[0].EvaluateSubchannel(Subchannels[10]), + Children[0].EvaluateSubchannel(Subchannels[11]), + Children[0].EvaluateSubchannel(Subchannels[12]), + Children[0].EvaluateSubchannel(Subchannels[13]), + Children[0].EvaluateSubchannel(Subchannels[14]), + Children[0].EvaluateSubchannel(Subchannels[15])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/QuaternionNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/QuaternionNode.cs index 54b09e738ca..26b29041cc0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/QuaternionNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/QuaternionNode.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -130,6 +132,67 @@ protected internal override string GetValue() } private Quaternion _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Quaternion Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(Visual.Orientation) => (reference as Visual).Orientation, + _ => GetProperty() + }; + + Quaternion GetProperty() + { + reference.Properties.TryGetQuaternion(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Add: + return + (Children[0] as QuaternionNode).Evaluate() + + (Children[1] as QuaternionNode).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as QuaternionNode).Evaluate() - + (Children[1] as QuaternionNode).Evaluate(); + case ExpressionNodeType.Negate: + return + -(Children[0] as QuaternionNode).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (QuaternionNode v1, QuaternionNode v2) => v1.Evaluate() * v2.Evaluate(), + (QuaternionNode v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, QuaternionNode v2) => v2.Evaluate() * s1.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Divide: + return + (Children[0] as QuaternionNode).Evaluate() / + (Children[1] as QuaternionNode).Evaluate(); + case ExpressionNodeType.QuaternionFromAxisAngle: + return Quaternion.CreateFromAxisAngle((Children[0] as Vector3Node).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as QuaternionNode).Evaluate() : + (Children[2] as QuaternionNode).Evaluate(); + case ExpressionNodeType.Swizzle: + return new Quaternion(this.EvaluateSubchannel(Subchannels[0]), this.EvaluateSubchannel(Subchannels[1]), this.EvaluateSubchannel(Subchannels[2]), this.EvaluateSubchannel(Subchannels[4])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ScalarNode.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ScalarNode.cs index fe3b7408b91..34f07643a56 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ScalarNode.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ScalarNode.cs @@ -2,6 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Numerics; +using Windows.UI.Composition; + namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { // Ignore warning: 'ScalarNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() @@ -258,6 +262,124 @@ protected internal override string GetValue() } private float _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public float Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(Visual.Opacity) => (reference as Visual).Opacity, + nameof(Visual.RotationAngle) => (reference as Visual).RotationAngle, + nameof(InsetClip.BottomInset) => (reference as InsetClip).BottomInset, + nameof(InsetClip.LeftInset) => (reference as InsetClip).LeftInset, + nameof(InsetClip.RightInset) => (reference as InsetClip).RightInset, + nameof(InsetClip.TopInset) => (reference as InsetClip).TopInset, + _ => GetProperty() + }; + + float GetProperty() + { + reference.Properties.TryGetScalar(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Negate: + return -(Children[0] as ScalarNode).Evaluate(); + case ExpressionNodeType.Add: + return (Children[0] as ScalarNode).Evaluate() + (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Subtract: + return (Children[0] as ScalarNode).Evaluate() - (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0] as ScalarNode).Evaluate() * (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Divide: + return (Children[0] as ScalarNode).Evaluate() / (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Min: + return MathF.Min((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Max: + return MathF.Max((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Absolute: + return MathF.Abs((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Sin: + return MathF.Sin((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Cos: + return MathF.Cos((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Asin: + return MathF.Asin((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Acos: + return MathF.Acos((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Atan: + return MathF.Atan((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Ceil: + return MathF.Ceiling((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Floor: + return MathF.Floor((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Ln: + return MathF.Log((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Log10: + return MathF.Log10((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Pow: + return MathF.Pow((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Round: + return MathF.Round((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Square: + return MathF.Pow((Children[0] as ScalarNode).Evaluate(), 2); + case ExpressionNodeType.Sqrt: + return MathF.Sqrt((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.ToDegrees: + return 180 * (Children[0] as ScalarNode).Evaluate() / MathF.PI; + case ExpressionNodeType.ToRadians: + return MathF.PI * (Children[0] as ScalarNode).Evaluate() / 180; + case ExpressionNodeType.Modulus: + return (Children[0] as ScalarNode).Evaluate() % (Children[1] as ScalarNode).Evaluate(); + case ExpressionNodeType.Conditional: + return (Children[0] as BooleanNode).Evaluate() ? (Children[1] as ScalarNode).Evaluate() : (Children[2] as ScalarNode).Evaluate(); + case ExpressionNodeType.Distance: + return Vector2.Distance((Children[0] as Vector2Node).Evaluate(), (Children[1] as Vector2Node).Evaluate()); + case ExpressionNodeType.Lerp: + { + var start = (Children[0] as ScalarNode).Evaluate(); + var end = (Children[1] as ScalarNode).Evaluate(); + var progress = (Children[2] as ScalarNode).Evaluate(); + return start + (progress * (end - start)); + } + + case ExpressionNodeType.Swizzle: + return Children[0] switch + { + ScalarNode n => n.Evaluate(), + Vector2Node n => Subchannels[0] switch + { + "X" => n.Evaluate().X, + _ => n.Evaluate().Y, + }, + Vector3Node n => Subchannels[0] switch + { + "X" => n.Evaluate().X, + "Y" => n.Evaluate().Y, + _ => n.Evaluate().Z, + }, + Vector4Node n => Subchannels[0] switch + { + "X" => n.Evaluate().X, + "Y" => n.Evaluate().Y, + "Z" => n.Evaluate().Z, + _ => n.Evaluate().W, + }, + _ => throw new NotImplementedException() + }; + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector2Node.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector2Node.cs index 31c9885e70e..91c7513c980 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector2Node.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector2Node.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -301,6 +303,68 @@ protected internal override string GetValue() } private Vector2 _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Vector2 Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(Visual.Size) => (reference as Visual).Size, + nameof(Visual.AnchorPoint) => (reference as Visual).AnchorPoint, + _ => GetProperty() + }; + + Vector2 GetProperty() + { + reference.Properties.TryGetVector2(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as Vector2Node).Evaluate() : + (Children[2] as Vector2Node).Evaluate(); + case ExpressionNodeType.Add: + return + (Children[0] as Vector2Node).Evaluate() + + (Children[1] as Vector2Node).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as Vector2Node).Evaluate() - + (Children[1] as Vector2Node).Evaluate(); + case ExpressionNodeType.Negate: + return + -(Children[0] as Vector2Node).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (Vector2Node v1, Vector2Node v2) => v1.Evaluate() * v2.Evaluate(), + (Vector2Node v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, Vector2Node v2) => s1.Evaluate() * v2.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Divide: + return + (Children[0] as Vector2Node).Evaluate() / + (Children[1] as Vector2Node).Evaluate(); + case ExpressionNodeType.Vector2: + return new Vector2((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Swizzle: + return new Vector2(Children[0].EvaluateSubchannel(Subchannels[0]), Children[0].EvaluateSubchannel(Subchannels[1])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector3Node.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector3Node.cs index 7493428608a..4a89f50991d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector3Node.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector3Node.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -324,6 +326,79 @@ protected internal override string GetValue() } private Vector3 _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Vector3 Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantValue: + return _value; + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + nameof(Visual.Offset) => (reference as Visual).Offset, + nameof(Visual.RotationAxis) => (reference as Visual).RotationAxis, + nameof(Visual.CenterPoint) => (reference as Visual).CenterPoint, + _ => GetProperty() + }; + + Vector3 GetProperty() + { + reference.Properties.TryGetVector3(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as Vector3Node).Evaluate() : + (Children[2] as Vector3Node).Evaluate(); + case ExpressionNodeType.Add: + return + (Children[0] as Vector3Node).Evaluate() + + (Children[1] as Vector3Node).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as Vector3Node).Evaluate() - + (Children[1] as Vector3Node).Evaluate(); + case ExpressionNodeType.Lerp: + { + var t = (Children[2] as ScalarNode).Evaluate(); + return + (Children[0] as Vector3Node).Evaluate() * t - + (Children[1] as Vector3Node).Evaluate() * (1 - t); + } + case ExpressionNodeType.Negate: + return + -(Children[0] as Vector3Node).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (Vector3Node v1, Vector3Node v2) => v1.Evaluate() * v2.Evaluate(), + (Vector3Node v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, Vector3Node v2) => s1.Evaluate() * v2.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Divide: + return + (Children[0] as Vector3Node).Evaluate() / + (Children[1] as Vector3Node).Evaluate(); + case ExpressionNodeType.Vector3: + return new Vector3( + (Children[0] as ScalarNode).Evaluate(), + (Children[1] as ScalarNode).Evaluate(), + (Children[2] as ScalarNode).Evaluate()); + case ExpressionNodeType.Swizzle: + return new Vector3(Children[0].EvaluateSubchannel(Subchannels[0]), Children[0].EvaluateSubchannel(Subchannels[1]), Children[0].EvaluateSubchannel(Subchannels[2])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector4Node.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector4Node.cs index 83b85093088..dadc67f5489 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector4Node.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/Vector4Node.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Numerics; +using Windows.UI.Composition; namespace Microsoft.Toolkit.Uwp.UI.Animations.Expressions { @@ -347,6 +349,70 @@ protected internal override string GetValue() } private Vector4 _value; + + /// + /// Evaluates the current value of the expression + /// + /// The current value of the expression + public Vector4 Evaluate() + { + switch (NodeType) + { + case ExpressionNodeType.ConstantParameter: + throw new NotImplementedException(); + case ExpressionNodeType.ReferenceProperty: + var reference = (Children[0] as ReferenceNode).Reference; + return PropertyName switch + { + _ => GetProperty() + }; + + Vector4 GetProperty() + { + reference.Properties.TryGetVector4(PropertyName, out var value); + return value; + } + + case ExpressionNodeType.Conditional: + return + (Children[0] as BooleanNode).Evaluate() ? + (Children[1] as Vector4Node).Evaluate() : + (Children[2] as Vector4Node).Evaluate(); + case ExpressionNodeType.Add: + return + (Children[0] as Vector4Node).Evaluate() + + (Children[1] as Vector4Node).Evaluate(); + case ExpressionNodeType.Subtract: + return + (Children[0] as Vector4Node).Evaluate() - + (Children[1] as Vector4Node).Evaluate(); + case ExpressionNodeType.Negate: + return + -(Children[0] as Vector4Node).Evaluate(); + case ExpressionNodeType.Multiply: + return (Children[0], Children[1]) switch + { + (Vector4Node v1, Vector4Node v2) => v1.Evaluate() * v2.Evaluate(), + (Vector4Node v1, ScalarNode s2) => v1.Evaluate() * s2.Evaluate(), + (ScalarNode s1, Vector4Node v2) => s1.Evaluate() * v2.Evaluate(), + _ => throw new NotImplementedException() + }; + case ExpressionNodeType.Divide: + return + (Children[0] as Vector4Node).Evaluate() / + (Children[1] as Vector4Node).Evaluate(); + case ExpressionNodeType.Vector3: + return new Vector4( + (Children[0] as ScalarNode).Evaluate(), + (Children[1] as ScalarNode).Evaluate(), + (Children[2] as ScalarNode).Evaluate(), + (Children[2] as ScalarNode).Evaluate()); + case ExpressionNodeType.Swizzle: + return new Vector4(Children[0].EvaluateSubchannel(Subchannels[0]), Children[0].EvaluateSubchannel(Subchannels[1]), Children[0].EvaluateSubchannel(Subchannels[2]), Children[0].EvaluateSubchannel(Subchannels[3])); + default: + throw new NotImplementedException(); + } + } } #pragma warning restore CS0660, CS0661 } \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/UI/Animations/Test_ExpressionNode.cs b/UnitTests/UnitTests.UWP/UI/Animations/Test_ExpressionNode.cs new file mode 100644 index 00000000000..c61d8bafc99 --- /dev/null +++ b/UnitTests/UnitTests.UWP/UI/Animations/Test_ExpressionNode.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Toolkit.Uwp; +using Windows.UI.Xaml.Controls; +using Microsoft.Toolkit.Uwp.UI.Animations; +using System.Numerics; +using Microsoft.Toolkit.Uwp.UI; +using System; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Hosting; +using Microsoft.Toolkit.Uwp.UI.Animations.Expressions; + +namespace UnitTests.UWP.UI.Animations +{ + [TestClass] + [TestCategory("Test_ExpressionNode")] + public class Test_ExpressionNode : VisualUITestBase + { + [TestMethod] + public async Task EvaluateExpressionNode() + { + await App.DispatcherQueue.EnqueueAsync(async () => + { + Grid grid = new(); + await SetTestContentAsync(grid); + + var visual = ElementCompositionPreview.GetElementVisual(grid); + + // Test basic vector algebra + visual.Offset = Vector3.UnitX; + var vector3AlgebraNode = (-visual.GetReference().Offset + + Vector3.UnitY + - Vector3.UnitZ) * 5; + + Assert.AreEqual(new Vector3(-5, 5, -5), vector3AlgebraNode.Evaluate()); + + // Test swizzle operation + visual.CenterPoint = Vector3.UnitY; + var swizzleNode = visual.GetReference().CenterPoint.GetSubchannels( + Vector3Node.Subchannel.Y, + Vector3Node.Subchannel.X, + Vector3Node.Subchannel.Y); + + Assert.AreEqual(new Vector3(1, 0, 1), swizzleNode.Evaluate()); + + // Test retrieving properties from set + var propertySet = visual.Compositor.CreatePropertySet(); + var propertyName = "test"; + var propertyNode = propertySet.GetReference().GetVector4Property(propertyName); + + propertySet.InsertVector4(propertyName, Vector4.UnitW); + Assert.AreEqual(Vector4.UnitW, propertyNode.Evaluate()); + + propertySet.InsertVector4(propertyName, Vector4.UnitX); + Assert.AreEqual(Vector4.UnitX, propertyNode.Evaluate()); + }); + } + } +} \ No newline at end of file diff --git a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj index d50b438032e..29f55845ee9 100644 --- a/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj +++ b/UnitTests/UnitTests.UWP/UnitTests.UWP.csproj @@ -231,6 +231,7 @@ +