diff --git a/Windows.Toolkit.Common.props b/Windows.Toolkit.Common.props index 51218d281..b8decafc5 100644 --- a/Windows.Toolkit.Common.props +++ b/Windows.Toolkit.Common.props @@ -9,7 +9,6 @@ (c) .NET Foundation and Contributors. All rights reserved. https://github.com/CommunityToolkit/Labs-Windows https://github.com/CommunityToolkit/Labs-Windows/releases - Icon.png https://raw.githubusercontent.com/CommunityToolkit/Labs-Windows/main/nuget.png $(NoWarn);NU1505;NU1504 diff --git a/common/GlobalUsings_WinUI.cs b/common/GlobalUsings_WinUI.cs index 547158a0a..8af6f322b 100644 --- a/common/GlobalUsings_WinUI.cs +++ b/common/GlobalUsings_WinUI.cs @@ -1,46 +1,56 @@ -// 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. - -// This file contains directives available to projects that are compiled for multiple frameworks. -// Learn more global using directives at https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/using-directive#global-modifier - -global using System.Runtime.InteropServices.WindowsRuntime; - -global using Windows.Foundation; -global using Windows.Foundation.Collections; - -#if !WINAPPSDK -global using Windows.ApplicationModel; -global using Windows.ApplicationModel.Activation; - -global using Windows.UI.Xaml.Automation; -global using Windows.UI.Xaml.Automation.Peers; - -global using Windows.UI.Xaml; -global using Windows.UI.Xaml.Controls; -global using Windows.UI.Xaml.Controls.Primitives; -global using Windows.UI.Xaml.Data; -global using Windows.UI.Xaml.Input; -global using Windows.UI.Xaml.Markup; -global using Windows.UI.Xaml.Media; -global using Windows.UI.Xaml.Media.Animation; -global using Windows.UI.Xaml.Navigation; - -#else - -global using Microsoft.UI.Xaml.Automation; -global using Microsoft.UI.Xaml.Automation.Peers; - -global using Microsoft.UI.Xaml; -global using Microsoft.UI.Xaml.Controls; -global using Microsoft.UI.Xaml.Controls.Primitives; -global using Microsoft.UI.Xaml.Data; -global using Microsoft.UI.Xaml.Input; -global using Microsoft.UI.Xaml.Markup; -global using Microsoft.UI.Xaml.Media; -global using Microsoft.UI.Xaml.Media.Animation; -global using Microsoft.UI.Xaml.Navigation; -#endif - -global using MUXC = Microsoft.UI.Xaml.Controls; +// 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. + +// This file contains directives available to projects that are compiled for multiple frameworks. +// Learn more global using directives at https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/using-directive#global-modifier + +global using System.Runtime.InteropServices.WindowsRuntime; + +global using Windows.Foundation; +global using Windows.Foundation.Collections; + +#if !WINAPPSDK +global using Windows.ApplicationModel; +global using Windows.ApplicationModel.Activation; + +global using Windows.UI.Xaml.Automation; +global using Windows.UI.Xaml.Automation.Peers; + +global using Windows.UI.Xaml; +global using Windows.UI.Xaml.Controls; +global using Windows.UI.Xaml.Controls.Primitives; +global using Windows.UI.Xaml.Data; +global using Windows.UI.Xaml.Input; +global using Windows.UI.Xaml.Markup; +global using Windows.UI.Xaml.Media; +global using Windows.UI.Xaml.Media.Animation; +global using Windows.UI.Xaml.Navigation; + +global using Windows.UI.Composition; +global using Windows.UI.Composition.Interactions; +global using Windows.UI.Xaml.Hosting; + +#else + +global using Microsoft.UI.Xaml.Automation; +global using Microsoft.UI.Xaml.Automation.Peers; + +global using Microsoft.UI.Xaml; +global using Microsoft.UI.Xaml.Controls; +global using Microsoft.UI.Xaml.Controls.Primitives; +global using Microsoft.UI.Xaml.Data; +global using Microsoft.UI.Xaml.Input; +global using Microsoft.UI.Xaml.Markup; +global using Microsoft.UI.Xaml.Media; +global using Microsoft.UI.Xaml.Media.Animation; +global using Microsoft.UI.Xaml.Navigation; + +global using Microsoft.UI.Composition; +global using Microsoft.UI.Composition.Interactions; +global using Microsoft.UI.Xaml.Hosting; +global using Microsoft.UI; + +#endif + +global using MUXC = Microsoft.UI.Xaml.Controls; \ No newline at end of file diff --git a/common/ProjectHeads/AllComponents/Tests.Uwp/CommunityToolkit.Tests.Uwp.csproj b/common/ProjectHeads/AllComponents/Tests.Uwp/CommunityToolkit.Tests.Uwp.csproj index 40b8ea00e..7ae118b1e 100644 --- a/common/ProjectHeads/AllComponents/Tests.Uwp/CommunityToolkit.Tests.Uwp.csproj +++ b/common/ProjectHeads/AllComponents/Tests.Uwp/CommunityToolkit.Tests.Uwp.csproj @@ -1,7 +1,6 @@  - true @@ -24,21 +23,18 @@ - {FD78002E-C4E6-4BF8-9EC3-C06250DFEF34} CommunityToolkit.Tests CommunityToolkit.Tests.Uwp $(VisualStudioVersion) - Designer - - + \ No newline at end of file diff --git a/common/ProjectHeads/AllComponents/Uwp/CommunityToolkit.App.Uwp.csproj b/common/ProjectHeads/AllComponents/Uwp/CommunityToolkit.App.Uwp.csproj index cced3a617..79a071224 100644 --- a/common/ProjectHeads/AllComponents/Uwp/CommunityToolkit.App.Uwp.csproj +++ b/common/ProjectHeads/AllComponents/Uwp/CommunityToolkit.App.Uwp.csproj @@ -1,7 +1,6 @@  - true @@ -24,7 +23,6 @@ - CommunityToolkit.App.Uwp CommunityToolkit.App.Uwp diff --git a/components/CompositionCollectionView/OpenSolution.bat b/components/CompositionCollectionView/OpenSolution.bat new file mode 100644 index 000000000..24dc1cb49 --- /dev/null +++ b/components/CompositionCollectionView/OpenSolution.bat @@ -0,0 +1,3 @@ +@ECHO OFF + +powershell ..\..\common\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %* \ No newline at end of file diff --git a/components/CompositionCollectionView/samples/Assets/ceiling.bmp b/components/CompositionCollectionView/samples/Assets/ceiling.bmp new file mode 100644 index 000000000..934b0ceb0 Binary files /dev/null and b/components/CompositionCollectionView/samples/Assets/ceiling.bmp differ diff --git a/components/CompositionCollectionView/samples/Assets/face.png b/components/CompositionCollectionView/samples/Assets/face.png new file mode 100644 index 000000000..eba7cb2df Binary files /dev/null and b/components/CompositionCollectionView/samples/Assets/face.png differ diff --git a/components/CompositionCollectionView/samples/Assets/floor.bmp b/components/CompositionCollectionView/samples/Assets/floor.bmp new file mode 100644 index 000000000..88181cc41 Binary files /dev/null and b/components/CompositionCollectionView/samples/Assets/floor.bmp differ diff --git a/components/CompositionCollectionView/samples/Assets/wall.bmp b/components/CompositionCollectionView/samples/Assets/wall.bmp new file mode 100644 index 000000000..491ddc977 Binary files /dev/null and b/components/CompositionCollectionView/samples/Assets/wall.bmp differ diff --git a/components/CompositionCollectionView/samples/BasicSample.xaml b/components/CompositionCollectionView/samples/BasicSample.xaml new file mode 100644 index 000000000..e4714e638 --- /dev/null +++ b/components/CompositionCollectionView/samples/BasicSample.xaml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/components/CompositionCollectionView/samples/BasicSample.xaml.cs b/components/CompositionCollectionView/samples/BasicSample.xaml.cs new file mode 100644 index 000000000..eeda87525 --- /dev/null +++ b/components/CompositionCollectionView/samples/BasicSample.xaml.cs @@ -0,0 +1,83 @@ +// 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 CommunityToolkit.Labs.WinUI; +using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; +using System.Xml.Linq; + +#if !WINAPPSDK +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +#endif + +namespace CompositionCollectionView.Sample +{ + [ToolkitSample(id: nameof(BasicSample), "Simple layout", description: "Displaying elements in a simple layout.")] + public sealed partial class BasicSample : Page + { + public BasicSample() + { + this.InitializeComponent(); + + var elements = new Dictionary() + { + { 0, null }, + { 1, null }, + { 2, null }, + { 3, null }, + { 4, null } + }; + #if !WINAPPSDK + + var layout = new SampleLayout((id) => + new Rectangle() + { + Width = 100, + Height = 100, + Fill = new SolidColorBrush(Windows.UI.Colors.CornflowerBlue) + }); + compositionCollectionView.SetLayout(layout); + compositionCollectionView.UpdateSource(elements); + + addButton.Click += (object sender, RoutedEventArgs e) => + { + elements.Add((uint)elements.Count, null); + compositionCollectionView.UpdateSource(elements); + }; + #endif + } + +#if !WINAPPSDK + public class SampleLayout : CompositionCollectionLayout + { + public SampleLayout(Func elementFactory) : base(elementFactory) + { + } + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + return ExpressionFunctions.Vector3(element.Id * 120, 0, 0); + } + + public override ScalarNode GetElementScaleNode(ElementReference element) => 1; + } +#endif + } +} diff --git a/components/CompositionCollectionView/samples/CanvasSample.cs b/components/CompositionCollectionView/samples/CanvasSample.cs new file mode 100644 index 000000000..4c48a9451 --- /dev/null +++ b/components/CompositionCollectionView/samples/CanvasSample.cs @@ -0,0 +1,97 @@ +// 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 CommunityToolkit.Labs.WinUI; +using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; +using System.Numerics; +using System.Xml.Linq; + +#if !WINAPPSDK +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +#endif + +namespace CompositionCollectionView.Sample +{ + [ToolkitSample(id: nameof(CanvasSample), "Canvas layout", description: "Displays elements in a 2d canvas, animating between updates.")] + public sealed partial class CanvasSample : Page + { + public CanvasSample() + { + this.InitializeComponent(); + +#if !WINAPPSDK + var elements = new Dictionary() + { + { 0, Vector2.Zero }, + { 1, Vector2.Zero }, + { 2, Vector2.Zero }, + { 3, Vector2.Zero }, + { 4, Vector2.Zero } + }; + + var layout = new CanvasLayout((id) => + new Rectangle() + { + Width = 100, + Height = 100, + Fill = new SolidColorBrush(Windows.UI.Colors.CornflowerBlue), + Stroke = new SolidColorBrush(Colors.Gray), + StrokeThickness = 1 + }); + + compositionCollectionView.SetLayout(layout); + compositionCollectionView.UpdateSource(elements); + + rearrangeButton.Click += (object sender, RoutedEventArgs e) => + { + var rnd = new Random(); + + for(uint i =0; i< elements.Count; i++) + { + elements[i] = new Vector2( + (float)(rnd.NextDouble() * compositionCollectionView.ActualWidth), + (float)(rnd.NextDouble() * compositionCollectionView.ActualHeight)); + } + compositionCollectionView.UpdateSource(elements); + }; +#endif + } + +#if !WINAPPSDK + public class CanvasLayout : CompositionCollectionLayout + { + public CanvasLayout(Func elementFactory) : base(elementFactory) + { + } + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + return new Vector3(element.Model.X, element.Model.Y, 0); + } + + protected override ElementTransition GetElementTransitionEasingFunction(ElementReference element) => + new(200, + Window.Current.Compositor.CreateCubicBezierEasingFunction(new Vector2(0.25f, 0.1f), new Vector2(0.25f, 1f))); + } +#endif + } +} diff --git a/components/CompositionCollectionView/samples/CanvasSample.xaml b/components/CompositionCollectionView/samples/CanvasSample.xaml new file mode 100644 index 000000000..e575a31a6 --- /dev/null +++ b/components/CompositionCollectionView/samples/CanvasSample.xaml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/components/CompositionCollectionView/samples/CompositionCollectionView.Samples.csproj b/components/CompositionCollectionView/samples/CompositionCollectionView.Samples.csproj new file mode 100644 index 000000000..d0930b55b --- /dev/null +++ b/components/CompositionCollectionView/samples/CompositionCollectionView.Samples.csproj @@ -0,0 +1,15 @@ + + + CompositionCollectionView + + + + + + + + + + + + diff --git a/components/CompositionCollectionView/samples/CompositionCollectionView.md b/components/CompositionCollectionView/samples/CompositionCollectionView.md new file mode 100644 index 000000000..a395862b2 --- /dev/null +++ b/components/CompositionCollectionView/samples/CompositionCollectionView.md @@ -0,0 +1,53 @@ +--- +title: CompositionCollectionView +author: arcadiogarcia +description: A composition-driven collection view with fully customizable look and behavior +keywords: CompositionCollectionView, Control, Layout +dev_langs: + - csharp +category: Controls +subcategory: Layout +discussion-id: 0 +issue-id: 135 +--- + + + + + + + +# CompositionCollectionView + +For more information about this experiment see: +- Discussion: https://github.com/CommunityToolkit/WindowsCommunityToolkit/discussions/4565 +- Issue: https://github.com/CommunityToolkit/Labs-Windows/issues/135 + +CompositionCollectionView is a control that can host and position a collection of elements using developer-defined layouts based on the composition animation APIs +This facilitates the task of creating complex, performant layouts that are integrated with touch gestures, allowing to reuse logic across them. + +A layout can be as simple as this one, which calculates an static position for each element according to its properties: + +> [!SAMPLE BasicSample] + +The CompositionCollectionView can automatically animate the elements as the source is updated, the layouts only need to provide timing and easing information: + +> [!SAMPLE CanvasSample] + +While a CompositionCollectionView is only using one layout at a time, it can transition to any other layout on demand, implicitly animating the elements if desired. + +This sample shows how easy it is to transition between two unrelated layouts: + +> [!SAMPLE SwitchLayoutsSample] + +Behaviors can be used to define interactions, gestures or any other custom logic that might be shared across layouts. The built in InteractionTrackerGesture simplifies the task of integrating touch into a layout and defining layout-independent touch gestures. + +This sample shows a scrollable list of elements that follow a custom path: + +> [!SAMPLE InteractionTrackerSample] + +Layouts can be as complicated as you want, your creativity is the limit! + +This sample is inspired by the classic Maze 3d screensaver. The user can move around the maze with touch gestures (collisions with the inner walls are ommited) and will transition to an overhead view when they touch the smiley face. + +> [!SAMPLE MazeSample] diff --git a/components/CompositionCollectionView/samples/Dependencies.props b/components/CompositionCollectionView/samples/Dependencies.props new file mode 100644 index 000000000..e622e1df4 --- /dev/null +++ b/components/CompositionCollectionView/samples/Dependencies.props @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml b/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml new file mode 100644 index 000000000..35d63aa33 --- /dev/null +++ b/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml @@ -0,0 +1,13 @@ + + + diff --git a/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml.cs b/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml.cs new file mode 100644 index 000000000..8f4e3ba78 --- /dev/null +++ b/components/CompositionCollectionView/samples/InteractionTrackerSample.xaml.cs @@ -0,0 +1,154 @@ +// 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 CommunityToolkit.Labs.WinUI; +using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; +using System.Numerics; + +#if !WINAPPSDK +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Composition.Interactions; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +#endif + + +namespace CompositionCollectionView.Sample +{ + [ToolkitSample(id: nameof(InteractionTrackerSample), "Interaction tracker layout", description: "Layout driven by an interaction tracker.")] + public sealed partial class InteractionTrackerSample : Page + { + const int ElementWidth = 100; + + public InteractionTrackerSample() + { + this.InitializeComponent(); + +#if !WINAPPSDK + Dictionary elements = new() + { + { 0, null }, + { 1, null }, + { 2, null }, + { 3, null }, + { 4, null } + }; + + var layout = new LinearLayout((id) => + new Rectangle() + { + Width = ElementWidth, + Height = ElementWidth, + Fill = new SolidColorBrush(Colors.CornflowerBlue), + Stroke = new SolidColorBrush(Colors.Gray), + StrokeThickness = 1 + }); + + compositionCollectionView.SetLayout(layout); + compositionCollectionView.UpdateSource(elements); +#endif + } +#if !WINAPPSDK + public class LinearLayout : CompositionCollectionLayout + { + public LinearLayout(Func elementFactory) : base(elementFactory) + { + } + + protected override void OnActivated() + { + if (TryGetBehavior>() is null) + { + // Tracker can't be created until activation, we don't have access to the root panel until then + var trackerBehavior = new InteractionTrackerBehavior(RootPanel); + AddBehavior(trackerBehavior); + + var tracker = trackerBehavior.Tracker; + var interactionSource = trackerBehavior.InteractionSource; + + UpdateTrackerLimits(); + + interactionSource.ScaleSourceMode = InteractionSourceMode.Disabled; + interactionSource.PositionXSourceMode = InteractionSourceMode.EnabledWithInertia; + interactionSource.PositionYSourceMode = InteractionSourceMode.Disabled; + } + + RootPanel.Background = new SolidColorBrush(Colors.Transparent); + RootPanel.PointerPressed += RootPointerPressed; + } + + protected override void OnDeactivated() + { + RootPanel.PointerPressed -= RootPointerPressed; + } + + void RootPointerPressed(object sender, PointerRoutedEventArgs e) + { + if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch) + { + var position = e.GetCurrentPoint(RootPanel); + GetBehavior>().InteractionSource.TryRedirectForManipulation(position); + } + } + + protected override void OnElementsUpdated() + { + UpdateTrackerLimits(); + } + + private void UpdateTrackerLimits() + { + var trackerBehavior = GetBehavior>(); + + var availableWidth = (float)RootPanel.ActualWidth - ElementWidth; + var elementsWidth = Source.Count() * ElementWidth * 1.2f; + + trackerBehavior.Tracker.MaxPosition = new Vector3(elementsWidth - ((float)RootPanel.ActualWidth + ElementWidth) / 2, 0, 0); + trackerBehavior.Tracker.MinPosition = new Vector3(-((float)RootPanel.ActualWidth - ElementWidth) / 2, 0, 0); + } + + private ScalarNode ScrollProgress(ElementReference element) + { + var availableWidth = RootPanelVisual.GetReference().Size.X - ElementWidth; + + return ExpressionFunctions.Clamp( + (element.Id * ElementWidth * 1.2f + - GetBehavior>().Tracker.GetReference().Position.X) / availableWidth, + 0, + 1); + } + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + var availableWidth = RootPanelVisual.GetReference().Size.X - ElementWidth; + + var xPosition = availableWidth * ScrollProgress(element); + + var yPosition = 50 * ExpressionFunctions.Sin(ScrollProgress(element) * (float)Math.PI); + + return ExpressionFunctions.Vector3(xPosition, yPosition, 0); + } + + public override ScalarNode GetElementScaleNode(ElementReference element) => 1.5f - ExpressionFunctions.Abs(0.5f - ScrollProgress(element)); + } +#endif + } +} diff --git a/components/CompositionCollectionView/samples/MazeSample.xaml b/components/CompositionCollectionView/samples/MazeSample.xaml new file mode 100644 index 000000000..6320b46b0 --- /dev/null +++ b/components/CompositionCollectionView/samples/MazeSample.xaml @@ -0,0 +1,13 @@ + + + diff --git a/components/CompositionCollectionView/samples/MazeSample.xaml.cs b/components/CompositionCollectionView/samples/MazeSample.xaml.cs new file mode 100644 index 000000000..1a9de5217 --- /dev/null +++ b/components/CompositionCollectionView/samples/MazeSample.xaml.cs @@ -0,0 +1,458 @@ +// 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 CommunityToolkit.Labs.WinUI; +using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; +using System.Numerics; + +#if !WINAPPSDK +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Composition.Interactions; +using Windows.UI.StartScreen; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Media.Imaging; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +#endif + + +namespace CompositionCollectionView.Sample +{ + [ToolkitSample(id: nameof(MazeSample), "Maze layout", description: "Layout driven by an interaction tracker.")] + public sealed partial class MazeSample : Page + { + public enum TileType { Floor, Ceiling, HorizontalWall, VerticalWall, Goal } + + const int TileWidth = 100; + const int MazeSize = 10; + + static readonly Vector3 GoalPosition = new Vector3(9, 0, 0); + + +#if !WINAPPSDK + private Dictionary elements { get; init; } +#endif + + public MazeSample() + { + this.InitializeComponent(); + +#if !WINAPPSDK + List tiles = new(); + + for (int i = 0; i < MazeSize; i++) + { + for (int j = 0; j < MazeSize; j++) + { + tiles.Add(new(TileType.Floor, i, j)); + tiles.Add(new(TileType.Ceiling, i, j)); + + if (j == 0) + { + tiles.Add(new(TileType.HorizontalWall, i, j)); + } + else if (j == MazeSize - 1) + { + tiles.Add(new(TileType.HorizontalWall, i, j + 1)); + } + + if (i == 0) + { + tiles.Add(new(TileType.VerticalWall, i, j)); + } + else if (i == MazeSize - 1) + { + tiles.Add(new(TileType.VerticalWall, i + 1, j)); + } + } + } + + var maze = new Maze(MazeSize, MazeSize); + tiles.AddRange(maze.Walls); + + tiles.Add(new(TileType.Goal, (int)GoalPosition.X, (int)GoalPosition.Y)); + + elements = tiles.OrderBy(x => x.Y).Select((x, i) => (index: (uint)i, element: x)).ToDictionary(x => x.index, x => x.element); + + var layout = new SpinningMazeLayout((id) => TileControl.Create()); + compositionCollectionView.SetLayout(layout); + compositionCollectionView.UpdateSource(elements); + + Visual visual = ElementCompositionPreview.GetElementVisual(compositionCollectionView); + var viewSize = visual.GetReference().Size; + + visual.StartAnimation(AnimationConstants.TransformMatrix, + ExpressionFunctions.CreateTranslation(ExpressionFunctions.Vector3(-viewSize.X / 2, -viewSize.Y / 2, 0)) + * ExpressionFunctions.Matrix4x4( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, -1.0f / viewSize.X, + 0.0f, 0.0f, 0.0f, 1.0f) * + ExpressionFunctions.CreateTranslation(ExpressionFunctions.Vector3(viewSize.X / 2, viewSize.Y / 2, 0))); +#endif + } + + public record Tile(TileType Type, int X, int Y); + +#if !WINAPPSDK + public abstract class MazeLayout : CompositionCollectionLayout + { + protected const string PositionNode = nameof(PositionNode); + protected const string ScaleNode = nameof(ScaleNode); + protected const string RotationNode = nameof(RotationNode); + const string CameraTransformNode = nameof(CameraTransformNode); + + public MazeLayout(CompositionCollectionLayout sourceLayout) : base(sourceLayout) + { + } + + public MazeLayout(Func elementFactory) : base(elementFactory) + { + } + + protected override void OnActivated() + { + var translation = ExpressionFunctions.CreateTranslation(AnimatableNodes.GetOrCreateVector3Node(PositionNode, Vector3.Zero).Reference); + + var scaleNode = AnimatableNodes.GetOrCreateScalarNode(ScaleNode, 1).Reference; + var scale = ExpressionFunctions.CreateScale(ExpressionFunctions.Vector3(scaleNode, scaleNode, scaleNode)); + + var rotationNode = AnimatableNodes.GetOrCreateVector3Node(RotationNode, Vector3.Zero).Reference; + var rotation = ExpressionFunctions.CreateMatrix4x4FromAxisAngle(Vector3.UnitX, rotationNode.X) * + ExpressionFunctions.CreateMatrix4x4FromAxisAngle(Vector3.UnitY, rotationNode.Y) * + ExpressionFunctions.CreateMatrix4x4FromAxisAngle(Vector3.UnitZ, rotationNode.Z); + + AnimatableNodes.GetOrCreateMatrix4x4Node(CameraTransformNode, Matrix4x4.Identity).Animate(translation * rotation * scale); + + TileControl.LoadBrushes(); + } + + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + var xPosition = element.Model.Type switch + { + _ => element.Model.X * TileWidth + }; + + var yPosition = element.Model.Type switch + { + TileType.Goal => (element.Model.Y + 0.5f) * TileWidth, + _ => element.Model.Y * TileWidth + }; + + var height = element.Model.Type switch + { + TileType.Floor => TileWidth, + _ => 0 + }; + + var camera = AnimatableNodes.GetOrCreateMatrix4x4Node(CameraTransformNode, Matrix4x4.Identity).Reference; + + return ExpressionFunctions.Transform(ExpressionFunctions.Vector4(xPosition, height, yPosition, 1), camera).XYZ; + } + + public override ScalarNode GetElementScaleNode(ElementReference element) + { + return AnimatableNodes.GetOrCreateScalarNode(ScaleNode, 1).Reference; + } + + public override QuaternionNode GetElementOrientationNode(ElementReference element) + { + var localOrientation = element.Model.Type switch + { + TileType.Floor => Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI / 2, 0), + TileType.Ceiling => Quaternion.CreateFromYawPitchRoll(0, (float)Math.PI / 2, 0), + TileType.VerticalWall => Quaternion.CreateFromYawPitchRoll((float)-Math.PI / 2, 0, 0), + TileType.HorizontalWall => Quaternion.Identity, + TileType.Goal => Quaternion.Identity, + _ => Quaternion.Identity + }; + + var rotationNode = AnimatableNodes.GetOrCreateVector3Node(RotationNode, Vector3.Zero).Reference; + var cameraOrientation = ExpressionFunctions.CreateQuaternionFromAxisAngle(Vector3.UnitX, rotationNode.X) * + ExpressionFunctions.CreateQuaternionFromAxisAngle(Vector3.UnitY, rotationNode.Y) * + ExpressionFunctions.CreateQuaternionFromAxisAngle(Vector3.UnitZ, rotationNode.Z); + + return cameraOrientation * localOrientation; + } + + protected override void ConfigureElement(ElementReference element) + { + if (element.Container is Rectangle rect) + { + rect.Fill = TileControl.BrushFor(element.Model.Type); + } + } + + protected override ElementTransition GetElementTransitionEasingFunction(ElementReference element) => + new(600, + Window.Current.Compositor.CreateCubicBezierEasingFunction(new Vector2(0.25f, 0.1f), new Vector2(0.25f, 1f))); + } + + public class SpinningMazeLayout : MazeLayout + { + public SpinningMazeLayout(CompositionCollectionLayout sourceLayout) : base(sourceLayout) + { + } + + public SpinningMazeLayout(Func elementFactory) : base(elementFactory) + { + } + + protected override void OnActivated() + { + base.OnActivated(); + + var animation = Compositor.CreateVector3KeyFrameAnimation(); + animation.Duration = TimeSpan.FromSeconds(5); + animation.InsertKeyFrame(0, Vector3.Zero); + animation.InsertKeyFrame(1, new Vector3(0, (float)Math.PI * 2, 0)); + animation.IterationBehavior = AnimationIterationBehavior.Forever; + + var rotationNode = AnimatableNodes.GetOrCreateVector3Node(RotationNode, Vector3.Zero); + rotationNode.Animate(animation); + + float mazeSide = TileWidth * MazeSize; + var mazeCenter = new Vector3(-mazeSide / 2, 0, -mazeSide / 2); + + AnimatableNodes.GetOrCreateVector3Node(PositionNode, Vector3.Zero).Animate(ExpressionFunctions.Vector3( + ExpressionFunctions.Sin(rotationNode.Reference.Y) * mazeSide * 1.5f, + mazeSide / 3, + -ExpressionFunctions.Cos(rotationNode.Reference.Y) * mazeSide * 1.5f + ) + (Vector3Node)mazeCenter); + + AnimatableNodes.GetOrCreateScalarNode(ScaleNode, 1).Animate(RootPanelVisual.GetReference().Size.Y / TileWidth); + RootPanel.Tapped += this.OnTapped; + } + + protected override void OnDeactivated() + { + RootPanel.Tapped -= OnTapped; + } + + private void OnTapped(object sender, TappedRoutedEventArgs e) + { + TransitionTo(x => new TraversableMazeLayout(this)); + } + + public override ScalarNode GetElementOpacityNode(ElementReference element) + { + return element.Model.Type switch + { + TileType.Ceiling => 0.3f, + _ => 1 + }; + } + } + + + public class TraversableMazeLayout : MazeLayout + { + public TraversableMazeLayout(Func elementFactory) : base(elementFactory) + { + } + + public TraversableMazeLayout(CompositionCollectionLayout sourceLayout) : base(sourceLayout) + { + } + + protected override void OnActivated() + { + base.OnActivated(); + + var trackerBehavior = TryGetBehavior>(); + + if (trackerBehavior is null) + { + // Tracker can't be created until activation, we don't have access to the root panel until then + trackerBehavior = new InteractionTrackerBehavior(RootPanel); + AddBehavior(trackerBehavior); + } + + var tracker = trackerBehavior.Tracker; + var interactionSource = trackerBehavior.InteractionSource; + + UpdateTrackerLimits(); + + interactionSource.ScaleSourceMode = InteractionSourceMode.Disabled; + interactionSource.PositionXSourceMode = InteractionSourceMode.EnabledWithInertia; + interactionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia; + + AnimatableNodes.GetOrCreateVector3Node(PositionNode, Vector3.Zero).Animate( + ExpressionFunctions.Vector3( + -tracker.GetReference().Position.X, + 0, + tracker.GetReference().Position.Y)); + + trackerBehavior.TrackerOwner.OnIdleStateEntered += this.OnTrackerIdleStateEntered; + + AnimatableNodes.GetOrCreateScalarNode(ScaleNode, 1).Animate(RootPanelVisual.GetReference().Size.Y / TileWidth); + AnimatableNodes.GetOrCreateVector3Node(RotationNode, Vector3.Zero).Value = new Vector3(0, 0, 0); + + RootPanel.Background = new SolidColorBrush(Colors.Black); + RootPanel.PointerPressed += RootPointerPressed; + } + + protected override void OnDeactivated() + { + RootPanel.PointerPressed -= RootPointerPressed; + GetBehavior>().TrackerOwner.OnIdleStateEntered -= this.OnTrackerIdleStateEntered; + } + + private void OnTrackerIdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args, InteractionTrackerState previousState) + { + if (Vector3.Distance(sender.Position, new Vector3(GoalPosition.X * TileWidth, -GoalPosition.Y * TileWidth, 0)) < TileWidth) + { + TransitionTo(x => new SpinningMazeLayout(this)); + } + } + + void RootPointerPressed(object sender, PointerRoutedEventArgs e) + { + if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch) + { + var position = e.GetCurrentPoint(RootPanel); + GetBehavior>().InteractionSource.TryRedirectForManipulation(position); + } + } + + protected override void OnElementsUpdated() + { + UpdateTrackerLimits(); + } + + private void UpdateTrackerLimits() + { + var trackerBehavior = GetBehavior>(); + + trackerBehavior.Tracker.MaxPosition = new Vector3(TileWidth * (MazeSize - 1), 0, 0); + trackerBehavior.Tracker.MinPosition = new Vector3(0, -TileWidth * (MazeSize - 1) + 20, 0); + } + + public override ScalarNode GetElementOpacityNode(ElementReference element) + { + return element.Model switch + { + { Type: TileType.Ceiling } => 1, + { Type: TileType.Floor } => 1, + { Type: TileType.Goal } => 1, + { Type: TileType.VerticalWall } and ({ X: 0 } or { X: MazeSize }) => 1, + { Type: TileType.HorizontalWall } and ({ Y: 0 } or { Y: MazeSize }) => 1, + _ => 0.3f + }; + } + } + + private class TileControl + { + private static ImageBrush? WallBrush, CeilingBrush, FloorBrush, GoalBrush; + + public static Rectangle Create() => new Rectangle() + { + Width = TileWidth, + Height = TileWidth + }; + + public static void LoadBrushes() + { + WallBrush = WallBrush ?? new ImageBrush() + { + ImageSource = new BitmapImage(new Uri("ms-appx:///CompositionCollectionViewExperiment.Samples/Assets/wall.bmp")) + }; + + CeilingBrush = CeilingBrush ?? new ImageBrush() + { + ImageSource = new BitmapImage(new Uri("ms-appx:///CompositionCollectionViewExperiment.Samples/Assets/ceiling.bmp")) + }; + + FloorBrush = FloorBrush ?? new ImageBrush() + { + ImageSource = new BitmapImage(new Uri("ms-appx:///CompositionCollectionViewExperiment.Samples/Assets/floor.bmp")) + }; + + GoalBrush = GoalBrush ?? new ImageBrush() + { + ImageSource = new BitmapImage(new Uri("ms-appx:///CompositionCollectionViewExperiment.Samples/Assets/face.png")) + }; + } + + public static ImageBrush? BrushFor(TileType type) => type switch + { + TileType.Ceiling => CeilingBrush, + TileType.Floor => FloorBrush, + TileType.Goal => GoalBrush, + _ => WallBrush + }; + } + +#endif + + private class Maze + { + public IReadOnlyList Walls { get; init; } + + public Maze(int width, int height) + { + Queue _pendingRooms = new(); + _pendingRooms.Enqueue(new Rect(0, 0, width, height)); + + List walls = new(); + var r = new Random(); + + while (_pendingRooms.Count > 0) + { + var room = _pendingRooms.Dequeue(); + var attemptHorizontalWall = r.NextDouble() > 0.5f; + if (attemptHorizontalWall && room.Height > 1) + { + var wallY = 1 + Math.Floor(r.NextDouble() * room.Height); + var doorX = Math.Floor(r.NextDouble() * (room.Width + 1)); + for (var x = 0; x < room.Width; x++) + { + if (x != doorX) + { + walls.Add(new(TileType.HorizontalWall, (int)(x + room.X), (int)(wallY + room.Y))); + } + } + _pendingRooms.Enqueue(new Rect(room.X, room.Y, room.Width, wallY)); + _pendingRooms.Enqueue(new Rect(room.X, room.Y + wallY, room.Width, (room.Height - wallY))); + } + else if (room.Width > 1) + { + var wallX = 1 + Math.Floor(r.NextDouble() * room.Width); + var doorY = Math.Floor(r.NextDouble() * (room.Height + 1)); + for (var y = 0; y < room.Height; y++) + { + if (y != doorY) + { + walls.Add(new(TileType.VerticalWall, (int)(wallX + room.X), (int)(y + room.Y))); + } + } + _pendingRooms.Enqueue(new Rect(room.X, room.Y, wallX, room.Height)); + _pendingRooms.Enqueue(new Rect(room.X + wallX, room.Y, (room.Width - wallX), room.Height)); + } + } + + Walls = walls; + } + } + } +} diff --git a/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml b/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml new file mode 100644 index 000000000..7a4d7081b --- /dev/null +++ b/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml.cs b/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml.cs new file mode 100644 index 000000000..e4bf63cfa --- /dev/null +++ b/components/CompositionCollectionView/samples/SwitchLayoutsSample.xaml.cs @@ -0,0 +1,135 @@ +// 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 CommunityToolkit.Labs.WinUI; +using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; +using System.Numerics; + +#if !WINAPPSDK +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Hosting; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; +using Windows.UI.Xaml.Shapes; +#else +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +#endif + + +namespace CompositionCollectionView.Sample +{ + [ToolkitSample(id: nameof(SwitchLayoutsSample), "Layout transition", description: "Transition between different layouts.")] + public sealed partial class SwitchLayoutsSample : Page + { + public SwitchLayoutsSample() + { + this.InitializeComponent(); + +#if !WINAPPSDK + Dictionary elements = new() + { + { 0, null }, + { 1, null }, + { 2, null }, + { 3, null }, + { 4, null } + }; + + var layout = new LinearLayout((id) => + new Rectangle() + { + Width = 100, + Height = 100, + Fill = new SolidColorBrush(Windows.UI.Colors.CornflowerBlue), + Stroke = new SolidColorBrush(Colors.Gray), + StrokeThickness = 1 + }); + compositionCollectionView.SetLayout(layout); + compositionCollectionView.UpdateSource(elements); +#endif + } + +#if !WINAPPSDK + public class LinearLayout : CompositionCollectionLayout + { + public LinearLayout(Func elementFactory) : base(elementFactory) + { + } + + public LinearLayout(CompositionCollectionLayout sourceLayout) : base(sourceLayout) + { + } + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + return ExpressionFunctions.Vector3(element.Id * 120, 0, 0); + } + + public override ScalarNode GetElementScaleNode(ElementReference element) => 1; + + protected override ElementTransition GetElementTransitionEasingFunction(ElementReference element) => + new(100, + Window.Current.Compositor.CreateCubicBezierEasingFunction(new Vector2(0.25f, 0.1f), new Vector2(0.25f, 1f))); + } + + public class StackLayout : CompositionCollectionLayout + { + public StackLayout(Func elementFactory) : base(elementFactory) + { + } + + public StackLayout(CompositionCollectionLayout sourceLayout) : base(sourceLayout) + { + } + + public override Vector3Node GetElementPositionNode(ElementReference element) + { + return ExpressionFunctions.Vector3(element.Id * 10, element.Id * 10, 0); + } + + public override ScalarNode GetElementScaleNode(ElementReference element) => (float)Math.Pow(0.95f, element.Id); + + protected override ElementTransition GetElementTransitionEasingFunction(ElementReference element) => + new(100, + Window.Current.Compositor.CreateCubicBezierEasingFunction(new Vector2(0.25f, 0.1f), new Vector2(0.25f, 1f))); + + protected override void ConfigureElement(ElementReference element) + { + element.Container.SetValue(Canvas.ZIndexProperty, -(int)element.Id); + } + } +#endif + + private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e) + { +#if !WINAPPSDK + if (sender is ToggleSwitch toggle) + { + if (toggle.IsOn && compositionCollectionView.Layout() is LinearLayout currentLinearLayout) + { + currentLinearLayout.TransitionTo(_ => new StackLayout(currentLinearLayout)); + } + else if (!toggle.IsOn && compositionCollectionView.Layout() is StackLayout currentStackLayout) + { + currentStackLayout.TransitionTo(_ => new LinearLayout(currentStackLayout)); + } + } +#endif + + } + } +} diff --git a/components/CompositionCollectionView/src/AdditionalAssemblyInfo.cs b/components/CompositionCollectionView/src/AdditionalAssemblyInfo.cs new file mode 100644 index 000000000..5ba7ccac3 --- /dev/null +++ b/components/CompositionCollectionView/src/AdditionalAssemblyInfo.cs @@ -0,0 +1,13 @@ +// 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.Runtime.CompilerServices; + +// These `InternalsVisibleTo` calls are intended to make it easier for +// for any internal code to be testable in all the different test projects +// used with the Labs infrastructure. +[assembly: InternalsVisibleTo("CompositionCollectionView.Tests.Uwp")] +[assembly: InternalsVisibleTo("CompositionCollectionView.Tests.WinAppSdk")] +[assembly: InternalsVisibleTo("CommunityToolkit.Labs.Tests.Uwp")] +[assembly: InternalsVisibleTo("CommunityToolkit.Labs.Tests.WinAppSdk")] diff --git a/components/CompositionCollectionView/src/AnimatableNodes/AnimatableCompositionNodeSet.cs b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableCompositionNodeSet.cs new file mode 100644 index 000000000..a11d7e910 --- /dev/null +++ b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableCompositionNodeSet.cs @@ -0,0 +1,144 @@ +// 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. +#nullable enable +using System.Numerics; + +namespace CommunityToolkit.Labs.WinUI; +public class AnimatableCompositionNodeSet : IDisposable +{ + private Dictionary _nodes = new(); + private Compositor _compositor; + + private bool disposedValue; + + public AnimatableCompositionNodeSet(Compositor compositor) + { + _compositor = compositor; + } + + public AnimatableScalarCompositionNode GetOrCreateScalarNode(string id, float defaultValue) + { + if (_nodes.ContainsKey(id)) + { + if (_nodes[id] is AnimatableScalarCompositionNode node) + { + return node; + } + else + { + throw new InvalidCastException("Node {id} is not a scalar node"); + } + } + else + { + var newNode = new AnimatableScalarCompositionNode(_compositor); + newNode.Value = defaultValue; + _nodes[id] = newNode; + return newNode; + } + } + + public AnimatableVector3CompositionNode GetOrCreateVector3Node(string id, Vector3 defaultValue) + { + if (_nodes.ContainsKey(id)) + { + if (_nodes[id] is AnimatableVector3CompositionNode node) + { + return node; + } + else + { + throw new InvalidCastException("Node {id} is not a vector3 node"); + } + } + else + { + var newNode = new AnimatableVector3CompositionNode(_compositor); + newNode.Value = defaultValue; + _nodes[id] = newNode; + return newNode; + } + } + + + public AnimatableQuaternionCompositionNode GetOrCreateQuaternionNode(string id, Quaternion defaultValue) + { + if (_nodes.ContainsKey(id)) + { + if (_nodes[id] is AnimatableQuaternionCompositionNode node) + { + return node; + } + else + { + throw new InvalidCastException("Node {id} is not a vector3 node"); + } + } + else + { + var newNode = new AnimatableQuaternionCompositionNode(_compositor); + newNode.Value = defaultValue; + _nodes[id] = newNode; + return newNode; + } + } + + public AnimatableMatrix4x4CompositionNode GetOrCreateMatrix4x4Node(string id, Matrix4x4 defaultValue) + { + if (_nodes.ContainsKey(id)) + { + if (_nodes[id] is AnimatableMatrix4x4CompositionNode node) + { + return node; + } + else + { + throw new InvalidCastException("Node {id} is not a matrix4x4 node"); + } + } + else + { + var newNode = new AnimatableMatrix4x4CompositionNode(_compositor); + newNode.Value = defaultValue; + _nodes[id] = newNode; + return newNode; + } + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + foreach (var node in _nodes) + { + if (node.Value is IDisposable disposable) + { + disposable.Dispose(); + } + } + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~AnimatableScalarCompositionNode() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/AnimatableNodes/AnimatableMatrix4x4CompositionNode.cs b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableMatrix4x4CompositionNode.cs new file mode 100644 index 000000000..405fbfbdf --- /dev/null +++ b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableMatrix4x4CompositionNode.cs @@ -0,0 +1,89 @@ +// 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. + +#nullable enable +using System.Numerics; +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public class AnimatableMatrix4x4CompositionNode : IDisposable +{ + private Visual _underlyingVisual; + private bool disposedValue; + private Matrix4x4Node? _currentAnimationNode = null; + + public Matrix4x4 Value + { + get + { + if (_currentAnimationNode is not null) + { + // When the node value is being driven by a ongoing scalarnode animation, reading the property might return a stale value, + // so we instead default to evaluating the original expression to get the most accurate value + return _currentAnimationNode.Evaluate(); + } + else + { + return ComposerValue; + } + } + set + { + _underlyingVisual.TransformMatrix = value; + _currentAnimationNode = null; + } + } + + public Matrix4x4 ComposerValue => _underlyingVisual.TransformMatrix; + + public AnimatableMatrix4x4CompositionNode(Compositor compositor) + { + _underlyingVisual = compositor.CreateShapeVisual(); + } + + public void Animate(CompositionAnimation animation) + { + _currentAnimationNode = null; + _underlyingVisual.StartAnimation(TransformMatrix, animation); + } + + public void Animate(Matrix4x4Node animation) + { + _currentAnimationNode = animation; + _underlyingVisual.StartAnimation(TransformMatrix, animation); + } + + public Matrix4x4Node Reference { get => _underlyingVisual.GetReference().TransformMatrix; } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _underlyingVisual.Dispose(); + _currentAnimationNode?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~AnimatableScalarCompositionNode() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/AnimatableNodes/AnimatableQuaternionCompositionNode.cs b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableQuaternionCompositionNode.cs new file mode 100644 index 000000000..356531644 --- /dev/null +++ b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableQuaternionCompositionNode.cs @@ -0,0 +1,88 @@ +// 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. + +#nullable enable +using System.Numerics; + +namespace CommunityToolkit.Labs.WinUI; +public class AnimatableQuaternionCompositionNode : IDisposable +{ + private Visual _underlyingVisual; + private bool disposedValue; + private QuaternionNode? _currentAnimationNode = null; + + public Quaternion Value + { + get + { + if (_currentAnimationNode is not null) + { + // When the node value is being driven by a ongoing scalarnode animation, reading the property might return a stale value, + // so we instead default to evaluating the original expression to get the most accurate value + return _currentAnimationNode.Evaluate(); + } + else + { + return ComposerValue; + } + } + set + { + _underlyingVisual.Orientation = value; + _currentAnimationNode = null; + } + } + + public Quaternion ComposerValue => _underlyingVisual.Orientation; + + public AnimatableQuaternionCompositionNode(Compositor compositor) + { + _underlyingVisual = compositor.CreateShapeVisual(); + } + + public void Animate(CompositionAnimation animation) + { + _currentAnimationNode = null; + _underlyingVisual.StartAnimation(AnimationConstants.Orientation, animation); + } + + public void Animate(QuaternionNode animation) + { + _currentAnimationNode = animation; + _underlyingVisual.StartAnimation(AnimationConstants.Orientation, animation); + } + + public QuaternionNode Reference { get => _underlyingVisual.GetReference().Orientation; } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _underlyingVisual.Dispose(); + _currentAnimationNode?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~AnimatableScalarCompositionNode() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/AnimatableNodes/AnimatableScalarCompositionNode.cs b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableScalarCompositionNode.cs new file mode 100644 index 000000000..8a9dbb935 --- /dev/null +++ b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableScalarCompositionNode.cs @@ -0,0 +1,91 @@ +// 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. + +#nullable enable +using System.Numerics; +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + + +namespace CommunityToolkit.Labs.WinUI; + +public class AnimatableScalarCompositionNode : IDisposable +{ + private Visual _underlyingVisual; + private bool disposedValue; + private ScalarNode? _currentAnimationNode = null; + + public float Value + { + get + { + if (_currentAnimationNode is not null) + { + // When the node value is being driven by a ongoing scalarnode animation, reading the property might return a stale value, + // so we instead default to evaluating the original expression to get the most accurate value + return _currentAnimationNode.Evaluate(); + } + else + { + return ComposerValue; + } + } + set + { + _underlyingVisual.Offset = new Vector3(value, 0, 0); + _currentAnimationNode = null; + } + } + + public float ComposerValue => _underlyingVisual.Offset.X; + + public AnimatableScalarCompositionNode(Compositor compositor) + { + _underlyingVisual = compositor.CreateShapeVisual(); + } + + public void Animate(CompositionAnimation animation) + { + _currentAnimationNode = null; + _underlyingVisual.StartAnimation(Offset.X, animation); + } + + public void Animate(ScalarNode animation) + { + _currentAnimationNode = animation; + _underlyingVisual.StartAnimation(Offset.X, animation); + } + + public ScalarNode Reference { get => _underlyingVisual.GetReference().Offset.X; } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _underlyingVisual.Dispose(); + _currentAnimationNode?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~AnimatableScalarCompositionNode() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/AnimatableNodes/AnimatableVector3CompositionNode.cs b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableVector3CompositionNode.cs new file mode 100644 index 000000000..7a0888503 --- /dev/null +++ b/components/CompositionCollectionView/src/AnimatableNodes/AnimatableVector3CompositionNode.cs @@ -0,0 +1,89 @@ +// 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. + +#nullable enable +using System.Numerics; +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public class AnimatableVector3CompositionNode : IDisposable +{ + private Visual _underlyingVisual; + private bool disposedValue; + private Vector3Node? _currentAnimationNode = null; + + public Vector3 Value + { + get + { + if (_currentAnimationNode is not null) + { + // When the node value is being driven by a ongoing scalarnode animation, reading the property might return a stale value, + // so we instead default to evaluating the original expression to get the most accurate value + return _currentAnimationNode.Evaluate(); + } + else + { + return ComposerValue; + } + } + set + { + _underlyingVisual.Offset = value; + _currentAnimationNode = null; + } + } + + public Vector3 ComposerValue => _underlyingVisual.Offset; + + public AnimatableVector3CompositionNode(Compositor compositor) + { + _underlyingVisual = compositor.CreateShapeVisual(); + } + + public void Animate(CompositionAnimation animation) + { + _currentAnimationNode = null; + _underlyingVisual.StartAnimation(Offset, animation); + } + + public void Animate(Vector3Node animation) + { + _currentAnimationNode = animation; + _underlyingVisual.StartAnimation(Offset, animation); + } + + public Vector3Node Reference { get => _underlyingVisual.GetReference().Offset; } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _underlyingVisual.Dispose(); + _currentAnimationNode?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~AnimatableScalarCompositionNode() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/AnimationConstants.cs b/components/CompositionCollectionView/src/AnimationConstants.cs new file mode 100644 index 000000000..6ce86827e --- /dev/null +++ b/components/CompositionCollectionView/src/AnimationConstants.cs @@ -0,0 +1,58 @@ +// 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. + +#nullable enable + +namespace CommunityToolkit.Labs.WinUI; +public static class AnimationConstants +{ + // Strings for all the animatable properties of a composition visual + // as listed in https://docs.microsoft.com/en-us/uwp/api/windows.ui.composition.compositionobject.startanimation?view=winrt-19041 + public static Vector2PropertyName AnchorPoint { get; } = new Vector2PropertyName(nameof(AnchorPoint)); + public static Vector3PropertyName CenterPoint { get; } = new Vector3PropertyName(nameof(CenterPoint)); + public static Vector3PropertyName Offset { get; } = new Vector3PropertyName(nameof(Offset)); + public static Vector3PropertyName Translation { get; } = new Vector3PropertyName(nameof(Translation)); + public static Vector3PropertyName Scale { get; } = new Vector3PropertyName(nameof(Scale)); + public static string Opacity { get; } = nameof(Opacity); + public static Vector4PropertyName Orientation { get; } = new Vector4PropertyName(nameof(Orientation)); + public static string RotationAngle { get; } = nameof(RotationAngle); + public static Vector3PropertyName RotationAxis { get; } = new Vector3PropertyName(nameof(RotationAxis)); + public static Vector2PropertyName Size { get; } = new Vector2PropertyName(nameof(Size)); + public static string TransformMatrix { get; } = nameof(TransformMatrix); + + public class PropertyName + { + private readonly string _value; + public PropertyName(string value) + { + this._value = value; + } + public static implicit operator string(PropertyName PropertyName) => PropertyName._value; + public override string ToString() => _value; + } + + public class Vector2PropertyName : PropertyName + { + public Vector2PropertyName(string value) : base(value) + { + } + public string X => this + ".X"; + public string Y => this + ".Y"; + } + + public class Vector3PropertyName : Vector2PropertyName + { + public Vector3PropertyName(string value) : base(value) + { + } + public string Z => this + ".Z"; + } + public class Vector4PropertyName : Vector3PropertyName + { + public Vector4PropertyName(string value) : base(value) + { + } + public string W => this + ".W"; + } +} diff --git a/components/CompositionCollectionView/src/Behaviors/ElementInteractionTrackerBehavior.cs b/components/CompositionCollectionView/src/Behaviors/ElementInteractionTrackerBehavior.cs new file mode 100644 index 000000000..80948a44c --- /dev/null +++ b/components/CompositionCollectionView/src/Behaviors/ElementInteractionTrackerBehavior.cs @@ -0,0 +1,53 @@ +// 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. +#nullable enable + +namespace CommunityToolkit.Labs.WinUI; +public class ElementInteractionTrackerBehavior : CompositionCollectionLayoutBehavior where TId : notnull +{ + Dictionary> _elementTrackers = new(); + + public override void Configure(CompositionCollectionLayout layout) + { + base.Configure(layout); + foreach (var tracker in _elementTrackers) + { + tracker.Value.Configure(layout); + } + } + + public InteractionTrackerBehavior CreateTrackerFor(ElementReference element) + { + if (TryGetTrackerFor(element.Id, out var tracker) && tracker is not null) + { + return tracker; + } + + tracker = new InteractionTrackerBehavior(element.Container); + tracker.Configure(Layout); + _elementTrackers[element.Id] = tracker; + return tracker; + } + + public bool TryGetTrackerFor(TId id, out InteractionTrackerBehavior? tracker) + { + return _elementTrackers.TryGetValue(id, out tracker); + } + + override public void OnActivated() + { + foreach (var tracker in _elementTrackers) + { + tracker.Value.OnActivated(); + } + } + + override public void OnDeactivated() + { + foreach (var tracker in _elementTrackers) + { + tracker.Value.OnDeactivated(); + } + } +} diff --git a/components/CompositionCollectionView/src/Behaviors/InteractionTrackerBehavior.cs b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerBehavior.cs new file mode 100644 index 000000000..7db71baa6 --- /dev/null +++ b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerBehavior.cs @@ -0,0 +1,194 @@ +// 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. +#nullable enable +using System.Numerics; + +namespace CommunityToolkit.Labs.WinUI; + +//The possible states of the tracker as documented in https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.composition.interactions.interactiontracker?view=windows-app-sdk-1.1#interactiontracker-states-and-transitions +public enum InteractionTrackerState { Idle, Inertia, Interacting, CustomAnimation }; + +public class InteractionTrackerBehavior : CompositionCollectionLayoutBehavior where TId : notnull +{ + public VisualInteractionSource InteractionSource { get; init; } + public InteractionTracker Tracker { get; init; } + public InteractionTrackerOwner TrackerOwner { get; init; } + + private List> _gestures = new List>(); + + public InteractionTrackerBehavior(FrameworkElement root) + { + var rootVisual = ElementCompositionPreview.GetElementVisual(root); + + InteractionSource = InitializeInteractionSource(); + TrackerOwner = new InteractionTrackerOwner(); + Tracker = InitializeTracker(); + + VisualInteractionSource InitializeInteractionSource() + { + var interactionSource = VisualInteractionSource.Create(rootVisual); + interactionSource.ScaleSourceMode = InteractionSourceMode.EnabledWithInertia; + interactionSource.PositionXSourceMode = InteractionSourceMode.EnabledWithInertia; + interactionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia; + interactionSource.IsPositionXRailsEnabled = false; + interactionSource.IsPositionYRailsEnabled = false; + interactionSource.ManipulationRedirectionMode = VisualInteractionSourceRedirectionMode.CapableTouchpadAndPointerWheel; + return interactionSource; + } + + InteractionTracker InitializeTracker() + { + var tracker = InteractionTracker.CreateWithOwner(rootVisual.Compositor, TrackerOwner); + tracker.InteractionSources.Add(InteractionSource); + return tracker; + } + } + + public void AddGesture(InteractionTrackerGesture gesture) + { + if (gesture.PreviewControl is { }) + { + gesture.PreviewControl.HorizontalAlignment = HorizontalAlignment.Left; + gesture.PreviewControl.VerticalAlignment = VerticalAlignment.Top; + Layout.RootPanel.Children.Add(gesture.PreviewControl); + gesture.Restart(); + } + + if (Layout.IsActive) + { + RegisterGestureHandlers(gesture); + } + + _gestures.Add(gesture); + } + + public InteractionTrackerGesture? GetGesture() where T : InteractionTrackerGesture => _gestures.OfType().FirstOrDefault(); + + public void RemoveGesture(InteractionTrackerGesture gesture) + { + gesture.Disable(); + UnregisterGestureHandlers(gesture); + + if (gesture.PreviewControl is { }) + { + Layout.RootPanel.Children.Remove(gesture.PreviewControl); + } + + _gestures.Remove(gesture); + if (gesture is IDisposable disposableGesture) + { + disposableGesture.Dispose(); + } + } + + private void RegisterGestureHandlers(InteractionTrackerGesture gesture) + { + TrackerOwner.OnInteractingStateEntered += gesture.InteractingStateEntered; + TrackerOwner.OnInertiaStateEntered += gesture.InertiaStateEntered; + TrackerOwner.OnValuesChanged += gesture.ValuesChanged; + } + + private void UnregisterGestureHandlers(InteractionTrackerGesture gesture) + { + TrackerOwner.OnInteractingStateEntered -= gesture.InteractingStateEntered; + TrackerOwner.OnInertiaStateEntered -= gesture.InertiaStateEntered; + TrackerOwner.OnValuesChanged -= gesture.ValuesChanged; + } + + public virtual void RestartGestures() + { + foreach (var gesture in _gestures) + { + gesture.Restart(); + } + } + + public void Disable() + { + InteractionSource.IsPositionXRailsEnabled = false; + InteractionSource.IsPositionYRailsEnabled = false; + + InteractionSource.PositionXSourceMode = InteractionSourceMode.EnabledWithInertia; + InteractionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia; + Tracker.MaxPosition = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + Tracker.MinPosition = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); + } + + override public void OnActivated() + { + foreach (var gesture in _gestures) + { + RegisterGestureHandlers(gesture); + } + } + + override public void OnDeactivated() + { + for (var i = _gestures.Count - 1; i >= 0; i--) + { + RemoveGesture(_gestures[i]); + } + } + + public class InteractionTrackerOwner : IInteractionTrackerOwner + { + public InteractionTrackerState CurrentState { get; private set; } + + public delegate void CustomAnimationStateEnteredHandler(InteractionTracker sender, InteractionTrackerCustomAnimationStateEnteredArgs args, InteractionTrackerState previousState); + public event CustomAnimationStateEnteredHandler? OnCustomAnimationStateEntered; + + public delegate void IdleStateEnteredHandler(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args, InteractionTrackerState previousState); + public event IdleStateEnteredHandler? OnIdleStateEntered; + + public delegate void InertiaStateEnteredHandler(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args, InteractionTrackerState previousState); + public event InertiaStateEnteredHandler? OnInertiaStateEntered; + + public delegate void InteractingStateEnteredHandler(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args, InteractionTrackerState previousState); + public event InteractingStateEnteredHandler? OnInteractingStateEntered; + + public delegate void RequestIgnoredHandler(InteractionTracker sender, InteractionTrackerRequestIgnoredArgs args); + public event RequestIgnoredHandler? OnRequestIgnored; + + public delegate void ValuesChangedHandler(InteractionTracker sender, InteractionTrackerValuesChangedArgs args); + public event ValuesChangedHandler? OnValuesChanged; + + public void CustomAnimationStateEntered(InteractionTracker sender, InteractionTrackerCustomAnimationStateEnteredArgs args) + { + OnCustomAnimationStateEntered?.Invoke(sender, args, CurrentState); + CurrentState = InteractionTrackerState.CustomAnimation; + } + + public void IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args) + { + OnIdleStateEntered?.Invoke(sender, args, CurrentState); + CurrentState = InteractionTrackerState.Idle; + } + + public void InertiaStateEntered(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args) + { + OnInertiaStateEntered?.Invoke(sender, args, CurrentState); + CurrentState = InteractionTrackerState.Inertia; + } + + public void InteractingStateEntered(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args) + { + OnInteractingStateEntered?.Invoke(sender, args, CurrentState); + CurrentState = InteractionTrackerState.Interacting; + } + + public void RequestIgnored(InteractionTracker sender, InteractionTrackerRequestIgnoredArgs args) => OnRequestIgnored?.Invoke(sender, args); + + public void ValuesChanged(InteractionTracker sender, InteractionTrackerValuesChangedArgs args) => OnValuesChanged?.Invoke(sender, args); + + public void Reset() + { + OnCustomAnimationStateEntered = null; + OnIdleStateEntered = null; + OnInertiaStateEntered = null; + OnInteractingStateEntered = null; + OnRequestIgnored = null; + OnValuesChanged = null; + } + } +} diff --git a/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/GesturePreviewControl.cs b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/GesturePreviewControl.cs new file mode 100644 index 000000000..e1d55487d --- /dev/null +++ b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/GesturePreviewControl.cs @@ -0,0 +1,47 @@ +// 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. +#nullable enable +using Animation = CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public abstract class GesturePreviewControl : UserControl +{ + private ScalarNode? _opacity; + private Matrix4x4Node? _transform; + + public ScalarNode? OpacityNode + { + get => _opacity; + set + { + _opacity = value; + ResetVisualState(); + } + } + public Matrix4x4Node? TransformNode + { + get => _transform; + set + { + _transform = value; + ResetVisualState(); + } + } + + public virtual void ResetVisualState() + { + var visual = ElementCompositionPreview.GetElementVisual(this); + if (TransformNode is { }) + { + visual.StartAnimation(Animation.TransformMatrix, TransformNode); + } + if (OpacityNode is { }) + { + visual.StartAnimation(Animation.Opacity, OpacityNode); + } + } + + public abstract void SetPageSize(int width, int height); + public abstract Task StartCommandCompletedAnimation(float offset = 0); +} diff --git a/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/InteractionTrackerGesture.cs b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/InteractionTrackerGesture.cs new file mode 100644 index 000000000..b627daa19 --- /dev/null +++ b/components/CompositionCollectionView/src/Behaviors/InteractionTrackerGesture/InteractionTrackerGesture.cs @@ -0,0 +1,143 @@ +// 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. +#nullable enable +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public abstract class InteractionTrackerGesture +{ + public event EventHandler? GestureCompleted; + + protected BindableCompositionPropertySet LayoutProperties { get; init; } + protected CompositionPropertySet GestureProperties { get; init; } + + const string InteractionInProgressId = "IIP"; + protected bool InteractionInProgress + { + get => GestureProperties.TryGetBoolean(InteractionInProgressId, out var inProgress) == CompositionGetValueStatus.Succeeded && inProgress; + set => GestureProperties.InsertBoolean(InteractionInProgressId, value); + } + + protected BooleanNode InteractionInProgressReference => GestureProperties.GetReference().GetBooleanProperty(InteractionInProgressId); + + const string CompletionInProgressId = "CIP"; + + protected bool CompletionInProgress + { + get => GestureProperties.TryGetBoolean(CompletionInProgressId, out var inProgress) == CompositionGetValueStatus.Succeeded && inProgress; + set => GestureProperties.InsertBoolean(CompletionInProgressId, value); + } + + protected BooleanNode CompletionInProgressReference => GestureProperties.GetReference().GetBooleanProperty(CompletionInProgressId); + + const string InertiaInProgressId = "NIP"; + + protected bool InertiaInProgress + { + get => GestureProperties.TryGetBoolean(InertiaInProgressId, out var inProgress) == CompositionGetValueStatus.Succeeded && inProgress; + set => GestureProperties.InsertBoolean(InertiaInProgressId, value); + } + + protected BooleanNode InertiaInProgressReference => GestureProperties.GetReference().GetBooleanProperty(InertiaInProgressId); + + private InteractionTrackerReferenceNode _tracker; + + protected DateTime InteractionStartedTime { get; private set; } + + protected InteractionTrackerGesture(Compositor compositor, InteractionTrackerReferenceNode tracker, BindableCompositionPropertySet layoutProperties) + { + LayoutProperties = layoutProperties; + GestureProperties = compositor.CreatePropertySet(); + _tracker = tracker; + InteractionInProgress = false; + CompletionInProgress = false; + InertiaInProgress = false; + } + + private bool _isDisabled = false; + + //Disabling a gesture prevents it from processing tracker updates immediately, it can't be undone + public void Disable() => _isDisabled = true; + + public void PauseAnimation() + { + if (PreviewControl is null) { return; } + var visual = ElementCompositionPreview.GetElementVisual(PreviewControl); + visual.StopAnimation(TransformMatrix); + visual.StopAnimation(Opacity); + } + + public void Restart() + { + InteractionInProgress = false; + CompletionInProgress = false; + + if (PreviewControl is null) { return; } + var visual = ElementCompositionPreview.GetElementVisual(PreviewControl); + + var visibility = GetPreviewVisibility(_tracker); + var opacity = GetPreviewOpacity(_tracker); + var transform = GetPreviewTransform(_tracker); + + visual.StartAnimation(TransformMatrix, transform); + visual.StartAnimation(Opacity, ExpressionFunctions.Conditional(visibility, + opacity, + 0)); + + PreviewControl?.ResetVisualState(); + } + + protected void InvokeGestureCompleted() + { + InteractionInProgress = false; + InertiaInProgress = false; + CompletionInProgress = true; + GestureCompleted?.Invoke(this, EventArgs.Empty); + } + public void ValuesChanged(InteractionTracker tracker, InteractionTrackerValuesChangedArgs args) + { + if (_isDisabled) + { + return; + } + OnValuesChanged(tracker, args); + } + + public void InteractingStateEntered(InteractionTracker _, InteractionTrackerInteractingStateEnteredArgs _1, InteractionTrackerState _3) + { + InteractionInProgress = true; + InertiaInProgress = false; + InteractionStartedTime = DateTime.Now; + } + + public void InertiaStateEntered(InteractionTracker tracker, InteractionTrackerInertiaStateEnteredArgs args, InteractionTrackerState _) + { + if (_isDisabled) + { + return; + } + InteractionInProgress = false; + InertiaInProgress = true; + OnInertiaStateEntered(tracker, args); + } + + protected virtual void OnValuesChanged(InteractionTracker _, InteractionTrackerValuesChangedArgs _1) { } + + protected virtual void OnInertiaStateEntered(InteractionTracker _, InteractionTrackerInertiaStateEnteredArgs _1) { } + + protected abstract ScalarNode GetPreviewOpacity(InteractionTrackerReferenceNode tracker); + protected abstract BooleanNode GetPreviewVisibility(InteractionTrackerReferenceNode tracker); + protected abstract Matrix4x4Node GetPreviewTransform(InteractionTrackerReferenceNode tracker); + + public GesturePreviewControl? PreviewControl { get; set; } + +} + +public abstract class InteractionTrackerGesture : InteractionTrackerGesture where TPanningGesturePreview : GesturePreviewControl, new() +{ + protected InteractionTrackerGesture(Compositor compositor, InteractionTrackerReferenceNode tracker, BindableCompositionPropertySet layoutProperties) : base(compositor, tracker, layoutProperties) + { + PreviewControl = new TPanningGesturePreview(); + } +} diff --git a/components/CompositionCollectionView/src/Behaviors/LayoutBehavior.cs b/components/CompositionCollectionView/src/Behaviors/LayoutBehavior.cs new file mode 100644 index 000000000..d76b7dbbb --- /dev/null +++ b/components/CompositionCollectionView/src/Behaviors/LayoutBehavior.cs @@ -0,0 +1,28 @@ +// 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. +#nullable enable + + +namespace CommunityToolkit.Labs.WinUI; +public abstract class CompositionCollectionLayoutBehavior where TId : notnull +{ + protected CompositionCollectionLayout Layout => _layout is null ? throw new InvalidOperationException("Behavior has not been added to any layout yet") : _layout; + private CompositionCollectionLayout? _layout = null; + + public virtual void Configure(CompositionCollectionLayout layout) + { + if (_layout != layout) + { + _layout = layout; + OnConfigure(); + } + } + + virtual public void ConfigureElement(ElementReference element) { } + virtual public void CleanupElement(ElementReference element) { } + + virtual public void OnConfigure() { } + virtual public void OnActivated() { } + virtual public void OnDeactivated() { } +} diff --git a/components/CompositionCollectionView/src/BindableCompositionPropertySet.cs b/components/CompositionCollectionView/src/BindableCompositionPropertySet.cs new file mode 100644 index 000000000..9a38fba79 --- /dev/null +++ b/components/CompositionCollectionView/src/BindableCompositionPropertySet.cs @@ -0,0 +1,125 @@ +// 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. +#nullable enable +using System.Numerics; +using Windows.UI; + +namespace CommunityToolkit.Labs.WinUI; + +public class BindableCompositionPropertySet : INotifyPropertyChanged, IDisposable +{ + private CompositionPropertySet _propertySet; + private bool disposedValue; + + public event PropertyChangedEventHandler? PropertyChanged; + + public BindableCompositionPropertySet(CompositionPropertySet propertySet) + { + _propertySet = propertySet; + } + +#if !WINAPPSDK + public void InsertColor(string propertyName, Color value) + { + _propertySet.InsertColor(propertyName, value); + OnPropertyChanged(propertyName); + } +#endif + + public void InsertMatrix3x2(string propertyName, Matrix3x2 value) + { + _propertySet.InsertMatrix3x2(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertMatrix4x4(string propertyName, Matrix4x4 value) + { + _propertySet.InsertMatrix4x4(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertQuaternion(string propertyName, Quaternion value) + { + _propertySet.InsertQuaternion(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertScalar(string propertyName, float value) + { + _propertySet.InsertScalar(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertVector2(string propertyName, Vector2 value) + { + _propertySet.InsertVector2(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertVector3(string propertyName, Vector3 value) + { + _propertySet.InsertVector3(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertVector4(string propertyName, Vector4 value) + { + _propertySet.InsertVector4(propertyName, value); + OnPropertyChanged(propertyName); + } + + public void InsertBoolean(string propertyName, bool value) + { + _propertySet.InsertBoolean(propertyName, value); + OnPropertyChanged(propertyName); + } + + private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + +#if !WINAPPSDK + public CompositionGetValueStatus TryGetColor(string propertyName, out Color value) => _propertySet.TryGetColor(propertyName, out value); +#endif + + public CompositionGetValueStatus TryGetMatrix3x2(string propertyName, out Matrix3x2 value) => _propertySet.TryGetMatrix3x2(propertyName, out value); + public CompositionGetValueStatus TryGetMatrix4x4(string propertyName, out Matrix4x4 value) => _propertySet.TryGetMatrix4x4(propertyName, out value); + public CompositionGetValueStatus TryGetQuaternion(string propertyName, out Quaternion value) => _propertySet.TryGetQuaternion(propertyName, out value); + public CompositionGetValueStatus TryGetScalar(string propertyName, out float value) => _propertySet.TryGetScalar(propertyName, out value); + public CompositionGetValueStatus TryGetVector2(string propertyName, out Vector2 value) => _propertySet.TryGetVector2(propertyName, out value); + public CompositionGetValueStatus TryGetVector3(string propertyName, out Vector3 value) => _propertySet.TryGetVector3(propertyName, out value); + public CompositionGetValueStatus TryGetVector4(string propertyName, out Vector4 value) => _propertySet.TryGetVector4(propertyName, out value); + public CompositionGetValueStatus TryGetBoolean(string propertyName, out bool value) => _propertySet.TryGetBoolean(propertyName, out value); + + public PropertySetReferenceNode GetReference() => _propertySet.GetReference(); + + #region IDisposable + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + _propertySet.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~BindableCompositionPropertySet() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + #endregion +} diff --git a/components/CompositionCollectionView/src/CommunityToolkit.Labs.WinUI.CompositionCollectionView.csproj b/components/CompositionCollectionView/src/CommunityToolkit.Labs.WinUI.CompositionCollectionView.csproj new file mode 100644 index 000000000..7fcbcfbda --- /dev/null +++ b/components/CompositionCollectionView/src/CommunityToolkit.Labs.WinUI.CompositionCollectionView.csproj @@ -0,0 +1,45 @@ + + + CompositionCollectionView + This package contains CompositionCollectionView. + 0.0.29 + warnings + True + + + CommunityToolkit.Labs.WinUI.CompositionCollectionViewRns + + + + + + + WMC1006;CS8034;Uno0001 + + + + WMC1006;CS8034;Uno0001 + + + + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + MSBuild:Compile + + + + + + diff --git a/components/CompositionCollectionView/src/CompositionCollectionLayout.Behaviors.cs b/components/CompositionCollectionView/src/CompositionCollectionLayout.Behaviors.cs new file mode 100644 index 000000000..f28a0ede7 --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionLayout.Behaviors.cs @@ -0,0 +1,52 @@ +// 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. +#nullable enable + +namespace CommunityToolkit.Labs.WinUI; +public abstract partial class CompositionCollectionLayout : ILayout, IDisposable where TId : notnull +{ + private List> _behaviors = new(); + + public void AddBehavior(CompositionCollectionLayoutBehavior behavior) + { + if (IsActive) + { + behavior.Configure(this); + } + + if (_behaviors.Contains(behavior)) + { + return; + } + + _behaviors.Add(behavior); + } + + public T GetBehavior() where T : CompositionCollectionLayoutBehavior => + _behaviors.OfType().First(); + + public T? TryGetBehavior() where T : CompositionCollectionLayoutBehavior => + _behaviors.OfType().FirstOrDefault(); + + public void RemoveBehavior(CompositionCollectionLayoutBehavior behavior) + { + _behaviors.Remove(behavior); + } + + private void ConfigureElementBehaviors(ElementReference elementReference) + { + foreach (var behavior in _behaviors) + { + behavior.ConfigureElement(elementReference); + } + } + + private void CleanupElementBehaviors(ElementReference elementReference) + { + foreach (var behavior in _behaviors) + { + behavior.CleanupElement(elementReference); + } + } +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionLayout.Overridable.cs b/components/CompositionCollectionView/src/CompositionCollectionLayout.Overridable.cs new file mode 100644 index 000000000..927b76d9e --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionLayout.Overridable.cs @@ -0,0 +1,62 @@ +// 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. +#nullable enable +using System.Numerics; + +namespace CommunityToolkit.Labs.WinUI; +public abstract partial class CompositionCollectionLayout : ILayout, IDisposable where TId : notnull +{ + /// + /// Invoked only once, when a CompositionCollectionView transitions to this layout + /// + protected virtual void OnActivated() { } + /// + /// Invoked only once, when a CompositionCollectionView transitions away from this layout + /// + protected virtual void OnDeactivated() { } + /// + /// Invoked every time the list of instantiated element is updated, when the layout is activated and in any successive source updates + /// + protected virtual void OnElementsUpdated() { } + + /// + /// Invoked only once per element and layout, after the element is registered with the layout (might happen on activation or when created on a source update) + /// + /// + protected virtual void ConfigureElement(ElementReference element) { } + /// + /// Invoked only once per element and layout, after the element is unregistered from the layout (might happen on deactivation or when destroyed on a source update) + /// + protected virtual void CleanupElement(ElementReference element) { } + + + + /// + /// Invoked per-element as part of a source update, before its animation has been updated. + /// This is where any composition property set/animation nodes can be updated + /// + /// + public virtual void UpdateElementData(ElementReference element) { } + /// + /// Invoked per-element as part of a source update, after its animation has been updated + /// + /// + public virtual void UpdateElement(ElementReference element) { } + + + public virtual Vector3Node GetElementPositionNode(ElementReference element) => Vector3.Zero; + public virtual ScalarNode GetElementScaleNode(ElementReference element) => 1; + public virtual ScalarNode GetElementOpacityNode(ElementReference element) => 1; + public virtual QuaternionNode GetElementOrientationNode(ElementReference element) => Quaternion.Identity; + protected virtual ElementTransition? GetElementTransitionEasingFunction(ElementReference element) => null; + + // These methods have a default implementation evaluates the value of the node returned by the layout and should + // be good enough for more cases. It should only be overriden when evaluating the nodes is not always enough to determine the latest value, + // e.g. if the node depends on a reference to another node which is also animated through composition and returns a stale value when evaluated + public virtual Vector3 GetElementPositionValue(ElementReference element) => GetElementPositionNode(element).Evaluate(); + public virtual float GetElementScaleValue(ElementReference element) => GetElementScaleNode(element).Evaluate(); + public virtual Quaternion GetElementOrientationValue(ElementReference element) => GetElementOrientationNode(element).Evaluate(); + public virtual float GetElementOpacityValue(ElementReference element) => GetElementOpacityNode(element).Evaluate(); + +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionLayout.Transition.cs b/components/CompositionCollectionView/src/CompositionCollectionLayout.Transition.cs new file mode 100644 index 000000000..6e7ccd78f --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionLayout.Transition.cs @@ -0,0 +1,159 @@ +// 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. +#nullable enable +using System.Numerics; +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public abstract partial class CompositionCollectionLayout : ILayout, IDisposable where TId : notnull +{ + public void Activate(Panel panel) + { + var rootPanelVisual = InitializeRootContainer(panel); + + _uiRoot = new( + panel, + rootPanelVisual); + + Activate(); + + Visual InitializeRootContainer(Panel root) + { + var rootContainer = ElementCompositionPreview.GetElementVisual(root); + rootContainer.Size = new Vector2((float)root.ActualWidth, (float)root.ActualHeight); + return rootContainer; + } + } + + private void Activate() + { + //The parent layout should only be accessible before we transition to the current layout, + //once we activate the current layout we dispose and stop referencing it + ParentLayout?.Dispose(); + ParentLayout = null; + IsActive = true; + + foreach (var behavior in _behaviors) + { + behavior.Configure(this); + } + + OnActivated(); + + foreach (var behavior in _behaviors) + { + behavior.OnActivated(); + } + } + + private void Deactivate() + { + OnDeactivated(); + + IsActive = false; + + + foreach (var behavior in _behaviors) + { + behavior.OnDeactivated(); + } + } + + public T TransitionTo(Func, T> factory, bool animateTransition = true) where T : CompositionCollectionLayout + { + if (!IsActive) + { + throw new InvalidOperationException("TransitionTo can only be used in active layouts. You might have already transitioned away from this layout."); + } + + Deactivate(); + + var newLayout = factory(this); + + foreach (var behavior in _behaviors) + { + newLayout.AddBehavior(behavior); + } + + newLayout.Activate(); + + TransferElements(); + + LayoutReplaced?.Invoke(this, newLayout, animateTransition); + + return newLayout; + + void TransferElements() + { + foreach (var (id, element) in _elements) + { + element.ReasignTo(newLayout); + newLayout._elements.Add(id, element); + CleanupElement(element); + CleanupElementBehaviors(element); + } + + //Configure the animations after all the elements have been added to the new layout, + //to allow elements to depend on each other + foreach (var (id, element) in _elements) + { + newLayout.ConfigureElement(element); + newLayout.ConfigureElementBehaviors(element); + + var currentPosition = GetElementPositionValue(element); + var currentScale = GetElementScaleValue(element); + var currentOrientation = GetElementOrientationValue(element); + var currentOpacity = GetElementOpacityValue(element); + + if (animateTransition && newLayout.GetElementTransitionEasingFunction(element) is ElementTransition transition) + { + TaskCompletionSource tsc = new(); + newLayout.StopElementAnimation(element); + + var progressAnimation = Compositor.CreateScalarKeyFrameAnimation(); + progressAnimation.Duration = TimeSpan.FromMilliseconds(transition.Length); + progressAnimation.StopBehavior = AnimationStopBehavior.SetToFinalValue; + progressAnimation.InsertKeyFrame(0, 0f, transition.EasingFunction); + progressAnimation.InsertKeyFrame(1, 1f, transition.EasingFunction); + + var animProgressNode = new AnimatableScalarCompositionNode(Compositor); + + element.Visual.StartAnimation(Offset, ExpressionFunctions.Lerp(currentPosition, newLayout.GetElementPositionNode(element), animProgressNode.Reference)); + var scale = newLayout.GetElementScaleNode(element); + element.Visual.StartAnimation(Scale, ExpressionFunctions.Lerp(new Vector3(currentScale), ExpressionFunctions.Vector3(scale, scale, scale), animProgressNode.Reference)); + element.Visual.StartAnimation(AnimationConstants.Orientation, ExpressionFunctions.Slerp(currentOrientation, newLayout.GetElementOrientationNode(element), animProgressNode.Reference)); + element.Visual.StartAnimation(Opacity, ExpressionFunctions.Lerp(currentOpacity, newLayout.GetElementOpacityNode(element), animProgressNode.Reference)); + + var batch = Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += (object _, CompositionBatchCompletedEventArgs _1) => + { + if (newLayout._elements.ContainsKey(element.Id)) + { + newLayout.ConfigureElementAnimation(element); + } + tsc.SetResult(true); + + batch.Dispose(); + animProgressNode.Dispose(); + progressAnimation.Dispose(); + }; + + animProgressNode.Animate(progressAnimation); + + batch.End(); + } + else + { + newLayout.ConfigureElementAnimation(element); + } + + newLayout.UpdateElement(element); + } + + _elements.Clear(); + + newLayout.OnElementsUpdated(); + } + } +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionLayout.Update.cs b/components/CompositionCollectionView/src/CompositionCollectionLayout.Update.cs new file mode 100644 index 000000000..6e1392a22 --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionLayout.Update.cs @@ -0,0 +1,169 @@ +// 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. +#nullable enable +using System.Numerics; +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + + +namespace CommunityToolkit.Labs.WinUI; +public abstract partial class CompositionCollectionLayout : ILayout, IDisposable where TId : notnull +{ + + bool _isUpdatingSource = false; + + private record SourceUpdate(IDictionary UpdatedElements, TaskCompletionSource TaskCompletion, bool Animated); + Queue _pendingSourceUpdates = new(); + + public async Task UpdateSource(IDictionary source, bool animate) + { + var tcs = new TaskCompletionSource(); + + if (!_isUpdatingSource) + { + _isUpdatingSource = true; + ProcessSourceUpdate(source, tcs, animate); + } + else + { + _pendingSourceUpdates.Enqueue(new SourceUpdate(source, tcs, animate)); + } + + await tcs.Task; + } + + private HashSet _ongoingSourceUpdateAnimation = new(); + + public async void ProcessSourceUpdate(IDictionary updatedElements, TaskCompletionSource taskCompletion, bool animate) + { + List> elementUpdateTask = new(); + + HashSet processedElements = new(); + + foreach (var (id, element) in _elements.ToArray()) + { + if (!updatedElements.ContainsKey(id)) + { + DestroyElement(element, id); + } + else + { + UpdateAndTransitionElement(element, updatedElements[id]); + } + } + foreach (var (id, model) in updatedElements) + { + if (processedElements.Contains(id)) + { + continue; + } + InstantiateElement(id, model); + } + + OnElementsUpdated(); + + taskCompletion.SetResult(true); + + if (elementUpdateTask.Any()) + { + await Task.WhenAll(elementUpdateTask); + } + + if (_pendingSourceUpdates.Count > 0) + { + var update = _pendingSourceUpdates.Dequeue(); + ProcessSourceUpdate(update.UpdatedElements, update.TaskCompletion, update.Animated); + } + else + { + _isUpdatingSource = false; + } + + void InstantiateElement(TId id, TItem item) + { + var element = ElementFactory(id); + RootPanel.Children.Add(element); + + var elementReference = new ElementReference(id, item, element,/* source, tracker, trackerOwner,*/ this); + UpdateElementData(elementReference); + _elements[id] = elementReference; + + ConfigureElement(elementReference); + ConfigureElementBehaviors(elementReference); + + ConfigureElementAnimation(elementReference); + UpdateElement(elementReference); + } + + void DestroyElement(ElementReference element, TId id) + { + CleanupElement(element); + CleanupElementBehaviors(element); + RootPanel.Children.Remove(element.Container); + _elements.Remove(id); + element.Dispose(); + } + + void UpdateAndTransitionElement(ElementReference element, TItem newData) + { + if (animate && GetElementTransitionEasingFunction(element) is ElementTransition transition) + { + var currentPosition = GetElementPositionValue(element); + var currentScale = GetElementScaleValue(element); + var currentOrientation = GetElementOrientationValue(element); + var currentOpacity = GetElementOpacityValue(element); + + TaskCompletionSource tsc = new(); + StopElementAnimation(element); + + element.Model = newData; + UpdateElementData(element); + + var progressAnimation = Compositor.CreateScalarKeyFrameAnimation(); + progressAnimation.Duration = TimeSpan.FromMilliseconds(transition.Length); + progressAnimation.StopBehavior = AnimationStopBehavior.SetToFinalValue; + progressAnimation.InsertKeyFrame(0, 0f, transition.EasingFunction); + progressAnimation.InsertKeyFrame(1, 1f, transition.EasingFunction); + + var animProgressNode = new AnimatableScalarCompositionNode(Compositor); + _ongoingSourceUpdateAnimation.Add(animProgressNode); + + element.Visual.StartAnimation(Offset, ExpressionFunctions.Lerp(currentPosition, GetElementPositionNode(element), animProgressNode.Reference)); + var scale = GetElementScaleNode(element); + element.Visual.StartAnimation(Scale, ExpressionFunctions.Lerp(new Vector3(currentScale), ExpressionFunctions.Vector3(scale, scale, scale), animProgressNode.Reference)); + element.Visual.StartAnimation(AnimationConstants.Orientation, ExpressionFunctions.Slerp(currentOrientation, GetElementOrientationNode(element), animProgressNode.Reference)); + element.Visual.StartAnimation(Opacity, ExpressionFunctions.Lerp(currentOpacity, GetElementOpacityNode(element), animProgressNode.Reference)); + + var batch = Compositor.CreateScopedBatch(CompositionBatchTypes.Animation); + batch.Completed += (object _, CompositionBatchCompletedEventArgs _1) => + { + if (IsActive) + { + ConfigureElementAnimation(element); + } + tsc.SetResult(true); + + batch.Dispose(); + animProgressNode.Dispose(); + progressAnimation.Dispose(); + + _ongoingSourceUpdateAnimation.Remove(animProgressNode); + }; + + animProgressNode.Animate(progressAnimation); + + batch.End(); + + elementUpdateTask.Add(tsc.Task); + } + else + { + element.Model = newData; + UpdateElementData(element); + } + + UpdateElement(element); + processedElements.Add(element.Id); + } + } +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionLayout.cs b/components/CompositionCollectionView/src/CompositionCollectionLayout.cs new file mode 100644 index 000000000..e3857c6a0 --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionLayout.cs @@ -0,0 +1,148 @@ +// 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. +#nullable enable +using static CommunityToolkit.Labs.WinUI.AnimationConstants; + +namespace CommunityToolkit.Labs.WinUI; +public interface ILayout +{ + + public delegate void LayoutReplacedHandler(ILayout startLayout, ILayout endLayout, bool isAnimated); + public event LayoutReplacedHandler? LayoutReplaced; + + void Activate(Panel panel); +} + +public abstract partial class CompositionCollectionLayout : ILayout, IDisposable where TId : notnull +{ + public CompositionCollectionLayout(Func elementFactory) + { + ElementFactory = elementFactory; + + var compositor = Window.Current.Compositor; + Properties = new BindableCompositionPropertySet(compositor.CreatePropertySet()); + AnimatableNodes = new AnimatableCompositionNodeSet(compositor); + } + + public CompositionCollectionLayout(CompositionCollectionLayout sourceLayout) + { + ParentLayout = sourceLayout; + + ElementFactory = sourceLayout.ElementFactory; + + _uiRoot = sourceLayout._uiRoot; + Properties = sourceLayout.Properties; + AnimatableNodes = sourceLayout.AnimatableNodes; + } + + private Dictionary> _elements { get; } = new(); + public IEnumerable Source => _elements.Keys; + public IEnumerable> Elements => _elements.Values; + + public event ILayout.LayoutReplacedHandler? LayoutReplaced; + public Panel RootPanel => GetVisualProperties().RootPanel; + public Visual RootPanelVisual => GetVisualProperties().RootPanelVisual; + public Compositor Compositor => GetVisualProperties().RootPanelVisual.Compositor; + public BindableCompositionPropertySet Properties { private init; get; } + public AnimatableCompositionNodeSet AnimatableNodes { private init; get; } + + public CompositionCollectionLayout? ParentLayout { get; private set; } + + public bool IsActive { get; private set; } = false; + + private bool disposedValue; + + public ElementReference? GetElement(TId obId) => + _elements.TryGetValue(obId, out var element) ? element : null; + + + // Protected properties provided for convenience when implementing layouts + protected ScalarNode ViewportWidthNode => RootPanelVisual.GetReference().Size.X; + protected ScalarNode ViewportHeightNode => RootPanelVisual.GetReference().Size.Y; + + + // Fields that we need to preserve across layouts + protected Func ElementFactory { get; init; } + + private record UIRoot(Panel RootPanel, Visual RootPanelVisual); + private UIRoot? _uiRoot; + + private UIRoot GetVisualProperties() + { + if (_uiRoot is null) + { + throw new InvalidOperationException("Tried to use this layout when it hasn't been activated yet."); + } + return _uiRoot; + } + + public void ConfigureElementAnimation(ElementReference element, Visual? proxyVisual = null) + { + var visual = proxyVisual ?? element.Visual; + + visual.StartAnimation(Offset, GetElementPositionNode(element)); + + var scale = GetElementScaleNode(element); + visual.StartAnimation(Scale, ExpressionFunctions.Vector3(scale, scale, scale)); + + visual.StartAnimation(AnimationConstants.Orientation, GetElementOrientationNode(element)); + + visual.StartAnimation(Opacity, GetElementOpacityNode(element)); + } + + public void StopElementsAnimation() + { + foreach (var (_, element) in _elements) + { + StopElementAnimation(element); + } + } + + public void StopElementAnimation(ElementReference element) + { + element.Visual.StopAnimation(Offset); + element.Visual.StopAnimation(Scale); + element.Visual.StopAnimation(Opacity); + element.Visual.StopAnimation(AnimationConstants.Orientation); + } + + public record ElementTransition(uint Length, CompositionEasingFunction EasingFunction); + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + if (IsActive) + { + //We only want to dipose these fields if the layout is still active + //If it isn't, that means other layout owns them now + Properties.Dispose(); + //InteractionSource.Dispose(); + //Tracker.Dispose(); + AnimatableNodes.Dispose(); + } + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~Layout() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionView.cs b/components/CompositionCollectionView/src/CompositionCollectionView.cs new file mode 100644 index 000000000..52d5e0bae --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionView.cs @@ -0,0 +1,77 @@ +// 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. +#nullable enable + + +// The Templated Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234235 +namespace CommunityToolkit.Labs.WinUI; + + +public sealed class CompositionCollectionView : Control +{ + private Canvas? _contentPanel; + private ILayout? _layout; + private Action? _pendingSourceUpdate; + public CompositionCollectionLayout? Layout() where TId : notnull => _layout as CompositionCollectionLayout; + + public delegate void LayoutChangedHandler(CompositionCollectionView sender, ILayout newLayout, bool isAnimated); + public event LayoutChangedHandler? LayoutChanged; + + public CompositionCollectionView() + { + this.DefaultStyleKey = typeof(CompositionCollectionView); + } + + public void SetLayout(ILayout layout) + { + _layout = layout; + _layout.LayoutReplaced += OnLayoutReplaced; + + if (_contentPanel is not null) + { + _layout.Activate(_contentPanel); + } + } + + public async Task UpdateSource(IDictionary source, bool animate = true) where TId : notnull + { + if (_contentPanel is not null && _layout as CompositionCollectionLayout is CompositionCollectionLayout layout) + { + await layout.UpdateSource(source, animate); + } + else + { + _pendingSourceUpdate = () => (_layout as CompositionCollectionLayout)?.UpdateSource(source, animate); + } + } + + private void OnLayoutReplaced(ILayout sender, ILayout newLayout, bool isAnimated) + { + if (_layout is not null) + { + _layout.LayoutReplaced -= OnLayoutReplaced; + } + + _layout = newLayout; + _layout.LayoutReplaced += OnLayoutReplaced; + LayoutChanged?.Invoke(this, _layout, isAnimated); + } + + protected override void OnApplyTemplate() + { + if (GetTemplateChild("contentPanel") is Canvas contentPanel) + { + _contentPanel = contentPanel; + if (_layout is not null) + { + _layout.Activate(_contentPanel); + } + if (_pendingSourceUpdate is not null) + { + _pendingSourceUpdate?.Invoke(); + _pendingSourceUpdate = null; + } + } + } +} diff --git a/components/CompositionCollectionView/src/CompositionCollectionView.projitems b/components/CompositionCollectionView/src/CompositionCollectionView.projitems new file mode 100644 index 000000000..6f352aebb --- /dev/null +++ b/components/CompositionCollectionView/src/CompositionCollectionView.projitems @@ -0,0 +1,36 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + bdb6d5d0-b5e2-4ade-9896-30cd295c2d7a + + + CompositionCollectionView + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + + + + \ No newline at end of file diff --git a/components/CompositionCollectionView/src/DeconstructPolyfillExtensions.cs b/components/CompositionCollectionView/src/DeconstructPolyfillExtensions.cs new file mode 100644 index 000000000..518883fed --- /dev/null +++ b/components/CompositionCollectionView/src/DeconstructPolyfillExtensions.cs @@ -0,0 +1,20 @@ +// 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; +using System.Collections.Generic; +using System.Text; + +namespace CommunityToolkit.Labs.WinUI; +internal static class DeconstructPolyfillExtensions +{ + public static void Deconstruct( + this KeyValuePair pair, + out T1 key, + out T2 value) + { + key = pair.Key; + value = pair.Value; + } +} diff --git a/components/CompositionCollectionView/src/Dependencies.WinUI2.props b/components/CompositionCollectionView/src/Dependencies.WinUI2.props new file mode 100644 index 000000000..3bd3202b1 --- /dev/null +++ b/components/CompositionCollectionView/src/Dependencies.WinUI2.props @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/components/CompositionCollectionView/src/Dependencies.WinUI3.props b/components/CompositionCollectionView/src/Dependencies.WinUI3.props new file mode 100644 index 000000000..cb9f67653 --- /dev/null +++ b/components/CompositionCollectionView/src/Dependencies.WinUI3.props @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/components/CompositionCollectionView/src/Dependencies.props b/components/CompositionCollectionView/src/Dependencies.props new file mode 100644 index 000000000..e622e1df4 --- /dev/null +++ b/components/CompositionCollectionView/src/Dependencies.props @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/CompositionCollectionView/src/ElementReference.cs b/components/CompositionCollectionView/src/ElementReference.cs new file mode 100644 index 000000000..f64b52e44 --- /dev/null +++ b/components/CompositionCollectionView/src/ElementReference.cs @@ -0,0 +1,71 @@ +// 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. +#nullable enable + +namespace CommunityToolkit.Labs.WinUI; +public record ElementReference : IDisposable where TId : notnull +{ + private bool disposedValue; + + public TId Id { get; init; } + public FrameworkElement Container { get; init; } + public Visual Visual { get; init; } + public CompositionPropertySet CompositionProperties { get; init; } + public AnimatableCompositionNodeSet AnimatableNodes { get; init; } + public CompositionCollectionLayout Layout { get; internal set; } + public TItem Model { get; internal set; } + + public ElementReference(TId id, TItem model, FrameworkElement container, CompositionCollectionLayout layout) + { + Id = id; + Container = container; + Visual = ElementCompositionPreview.GetElementVisual(Container); + CompositionProperties = Visual.Compositor.CreatePropertySet(); + AnimatableNodes = new AnimatableCompositionNodeSet(Visual.Compositor); + Layout = layout; + Model = model; + } + + internal void ReasignTo(CompositionCollectionLayout newLayout) + { + Layout = newLayout; + } + + public void SetZIndex(int zIndex) + { + //TODO: allow allow elements to process their own z index changes + Canvas.SetZIndex(Container, zIndex); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + CompositionProperties.Dispose(); + AnimatableNodes.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ElementReference() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/CompositionExtensions.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/CompositionExtensions.cs new file mode 100644 index 000000000..b40b9678a --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/CompositionExtensions.cs @@ -0,0 +1,305 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class CompositionExtensions. + /// + public static class CompositionExtensions + { + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// AmbientLightReferenceNode. + public static AmbientLightReferenceNode GetReference(this AmbientLight compObj) + { + return new AmbientLightReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// ColorBrushReferenceNode. + public static ColorBrushReferenceNode GetReference(this CompositionColorBrush compObj) + { + return new ColorBrushReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// DistantLightReferenceNode. + public static DistantLightReferenceNode GetReference(this DistantLight compObj) + { + return new DistantLightReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// DropShadowReferenceNode. + public static DropShadowReferenceNode GetReference(this DropShadow compObj) + { + return new DropShadowReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// InsetClipReferenceNode. + public static InsetClipReferenceNode GetReference(this InsetClip compObj) + { + return new InsetClipReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// InteractionTrackerReferenceNode. + public static InteractionTrackerReferenceNode GetReference(this InteractionTracker compObj) + { + return new InteractionTrackerReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// NineGridBrushReferenceNode. + public static NineGridBrushReferenceNode GetReference(this CompositionNineGridBrush compObj) + { + return new NineGridBrushReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// PointLightReferenceNode. + public static PointLightReferenceNode GetReference(this PointLight compObj) + { + return new PointLightReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// PropertySetReferenceNode. + public static PropertySetReferenceNode GetReference(this CompositionPropertySet compObj) + { + return new PropertySetReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// SpotLightReferenceNode. + public static SpotLightReferenceNode GetReference(this SpotLight compObj) + { + return new SpotLightReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// SurfaceBrushReferenceNode. + public static SurfaceBrushReferenceNode GetReference(this CompositionSurfaceBrush compObj) + { + return new SurfaceBrushReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this CompositionObject. + /// + /// The comp object. + /// VisualReferenceNode. + public static VisualReferenceNode GetReference(this Visual compObj) + { + return new VisualReferenceNode(null, compObj); + } + + /// + /// Create an ExpressionNode reference to this specialized PropertySet. + /// + /// A class that derives from PropertySetReferenceNode. + /// The ps. + /// T. + /// Invalid property set specialization + public static T GetSpecializedReference(this CompositionPropertySet ps) + where T : PropertySetReferenceNode + { + if (typeof(T) == typeof(ManipulationPropertySetReferenceNode)) + { + return new ManipulationPropertySetReferenceNode(null, ps) as T; + } + else if (typeof(T) == typeof(PointerPositionPropertySetReferenceNode)) + { + return new PointerPositionPropertySetReferenceNode(null, ps) as T; + } + else + { + throw new System.Exception("Invalid property set specialization"); + } + } + + /// + /// Connects the specified ExpressionNode with the specified property of the object. + /// + /// The comp object. + /// The name of the property that the expression will target. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void StartAnimation(this CompositionObject compObject, string propertyName, ExpressionNode expressionNode) + { + compObject.StartAnimation(propertyName, CreateExpressionAnimationFromNode(compObject.Compositor, expressionNode)); + } + + /// + /// Inserts a KeyFrame whose value is calculated using the specified ExpressionNode. + /// + /// The keyframe animation. + /// The time the key frame should occur at, expressed as a percentage of the animation Duration. Allowed value is from 0.0 to 1.0. + /// The root ExpressionNode that represents the ExpressionAnimation. + /// The easing function to use when interpolating between frames. + public static void InsertExpressionKeyFrame(this KeyFrameAnimation keyframeAnimation, float normalizedProgressKey, ExpressionNode expressionNode, CompositionEasingFunction easing = null) + { + expressionNode.ClearReferenceInfo(); + + keyframeAnimation.InsertExpressionKeyFrame(normalizedProgressKey, expressionNode.ToExpressionString(), easing); + + expressionNode.SetAllParameters(keyframeAnimation); + } + + /// + /// Use the value of specified ExpressionNode to determine if this inertia modifier should be chosen. + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetCondition(this InteractionTrackerInertiaRestingValue modifier, ExpressionNode expressionNode) + { + modifier.Condition = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Use the value of specified ExpressionNode as the resting value for this inertia modifier. + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetRestingValue(this InteractionTrackerInertiaRestingValue modifier, ExpressionNode expressionNode) + { + modifier.RestingValue = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Use the value of specified ExpressionNode to determine if this inertia modifier should be chosen. + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetCondition(this InteractionTrackerInertiaMotion modifier, ExpressionNode expressionNode) + { + modifier.Condition = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Use the value of specified ExpressionNode to dictate the motion for this inertia modifier. + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetMotion(this InteractionTrackerInertiaMotion modifier, ExpressionNode expressionNode) + { + modifier.Motion = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Use the value of specified ExpressionNode to determine if this composition conditional value modifier should be chosen. + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetCondition(this CompositionConditionalValue modifier, ExpressionNode expressionNode) + { + modifier.Condition = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Use the value of specified ExpressionNode as the value for this composition conditional value + /// + /// The modifier. + /// The root ExpressionNode that represents the ExpressionAnimation. + public static void SetValue(this CompositionConditionalValue modifier, ExpressionNode expressionNode) + { + modifier.Value = CreateExpressionAnimationFromNode(modifier.Compositor, expressionNode); + } + + /// + /// Creates the expression animation from node. + /// + /// The compositor. + /// The expression node. + /// ExpressionAnimation. + private static ExpressionAnimation CreateExpressionAnimationFromNode(Compositor compositor, ExpressionNode expressionNode) + { + // Only create a new animation if this node hasn't already generated one before, so we don't have to re-parse the expression string. + if (expressionNode.ExpressionAnimation == null) + { + expressionNode.ClearReferenceInfo(); + expressionNode.ExpressionAnimation = compositor.CreateExpressionAnimation(expressionNode.ToExpressionString()); + } + + // We need to make sure all parameters are up to date, even if the animation already existed. + expressionNode.SetAllParameters(expressionNode.ExpressionAnimation); + + 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 + }; + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionFunctions.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionFunctions.cs new file mode 100644 index 000000000..b6663b84b --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionFunctions.cs @@ -0,0 +1,1335 @@ +// 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.Collections.Generic; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class ExpressionFunctions. + /// + public static class ExpressionFunctions + { + //TODO add all of these to evaluate() + + + /// + /// Returns the angle (in radians) whose cosine is the specified number. + /// + /// Value between -1 and 1, for which to calculate the arccosine (the inverse cosine). + /// ScalarNode. + public static ScalarNode ACos(ScalarNode val) + { + return Function(ExpressionNodeType.Acos, val); + } + + /// + /// Returns the angle (in radians) whose sine is the specified number. + /// + /// Value between -1 and 1, for which to calculate the arcsine (the inverse sine). + /// ScalarNode. + public static ScalarNode ASin(ScalarNode val) + { + return Function(ExpressionNodeType.Asin, val); + } + + /// + /// Returns the angle (in radians) whose tangent is the specified number. + /// + /// Value for which to calculate the arctan (the inverse tan). + /// ScalarNode. + public static ScalarNode ATan(ScalarNode val) + { + return Function(ExpressionNodeType.Atan, val); + } + + /// + /// Returns the smallest integral value that is greater than or equal to the specified value. + /// + /// The floating point number to round. + /// ScalarNode. + public static ScalarNode Ceil(ScalarNode val) + { + return Function(ExpressionNodeType.Ceil, val); + } + + /// + /// Returns the cosine of the specified angle (in radians). + /// + /// An angle, measured in radians. + /// ScalarNode. + public static ScalarNode Cos(ScalarNode val) + { + return Function(ExpressionNodeType.Cos, val); + } + + /// + /// Returns the largest integer less than or equal to the specified value. + /// + /// The floating point number to round. + /// ScalarNode. + public static ScalarNode Floor(ScalarNode val) + { + return Function(ExpressionNodeType.Floor, val); + } + + /// + /// Returns the natural (base e) logarithm of a specified number. + /// + /// The number whose natural logarithm is to be returned. + /// ScalarNode. + public static ScalarNode Ln(ScalarNode val) + { + return Function(ExpressionNodeType.Ln, val); + } + + /// + /// Returns the base 10 logarithm of a specified number. + /// + /// The number whose base 10 logarithm is to be calculated. + /// ScalarNode. + public static ScalarNode Log10(ScalarNode val) + { + return Function(ExpressionNodeType.Log10, val); + } + + /// + /// Returns a specified number raised to the specified power. + /// + /// A floating-point number to be raised to a power. + /// A floating-point number that specifies a power. + /// ScalarNode. + public static ScalarNode Pow(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Pow, val1, val2); + } + + /// + /// Rounds a floating point value to the nearest integral value. + /// + /// The floating point number to round. + /// ScalarNode. + public static ScalarNode Round(ScalarNode val) + { + return Function(ExpressionNodeType.Round, val); + } + + /// + /// Returns the sine of the specified angle (in radians). + /// + /// An angle, measured in radians. + /// ScalarNode. + public static ScalarNode Sin(ScalarNode val) + { + return Function(ExpressionNodeType.Sin, val); + } + + /// + /// Returns the specified number multiplied by itself. + /// + /// The floating point number to square. + /// ScalarNode. + public static ScalarNode Square(ScalarNode val) + { + return Function(ExpressionNodeType.Square, val); + } + + /// + /// Returns the square root of a specified number. + /// + /// The number whose square root is to be returned. + /// ScalarNode. + public static ScalarNode Sqrt(ScalarNode val) + { + return Function(ExpressionNodeType.Sqrt, val); + } + + /// + /// Returns the tangent of the specified angle (in radians). + /// + /// An angle, measured in radians. + /// ScalarNode. + public static ScalarNode Tan(ScalarNode val) + { + return Function(ExpressionNodeType.Tan, val); + } + + /// + /// Converts an angle in radians to degrees as: val*180/PI. + /// + /// A floating point value that represents an angle in radians. + /// ScalarNode. + public static ScalarNode ToDegrees(ScalarNode val) + { + return Function(ExpressionNodeType.ToDegrees, val); + } + + /// + /// Converts an angle in degrees to radians as: val*PI/180. + /// + /// A floating point value that represents an angle in degrees. + /// ScalarNode. + public static ScalarNode ToRadians(ScalarNode val) + { + return Function(ExpressionNodeType.ToRadians, val); + } + + // System.Numerics functions + + /// + /// Returns the absolute value of the specified input. For vectors, the absolute value of each subchannel is returned. + /// + /// The input value. + /// ScalarNode. + public static ScalarNode Abs(ScalarNode val) + { + return Function(ExpressionNodeType.Absolute, val); + } + + /// + /// Returns the absolute value of the specified input. For vectors, the absolute value of each subchannel is returned. + /// + /// The input value. + /// Vector2Node. + public static Vector2Node Abs(Vector2Node val) + { + return Function(ExpressionNodeType.Absolute, val); + } + + /// + /// Returns the absolute value of the specified input. For vectors, the absolute value of each subchannel is returned. + /// + /// The input value. + /// Vector3Node. + public static Vector3Node Abs(Vector3Node val) + { + return Function(ExpressionNodeType.Absolute, val); + } + + /// + /// Returns the absolute value of the specified input. For vectors, the absolute value of each subchannel is returned. + /// . + /// + /// The input value. + /// Vector4Node. + public static Vector4Node Abs(Vector4Node val) + { + return Function(ExpressionNodeType.Absolute, val); + } + + /// + /// Restricts a value to be within a specified range. For vectors, each subchannel is clamped. + /// + /// The value to clamp. + /// The specified minimum range. + /// The specified maximum range. + /// ScalarNode. + public static ScalarNode Clamp(ScalarNode val, ScalarNode min, ScalarNode max) + { + return Function(ExpressionNodeType.Clamp, val, min, max); + } + + /// + /// Restricts a value to be within a specified range. For vectors, each subchannel is clamped. + /// + /// The value to clamp. + /// The specified minimum range. + /// The specified maximum range. + /// Vector2Node. + public static Vector2Node Clamp(Vector2Node val, Vector2Node min, Vector2Node max) + { + return Function(ExpressionNodeType.Clamp, val, min, max); + } + + /// + /// Restricts a value to be within a specified range. For vectors, each subchannel is clamped. + /// + /// The value to clamp. + /// The specified minimum range. + /// The specified maximum range. + /// Vector3Node. + public static Vector3Node Clamp(Vector3Node val, Vector3Node min, Vector3Node max) + { + return Function(ExpressionNodeType.Clamp, val, min, max); + } + + /// + /// Restricts a value to be within a specified range. For vectors, each subchannel is clamped. + /// + /// The value to clamp. + /// The specified minimum range. + /// The specified maximum range. + /// Vector4Node. + public static Vector4Node Clamp(Vector4Node val, Vector4Node min, Vector4Node max) + { + return Function(ExpressionNodeType.Clamp, val, min, max); + } + + /// + /// Linearly interpolates between two colors in the default color space. + /// + /// Color source value 1. + /// Color source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// ColorNode. + public static ColorNode ColorLerp(ColorNode val1, ColorNode val2, ScalarNode progress) + { + return Function(ExpressionNodeType.ColorLerp, val1, val2, progress); + } + + /// + /// Linearly interpolates between two colors in the HSL color space. + /// + /// Color source value 1. + /// Color source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// ColorNode. + public static ColorNode ColorLerpHsl(ColorNode val1, ColorNode val2, ScalarNode progress) + { + return Function(ExpressionNodeType.ColorLerpHsl, val1, val2, progress); + } + + /// + /// Linearly interpolates between two colors in the RBG color space. + /// + /// Color source value 1. + /// Color source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// ColorNode. + public static ColorNode ColorLerpRgb(ColorNode val1, ColorNode val2, ScalarNode progress) + { + return Function(ExpressionNodeType.ColorLerpRgb, val1, val2, progress); + } + + /// + /// Concatenates two Quaternions; the result represents the first rotation followed by the second rotation. + /// + /// The first quaternion rotation in the series. + /// The second quaternion rotation in the series. + /// QuaternionNode. + public static QuaternionNode Concatenate(QuaternionNode val1, QuaternionNode val2) + { + return Function(ExpressionNodeType.Concatenate, val1, val2); + } + + /// + /// Returns the distance between two vectors as: sqrt((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Distance(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Distance, val1, val2); + } + + /// + /// Returns the distance between two vectors as: sqrt((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Distance(Vector2Node val1, Vector2Node val2) + { + return Function(ExpressionNodeType.Distance, val1, val2); + } + + /// + /// Returns the distance between two vectors as: sqrt((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Distance(Vector3Node val1, Vector3Node val2) + { + return Function(ExpressionNodeType.Distance, val1, val2); + } + + /// + /// Returns the distance between two vectors as: sqrt((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Distance(Vector4Node val1, Vector4Node val2) + { + return Function(ExpressionNodeType.Distance, val1, val2); + } + + /// + /// Returns the squared distance between two vectors as: ((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode DistanceSquared(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.DistanceSquared, val1, val2); + } + + /// + /// Returns the squared distance between two vectors as: ((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode DistanceSquared(Vector2Node val1, Vector2Node val2) + { + return Function(ExpressionNodeType.DistanceSquared, val1, val2); + } + + /// + /// Returns the squared distance between two vectors as: ((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode DistanceSquared(Vector3Node val1, Vector3Node val2) + { + return Function(ExpressionNodeType.DistanceSquared, val1, val2); + } + + /// + /// Returns the squared distance between two vectors as: ((x1-x2)^2 + (y1-y2)^2 + ...). + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode DistanceSquared(Vector4Node val1, Vector4Node val2) + { + return Function(ExpressionNodeType.DistanceSquared, val1, val2); + } + + /// + /// Returns the inverse of the specified matrix. + /// + /// The matrix to invert. + /// Matrix3x2Node. + public static Matrix3x2Node Inverse(Matrix3x2Node val) + { + return Function(ExpressionNodeType.Inverse, val); + } + + /// + /// Returns the inverse of the specified matrix. + /// + /// The matrix to invert. + /// Matrix4x4Node. + public static Matrix4x4Node Inverse(Matrix4x4Node val) + { + return Function(ExpressionNodeType.Inverse, val); + } + + /// + /// Returns the length of the vector as: sqrt(x^2 + y^2 + ...). + /// + /// Vector value to return the length of. + /// ScalarNode. + public static ScalarNode Length(ScalarNode val) + { + return Function(ExpressionNodeType.Length, val); + } + + /// + /// Returns the length of the vector as: sqrt(x^2 + y^2 + ...). + /// + /// Vector value to return the length of. + /// ScalarNode. + public static ScalarNode Length(Vector2Node val) + { + return Function(ExpressionNodeType.Length, val); + } + + /// + /// Returns the length of the vector as: sqrt(x^2 + y^2 + ...). + /// + /// Vector value to return the length of. + /// ScalarNode. + public static ScalarNode Length(Vector3Node val) + { + return Function(ExpressionNodeType.Length, val); + } + + /// + /// Returns the length of the vector as: sqrt(x^2 + y^2 + ...). + /// + /// Vector value to return the length of. + /// ScalarNode. + public static ScalarNode Length(Vector4Node val) + { + return Function(ExpressionNodeType.Length, val); + } + + /// + /// Returns the length of the vector as: sqrt(x^2 + y^2 + ...). + /// + /// Vector value to return the length of. + /// ScalarNode. + public static ScalarNode Length(QuaternionNode val) + { + return Function(ExpressionNodeType.Length, val); + } + + /// + /// Returns the squared length of the vector as: (x^2 + y^2 + ...). + /// + /// Vector value to return the length squared of. + /// ScalarNode. + public static ScalarNode LengthSquared(ScalarNode val) + { + return Function(ExpressionNodeType.LengthSquared, val); + } + + /// + /// Returns the squared length of the vector as: (x^2 + y^2 + ...). + /// + /// Vector value to return the length squared of. + /// ScalarNode. + public static ScalarNode LengthSquared(Vector2Node val) + { + return Function(ExpressionNodeType.LengthSquared, val); + } + + /// + /// Returns the squared length of the vector as: (x^2 + y^2 + ...). + /// + /// Vector value to return the length squared of. + /// ScalarNode. + public static ScalarNode LengthSquared(Vector3Node val) + { + return Function(ExpressionNodeType.LengthSquared, val); + } + + /// + /// Returns the squared length of the vector as: (x^2 + y^2 + ...). + /// + /// Vector value to return the length squared of. + /// ScalarNode. + public static ScalarNode LengthSquared(Vector4Node val) + { + return Function(ExpressionNodeType.LengthSquared, val); + } + + /// + /// Returns the squared length of the vector as: (x^2 + y^2 + ...). + /// + /// Vector value to return the length squared of. + /// ScalarNode. + public static ScalarNode LengthSquared(QuaternionNode val) + { + return Function(ExpressionNodeType.LengthSquared, val); + } + + /// + /// Linearly interpolates between two vectors as: Output.x = x1 + (x2-x1)*progress. + /// + /// Source value 1. + /// Source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// ScalarNode. + public static ScalarNode Lerp(ScalarNode val1, ScalarNode val2, ScalarNode progress) + { + return Function(ExpressionNodeType.Lerp, val1, val2, progress); + } + + /// + /// Linearly interpolates between two vectors as: Output.x = x1 + (x2-x1)*progress. + /// + /// Source value 1. + /// Source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// Vector2Node. + public static Vector2Node Lerp(Vector2Node val1, Vector2Node val2, ScalarNode progress) + { + return Function(ExpressionNodeType.Lerp, val1, val2, progress); + } + + /// + /// Linearly interpolates between two vectors as: Output.x = x1 + (x2-x1)*progress. + /// + /// Source value 1. + /// Source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// Vector3Node. + public static Vector3Node Lerp(Vector3Node val1, Vector3Node val2, ScalarNode progress) + { + return Function(ExpressionNodeType.Lerp, val1, val2, progress); + } + + /// + /// Linearly interpolates between two vectors as: Output.x = x1 + (x2-x1)*progress. + /// + /// Source value 1. + /// Source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// Vector4Node. + public static Vector4Node Lerp(Vector4Node val1, Vector4Node val2, ScalarNode progress) + { + return Function(ExpressionNodeType.Lerp, val1, val2, progress); + } + + /// + /// Returns the maximum of two values. For vectors, the max of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Max(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Max, val1, val2); + } + + /// + /// Returns the maximum of two values. For vectors, the max of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector2Node. + public static Vector2Node Max(Vector2Node val1, Vector2Node val2) + { + return Function(ExpressionNodeType.Max, val1, val2); + } + + /// + /// Returns the maximum of two values. For vectors, the max of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector3Node. + public static Vector3Node Max(Vector3Node val1, Vector3Node val2) + { + return Function(ExpressionNodeType.Max, val1, val2); + } + + /// + /// Returns the maximum of two values. For vectors, the max of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector4Node. + public static Vector4Node Max(Vector4Node val1, Vector4Node val2) + { + return Function(ExpressionNodeType.Max, val1, val2); + } + + /// + /// Returns the minimum of two values. For vectors, the min of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// ScalarNode. + public static ScalarNode Min(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Min, val1, val2); + } + + /// + /// Returns the minimum of two values. For vectors, the min of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector2Node. + public static Vector2Node Min(Vector2Node val1, Vector2Node val2) + { + return Function(ExpressionNodeType.Min, val1, val2); + } + + /// + /// Returns the minimum of two values. For vectors, the min of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector3Node. + public static Vector3Node Min(Vector3Node val1, Vector3Node val2) + { + return Function(ExpressionNodeType.Min, val1, val2); + } + + /// + /// Returns the minimum of two values. For vectors, the min of each subchannel is returned. + /// + /// Source value 1. + /// Source value 2. + /// Vector4Node. + public static Vector4Node Min(Vector4Node val1, Vector4Node val2) + { + return Function(ExpressionNodeType.Min, val1, val2); + } + + /// + /// Returns the remainder resulting from dividing val1/val2. For vectors, the remainder for each subchannel is returned. + /// + /// The numerator value. + /// The denominator value. + /// ScalarNode. + public static ScalarNode Mod(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Modulus, val1, val2); + } + + /// + /// Returns the remainder resulting from dividing val1/val2. For vectors, the remainder for each subchannel is returned. + /// + /// The numerator value. + /// The denominator value. + /// Vector2Node. + public static Vector2Node Mod(Vector2Node val1, Vector2Node val2) + { + return Function(ExpressionNodeType.Modulus, val1, val2); + } + + /// + /// Returns the remainder resulting from dividing val1/val2. For vectors, the remainder for each subchannel is returned. + /// + /// The numerator value. + /// The denominator value. + /// Vector3Node. + public static Vector3Node Mod(Vector3Node val1, Vector3Node val2) + { + return Function(ExpressionNodeType.Modulus, val1, val2); + } + + /// + /// Returns the remainder resulting from dividing val1/val2. For vectors, the remainder for each subchannel is returned. + /// + /// The numerator value. + /// The denominator value. + /// Vector4Node. + public static Vector4Node Mod(Vector4Node val1, Vector4Node val2) + { + return Function(ExpressionNodeType.Modulus, val1, val2); + } + + /// + /// Returns the normalized version of a vector. + /// + /// Vector value to normalize. + /// Vector2Node. + public static Vector2Node Normalize(Vector2Node val) + { + return Function(ExpressionNodeType.Normalize, val); + } + + /// + /// Returns the normalized version of a vector. + /// + /// Vector value to normalize. + /// Vector3Node. + public static Vector3Node Normalize(Vector3Node val) + { + return Function(ExpressionNodeType.Normalize, val); + } + + /// + /// Returns the normalized version of a vector. + /// + /// Vector value to normalize. + /// Vector4Node. + public static Vector4Node Normalize(Vector4Node val) + { + return Function(ExpressionNodeType.Normalize, val); + } + + /// + /// Returns the normalized version of a vector. + /// + /// Vector value to normalize. + /// QuaternionNode. + public static QuaternionNode Normalize(QuaternionNode val) + { + return Function(ExpressionNodeType.Normalize, val); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// ScalarNode. + public static ScalarNode Scale(ScalarNode val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// Vector2Node. + public static Vector2Node Scale(Vector2Node val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// Vector3Node. + public static Vector3Node Scale(Vector3Node val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// Vector4Node. + public static Vector4Node Scale(Vector4Node val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// Matrix3x2Node. + public static Matrix3x2Node Scale(Matrix3x2Node val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Multiply each subchannel of the specified vector/matrix by a float value. + /// + /// Source value to scale. + /// Scaling value. + /// Matrix4x4Node. + public static Matrix4x4Node Scale(Matrix4x4Node val1, ScalarNode val2) + { + return Function(ExpressionNodeType.Scale, val1, val2); + } + + /// + /// Spherically interpolates between two quaternions. + /// + /// Quaternion source value 1. + /// Quaternion source value 2. + /// A value between 0 and 1.0 indicating the weight of val2. + /// QuaternionNode. + public static QuaternionNode Slerp(QuaternionNode val1, QuaternionNode val2, ScalarNode progress) + { + return Function(ExpressionNodeType.Slerp, val1, val2, progress); + } + + /// + /// Transforms a vector by the specified matrix. + /// + /// Vector to be transformed. + /// The transformation matrix. + /// Vector2Node. + public static Vector2Node Transform(Vector2Node val1, Matrix3x2Node val2) + { + return Function(ExpressionNodeType.Transform, val1, val2); + } + + /// + /// Transforms a vector by the specified matrix. + /// + /// Vector to be transformed. + /// The transformation matrix. + /// Vector4Node. + public static Vector4Node Transform(Vector4Node val1, Matrix4x4Node val2) + { + return Function(ExpressionNodeType.Transform, val1, val2); + } + + // System.Numerics Type Constructors + + /// + /// Creates a vector whose subchannels have the specified values. + /// + /// The x. + /// The y. + /// Vector2Node. + public static Vector2Node Vector2(ScalarNode x, ScalarNode y) + { + return Function(ExpressionNodeType.Vector2, x, y); + } + + /// + /// Creates a vector whose subchannels have the specified values. + /// + /// The x. + /// The y. + /// The z. + /// Vector3Node. + public static Vector3Node Vector3(ScalarNode x, ScalarNode y, ScalarNode z) + { + return Function(ExpressionNodeType.Vector3, x, y, z); + } + + /// + /// Creates a vector whose subchannels have the specified values. + /// + /// The x. + /// The y. + /// The z. + /// The w. + /// Vector4Node. + public static Vector4Node Vector4(ScalarNode x, ScalarNode y, ScalarNode z, ScalarNode w) + { + return Function(ExpressionNodeType.Vector4, x, y, z, w); + } + + /// + /// Creates a color in the HSL format. + /// + /// Hue + /// Saturation + /// Luminosity + /// ColorNode. + public static ColorNode ColorHsl(ScalarNode h, ScalarNode s, ScalarNode l) + { + return Function(ExpressionNodeType.ColorHsl, h, s, l); + } + + /// + /// Creates a Color in the ARGB format. + /// + /// The alpha. + /// The red. + /// The green. + /// The blue. + /// ColorNode. + public static ColorNode ColorRgb(ScalarNode alpha, ScalarNode red, ScalarNode green, ScalarNode blue) + { + return Function(ExpressionNodeType.ColorRgb, alpha, red, green, blue); + } + + /// + /// Creates a quaternion whose subchannels have the specified values. + /// + /// The x. + /// The y. + /// The z. + /// The w. + /// QuaternionNode. + public static QuaternionNode Quaternion(ScalarNode x, ScalarNode y, ScalarNode z, ScalarNode w) + { + return Function(ExpressionNodeType.Quaternion, x, y, z, w); + } + + /// + /// Creates a matrix whose subchannels have the specified values. + /// + /// The channel11. + /// The channel12. + /// The channel21. + /// The channel22. + /// The channel31. + /// The channel32. + /// Matrix3x2Node. + public static Matrix3x2Node Matrix3x2(ScalarNode channel11, ScalarNode channel12, ScalarNode channel21, ScalarNode channel22, ScalarNode channel31, ScalarNode channel32) + { + return Function(ExpressionNodeType.Matrix3x2, channel11, channel12, channel21, channel22, channel31, channel32); + } + + /// + /// Creates a matrix whose subchannels have the specified values. + /// + /// The channel11. + /// The channel12. + /// The channel13. + /// The channel14. + /// The channel21. + /// The channel22. + /// The channel23. + /// The channel24. + /// The channel31. + /// The channel32. + /// The channel33. + /// The channel34. + /// The channel41. + /// The channel42. + /// The channel43. + /// The channel44. + /// Matrix4x4Node. +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public static Matrix4x4Node Matrix4x4(ScalarNode channel11, ScalarNode channel12, ScalarNode channel13, ScalarNode channel14, + ScalarNode channel21, ScalarNode channel22, ScalarNode channel23, ScalarNode channel24, + ScalarNode channel31, ScalarNode channel32, ScalarNode channel33, ScalarNode channel34, + ScalarNode channel41, ScalarNode channel42, ScalarNode channel43, ScalarNode channel44) +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + { + return Function(ExpressionNodeType.Matrix4x4, channel11, channel12, channel13, channel14, channel21, channel22, channel23, channel24, channel31, channel32, channel33, channel34, channel41, channel42, channel43, channel44); + } + + /// + /// Creates a 4x4 matrix from a 3x2 matrix. + /// + /// The value. + /// Matrix4x4Node. + public static Matrix4x4Node Matrix4x4(Matrix3x2Node val) + { +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + return Function( + ExpressionNodeType.Matrix4x4, + val.Channel11, val.Channel12, (ScalarNode)0, (ScalarNode)0, + val.Channel21, val.Channel22, (ScalarNode)0, (ScalarNode)0, + (ScalarNode)0, (ScalarNode)0, (ScalarNode)1, (ScalarNode)0, + val.Channel31, val.Channel32, (ScalarNode)0, (ScalarNode)1); +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + } + + /// + /// Creates a translation matrix from the specified vector. + /// + /// Source translation vector. + /// Matrix3x2Node. + public static Matrix3x2Node CreateTranslation(Vector2Node val) + { + return Function(ExpressionNodeType.Matrix3x2FromTranslation, val); + } + + /// + /// Creates a translation matrix from the specified vector. + /// + /// Source translation vector. + /// Matrix4x4Node. + public static Matrix4x4Node CreateTranslation(Vector3Node val) + { + return Function(ExpressionNodeType.Matrix4x4FromTranslation, val); + } + + /// + /// Creates a scale matrix from the specified vector scale. + /// + /// Source scaling vector. + /// Matrix3x2Node. + public static Matrix3x2Node CreateScale(Vector2Node val) + { + return Function(ExpressionNodeType.Matrix3x2FromScale, val); + } + + /// + /// Creates a scale matrix from the specified vector scale. + /// + /// Source scaling vector. + /// Matrix4x4Node. + public static Matrix4x4Node CreateScale(Vector3Node val) + { + return Function(ExpressionNodeType.Matrix4x4FromScale, val); + } + + /// + /// Creates a skew matrix from the specified angles in radians. + /// + /// X angle, in radians. + /// Y angle, in radians. + /// The centerpoint for the operation. + /// Matrix3x2Node. + public static Matrix3x2Node CreateSkew(ScalarNode xAngle, ScalarNode yAngle, Vector2Node centerPoint) + { + return Function(ExpressionNodeType.Matrix3x2FromSkew, xAngle, yAngle, centerPoint); + } + + /// + /// Creates a rotation matrix using the given rotation in radians. + /// + /// Angle, in radians. + /// Matrix3x2Node. + public static Matrix3x2Node CreateRotation(ScalarNode angle) + { + return Function(ExpressionNodeType.Matrix3x2FromRotation, angle); + } + + /// + /// Creates a matrix that rotates around an arbitrary vector. + /// + /// Rotation axis + /// Angle, in radians. + /// Matrix4x4Node. + public static Matrix4x4Node CreateMatrix4x4FromAxisAngle(Vector3Node axis, ScalarNode angle) + { + return Function(ExpressionNodeType.Matrix4x4FromAxisAngle, axis, angle); + } + + /// + /// Creates a quaternion that rotates around an arbitrary vector. + /// + /// Rotation axis + /// Angle, in radians. + /// QuaternionNode. + public static QuaternionNode CreateQuaternionFromAxisAngle(Vector3Node axis, ScalarNode angle) + { + return Function(ExpressionNodeType.QuaternionFromAxisAngle, axis, angle); + } + + /// + /// Performs a logical AND operation on two boolean values as: val1 && val2. + /// + /// The val1. + /// The val2. + /// BooleanNode. + public static BooleanNode And(BooleanNode val1, BooleanNode val2) + { + return Function(ExpressionNodeType.And, val1, val2); + } + + /// + /// Performs a logical OR operation on two boolean values as: val1 || val2. + /// + /// The val1. + /// The val2. + /// BooleanNode. + public static BooleanNode Or(BooleanNode val1, BooleanNode val2) + { + return Function(ExpressionNodeType.Or, val1, val2); + } + + /// + /// Performs a logical NOT operation on a specified boolean value as: !val. + /// + /// The value. + /// BooleanNode. + public static BooleanNode Not(BooleanNode val) + { + return Function(ExpressionNodeType.Not, val); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// ScalarNode. + public static ScalarNode Conditional(BooleanNode condition, ScalarNode trueCase, ScalarNode falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// Vector2Node. + public static Vector2Node Conditional(BooleanNode condition, Vector2Node trueCase, Vector2Node falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// Vector3Node. + public static Vector3Node Conditional(BooleanNode condition, Vector3Node trueCase, Vector3Node falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// Vector4Node. + public static Vector4Node Conditional(BooleanNode condition, Vector4Node trueCase, Vector4Node falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// ColorNode. + public static ColorNode Conditional(BooleanNode condition, ColorNode trueCase, ColorNode falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// QuaternionNode. + public static QuaternionNode Conditional(BooleanNode condition, QuaternionNode trueCase, QuaternionNode falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// Matrix3x2Node. + public static Matrix3x2Node Conditional(BooleanNode condition, Matrix3x2Node trueCase, Matrix3x2Node falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Returns one of two values, depending on the value of the boolean condition. + /// + /// Boolean value used to determine whether to return the value represented by 'trueCase' or 'falseCase'. + /// Value to return if 'condition' evaluates to true. + /// Value to return if 'condition' evaluates to false. + /// Matrix4x4Node. + public static Matrix4x4Node Conditional(BooleanNode condition, Matrix4x4Node trueCase, Matrix4x4Node falseCase) + { + return Function(ExpressionNodeType.Conditional, condition, trueCase, falseCase); + } + + /// + /// Functions the specified node type. + /// + /// A class that derives from ExpressionNode. + /// Type of the node. + /// The expression function parameters. + /// T. + internal static T Function(ExpressionNodeType nodeType, params ExpressionNode[] expressionFunctionParams) + where T : ExpressionNode + { + T newNode = ExpressionNode.CreateExpressionNode(); + + (newNode as ExpressionNode).NodeType = nodeType; + foreach (var param in expressionFunctionParams) + { + (newNode as ExpressionNode).Children.Add(param); + } + + return newNode; + } + + /// + /// Gets the type of the node information from. + /// + /// The type. + /// ExpressionNodeInfo. + internal static ExpressionNodeInfo GetNodeInfoFromType(ExpressionNodeType type) + { + return _expressionNodeInfo[type]; + } + + /// + /// Struct ExpressionNodeInfo + /// + internal struct ExpressionNodeInfo + { + /// + /// Initializes a new instance of the struct. + /// + /// Kind of the node operation. + /// The operation string. + public ExpressionNodeInfo(OperationType nodeOperationKind, string operationString) + { + NodeOperationKind = nodeOperationKind; + OperationString = operationString; + } + + /// + /// Gets or sets the kind of the node operation. + /// + /// The kind of the node operation. + internal OperationType NodeOperationKind { get; set; } + + /// + /// Gets or sets the operation string. + /// + /// The operation string. + internal string OperationString { get; set; } + } + + /// + /// The expression node information + /// + private static readonly Dictionary _expressionNodeInfo = new Dictionary + { + { ExpressionNodeType.ConstantValue, new ExpressionNodeInfo(OperationType.Constant, null) }, + { ExpressionNodeType.ConstantParameter, new ExpressionNodeInfo(OperationType.Constant, null) }, + { ExpressionNodeType.CurrentValueProperty, new ExpressionNodeInfo(OperationType.Reference, null) }, + { ExpressionNodeType.Reference, new ExpressionNodeInfo(OperationType.Reference, null) }, + { ExpressionNodeType.ReferenceProperty, new ExpressionNodeInfo(OperationType.Reference, null) }, + { ExpressionNodeType.StartingValueProperty, new ExpressionNodeInfo(OperationType.Reference, null) }, + { ExpressionNodeType.TargetReference, new ExpressionNodeInfo(OperationType.Reference, null) }, + { ExpressionNodeType.Conditional, new ExpressionNodeInfo(OperationType.Conditional, null) }, + { ExpressionNodeType.Swizzle, new ExpressionNodeInfo(OperationType.Swizzle, null) }, + { ExpressionNodeType.Add, new ExpressionNodeInfo(OperationType.Operator, "+") }, + { ExpressionNodeType.And, new ExpressionNodeInfo(OperationType.Operator, "&&") }, + { ExpressionNodeType.Divide, new ExpressionNodeInfo(OperationType.Operator, "/") }, + { ExpressionNodeType.Equals, new ExpressionNodeInfo(OperationType.Operator, "==") }, + { ExpressionNodeType.GreaterThan, new ExpressionNodeInfo(OperationType.Operator, ">") }, + { ExpressionNodeType.GreaterThanEquals, new ExpressionNodeInfo(OperationType.Operator, ">=") }, + { ExpressionNodeType.LessThan, new ExpressionNodeInfo(OperationType.Operator, "<") }, + { ExpressionNodeType.LessThanEquals, new ExpressionNodeInfo(OperationType.Operator, "<=") }, + { ExpressionNodeType.Multiply, new ExpressionNodeInfo(OperationType.Operator, "*") }, + { ExpressionNodeType.Not, new ExpressionNodeInfo(OperationType.UnaryOperator, "!") }, + { ExpressionNodeType.NotEquals, new ExpressionNodeInfo(OperationType.Operator, "!=") }, + { ExpressionNodeType.Or, new ExpressionNodeInfo(OperationType.Operator, "||") }, + { ExpressionNodeType.Subtract, new ExpressionNodeInfo(OperationType.Operator, "-") }, + { ExpressionNodeType.Absolute, new ExpressionNodeInfo(OperationType.Function, "abs") }, + { ExpressionNodeType.Acos, new ExpressionNodeInfo(OperationType.Function, "acos") }, + { ExpressionNodeType.Asin, new ExpressionNodeInfo(OperationType.Function, "asin") }, + { ExpressionNodeType.Atan, new ExpressionNodeInfo(OperationType.Function, "atan") }, + { ExpressionNodeType.Cos, new ExpressionNodeInfo(OperationType.Function, "cos") }, + { ExpressionNodeType.Ceil, new ExpressionNodeInfo(OperationType.Function, "ceil") }, + { ExpressionNodeType.Clamp, new ExpressionNodeInfo(OperationType.Function, "clamp") }, + { ExpressionNodeType.ColorHsl, new ExpressionNodeInfo(OperationType.Function, "colorhsl") }, + { ExpressionNodeType.ColorRgb, new ExpressionNodeInfo(OperationType.Function, "colorrgb") }, + { ExpressionNodeType.ColorLerp, new ExpressionNodeInfo(OperationType.Function, "colorlerp") }, + { ExpressionNodeType.ColorLerpHsl, new ExpressionNodeInfo(OperationType.Function, "colorhsllerp") }, + { ExpressionNodeType.ColorLerpRgb, new ExpressionNodeInfo(OperationType.Function, "colorrgblerp") }, + { ExpressionNodeType.Concatenate, new ExpressionNodeInfo(OperationType.Function, "concatenate") }, + { ExpressionNodeType.Distance, new ExpressionNodeInfo(OperationType.Function, "distance") }, + { ExpressionNodeType.DistanceSquared, new ExpressionNodeInfo(OperationType.Function, "distancesquared") }, + { ExpressionNodeType.Floor, new ExpressionNodeInfo(OperationType.Function, "floor") }, + { ExpressionNodeType.Inverse, new ExpressionNodeInfo(OperationType.Function, "inverse") }, + { ExpressionNodeType.Length, new ExpressionNodeInfo(OperationType.Function, "length") }, + { ExpressionNodeType.LengthSquared, new ExpressionNodeInfo(OperationType.Function, "lengthsquared") }, + { ExpressionNodeType.Lerp, new ExpressionNodeInfo(OperationType.Function, "lerp") }, + { ExpressionNodeType.Ln, new ExpressionNodeInfo(OperationType.Function, "ln") }, + { ExpressionNodeType.Log10, new ExpressionNodeInfo(OperationType.Function, "log10") }, + { ExpressionNodeType.Max, new ExpressionNodeInfo(OperationType.Function, "max") }, + { ExpressionNodeType.Matrix3x2FromRotation, new ExpressionNodeInfo(OperationType.Function, "matrix3x2.createrotation") }, + { ExpressionNodeType.Matrix3x2FromScale, new ExpressionNodeInfo(OperationType.Function, "matrix3x2.createscale") }, + { ExpressionNodeType.Matrix3x2FromSkew, new ExpressionNodeInfo(OperationType.Function, "matrix3x2.createskew") }, + { ExpressionNodeType.Matrix3x2FromTranslation, new ExpressionNodeInfo(OperationType.Function, "matrix3x2.createtranslation") }, + { ExpressionNodeType.Matrix3x2, new ExpressionNodeInfo(OperationType.Function, "matrix3x2") }, + { ExpressionNodeType.Matrix4x4FromAxisAngle, new ExpressionNodeInfo(OperationType.Function, "matrix4x4.createfromaxisangle") }, + { ExpressionNodeType.Matrix4x4FromScale, new ExpressionNodeInfo(OperationType.Function, "matrix4x4.createscale") }, + { ExpressionNodeType.Matrix4x4FromTranslation, new ExpressionNodeInfo(OperationType.Function, "matrix4x4.createtranslation") }, + { ExpressionNodeType.Matrix4x4, new ExpressionNodeInfo(OperationType.Function, "matrix4x4") }, + { ExpressionNodeType.Min, new ExpressionNodeInfo(OperationType.Function, "min") }, + { ExpressionNodeType.Modulus, new ExpressionNodeInfo(OperationType.Function, "mod") }, + { ExpressionNodeType.Negate, new ExpressionNodeInfo(OperationType.Function, "-") }, + { ExpressionNodeType.Normalize, new ExpressionNodeInfo(OperationType.Function, "normalize") }, + { ExpressionNodeType.Pow, new ExpressionNodeInfo(OperationType.Function, "pow") }, + { ExpressionNodeType.QuaternionFromAxisAngle, new ExpressionNodeInfo(OperationType.Function, "quaternion.createfromaxisangle") }, + { ExpressionNodeType.Quaternion, new ExpressionNodeInfo(OperationType.Function, "quaternion") }, + { ExpressionNodeType.Round, new ExpressionNodeInfo(OperationType.Function, "round") }, + { ExpressionNodeType.Scale, new ExpressionNodeInfo(OperationType.Function, "scale") }, + { ExpressionNodeType.Sin, new ExpressionNodeInfo(OperationType.Function, "sin") }, + { ExpressionNodeType.Slerp, new ExpressionNodeInfo(OperationType.Function, "slerp") }, + { ExpressionNodeType.Sqrt, new ExpressionNodeInfo(OperationType.Function, "sqrt") }, + { ExpressionNodeType.Square, new ExpressionNodeInfo(OperationType.Function, "square") }, + { ExpressionNodeType.Tan, new ExpressionNodeInfo(OperationType.Function, "tan") }, + { ExpressionNodeType.ToDegrees, new ExpressionNodeInfo(OperationType.Function, "todegrees") }, + { ExpressionNodeType.ToRadians, new ExpressionNodeInfo(OperationType.Function, "toradians") }, + { ExpressionNodeType.Transform, new ExpressionNodeInfo(OperationType.Function, "transform") }, + { ExpressionNodeType.Vector2, new ExpressionNodeInfo(OperationType.Function, "vector2") }, + { ExpressionNodeType.Vector3, new ExpressionNodeInfo(OperationType.Function, "vector3") }, + { ExpressionNodeType.Vector4, new ExpressionNodeInfo(OperationType.Function, "vector4") }, + }; + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/BooleanNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/BooleanNode.cs new file mode 100644 index 000000000..163605c34 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/BooleanNode.cs @@ -0,0 +1,196 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'BooleanNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class BooleanNode. This class cannot be inherited. + /// + /// + public sealed class BooleanNode : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal BooleanNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [value]. + internal BooleanNode(bool value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal BooleanNode(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// if set to true [value]. + internal BooleanNode(string paramName, bool value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetBooleanParameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// if set to true [value]. + /// The result of the conversion. + public static implicit operator BooleanNode(bool value) + { + return new BooleanNode(value); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(BooleanNode left, BooleanNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(BooleanNode left, BooleanNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Implements the & operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator &(BooleanNode left, BooleanNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.And, left, right); + } + + /// + /// Implements the | operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator |(BooleanNode left, BooleanNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Or, left, right); + } + + /// + /// Implements the ! operator. + /// + /// The value. + /// The result of the operator. + public static BooleanNode operator !(BooleanNode value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Not, value); + } + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return _value ? "true" : "false"; + } + + 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($"Operation ${NodeType} not implemented for BooleanNode"); + } + + 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 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ColorNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ColorNode.cs new file mode 100644 index 000000000..efe2e7f22 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ColorNode.cs @@ -0,0 +1,137 @@ +#nullable disable +// 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 Windows.UI; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'ColorNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class ColorNode. This class cannot be inherited. + /// + /// + public sealed class ColorNode : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal ColorNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal ColorNode(Color value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal ColorNode(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal ColorNode(string paramName, Color value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetColorParameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator ColorNode(Color value) + { + return new ColorNode(value); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(ColorNode left, ColorNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(ColorNode left, ColorNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"ColorRgb({_value.A},{_value.R},{_value.G},{_value.B})"; + } + + 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($"Operation ${NodeType} not implemented for ColorNode"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNode.cs new file mode 100644 index 000000000..106f995f3 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNode.cs @@ -0,0 +1,734 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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.Numerics; +using Windows.UI; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class ExpressionNode. + /// + public abstract class ExpressionNode : IDisposable + { + private List _objRefList = null; + private Dictionary _compObjToParamNameMap = null; + private Dictionary _constParamMap = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + + /// + /// Initializes a new instance of the class. + /// + internal ExpressionNode() + { + } + + /// + /// Resolve a named reference parameter to the CompositionObject it will refer to. + /// + /// The string name of the parameter to be resolved. + /// The composition object that the parameter should resolve to. + public void SetReferenceParameter(string parameterName, CompositionObject compObj) + { + // Make sure we have our reference list populated + EnsureReferenceInfo(); + + for (int i = 0; i < _objRefList.Count; i++) + { + if (string.Compare(_objRefList[i].ParameterName, parameterName, true /*ignoreCase*/) == 0) + { + var item = _objRefList[i]; + item.CompObject = compObj; + _objRefList[i] = item; + } + } + } + + /// + /// Resolve a named parameter to the boolean value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetBooleanParameter(string parameterName, bool value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the float value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetScalarParameter(string parameterName, float value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Vector2 value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetVector2Parameter(string parameterName, Vector2 value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Vector3 value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetVector3Parameter(string parameterName, Vector3 value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Vector4 value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetVector4Parameter(string parameterName, Vector4 value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Color value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetColorParameter(string parameterName, Color value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Quaternion value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetQuaternionParameter(string parameterName, Quaternion value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Matrix3x2 value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetMatrix3x2Parameter(string parameterName, Matrix3x2 value) + { + _constParamMap[parameterName] = value; + } + + /// + /// Resolve a named parameter to the Matrix4x4 value it will use. + /// + /// The string name of the parameter to be resolved. + /// The value that the parameter should resolve to. + public void SetMatrix4x4Parameter(string parameterName, Matrix4x4 value) + { + _constParamMap[parameterName] = value; + } + + private bool disposedValue; + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _objRefList = null; + _compObjToParamNameMap = null; + _constParamMap = null; + Subchannels = null; + PropertyName = null; + NodeType = ExpressionNodeType.Count; + + // Note: we don't recursively dispose all child nodes, as those nodes could be in use by a different Expression + Children = null; + + if (ExpressionAnimation != null) + { + ExpressionAnimation.Dispose(); + ExpressionAnimation = null; + } + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Creates the expression node. + /// + /// A class that derives from ExpressionNode + /// T. + /// unexpected type + internal static T CreateExpressionNode() + where T : ExpressionNode + { + T newNode; + + if (typeof(T) == typeof(BooleanNode)) + { + newNode = new BooleanNode() as T; + } + else if (typeof(T) == typeof(ScalarNode)) + { + newNode = new ScalarNode() as T; + } + else if (typeof(T) == typeof(Vector2Node)) + { + newNode = new Vector2Node() as T; + } + else if (typeof(T) == typeof(Vector3Node)) + { + newNode = new Vector3Node() as T; + } + else if (typeof(T) == typeof(Vector4Node)) + { + newNode = new Vector4Node() as T; + } + else if (typeof(T) == typeof(ColorNode)) + { + newNode = new ColorNode() as T; + } + else if (typeof(T) == typeof(QuaternionNode)) + { + newNode = new QuaternionNode() as T; + } + else if (typeof(T) == typeof(Matrix3x2Node)) + { + newNode = new Matrix3x2Node() as T; + } + else if (typeof(T) == typeof(Matrix4x4Node)) + { + newNode = new Matrix4x4Node() as T; + } + else + { + throw new Exception("unexpected type"); + } + + return newNode; + } + + /// + /// Creates the value keyword. + /// + /// A class that derives from ExpressionNode + /// Kind of the keyword. + /// T. + /// Invalid ValueKeywordKind + internal static T CreateValueKeyword(ValueKeywordKind keywordKind) + where T : ExpressionNode + { + T node = CreateExpressionNode(); + + (node as ExpressionNode).ParamName = null; + + switch (keywordKind) + { + case ValueKeywordKind.CurrentValue: + (node as ExpressionNode).NodeType = ExpressionNodeType.CurrentValueProperty; + break; + + case ValueKeywordKind.StartingValue: + (node as ExpressionNode).NodeType = ExpressionNodeType.StartingValueProperty; + break; + + default: + throw new Exception("Invalid ValueKeywordKind"); + } + + return node; + } + + /// + /// To the expression string. + /// + /// System.String. + internal string ToExpressionString() + { + if (_objRefList == null) + { + EnsureReferenceInfo(); + } + + return ToExpressionStringInternal(); + } + + /// + /// Clears the reference information. + /// + /// Reference and paramName can't both be null + internal void ClearReferenceInfo() + { + _objRefList = null; + ParamName = null; + foreach (var child in Children) + { + child.ClearReferenceInfo(); + } + } + + /// + /// Ensures the reference information. + /// + /// Reference and paramName can't both be null + internal void EnsureReferenceInfo() + { + if (_objRefList == null) + { + // Get all ReferenceNodes in this expression + HashSet referenceNodes = new HashSet(); + PopulateParameterNodes(ref _constParamMap, ref referenceNodes); + + // Find all CompositionObjects across all referenceNodes that need a paramName to be created + HashSet compObjects = new HashSet(); + foreach (var refNode in referenceNodes) + { + if ((refNode.Reference != null) && (refNode.GetReferenceParamString() == null)) + { + compObjects.Add(refNode.Reference); + } + } + + // Create a map to store the generated paramNames for each CompObj + _compObjToParamNameMap = new Dictionary(); + var paramCount = 0u; + foreach (var compObj in compObjects) + { + string paramName = CreateUniqueParamNameFromIndex(paramCount++); + + _compObjToParamNameMap.Add(compObj, paramName); + } + + // Go through all reference nodes again to create our full list of referenceInfo. This time, if + // the param name is null, look it up from our new map and store it. + _objRefList = new List(); + foreach (var refNode in referenceNodes) + { + string paramName = refNode.GetReferenceParamString(); + + if ((refNode.Reference == null) && (paramName == null)) + { + // This can't happen - if the ref is null it must be because it's a named param + throw new Exception("Reference and paramName can't both be null"); + } + + if (paramName == null) + { + paramName = _compObjToParamNameMap[refNode.Reference]; + } + + _objRefList.Add(new ReferenceInfo(paramName, refNode.Reference)); + refNode.ParamName = paramName; + } + } + + // Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, BA... + // This implementation aggregates characters in reverse order to avoid having to + // precompute the exact number of characters in the resulting string. This is not + // important in this context as the only critical property to maintain is to have + // a unique mapping to each input value to the resulting sequence of letters. + //[System.Runtime.CompilerServices.SkipLocalsInit] + static unsafe string CreateUniqueParamNameFromIndex(uint i) + { + const int alphabetLength = 'Z' - 'A' + 1; + + // The total length of the resulting sequence is guaranteed to always + // be less than 8, given that log26(4294967295) ≈ 6.8. In this case we + // are just allocating the immediate next power of two following that. + // Note: this is using a char* buffer instead of Span as the latter + // is not referenced here, and we don't want to pull in an extra package. + char* characters = stackalloc char[8]; + + characters[0] = (char)('A' + (i % alphabetLength)); + + int totalCharacters = 1; + + while ((i /= alphabetLength) > 0) + { + i--; + + characters[totalCharacters++] = (char)('A' + (i % alphabetLength)); + } + + return new string(characters, 0, totalCharacters); + } + } + + /// + /// Sets all parameters. + /// + /// The animation. + internal void SetAllParameters(CompositionAnimation animation) + { + // Make sure the list is populated + EnsureReferenceInfo(); + + foreach (var refInfo in _objRefList) + { + animation.SetReferenceParameter(refInfo.ParameterName, refInfo.CompObject); + } + + foreach (var constParam in _constParamMap) + { + if (constParam.Value.GetType() == typeof(bool)) + { + animation.SetBooleanParameter(constParam.Key, (bool)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(float)) + { + animation.SetScalarParameter(constParam.Key, (float)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Vector2)) + { + animation.SetVector2Parameter(constParam.Key, (Vector2)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Vector3)) + { + animation.SetVector3Parameter(constParam.Key, (Vector3)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Vector4)) + { + animation.SetVector4Parameter(constParam.Key, (Vector4)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Color)) + { + animation.SetColorParameter(constParam.Key, (Color)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Quaternion)) + { + animation.SetQuaternionParameter(constParam.Key, (Quaternion)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Matrix3x2)) + { + animation.SetMatrix3x2Parameter(constParam.Key, (Matrix3x2)constParam.Value); + } + else if (constParam.Value.GetType() == typeof(Matrix4x4)) + { + animation.SetMatrix4x4Parameter(constParam.Key, (Matrix4x4)constParam.Value); + } + else + { + throw new Exception($"Unexpected constant parameter datatype ({constParam.Value.GetType()})"); + } + } + } + + /// + /// Gets the value. + /// + /// System.String. + protected internal abstract string GetValue(); + + /// + /// Subchannelses the internal. + /// + /// A class that derives from ExpressionNode. + /// The subchannels. + /// T. + protected internal T SubchannelsInternal(params string[] subchannels) + where T : ExpressionNode + { + ExpressionNodeType swizzleNodeType = ExpressionNodeType.Swizzle; + T newNode; + + switch (subchannels.GetLength(0)) + { + case 1: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + case 2: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + case 3: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + case 4: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + case 6: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + case 16: + newNode = ExpressionFunctions.Function(swizzleNodeType, this) as T; + break; + + default: + throw new Exception($"Invalid subchannel count ({subchannels.GetLength(0)})"); + } + + (newNode as ExpressionNode).Subchannels = subchannels; + + return newNode; + } + + /// + /// Populates the parameter nodes. + /// + /// The constant parameter map. + /// The reference nodes. + protected internal void PopulateParameterNodes(ref Dictionary constParamMap, ref HashSet referenceNodes) + { + var refNode = this as ReferenceNode; + if ((refNode != null) && (refNode.NodeType != ExpressionNodeType.TargetReference)) + { + referenceNodes.Add(refNode); + } + + if ((_constParamMap != null) && (_constParamMap != constParamMap)) + { + foreach (var entry in _constParamMap) + { + // If this parameter hasn't already been set on the root, use this node's parameter info + if (!constParamMap.ContainsKey(entry.Key)) + { + constParamMap[entry.Key] = entry.Value; + } + } + } + + foreach (var child in Children) + { + child.PopulateParameterNodes(ref constParamMap, ref referenceNodes); + } + } + + private OperationType GetOperationKind() + { + return ExpressionFunctions.GetNodeInfoFromType(NodeType).NodeOperationKind; + } + + private string GetOperationString() + { + return ExpressionFunctions.GetNodeInfoFromType(NodeType).OperationString; + } + + private string ToExpressionStringInternal() + { + string ret; + + // Do a recursive depth-first traversal of the node tree to print out the full expression string + switch (GetOperationKind()) + { + case OperationType.Function: + if (Children.Count == 0) + { + throw new Exception("Can't have an expression function with no params"); + } + + ret = $"{GetOperationString()}({Children[0].ToExpressionStringInternal()}"; + for (int i = 1; i < Children.Count; i++) + { + ret += "," + Children[i].ToExpressionStringInternal(); + } + + ret += ")"; + break; + + case OperationType.Operator: + if (Children.Count != 2) + { + throw new Exception("Can't have an operator that doesn't have 2 exactly params"); + } + + ret = $"({Children[0].ToExpressionStringInternal()} {GetOperationString()} {Children[1].ToExpressionStringInternal()})"; + break; + + case OperationType.UnaryOperator: + if (Children.Count != 1) + { + throw new Exception("Can't have an unary operator that doesn't have exactly one params"); + } + + ret = $"( {GetOperationString()} {Children[0].ToExpressionStringInternal()} )"; + break; + + case OperationType.Constant: + if (Children.Count == 0) + { + // If a parameterName was specified, use it. Otherwise write the value. + ret = ParamName ?? GetValue(); + } + else + { + throw new Exception("Constants must have 0 children"); + } + + break; + + case OperationType.Swizzle: + if (Children.Count != 1) + { + throw new Exception("Swizzles should have exactly 1 child"); + } + + string swizzleString = string.Empty; + foreach (var sub in Subchannels) + { + swizzleString += sub; + } + + ret = $"{Children[0].ToExpressionStringInternal()}.{swizzleString}"; + break; + + case OperationType.Reference: + if ((NodeType == ExpressionNodeType.Reference) || + (NodeType == ExpressionNodeType.TargetReference)) + { + // This is the reference node itself + if (Children.Count != 0) + { + throw new Exception("References cannot have children"); + } + + ret = (this as ReferenceNode).GetReferenceParamString(); + } + else if (NodeType == ExpressionNodeType.ReferenceProperty) + { + // This is the property node of the reference + if (Children.Count != 1) + { + throw new Exception("Reference properties must have exactly one child"); + } + + if (PropertyName == null) + { + throw new Exception("Reference properties must have a property name"); + } + + ret = $"{Children[0].ToExpressionStringInternal()}.{PropertyName}"; + } + else if (NodeType == ExpressionNodeType.StartingValueProperty) + { + // This is a "this.StartingValue" node + if (Children.Count != 0) + { + throw new Exception("StartingValue references Cannot have children"); + } + + ret = "this.StartingValue"; + } + else if (NodeType == ExpressionNodeType.CurrentValueProperty) + { + // This is a "this.CurrentValue" node + if (Children.Count != 0) + { + throw new Exception("CurrentValue references Cannot have children"); + } + + ret = "this.CurrentValue"; + } + else + { + throw new Exception("Unexpected NodeType for OperationType.Reference"); + } + + break; + + case OperationType.Conditional: + if (Children.Count != 3) + { + throw new Exception("Conditionals must have exactly 3 children"); + } + + ret = $"(({Children[0].ToExpressionStringInternal()}) ? ({Children[1].ToExpressionStringInternal()}) : ({Children[2].ToExpressionStringInternal()}))"; + break; + + default: + throw new Exception($"Unexpected operation type ({GetOperationKind()}), nodeType = {NodeType}"); + } + + return ret; + } + + /// + /// Struct ReferenceInfo + /// + internal struct ReferenceInfo + { + /// + /// Initializes a new instance of the struct. + /// + /// Name of the parameter. + /// The comp object. + public ReferenceInfo(string paramName, CompositionObject compObj) + { + ParameterName = paramName; + CompObject = compObj; + } + + /// + /// Gets or sets the name of the parameter. + /// + /// The name of the parameter. + public string ParameterName { get; set; } + + /// + /// Gets or sets the comp object. + /// + /// The comp object. + public CompositionObject CompObject { get; set; } + } + + /// + /// Gets or sets the name of the property. + /// + /// The name of the property. + internal string PropertyName { get; set; } + + /// + /// Gets or sets the type of the node. + /// + /// The type of the node. + internal ExpressionNodeType NodeType { get; set; } + + /// + /// Gets or sets the children. + /// + /// The children. + internal List Children { get; set; } = new List(); + + /// + /// Gets or sets the name of the parameter. + /// + /// The name of the parameter. + internal string ParamName { get; set; } + + /// + /// Gets or sets the expression animation. + /// + /// The expression animation. + internal ExpressionAnimation ExpressionAnimation { get; set; } + + /// + /// Gets or sets the subchannels. + /// + /// The subchannels. + protected internal string[] Subchannels { get; set; } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNodeType.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNodeType.cs new file mode 100644 index 000000000..84d2ee4a2 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ExpressionNodeType.cs @@ -0,0 +1,387 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Enum ExpressionNodeType + /// + internal enum ExpressionNodeType + { + /// + /// The constant value + /// + ConstantValue, + + /// + /// The constant parameter + /// + ConstantParameter, + + /// + /// The current value property + /// + CurrentValueProperty, + + /// + /// The reference + /// + Reference, + + /// + /// The reference property + /// + ReferenceProperty, + + /// + /// The starting value property + /// + StartingValueProperty, + + /// + /// The target reference + /// + TargetReference, + + /// + /// The conditional + /// + Conditional, + + /// + /// The swizzle + /// + Swizzle, + + /// + /// The add + /// + Add, + + /// + /// The and + /// + And, + + /// + /// The divide + /// + Divide, + + /// + /// The equals + /// + Equals, + + /// + /// The greater than + /// + GreaterThan, + + /// + /// The greater than equals + /// + GreaterThanEquals, + + /// + /// The less than + /// + LessThan, + + /// + /// The less than equals + /// + LessThanEquals, + + /// + /// The multiply + /// + Multiply, + + /// + /// The not + /// + Not, + + /// + /// The not equals + /// + NotEquals, + + /// + /// The or + /// + Or, + + /// + /// The subtract + /// + Subtract, + + /// + /// The absolute + /// + Absolute, + + /// + /// The acos + /// + Acos, + + /// + /// The asin + /// + Asin, + + /// + /// The atan + /// + Atan, + + /// + /// The cos + /// + Cos, + + /// + /// The ceil + /// + Ceil, + + /// + /// The clamp + /// + Clamp, + + /// + /// The color HSL + /// + ColorHsl, + + /// + /// The color RGB + /// + ColorRgb, + + /// + /// The color lerp + /// + ColorLerp, + + /// + /// The color lerp HSL + /// + ColorLerpHsl, + + /// + /// The color lerp RGB + /// + ColorLerpRgb, + + /// + /// The concatenate + /// + Concatenate, + + /// + /// The distance + /// + Distance, + + /// + /// The distance squared + /// + DistanceSquared, + + /// + /// The floor + /// + Floor, + + /// + /// The inverse + /// + Inverse, + + /// + /// The length + /// + Length, + + /// + /// The length squared + /// + LengthSquared, + + /// + /// The lerp + /// + Lerp, + + /// + /// The ln + /// + Ln, + + /// + /// The log10 + /// + Log10, + + /// + /// The maximum + /// + Max, + + /// + /// The matrix3x2 from rotation + /// + Matrix3x2FromRotation, + + /// + /// The matrix3x2 from scale + /// + Matrix3x2FromScale, + + /// + /// The matrix3x2 from skew + /// + Matrix3x2FromSkew, + + /// + /// The matrix3x2 from translation + /// + Matrix3x2FromTranslation, + + /// + /// The matrix3x2 + /// + Matrix3x2, + + /// + /// The matrix4x4 from axis angle + /// + Matrix4x4FromAxisAngle, + + /// + /// The matrix4x4 from scale + /// + Matrix4x4FromScale, + + /// + /// The matrix4x4 from translation + /// + Matrix4x4FromTranslation, + + /// + /// The matrix4x4 + /// + Matrix4x4, + + /// + /// The minimum + /// + Min, + + /// + /// The modulus + /// + Modulus, + + /// + /// The negate + /// + Negate, + + /// + /// The normalize + /// + Normalize, + + /// + /// The pow + /// + Pow, + + /// + /// The quaternion from axis angle + /// + QuaternionFromAxisAngle, + + /// + /// The quaternion + /// + Quaternion, + + /// + /// The round + /// + Round, + + /// + /// The scale + /// + Scale, + + /// + /// The sin + /// + Sin, + + /// + /// The slerp + /// + Slerp, + + /// + /// The SQRT + /// + Sqrt, + + /// + /// The square + /// + Square, + + /// + /// The tan + /// + Tan, + + /// + /// To degrees + /// + ToDegrees, + + /// + /// To radians + /// + ToRadians, + + /// + /// The transform + /// + Transform, + + /// + /// The vector2 + /// + Vector2, + + /// + /// The vector3 + /// + Vector3, + + /// + /// The vector4 + /// + Vector4, + + /// + /// The count + /// + Count + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix3x2Node.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix3x2Node.cs new file mode 100644 index 000000000..7e6356a53 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix3x2Node.cs @@ -0,0 +1,394 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; +using System.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'Matrix3x2Node' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + + /// + /// Class Matrix3x2Node. This class cannot be inherited. + /// + /// + public sealed class Matrix3x2Node : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal Matrix3x2Node() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal Matrix3x2Node(Matrix3x2 value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal Matrix3x2Node(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal Matrix3x2Node(string paramName, Matrix3x2 value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetMatrix3x2Parameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Matrix3x2Node(Matrix3x2 value) + { + return new Matrix3x2Node(value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix3x2Node operator +(Matrix3x2Node left, Matrix3x2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix3x2Node operator -(Matrix3x2Node left, Matrix3x2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static Matrix3x2Node operator -(Matrix3x2Node value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix3x2Node operator *(Matrix3x2Node left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix3x2Node operator *(Matrix3x2Node left, Matrix3x2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(Matrix3x2Node left, Matrix3x2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(Matrix3x2Node left, Matrix3x2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Enum Subchannel + /// + public enum Subchannel + { + /// + /// The channel11 + /// + Channel11, + + /// + /// The channel12 + /// + Channel12, + + /// + /// The channel21 + /// + Channel21, + + /// + /// The channel22 + /// + Channel22, + + /// + /// The channel31 + /// + Channel31, + + /// + /// The channel32 + /// + Channel32, + } + + /// + /// Gets the channel11. + /// + /// The channel11. + public ScalarNode Channel11 + { + get { return GetSubchannels(Subchannel.Channel11); } + } + + /// + /// Gets the channel12. + /// + /// The channel12. + public ScalarNode Channel12 + { + get { return GetSubchannels(Subchannel.Channel12); } + } + + /// + /// Gets the channel21. + /// + /// The channel21. + public ScalarNode Channel21 + { + get { return GetSubchannels(Subchannel.Channel21); } + } + + /// + /// Gets the channel22. + /// + /// The channel22. + public ScalarNode Channel22 + { + get { return GetSubchannels(Subchannel.Channel22); } + } + + /// + /// Gets the channel31. + /// + /// The channel31. + public ScalarNode Channel31 + { + get { return GetSubchannels(Subchannel.Channel31); } + } + + /// + /// Gets the channel32. + /// + /// The channel32. + public ScalarNode Channel32 + { + get { return GetSubchannels(Subchannel.Channel32); } + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The subchannel. + /// ScalarNode + public ScalarNode GetSubchannels(Subchannel s) + { + return SubchannelsInternal(s.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// Vector2Node + public Vector2Node GetSubchannels(Subchannel s1, Subchannel s2) + { + return SubchannelsInternal(s1.ToString(), s2.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// Vector3Node + public Vector3Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// Vector4Node + public Vector4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// Matrix3x2Node + public Matrix3x2Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, Subchannel s5, Subchannel s6) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), s5.ToString(), s6.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// The seventh subchannel. + /// The eighth subchannel. + /// The ninth subchannel. + /// The tenth subchannel. + /// The eleventh subchannel. + /// The twelfth subchannel. + /// The thirteenth subchannel. + /// The fourteenth subchannel. + /// The fifteenth subchannel. + /// The sixteenth subchannel. + /// Matrix4x4Node +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public Matrix4x4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, + Subchannel s5, Subchannel s6, Subchannel s7, Subchannel s8, + Subchannel s9, Subchannel s10, Subchannel s11, Subchannel s12, + Subchannel s13, Subchannel s14, Subchannel s15, Subchannel s16) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), + s5.ToString(), s6.ToString(), s7.ToString(), s8.ToString(), + s9.ToString(), s10.ToString(), s11.ToString(), s12.ToString(), + s13.ToString(), s14.ToString(), s15.ToString(), s16.ToString()); + } +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Matrix3x2({_value.M11.ToCompositionString()},{_value.M12.ToCompositionString()},{_value.M21.ToCompositionString()},{_value.M22.ToCompositionString()},{_value.M31.ToCompositionString()},{_value.M32.ToCompositionString()})"; + } + + 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($"Operation ${NodeType} not implemented for Matrix3x2Node"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.Subchannel.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.Subchannel.cs new file mode 100644 index 000000000..e2f41954d --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.Subchannel.cs @@ -0,0 +1,99 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class Matrix4x4Node. This class cannot be inherited. + /// + /// + public sealed partial class Matrix4x4Node : ExpressionNode + { + /// + /// Enum Subchannel + /// + public enum Subchannel + { + /// + /// The channel11 + /// + Channel11, + + /// + /// The channel12 + /// + Channel12, + + /// + /// The channel13 + /// + Channel13, + + /// + /// The channel14 + /// + Channel14, + + /// + /// The channel21 + /// + Channel21, + + /// + /// The channel22 + /// + Channel22, + + /// + /// The channel23 + /// + Channel23, + + /// + /// The channel24 + /// + Channel24, + + /// + /// The channel31 + /// + Channel31, + + /// + /// The channel32 + /// + Channel32, + + /// + /// The channel33 + /// + Channel33, + + /// + /// The channel34 + /// + Channel34, + + /// + /// The channel41 + /// + Channel41, + + /// + /// The channel42 + /// + Channel42, + + /// + /// The channel43 + /// + Channel43, + + /// + /// The channel44 + /// + Channel44, + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.cs new file mode 100644 index 000000000..191602f27 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Matrix4x4Node.cs @@ -0,0 +1,487 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'Matrix4x4Node' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class Matrix4x4Node. This class cannot be inherited. + /// + /// + public sealed partial class Matrix4x4Node : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal Matrix4x4Node() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal Matrix4x4Node(Matrix4x4 value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal Matrix4x4Node(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal Matrix4x4Node(string paramName, Matrix4x4 value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetMatrix4x4Parameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Matrix4x4Node(Matrix4x4 value) + { + return new Matrix4x4Node(value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix4x4Node operator +(Matrix4x4Node left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix4x4Node operator -(Matrix4x4Node left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static Matrix4x4Node operator -(Matrix4x4Node value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix4x4Node operator *(Matrix4x4Node left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix4x4Node operator *(Matrix4x4Node left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(Matrix4x4Node left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(Matrix4x4Node left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Gets the channel11. + /// + /// The channel11. + public ScalarNode Channel11 + { + get { return GetSubchannels(Subchannel.Channel11); } + } + + /// + /// Gets the channel12. + /// + /// The channel12. + public ScalarNode Channel12 + { + get { return GetSubchannels(Subchannel.Channel12); } + } + + /// + /// Gets the channel13. + /// + /// The channel13. + public ScalarNode Channel13 + { + get { return GetSubchannels(Subchannel.Channel13); } + } + + /// + /// Gets the channel14. + /// + /// The channel14. + public ScalarNode Channel14 + { + get { return GetSubchannels(Subchannel.Channel14); } + } + + /// + /// Gets the channel21. + /// + /// The channel21. + public ScalarNode Channel21 + { + get { return GetSubchannels(Subchannel.Channel21); } + } + + /// + /// Gets the channel22. + /// + /// The channel22. + public ScalarNode Channel22 + { + get { return GetSubchannels(Subchannel.Channel22); } + } + + /// + /// Gets the channel23. + /// + /// The channel23. + public ScalarNode Channel23 + { + get { return GetSubchannels(Subchannel.Channel23); } + } + + /// + /// Gets the channel24. + /// + /// The channel24. + public ScalarNode Channel24 + { + get { return GetSubchannels(Subchannel.Channel24); } + } + + /// + /// Gets the channel31. + /// + /// The channel31. + public ScalarNode Channel31 + { + get { return GetSubchannels(Subchannel.Channel31); } + } + + /// + /// Gets the channel32. + /// + /// The channel32. + public ScalarNode Channel32 + { + get { return GetSubchannels(Subchannel.Channel32); } + } + + /// + /// Gets the channel33. + /// + /// The channel33. + public ScalarNode Channel33 + { + get { return GetSubchannels(Subchannel.Channel33); } + } + + /// + /// Gets the channel34. + /// + /// The channel34. + public ScalarNode Channel34 + { + get { return GetSubchannels(Subchannel.Channel34); } + } + + /// + /// Gets the channel41. + /// + /// The channel41. + public ScalarNode Channel41 + { + get { return GetSubchannels(Subchannel.Channel41); } + } + + /// + /// Gets the channel42. + /// + /// The channel42. + public ScalarNode Channel42 + { + get { return GetSubchannels(Subchannel.Channel42); } + } + + /// + /// Gets the channel43. + /// + /// The channel43. + public ScalarNode Channel43 + { + get { return GetSubchannels(Subchannel.Channel43); } + } + + /// + /// Gets the channel44. + /// + /// The channel44. + public ScalarNode Channel44 + { + get { return GetSubchannels(Subchannel.Channel44); } + } + + /// + /// Gets the channel11 channel22 channel33. + /// + /// The channel11 channel22 channel33. + public Vector3Node Channel11Channel22Channel33 + { + get { return GetSubchannels(Subchannel.Channel11, Subchannel.Channel22, Subchannel.Channel33); } + } + + /// + /// Gets the channel41 channel42 channel43. + /// + /// The channel41 channel42 channel43. + public Vector3Node Channel41Channel42Channel43 + { + get { return GetSubchannels(Subchannel.Channel41, Subchannel.Channel42, Subchannel.Channel43); } + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The s. + /// ScalarNode. + public ScalarNode GetSubchannels(Subchannel s) + { + return SubchannelsInternal(s.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// Vector2Node + public Vector2Node GetSubchannels(Subchannel s1, Subchannel s2) + { + return SubchannelsInternal(s1.ToString(), s2.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// Vector3Node + public Vector3Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// Vector4Node + public Vector4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// Matrix3x2Node + public Matrix3x2Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, Subchannel s5, Subchannel s6) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), s5.ToString(), s6.ToString()); + } + + /// + /// Create a new type by re-arranging the Matrix subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// The seventh subchannel. + /// The eighth subchannel. + /// The ninth subchannel. + /// The tenth subchannel. + /// The eleventh subchannel. + /// The twelfth subchannel. + /// The thirteenth subchannel. + /// The fourteenth subchannel. + /// The fifteenth subchannel. + /// The sixteenth subchannel. + /// Matrix4x4Node +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public Matrix4x4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, + Subchannel s5, Subchannel s6, Subchannel s7, Subchannel s8, + Subchannel s9, Subchannel s10, Subchannel s11, Subchannel s12, + Subchannel s13, Subchannel s14, Subchannel s15, Subchannel s16) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), + s5.ToString(), s6.ToString(), s7.ToString(), s8.ToString(), + s9.ToString(), s10.ToString(), s11.ToString(), s12.ToString(), + s13.ToString(), s14.ToString(), s15.ToString(), s16.ToString()); + } +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Matrix4x4({_value.M11.ToCompositionString()},{_value.M12.ToCompositionString()},{_value.M13.ToCompositionString()},{_value.M14.ToCompositionString()}," + + $"{_value.M21.ToCompositionString()},{_value.M22.ToCompositionString()},{_value.M23.ToCompositionString()},{_value.M24.ToCompositionString()}," + + $"{_value.M31.ToCompositionString()},{_value.M32.ToCompositionString()},{_value.M33.ToCompositionString()},{_value.M34.ToCompositionString()}," + + $"{_value.M41.ToCompositionString()},{_value.M42.ToCompositionString()},{_value.M43.ToCompositionString()},{_value.M44.ToCompositionString()})"; + } + + 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($"Operation ${NodeType} not implemented for Matrix4x4Node"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/QuaternionNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/QuaternionNode.cs new file mode 100644 index 000000000..c15a118ab --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/QuaternionNode.cs @@ -0,0 +1,198 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; +using System.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'QuaternionNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class QuaternionNode. This class cannot be inherited. + /// + /// + public sealed class QuaternionNode : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal QuaternionNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal QuaternionNode(Quaternion value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal QuaternionNode(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal QuaternionNode(string paramName, Quaternion value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetQuaternionParameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator QuaternionNode(Quaternion value) + { + return new QuaternionNode(value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static QuaternionNode operator *(QuaternionNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static QuaternionNode operator *(QuaternionNode left, QuaternionNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the / operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static QuaternionNode operator /(QuaternionNode left, QuaternionNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Divide, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(QuaternionNode left, QuaternionNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(QuaternionNode left, QuaternionNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Quaternion({_value.X.ToCompositionString()},{_value.Y.ToCompositionString()},{_value.Z.ToCompositionString()},{_value.W.ToCompositionString()})"; + } + + 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($"Operation ${NodeType} not implemented for QuaternionNode"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ScalarNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ScalarNode.cs new file mode 100644 index 000000000..f0d736286 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ScalarNode.cs @@ -0,0 +1,394 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; +using System.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'ScalarNode' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class ScalarNode. This class cannot be inherited. + /// + /// + public sealed class ScalarNode : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal ScalarNode() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal ScalarNode(float value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal ScalarNode(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal ScalarNode(string paramName, float value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetScalarParameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator ScalarNode(float value) + { + return new ScalarNode(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator ScalarNode(int value) + { + return new ScalarNode((float)value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static ScalarNode operator +(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static ScalarNode operator -(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static ScalarNode operator -(ScalarNode value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static ScalarNode operator *(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator *(ScalarNode left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator *(ScalarNode left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator *(ScalarNode left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Matrix4x4Node operator *(ScalarNode left, Matrix4x4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the / operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static ScalarNode operator /(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Divide, left, right); + } + + /// + /// Implements the % operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static ScalarNode operator %(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Modulus, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Implements the <= operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator <=(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.LessThanEquals, left, right); + } + + /// + /// Implements the < operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator <(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.LessThan, left, right); + } + + /// + /// Implements the >= operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator >=(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.GreaterThanEquals, left, right); + } + + /// + /// Implements the > operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator >(ScalarNode left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.GreaterThan, left, right); + } + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return _value.ToCompositionString(); + } + + 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 (float)Math.Min((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Max: + return (float)Math.Max((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Absolute: + return (float)Math.Abs((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Sin: + return (float)Math.Sin((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Cos: + return (float)Math.Cos((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Asin: + return (float)Math.Asin((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Acos: + return (float)Math.Acos((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Atan: + return (float)Math.Atan((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Ceil: + return (float)Math.Ceiling((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Floor: + return (float)Math.Floor((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Ln: + return (float)Math.Log((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Log10: + return (float)Math.Log10((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Pow: + return (float)Math.Pow((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()); + case ExpressionNodeType.Round: + return (float)Math.Round((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.Square: + return (float)Math.Pow((Children[0] as ScalarNode).Evaluate(), 2); + case ExpressionNodeType.Sqrt: + return (float)Math.Sqrt((Children[0] as ScalarNode).Evaluate()); + case ExpressionNodeType.ToDegrees: + return 180 * (Children[0] as ScalarNode).Evaluate() / (float)Math.PI; + case ExpressionNodeType.ToRadians: + return (float)Math.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.Clamp: + return (float)Math.Min(Math.Max((Children[0] as ScalarNode).Evaluate(), (Children[1] as ScalarNode).Evaluate()), (Children[2] as ScalarNode).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.Length: + return Children[0] switch + { + Vector2Node v2 => v2.Evaluate().Length(), + Vector3Node v3 => v3.Evaluate().Length(), + Vector4Node v4 => v4.Evaluate().Length(), + _ => throw new NotImplementedException() + }; + 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($"Operation ${NodeType} not implemented for ScalarNode"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ValueKeywordKind.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ValueKeywordKind.cs new file mode 100644 index 000000000..c13fd8b98 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/ValueKeywordKind.cs @@ -0,0 +1,22 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Enum ValueKeywordKind + /// + internal enum ValueKeywordKind + { + /// + /// The current value + /// + CurrentValue, + + /// + /// The starting value + /// + StartingValue, + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector2Node.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector2Node.cs new file mode 100644 index 000000000..317ccb6ac --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector2Node.cs @@ -0,0 +1,370 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; +using System.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'Vector2Node' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class Vector2Node. This class cannot be inherited. + /// + /// + public sealed class Vector2Node : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal Vector2Node() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal Vector2Node(Vector2 value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal Vector2Node(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal Vector2Node(string paramName, Vector2 value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetVector2Parameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector2Node(Vector2 value) + { + return new Vector2Node(value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator +(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator -(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static Vector2Node operator -(Vector2Node value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator *(Vector2Node left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator *(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the / operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator /(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Divide, left, right); + } + + /// + /// Implements the % operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector2Node operator %(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Modulus, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(Vector2Node left, Vector2Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Enum Subchannel + /// + public enum Subchannel + { + /// + /// The x channel + /// + X, + + /// + /// The y channel + /// + Y + } + + /// + /// Gets the x subchannel. + /// + /// The x subchannel. + public ScalarNode X + { + get { return GetSubchannels(Subchannel.X); } + } + + /// + /// Gets the y subchannel. + /// + /// The y subchannel. + public ScalarNode Y + { + get { return GetSubchannels(Subchannel.Y); } + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The subchannel + /// ScalarNode + public ScalarNode GetSubchannels(Subchannel s) + { + return SubchannelsInternal(s.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// Vector2Node. + public Vector2Node GetSubchannels(Subchannel s1, Subchannel s2) + { + return SubchannelsInternal(s1.ToString(), s2.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// Vector3Node + public Vector3Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// Vector4Node + public Vector4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// Matrix3x2Node + public Matrix3x2Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, Subchannel s5, Subchannel s6) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), s5.ToString(), s6.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// The seventh subchannel. + /// The eighth subchannel. + /// The ninth subchannel. + /// The tenth subchannel. + /// The eleventh subchannel. + /// The twelfth subchannel. + /// The thirteenth subchannel. + /// The fourteenth subchannel. + /// The fifteenth subchannel. + /// The sixteenth subchannel. + /// Matrix4x4Node +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public Matrix4x4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, + Subchannel s5, Subchannel s6, Subchannel s7, Subchannel s8, + Subchannel s9, Subchannel s10, Subchannel s11, Subchannel s12, + Subchannel s13, Subchannel s14, Subchannel s15, Subchannel s16) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), + s5.ToString(), s6.ToString(), s7.ToString(), s8.ToString(), + s9.ToString(), s10.ToString(), s11.ToString(), s12.ToString(), + s13.ToString(), s14.ToString(), s15.ToString(), s16.ToString()); + } +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Vector2({_value.X.ToCompositionString()},{_value.Y.ToCompositionString()})"; + } + + 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($"Operation ${NodeType} not implemented for Vector2Node"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector3Node.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector3Node.cs new file mode 100644 index 000000000..15d464d58 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector3Node.cs @@ -0,0 +1,399 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'Vector3Node' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class Vector3Node. This class cannot be inherited. + /// + /// + public sealed class Vector3Node : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal Vector3Node() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal Vector3Node(Vector3 value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal Vector3Node(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal Vector3Node(string paramName, Vector3 value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetVector3Parameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector3Node(Vector3 value) + { + return new Vector3Node(value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator +(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator -(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static Vector3Node operator -(Vector3Node value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator *(Vector3Node left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator *(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the / operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator /(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Divide, left, right); + } + + /// + /// Implements the % operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector3Node operator %(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Modulus, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(Vector3Node left, Vector3Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Enum Subchannel + /// + public enum Subchannel + { + /// + /// The x channel + /// + X, + + /// + /// The y channel + /// + Y, + + /// + /// The z channel + /// + Z + } + + /// + /// Gets the x channel. + /// + /// The x. + public ScalarNode X + { + get { return GetSubchannels(Subchannel.X); } + } + + /// + /// Gets the y channel. + /// + /// The y. + public ScalarNode Y + { + get { return GetSubchannels(Subchannel.Y); } + } + + /// + /// Gets the z channel. + /// + /// The z. + public ScalarNode Z + { + get { return GetSubchannels(Subchannel.Z); } + } + + /// + /// Gets the x and y channel. + /// + /// The xy. + public Vector2Node XY + { + get { return GetSubchannels(Subchannel.X, Subchannel.Y); } + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The subchannel. + /// ScalarNode + public ScalarNode GetSubchannels(Subchannel s) + { + return SubchannelsInternal(s.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// Vector2Node + public Vector2Node GetSubchannels(Subchannel s1, Subchannel s2) + { + return SubchannelsInternal(s1.ToString(), s2.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// Vector3Node + public Vector3Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// Vector4Node + public Vector4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// Matrix3x2Node + public Matrix3x2Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, Subchannel s5, Subchannel s6) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), s5.ToString(), s6.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// The seventh subchannel. + /// The eighth subchannel. + /// The ninth subchannel. + /// The tenth subchannel. + /// The eleventh subchannel. + /// The twelfth subchannel. + /// The thirteenth subchannel. + /// The fourteenth subchannel. + /// The fifteenth subchannel. + /// The sixteenth subchannel. + /// Matrix4x4Node +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public Matrix4x4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, + Subchannel s5, Subchannel s6, Subchannel s7, Subchannel s8, + Subchannel s9, Subchannel s10, Subchannel s11, Subchannel s12, + Subchannel s13, Subchannel s14, Subchannel s15, Subchannel s16) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), + s5.ToString(), s6.ToString(), s7.ToString(), s8.ToString(), + s9.ToString(), s10.ToString(), s11.ToString(), s12.ToString(), + s13.ToString(), s14.ToString(), s15.ToString(), s16.ToString()); + } +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Vector3({_value.X.ToCompositionString()},{_value.Y.ToCompositionString()},{_value.Z.ToCompositionString()})"; + } + + 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, + nameof(Visual.Scale) => (reference as Visual).Scale, + _ => 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: + return Vector3.Lerp((Children[0] as Vector3Node).Evaluate(), (Children[1] as Vector3Node).Evaluate(), (Children[2] as ScalarNode).Evaluate()); + 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($"Operation ${NodeType} not implemented for Vector3Node"); + } + } + } +#pragma warning restore CS0660, CS0661 +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector4Node.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector4Node.cs new file mode 100644 index 000000000..cb46d9577 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionNodes/Vector4Node.cs @@ -0,0 +1,421 @@ +#pragma warning disable CS8602 // Dereference of a possibly null reference. +// 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; +using System.Numerics; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // Ignore warning: 'Vector4Node' defines operator == or operator != but does not override Object.Equals(object o) && Object.GetHashCode() +#pragma warning disable CS0660, CS0661 + /// + /// Class Vector4Node. This class cannot be inherited. + /// + /// + public sealed class Vector4Node : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + internal Vector4Node() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + internal Vector4Node(Vector4 value) + { + _value = value; + NodeType = ExpressionNodeType.ConstantValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + internal Vector4Node(string paramName) + { + ParamName = paramName; + NodeType = ExpressionNodeType.ConstantParameter; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The value. + internal Vector4Node(string paramName, Vector4 value) + { + ParamName = paramName; + _value = value; + NodeType = ExpressionNodeType.ConstantParameter; + + SetVector4Parameter(paramName, value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static implicit operator Vector4Node(Vector4 value) + { + return new Vector4Node(value); + } + + /// + /// Implements the + operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator +(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Add, left, right); + } + + /// + /// Implements the - operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator -(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Subtract, left, right); + } + + /// + /// Implements the - operator. + /// + /// The value. + /// The result of the operator. + public static Vector4Node operator -(Vector4Node value) + { + return ExpressionFunctions.Function(ExpressionNodeType.Negate, value); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator *(Vector4Node left, ScalarNode right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the * operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator *(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Multiply, left, right); + } + + /// + /// Implements the / operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator /(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Divide, left, right); + } + + /// + /// Implements the % operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static Vector4Node operator %(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Modulus, left, right); + } + + /// + /// Implements the == operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator ==(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.Equals, left, right); + } + + /// + /// Implements the != operator. + /// + /// The left. + /// The right. + /// The result of the operator. + public static BooleanNode operator !=(Vector4Node left, Vector4Node right) + { + return ExpressionFunctions.Function(ExpressionNodeType.NotEquals, left, right); + } + + /// + /// Enum Subchannel + /// + public enum Subchannel + { + /// + /// The x channel + /// + X, + + /// + /// The y channel + /// + Y, + + /// + /// The z channel + /// + Z, + + /// + /// The w channel + /// + W + } + + /// + /// Gets the x channel. + /// + /// The x. + public ScalarNode X + { + get { return GetSubchannels(Subchannel.X); } + } + + /// + /// Gets the y channel. + /// + /// The y. + public ScalarNode Y + { + get { return GetSubchannels(Subchannel.Y); } + } + + /// + /// Gets the z channel. + /// + /// The z. + public ScalarNode Z + { + get { return GetSubchannels(Subchannel.Z); } + } + + /// + /// Gets the w channel. + /// + /// The w. + public ScalarNode W + { + get { return GetSubchannels(Subchannel.W); } + } + + /// + /// Gets the x and y channels. + /// + /// The xy. + public Vector2Node XY + { + get { return GetSubchannels(Subchannel.X, Subchannel.Y); } + } + + /// + /// Gets the x, y, and z channels. + /// + /// The xyz. + public Vector3Node XYZ + { + get { return GetSubchannels(Subchannel.X, Subchannel.Y, Subchannel.Z); } + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The subchannel. + /// ScalarNode + public ScalarNode GetSubchannels(Subchannel s) + { + return SubchannelsInternal(s.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// Vector2Node + public Vector2Node GetSubchannels(Subchannel s1, Subchannel s2) + { + return SubchannelsInternal(s1.ToString(), s2.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// Vector3Node + public Vector3Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// Vector4Node + public Vector4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// Matrix3x2Node + public Matrix3x2Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, Subchannel s5, Subchannel s6) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), s5.ToString(), s6.ToString()); + } + + /// + /// Create a new type by re-arranging the Vector subchannels. + /// + /// The first subchannel. + /// The second subchannel. + /// The third subchannel. + /// The fourth subchannel. + /// The fifth subchannel. + /// The sixth subchannel. + /// The seventh subchannel. + /// The eighth subchannel. + /// The ninth subchannel. + /// The tenth subchannel. + /// The eleventh subchannel. + /// The twelfth subchannel. + /// The thirteenth subchannel. + /// The fourteenth subchannel. + /// The fifteenth subchannel. + /// The sixteenth subchannel. + /// Matrix4x4Node +#pragma warning disable SA1117 // Parameters must be on same line or separate lines + public Matrix4x4Node GetSubchannels(Subchannel s1, Subchannel s2, Subchannel s3, Subchannel s4, + Subchannel s5, Subchannel s6, Subchannel s7, Subchannel s8, + Subchannel s9, Subchannel s10, Subchannel s11, Subchannel s12, + Subchannel s13, Subchannel s14, Subchannel s15, Subchannel s16) + { + return SubchannelsInternal(s1.ToString(), s2.ToString(), s3.ToString(), s4.ToString(), + s5.ToString(), s6.ToString(), s7.ToString(), s8.ToString(), + s9.ToString(), s10.ToString(), s11.ToString(), s12.ToString(), + s13.ToString(), s14.ToString(), s15.ToString(), s16.ToString()); + } +#pragma warning restore SA1117 // Parameters must be on same line or separate lines + + /// + /// Gets the value. + /// + /// System.String. + protected internal override string GetValue() + { + return $"Vector4({_value.X.ToCompositionString()},{_value.Y.ToCompositionString()},{_value.Z.ToCompositionString()},{_value.W.ToCompositionString()})"; + } + + 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.Transform: + return + Vector4.Transform((Children[0] as Vector4Node).Evaluate(), (Children[1] as Matrix4x4Node).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.Vector4: + 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($"Operation ${NodeType} not implemented for Vector4Node"); + } + } + } +#pragma warning restore CS0660, CS0661 +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Constant.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Constant.cs new file mode 100644 index 000000000..2522bd484 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Constant.cs @@ -0,0 +1,216 @@ +// 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.Numerics; +using Windows.UI; + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // ExpressionValues is a static class instead of a namespace to improve intellisense discoverablity and consistency with the other helper classes. + + /// + /// Class ExpressionValues. + /// + public static partial class ExpressionValues + { + /// + /// Create a constant parameter whose value can be changed without recreating the expression. + /// + public static class Constant + { + // Constant parameters with no default value + + /// + /// Creates a named constant parameter of type bool. + /// + /// The name that will be used to refer to the parameter at a later time. + /// BooleanNode + public static BooleanNode CreateConstantBoolean(string paramName) + { + return new BooleanNode(paramName); + } + + /// + /// Creates a named constant parameter of type float. + /// + /// The name that will be used to refer to the parameter at a later time. + /// ScalarNode. + public static ScalarNode CreateConstantScalar(string paramName) + { + return new ScalarNode(paramName); + } + + /// + /// Creates a named constant parameter of type Vector2. + /// + /// The name that will be used to refer to the parameter at a later time. + /// Vector2Node. + public static Vector2Node CreateConstantVector2(string paramName) + { + return new Vector2Node(paramName); + } + + /// + /// Creates a named constant parameter of type Vector3. + /// + /// The name that will be used to refer to the parameter at a later time. + /// Vector3Node. + public static Vector3Node CreateConstantVector3(string paramName) + { + return new Vector3Node(paramName); + } + + /// + /// Creates a named constant parameter of type Vector4. + /// + /// The name that will be used to refer to the parameter at a later time. + /// Vector4Node. + public static Vector4Node CreateConstantVector4(string paramName) + { + return new Vector4Node(paramName); + } + + /// + /// Creates a named constant parameter of type Color. + /// + /// The name that will be used to refer to the parameter at a later time. + /// ColorNode. + public static ColorNode CreateConstantColor(string paramName) + { + return new ColorNode(paramName); + } + + /// + /// Creates a named constant parameter of type Quaternion. + /// + /// The name that will be used to refer to the parameter at a later time. + /// QuaternionNode. + public static QuaternionNode CreateConstantQuaternion(string paramName) + { + return new QuaternionNode(paramName); + } + + /// + /// Creates a named constant parameter of type Matrix3x2. + /// + /// The name that will be used to refer to the parameter at a later time. + /// Matrix3x2Node. + public static Matrix3x2Node CreateConstantMatrix3x2(string paramName) + { + return new Matrix3x2Node(paramName); + } + + /// + /// Creates a named constant parameter of type Matrix4x4. + /// + /// The name that will be used to refer to the parameter at a later time. + /// Matrix4x4Node. + public static Matrix4x4Node CreateConstantMatrix4x4(string paramName) + { + return new Matrix4x4Node(paramName); + } + + // Constant parameters with a default value + + /// + /// Creates a named constant parameter of type bool, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// BooleanNode. + public static BooleanNode CreateConstantBoolean(string paramName, bool value) + { + return new BooleanNode(paramName, value); + } + + /// + /// Creates a named constant parameter of type float, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// ScalarNode. + public static ScalarNode CreateConstantScalar(string paramName, float value) + { + return new ScalarNode(paramName, value); + } + + /// + /// Creates a named constant parameter of type Vector2, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// Vector2Node. + public static Vector2Node CreateConstantVector2(string paramName, Vector2 value) + { + return new Vector2Node(paramName, value); + } + + /// + /// Creates a named constant parameter of type Vector3, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// Vector3Node. + public static Vector3Node CreateConstantVector3(string paramName, Vector3 value) + { + return new Vector3Node(paramName, value); + } + + /// + /// Creates a named constant parameter of type Vector4, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// Vector4Node. + public static Vector4Node CreateConstantVector4(string paramName, Vector4 value) + { + return new Vector4Node(paramName, value); + } + + /// + /// Creates a named constant parameter of type Color, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// ColorNode. + public static ColorNode CreateConstantColor(string paramName, Color value) + { + return new ColorNode(paramName, value); + } + + /// + /// Creates a named constant parameter of type Quaternion, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// QuaternionNode. + public static QuaternionNode CreateConstantQuaternion(string paramName, Quaternion value) + { + return new QuaternionNode(paramName, value); + } + + /// + /// Creates a named constant parameter of type Matrix3x2, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// Matrix3x2Node. + public static Matrix3x2Node CreateConstantMatrix3x2(string paramName, Matrix3x2 value) + { + return new Matrix3x2Node(paramName, value); + } + + /// + /// Creates a named constant parameter of type Matrix4x4, initialized with the specified value. + /// + /// The name that will be used to refer to the parameter at a later time. + /// The value of the parameter. + /// Matrix4x4Node. + public static Matrix4x4Node CreateConstantMatrix4x4(string paramName, Matrix4x4 value) + { + return new Matrix4x4Node(paramName, value); + } + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.CurrentValue.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.CurrentValue.cs new file mode 100644 index 000000000..251d90852 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.CurrentValue.cs @@ -0,0 +1,101 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // ExpressionValues is a static class instead of a namespace to improve intellisense discoverablity and consistency with the other helper classes. + + /// + /// Class ExpressionValues. + /// + public static partial class ExpressionValues + { + /// + /// Refer to the current value of the property this expression is connected to. + /// + public static class CurrentValue + { + /// + /// Create a reference to the current value of the boolean property that this expression will be connected to. + /// + /// BooleanNode. + public static BooleanNode CreateBooleanCurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the float property that this expression will be connected to. + /// + /// ScalarNode. + public static ScalarNode CreateScalarCurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Vector2 property that this expression will be connected to. + /// + /// Vector2Node. + public static Vector2Node CreateVector2CurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Vector3 property that this expression will be connected to. + /// + /// Vector3Node. + public static Vector3Node CreateVector3CurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Vector4 property that this expression will be connected to. + /// + /// Vector4Node. + public static Vector4Node CreateVector4CurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Color property that this expression will be connected to. + /// + /// ColorNode. + public static ColorNode CreateColorCurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Quaternion property that this expression will be connected to. + /// + /// QuaternionNode. + public static QuaternionNode CreateQuaternionCurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Matrix3x2 property that this expression will be connected to. + /// + /// Matrix3x2Node. + public static Matrix3x2Node CreateMatrix3x2CurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + + /// + /// Create a reference to the current value of the Matrix4x4 property that this expression will be connected to. + /// + /// Matrix4x4Node. + public static Matrix4x4Node CreateMatrix4x4CurrentValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.CurrentValue); + } + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Reference.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Reference.cs new file mode 100644 index 000000000..c6ce10667 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Reference.cs @@ -0,0 +1,140 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // ExpressionValues is a static class instead of a namespace to improve intellisense discoverablity and consistency with the other helper classes. + + /// + /// Class ExpressionValues. + /// + public static partial class ExpressionValues + { + /// + /// Create a reference to a CompositionObject. + /// + public static class Reference + { + /// + /// Creates a named reference parameter to an AmbientLight. + /// + /// The name that will be used to refer to the parameter at a later time. + /// AmbientLightReferenceNode. + public static AmbientLightReferenceNode CreateAmbientLightReference(string parameterName) + { + return new AmbientLightReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a ColorBrush. + /// + /// The name that will be used to refer to the parameter at a later time. + /// ColorBrushReferenceNode. + public static ColorBrushReferenceNode CreateColorBrushReference(string parameterName) + { + return new ColorBrushReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a DistantLight. + /// + /// The name that will be used to refer to the parameter at a later time. + /// DistantLightReferenceNode. + public static DistantLightReferenceNode CreateDistantLightReference(string parameterName) + { + return new DistantLightReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a DropShadow. + /// + /// The name that will be used to refer to the parameter at a later time. + /// DropShadowReferenceNode. + public static DropShadowReferenceNode CreateDropShadowReference(string parameterName) + { + return new DropShadowReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to an InsetClip. + /// + /// The name that will be used to refer to the parameter at a later time. + /// InsetClipReferenceNode. + public static InsetClipReferenceNode CreateInsetClipReference(string parameterName) + { + return new InsetClipReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to an InteractionTracker. + /// + /// The name that will be used to refer to the parameter at a later time. + /// InteractionTrackerReferenceNode. + public static InteractionTrackerReferenceNode CreateInteractionTrackerReference(string parameterName) + { + return new InteractionTrackerReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a NineGridBrush. + /// + /// The name that will be used to refer to the parameter at a later time. + /// NineGridBrushReferenceNode. + public static NineGridBrushReferenceNode CreateNineGridBrushReference(string parameterName) + { + return new NineGridBrushReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a PointLight. + /// + /// The name that will be used to refer to the parameter at a later time. + /// PointLightReferenceNode. + public static PointLightReferenceNode CreatePointLightReference(string parameterName) + { + return new PointLightReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a PropertySet. + /// + /// The name that will be used to refer to the parameter at a later time. + /// PropertySetReferenceNode. + public static PropertySetReferenceNode CreatePropertySetReference(string parameterName) + { + return new PropertySetReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a SpotLight. + /// + /// The name that will be used to refer to the parameter at a later time. + /// SpotLightReferenceNode. + public static SpotLightReferenceNode CreateSpotLightReference(string parameterName) + { + return new SpotLightReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a SurfaceBrush. + /// + /// The name that will be used to refer to the parameter at a later time. + /// SurfaceBrushReferenceNode. + public static SurfaceBrushReferenceNode CreateSurfaceBrushReference(string parameterName) + { + return new SurfaceBrushReferenceNode(parameterName); + } + + /// + /// Creates a named reference parameter to a Visual. + /// + /// The name that will be used to refer to the parameter at a later time. + /// VisualReferenceNode. + public static VisualReferenceNode CreateVisualReference(string parameterName) + { + return new VisualReferenceNode(parameterName); + } + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.StartingValue.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.StartingValue.cs new file mode 100644 index 000000000..1feabfcc3 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.StartingValue.cs @@ -0,0 +1,101 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // ExpressionValues is a static class instead of a namespace to improve intellisense discoverablity and consistency with the other helper classes. + + /// + /// Class ExpressionValues. + /// + public static partial class ExpressionValues + { + /// + /// Refer to the value of the property this expression is connected to, sampled during the first frame of execution. + /// + public static class StartingValue + { + /// + /// Create a reference to the starting value of the boolean property that this expression will be connected to. + /// + /// BooleanNode. + public static BooleanNode CreateBooleanStartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the float property that this expression will be connected to. + /// + /// ScalarNode. + public static ScalarNode CreateScalarStartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Vector2 property that this expression will be connected to. + /// + /// Vector2Node. + public static Vector2Node CreateVector2StartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Vector3 property that this expression will be connected to. + /// + /// Vector3Node. + public static Vector3Node CreateVector3StartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Vector4 property that this expression will be connected to. + /// + /// Vector4Node. + public static Vector4Node CreateVector4StartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Color property that this expression will be connected to. + /// + /// ColorNode. + public static ColorNode CreateColorStartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Quaternion property that this expression will be connected to. + /// + /// QuaternionNode. + public static QuaternionNode CreateQuaternionStartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Matrix3x2 property that this expression will be connected to. + /// + /// Matrix3x2Node. + public static Matrix3x2Node CreateMatrix3x2StartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + + /// + /// Create a reference to the starting value of the Matrix4x4 property that this expression will be connected to. + /// + /// Matrix4x4Node. + public static Matrix4x4Node CreateMatrix4x4StartingValue() + { + return ExpressionNode.CreateValueKeyword(ValueKeywordKind.StartingValue); + } + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Target.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Target.cs new file mode 100644 index 000000000..cd95b1bd6 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ExpressionValues/ExpressionValues.Target.cs @@ -0,0 +1,128 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + // ExpressionValues is a static class instead of a namespace to improve intellisense discoverablity and consistency with the other helper classes. + + /// + /// Class ExpressionValues. + /// + public static partial class ExpressionValues + { + /// + /// Create a reference to the CompositionObject this expression will be connected to. + /// + public static class Target + { + /// + /// Create a reference to the AmbientLight target that this expression will be connected to. + /// + /// AmbientLightReferenceNode. + public static AmbientLightReferenceNode CreateAmbientLightTarget() + { + return AmbientLightReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the ColorBrush target that this expression will be connected to. + /// + /// ColorBrushReferenceNode. + public static ColorBrushReferenceNode CreateColorBrushTarget() + { + return ColorBrushReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the DistantLight target that this expression will be connected to. + /// + /// DistantLightReferenceNode. + public static DistantLightReferenceNode CreateDistantLightTarget() + { + return DistantLightReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the DropShadow target that this expression will be connected to. + /// + /// DropShadowReferenceNode. + public static DropShadowReferenceNode CreateDropShadowTarget() + { + return DropShadowReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the InsetClip target that this expression will be connected to. + /// + /// InsetClipReferenceNode. + public static InsetClipReferenceNode CreateInsetClipTarget() + { + return InsetClipReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the InteractionTracker target that this expression will be connected to. + /// + /// InteractionTrackerReferenceNode. + public static InteractionTrackerReferenceNode CreateInteractionTrackerTarget() + { + return InteractionTrackerReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the NineGridBrush target that this expression will be connected to. + /// + /// NineGridBrushReferenceNode. + public static NineGridBrushReferenceNode CreateNineGridBrushTarget() + { + return NineGridBrushReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the PointLight target that this expression will be connected to. + /// + /// PointLightReferenceNode. + public static PointLightReferenceNode CreatePointLightTarget() + { + return PointLightReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the PropertySet target that this expression will be connected to. + /// + /// PropertySetReferenceNode. + public static PropertySetReferenceNode CreatePropertySetTarget() + { + return PropertySetReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the SpotLight target that this expression will be connected to. + /// + /// SpotLightReferenceNode. + public static SpotLightReferenceNode CreateSpotLightTarget() + { + return SpotLightReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the SurfaceBrush target that this expression will be connected to. + /// + /// SurfaceBrushReferenceNode. + public static SurfaceBrushReferenceNode CreateSurfaceBrushTarget() + { + return SurfaceBrushReferenceNode.CreateTargetReference(); + } + + /// + /// Create a reference to the Visual target that this expression will be connected to. + /// + /// VisualReferenceNode. + public static VisualReferenceNode CreateVisualTarget() + { + return VisualReferenceNode.CreateTargetReference(); + } + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/FloatExtensions.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/FloatExtensions.cs new file mode 100644 index 000000000..61885c718 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/FloatExtensions.cs @@ -0,0 +1,48 @@ +// 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.Diagnostics.Contracts; + +namespace Microsoft.Toolkit.Uwp.UI.Animations +{ + /// + /// An extension for the type + /// + internal static class FloatExtensions + { + /// + /// Returns a representation of a that avoids scientific notation, which is not compatible with the composition expression animations API + /// + /// The input to process + /// A representation of that can be used in a expression animation + [Pure] + public static string ToCompositionString(this float number) + { + var defaultString = number.ToString(System.Globalization.CultureInfo.InvariantCulture); + var eIndex = defaultString.IndexOf('E'); + + // If the default string representation is not in scientific notation, we can use it + if (eIndex == -1) + { + return defaultString; + } + + // If the number uses scientific notation because it is too large, we can print it without the decimal places + var exponent = int.Parse(defaultString.Substring(eIndex + 1)); + if (exponent >= 0) + { + return number.ToString($"F0", System.Globalization.CultureInfo.InvariantCulture); + } + + // Otherwise, we need to print it with the right number of decimals + var decimalPlaces = -exponent // The number of decimal places is the exponent of 10 + + eIndex // Plus each character in the mantissa + + (number < 0 ? + -3 : // Minus the sign, dot and first number of the mantissa if negative + -2); // Minus the dot and first number of the mantissa otherwise + + return number.ToString($"F{decimalPlaces}", System.Globalization.CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/OperationType.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/OperationType.cs new file mode 100644 index 000000000..e5804650f --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/OperationType.cs @@ -0,0 +1,47 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Enum OperationType + /// + internal enum OperationType + { + /// + /// The function + /// + Function, + + /// + /// The operator (takes two operands) + /// + Operator, + + /// + /// The operator that only takes one operand + /// + UnaryOperator, + + /// + /// The constant + /// + Constant, + + /// + /// The reference + /// + Reference, + + /// + /// The conditional + /// + Conditional, + + /// + /// The swizzle + /// + Swizzle, + } +} \ No newline at end of file diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/AmbientLightReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/AmbientLightReferenceNode.cs new file mode 100644 index 000000000..6ca6db131 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/AmbientLightReferenceNode.cs @@ -0,0 +1,44 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class AmbientLightReferenceNode. This class cannot be inherited. + /// + /// + public sealed class AmbientLightReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The light. + internal AmbientLightReferenceNode(string paramName, AmbientLight light = null) + : base(paramName, light) + { + } + + /// + /// Creates the target reference. + /// + /// AmbientLightReferenceNode. + internal static AmbientLightReferenceNode CreateTargetReference() + { + var node = new AmbientLightReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ColorBrushReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ColorBrushReferenceNode.cs new file mode 100644 index 000000000..4812723ec --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ColorBrushReferenceNode.cs @@ -0,0 +1,44 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class ColorBrushReferenceNode. This class cannot be inherited. + /// + /// + public sealed class ColorBrushReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The brush. + internal ColorBrushReferenceNode(string paramName, CompositionColorBrush brush = null) + : base(paramName, brush) + { + } + + /// + /// Creates the target reference. + /// + /// ColorBrushReferenceNode. + internal static ColorBrushReferenceNode CreateTargetReference() + { + var node = new ColorBrushReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DistantLightReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DistantLightReferenceNode.cs new file mode 100644 index 000000000..96e73961c --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DistantLightReferenceNode.cs @@ -0,0 +1,53 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class DistantLightReferenceNode. This class cannot be inherited. + /// + /// + public sealed class DistantLightReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The light. + internal DistantLightReferenceNode(string paramName, DistantLight light = null) + : base(paramName, light) + { + } + + /// + /// Creates the target reference. + /// + /// DistantLightReferenceNode. + internal static DistantLightReferenceNode CreateTargetReference() + { + var node = new DistantLightReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + + /// + /// Gets the direction. + /// + /// The direction. + public Vector3Node Direction + { + get { return ReferenceProperty("Direction"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DropShadowReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DropShadowReferenceNode.cs new file mode 100644 index 000000000..ea7d0a310 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/DropShadowReferenceNode.cs @@ -0,0 +1,71 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class DropShadowReferenceNode. This class cannot be inherited. + /// + /// + public sealed class DropShadowReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The source. + internal DropShadowReferenceNode(string paramName, DropShadow source = null) + : base(paramName, source) + { + } + + /// + /// Creates the target reference. + /// + /// DropShadowReferenceNode. + internal static DropShadowReferenceNode CreateTargetReference() + { + var node = new DropShadowReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the blur radius. + /// + /// The blur radius. + public ScalarNode BlurRadius + { + get { return ReferenceProperty("BlurRadius"); } + } + + /// + /// Gets the opacity. + /// + /// The opacity. + public ScalarNode Opacity + { + get { return ReferenceProperty("Opacity"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector3Node Offset + { + get { return ReferenceProperty("Offset"); } + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InsetClipReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InsetClipReferenceNode.cs new file mode 100644 index 000000000..a148748d2 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InsetClipReferenceNode.cs @@ -0,0 +1,134 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class InsetClipReferenceNode. This class cannot be inherited. + /// + /// + public sealed class InsetClipReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The ic. + internal InsetClipReferenceNode(string paramName, InsetClip ic = null) + : base(paramName, ic) + { + } + + /// + /// Creates the target reference. + /// + /// InsetClipReferenceNode. + internal static InsetClipReferenceNode CreateTargetReference() + { + var node = new InsetClipReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the bottom inset. + /// + /// The bottom inset. + public ScalarNode BottomInset + { + get { return ReferenceProperty("BottomInset"); } + } + + /// + /// Gets the left inset. + /// + /// The left inset. + public ScalarNode LeftInset + { + get { return ReferenceProperty("LeftInset"); } + } + + /// + /// Gets the right inset. + /// + /// The right inset. + public ScalarNode RightInset + { + get { return ReferenceProperty("RightInset"); } + } + + /// + /// Gets the top inset. + /// + /// The top inset. + public ScalarNode TopInset + { + get { return ReferenceProperty("TopInset"); } + } + + /// + /// Gets the rotation angle. + /// + /// The rotation angle. + public ScalarNode RotationAngle + { + get { return ReferenceProperty("RotationAngle"); } + } + + /// + /// Gets the rotation angle in degrees. + /// + /// The rotation angle in degrees. + public ScalarNode RotationAngleInDegrees + { + get { return ReferenceProperty("RotationAngleInDegrees"); } + } + + /// + /// Gets the anchor point. + /// + /// The anchor point. + public Vector2Node AnchorPoint + { + get { return ReferenceProperty("AnchorPoint"); } + } + + /// + /// Gets the center point. + /// + /// The center point. + public Vector2Node CenterPoint + { + get { return ReferenceProperty("CenterPoint"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector2Node Offset + { + get { return ReferenceProperty("Offset"); } + } + + /// + /// Gets the scale. + /// + /// The scale. + public Vector2Node Scale + { + get { return ReferenceProperty("Scale"); } + } + + /// + /// Gets the transform matrix. + /// + /// The transform matrix. + public Matrix3x2Node TransformMatrix + { + get { return ReferenceProperty("TransformMatrix"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InteractionTrackerReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InteractionTrackerReferenceNode.cs new file mode 100644 index 000000000..cf0e765bf --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/InteractionTrackerReferenceNode.cs @@ -0,0 +1,152 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class InteractionTrackerReferenceNode. This class cannot be inherited. + /// + /// + public sealed class InteractionTrackerReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// It. + internal InteractionTrackerReferenceNode(string paramName, InteractionTracker it = null) + : base(paramName, it) + { + } + + /// + /// Creates the target reference. + /// + /// InteractionTrackerReferenceNode. + internal static InteractionTrackerReferenceNode CreateTargetReference() + { + var node = new InteractionTrackerReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the is position rounding suggested. + /// + /// The is position rounding suggested. + public BooleanNode IsPositionRoundingSuggested + { + get { return ReferenceProperty("IsPositionRoundingSuggested"); } + } + + /// + /// Gets the minimum scale. + /// + /// The minimum scale. + public ScalarNode MinScale + { + get { return ReferenceProperty("MinScale"); } + } + + /// + /// Gets the maximum scale. + /// + /// The maximum scale. + public ScalarNode MaxScale + { + get { return ReferenceProperty("MaxScale"); } + } + + /// + /// Gets the natural resting scale. + /// + /// The natural resting scale. + public ScalarNode NaturalRestingScale + { + get { return ReferenceProperty("NaturalRestingScale"); } + } + + /// + /// Gets the scale. + /// + /// The scale. + public ScalarNode Scale + { + get { return ReferenceProperty("Scale"); } + } + + /// + /// Gets the scale inertia decay rate. + /// + /// The scale inertia decay rate. + public ScalarNode ScaleInertiaDecayRate + { + get { return ReferenceProperty("ScaleInertiaDecayRate"); } + } + + /// + /// Gets the scale velocity in percent per second. + /// + /// The scale velocity in percent per second. + public ScalarNode ScaleVelocityInPercentPerSecond + { + get { return ReferenceProperty("ScaleVelocityInPercentPerSecond"); } + } + + /// + /// Gets the minimum position. + /// + /// The minimum position. + public Vector3Node MinPosition + { + get { return ReferenceProperty("MinPosition"); } + } + + /// + /// Gets the maximum position. + /// + /// The maximum position. + public Vector3Node MaxPosition + { + get { return ReferenceProperty("MaxPosition"); } + } + + /// + /// Gets the natural resting position. + /// + /// The natural resting position. + public Vector3Node NaturalRestingPosition + { + get { return ReferenceProperty("NaturalRestingPosition"); } + } + + /// + /// Gets the position. + /// + /// The position. + public Vector3Node Position + { + get { return ReferenceProperty("Position"); } + } + + /// + /// Gets the position inertia decay rate. + /// + /// The position inertia decay rate. + public Vector3Node PositionInertiaDecayRate + { + get { return ReferenceProperty("PositionInertiaDecayRate"); } + } + + /// + /// Gets the position velocity in pixels per second. + /// + /// The position velocity in pixels per second. + public Vector3Node PositionVelocityInPixelsPerSecond + { + get { return ReferenceProperty("PositionVelocityInPixelsPerSecond"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ManipulationPropertySetReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ManipulationPropertySetReferenceNode.cs new file mode 100644 index 000000000..15ea03f8a --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ManipulationPropertySetReferenceNode.cs @@ -0,0 +1,77 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class ManipulationPropertySetReferenceNode. This class cannot be inherited. + /// + /// + public sealed class ManipulationPropertySetReferenceNode : PropertySetReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The ps. + internal ManipulationPropertySetReferenceNode(string paramName, CompositionPropertySet ps = null) + : base(paramName, ps) + { + } + + /// + /// Initializes a new instance of the class. + /// Needed for GetSpecializedReference + /// + internal ManipulationPropertySetReferenceNode() + : base(null, null) + { + } + + /// + /// Gets the center point. + /// + /// The center point. + public Vector3Node CenterPoint + { + get { return ReferenceProperty("CenterPoint"); } + } + + /// + /// Gets the pan. + /// + /// The pan. + public Vector3Node Pan + { + get { return ReferenceProperty("Pan"); } + } + + /// + /// Gets the scale. + /// + /// The scale. + public Vector3Node Scale + { + get { return ReferenceProperty("Scale"); } + } + + /// + /// Gets the translation. + /// + /// The translation. + public Vector3Node Translation + { + get { return ReferenceProperty("Translation"); } + } + + /// + /// Gets the matrix. + /// + /// The matrix. + public Matrix4x4Node Matrix + { + get { return ReferenceProperty("Matrix"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/NineGridBrushReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/NineGridBrushReferenceNode.cs new file mode 100644 index 000000000..6b3720ab5 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/NineGridBrushReferenceNode.cs @@ -0,0 +1,107 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class NineGridBrushReferenceNode. This class cannot be inherited. + /// + /// + public sealed class NineGridBrushReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The brush. + internal NineGridBrushReferenceNode(string paramName, CompositionNineGridBrush brush = null) + : base(paramName, brush) + { + } + + /// + /// Creates the target reference. + /// + /// NineGridBrushReferenceNode. + internal static NineGridBrushReferenceNode CreateTargetReference() + { + var node = new NineGridBrushReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the bottom inset. + /// + /// The bottom inset. + public ScalarNode BottomInset + { + get { return ReferenceProperty("BottomInset"); } + } + + /// + /// Gets the bottom inset scale. + /// + /// The bottom inset scale. + public ScalarNode BottomInsetScale + { + get { return ReferenceProperty("BottomInsetScale"); } + } + + /// + /// Gets the left inset. + /// + /// The left inset. + public ScalarNode LeftInset + { + get { return ReferenceProperty("LeftInset"); } + } + + /// + /// Gets the left inset scale. + /// + /// The left inset scale. + public ScalarNode LeftInsetScale + { + get { return ReferenceProperty("LeftInsetScale"); } + } + + /// + /// Gets the right inset. + /// + /// The right inset. + public ScalarNode RightInset + { + get { return ReferenceProperty("RightInset"); } + } + + /// + /// Gets the right inset scale. + /// + /// The right inset scale. + public ScalarNode RightInsetScale + { + get { return ReferenceProperty("RightInsetScale"); } + } + + /// + /// Gets the top inset. + /// + /// The top inset. + public ScalarNode TopInset + { + get { return ReferenceProperty("TopInset"); } + } + + /// + /// Gets the top inset scale. + /// + /// The top inset scale. + public ScalarNode TopInsetScale + { + get { return ReferenceProperty("TopInsetScale"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointLightReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointLightReferenceNode.cs new file mode 100644 index 000000000..8aa61c916 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointLightReferenceNode.cs @@ -0,0 +1,89 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class PointLightReferenceNode. This class cannot be inherited. + /// + /// + public sealed class PointLightReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The light. + internal PointLightReferenceNode(string paramName, PointLight light = null) + : base(paramName, light) + { + } + + /// + /// Creates the target reference. + /// + /// PointLightReferenceNode. + internal static PointLightReferenceNode CreateTargetReference() + { + var node = new PointLightReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the constant attenuation. + /// + /// The constant attenuation. + public ScalarNode ConstantAttenuation + { + get { return ReferenceProperty("ConstantAttenuation"); } + } + + /// + /// Gets the linear attenuation. + /// + /// The linear attenuation. + public ScalarNode LinearAttenuation + { + get { return ReferenceProperty("LinearAttenuation"); } + } + + /// + /// Gets the quadratic attenuation. + /// + /// The quadratic attenuation. + public ScalarNode QuadraticAttentuation + { + get { return ReferenceProperty("QuadraticAttentuation"); } + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + + /// + /// Gets the direction. + /// + /// The direction. + public Vector3Node Direction + { + get { return ReferenceProperty("Direction"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector3Node Offset + { + get { return ReferenceProperty("Offset"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointerPositionPropertySetReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointerPositionPropertySetReferenceNode.cs new file mode 100644 index 000000000..d1099fde0 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PointerPositionPropertySetReferenceNode.cs @@ -0,0 +1,41 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class PointerPositionPropertySetReferenceNode. This class cannot be inherited. + /// + /// + public sealed class PointerPositionPropertySetReferenceNode : PropertySetReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The ps. + internal PointerPositionPropertySetReferenceNode(string paramName, CompositionPropertySet ps = null) + : base(paramName, ps) + { + } + + /// + /// Initializes a new instance of the class. + /// Needed for GetSpecializedReference + /// + internal PointerPositionPropertySetReferenceNode() + : base(null, null) + { + } + + /// + /// Gets the position. + /// + /// The position. + public Vector3Node Position + { + get { return ReferenceProperty("Position"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PropertySetReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PropertySetReferenceNode.cs new file mode 100644 index 000000000..2aab654e9 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/PropertySetReferenceNode.cs @@ -0,0 +1,50 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class PropertySetReferenceNode. + /// + /// + public class PropertySetReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The ps. + internal PropertySetReferenceNode(string paramName, CompositionPropertySet ps = null) + : base(paramName, ps) + { + } + + /// + /// Initializes a new instance of the class. + /// + // Needed for GetSpecializedReference<>() + internal PropertySetReferenceNode() + : base(null, null) + { + } + + /// + /// Gets or sets the source. + /// + /// The source. + internal CompositionPropertySet Source { get; set; } + + /// + /// Creates the target reference. + /// + /// PropertySetReferenceNode. + internal static PropertySetReferenceNode CreateTargetReference() + { + var node = new PropertySetReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ReferenceNode.cs new file mode 100644 index 000000000..affcc9d69 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/ReferenceNode.cs @@ -0,0 +1,165 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class ReferenceNode. + /// + /// + public abstract class ReferenceNode : ExpressionNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The comp object. + internal ReferenceNode(string paramName, CompositionObject compObj = null) + { + Reference = compObj; + NodeType = ExpressionNodeType.Reference; + ParamName = paramName; + } + + /// + /// Gets the reference. + /// + /// The reference. + public CompositionObject Reference { get; private set; } + + /// + /// Create a reference to the specified boolean property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// BooleanNode. + public BooleanNode GetBooleanProperty(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified float property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// ScalarNode + public ScalarNode GetScalarProperty(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Vector2 property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// Vector2Node + public Vector2Node GetVector2Property(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Vector3 property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// Vector3Node + public Vector3Node GetVector3Property(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Vector4 property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// Vector4Node + public Vector4Node GetVector4Property(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Color property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// ColorNode + public ColorNode GetColorProperty(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Quaternion property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// QuaternionNode + public QuaternionNode GetQuaternionProperty(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Matrix3x2 property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// Matrix3x2Node + public Matrix3x2Node GetMatrix3x2Property(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Create a reference to the specified Matrix4x4 property. This maybe be a property on the CompositionObject directly, or on the its PropertySet. + /// + /// The name of the property to reference. + /// Matrix4x4Node + public Matrix4x4Node GetMatrix4x4Property(string propertyName) + { + return ReferenceProperty(propertyName); + } + + /// + /// Gets the reference parameter string. + /// + /// System.String. + internal string GetReferenceParamString() + { + if (NodeType == ExpressionNodeType.TargetReference) + { + return "this.target"; + } + else + { + return ParamName; + } + } + + /// + /// References the property. + /// + /// A class that derives from ExpressionNode. + /// Name of the property. + /// T. + protected internal T ReferenceProperty(string propertyName) + where T : ExpressionNode + { + T newNode = ExpressionNode.CreateExpressionNode(); + + (newNode as ExpressionNode).NodeType = ExpressionNodeType.ReferenceProperty; + (newNode as ExpressionNode).Children.Add(this); + (newNode as ExpressionNode).PropertyName = propertyName; + + return newNode; + } + + /// + /// Gets the value. + /// + /// System.String. + /// GetValue is not implemented for ReferenceNode and shouldn't be called + protected internal override string GetValue() + { + throw new NotImplementedException("GetValue is not implemented for ReferenceNode and shouldn't be called"); + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SpotLightReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SpotLightReferenceNode.cs new file mode 100644 index 000000000..ba4055aa3 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SpotLightReferenceNode.cs @@ -0,0 +1,143 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class SpotLightReferenceNode. This class cannot be inherited. + /// + /// + public sealed class SpotLightReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The light. + internal SpotLightReferenceNode(string paramName, SpotLight light = null) + : base(paramName, light) + { + } + + /// + /// Creates the target reference. + /// + /// SpotLightReferenceNode. + internal static SpotLightReferenceNode CreateTargetReference() + { + var node = new SpotLightReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the constant attenuation. + /// + /// The constant attenuation. + public ScalarNode ConstantAttenuation + { + get { return ReferenceProperty("ConstantAttenuation"); } + } + + /// + /// Gets the linear attenuation. + /// + /// The linear attenuation. + public ScalarNode LinearAttenuation + { + get { return ReferenceProperty("LinearAttenuation"); } + } + + /// + /// Gets the quadratic attenuation. + /// + /// The quadratic attenuation. + public ScalarNode QuadraticAttentuation + { + get { return ReferenceProperty("QuadraticAttentuation"); } + } + + /// + /// Gets the inner cone angle. + /// + /// The inner cone angle. + public ScalarNode InnerConeAngle + { + get { return ReferenceProperty("InnerConeAngle"); } + } + + /// + /// Gets the inner cone angle in degrees. + /// + /// The inner cone angle in degrees. + public ScalarNode InnerConeAngleInDegrees + { + get { return ReferenceProperty("InnerConeAngleInDegrees"); } + } + + /// + /// Gets the outer cone angle. + /// + /// The outer cone angle. + public ScalarNode OuterConeAngle + { + get { return ReferenceProperty("OuterConeAngle"); } + } + + /// + /// Gets the outer cone angle in degrees. + /// + /// The outer cone angle in degrees. + public ScalarNode OuterConeAngleInDegrees + { + get { return ReferenceProperty("OuterConeAngleInDegrees"); } + } + + /// + /// Gets the color. + /// + /// The color. + public ColorNode Color + { + get { return ReferenceProperty("Color"); } + } + + /// + /// Gets the color of the inner cone. + /// + /// The color of the inner cone. + public ColorNode InnerConeColor + { + get { return ReferenceProperty("InnerConeColor"); } + } + + /// + /// Gets the color of the outer cone. + /// + /// The color of the outer cone. + public ColorNode OuterConeColor + { + get { return ReferenceProperty("OuterConeColor"); } + } + + /// + /// Gets the direction. + /// + /// The direction. + public Vector3Node Direction + { + get { return ReferenceProperty("Direction"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector3Node Offset + { + get { return ReferenceProperty("Offset"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SurfaceBrushReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SurfaceBrushReferenceNode.cs new file mode 100644 index 000000000..6b51cd66a --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/SurfaceBrushReferenceNode.cs @@ -0,0 +1,152 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class SurfaceBrushReferenceNode. This class cannot be inherited. + /// + /// + public sealed class SurfaceBrushReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The brush. + internal SurfaceBrushReferenceNode(string paramName, CompositionSurfaceBrush brush = null) + : base(paramName, brush) + { + } + + /// + /// Creates the target reference. + /// + /// SurfaceBrushReferenceNode. + internal static SurfaceBrushReferenceNode CreateTargetReference() + { + var node = new SurfaceBrushReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the horizontal alignment ratio. + /// + /// The horizontal alignment ratio. + public ScalarNode HorizontalAlignmentRatio + { + get { return ReferenceProperty("HorizontalAlignmentRatio"); } + } + + /// + /// Gets the vertical alignment ratio. + /// + /// The vertical alignment ratio. + public ScalarNode VerticalAlignmentRatio + { + get { return ReferenceProperty("VerticalAlignmentRatio"); } + } + + /// + /// Gets the bottom inset. + /// + /// The bottom inset. + public ScalarNode BottomInset + { + get { return ReferenceProperty("BottomInset"); } + } + + /// + /// Gets the left inset. + /// + /// The left inset. + public ScalarNode LeftInset + { + get { return ReferenceProperty("LeftInset"); } + } + + /// + /// Gets the right inset. + /// + /// The right inset. + public ScalarNode RightInset + { + get { return ReferenceProperty("RightInset"); } + } + + /// + /// Gets the top inset. + /// + /// The top inset. + public ScalarNode TopInset + { + get { return ReferenceProperty("TopInset"); } + } + + /// + /// Gets the rotation angle. + /// + /// The rotation angle. + public ScalarNode RotationAngle + { + get { return ReferenceProperty("RotationAngle"); } + } + + /// + /// Gets the rotation angle in degrees. + /// + /// The rotation angle in degrees. + public ScalarNode RotationAngleInDegrees + { + get { return ReferenceProperty("RotationAngleInDegrees"); } + } + + /// + /// Gets the anchor point. + /// + /// The anchor point. + public Vector2Node AnchorPoint + { + get { return ReferenceProperty("AnchorPoint"); } + } + + /// + /// Gets the center point. + /// + /// The center point. + public Vector2Node CenterPoint + { + get { return ReferenceProperty("CenterPoint"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector2Node Offset + { + get { return ReferenceProperty("Offset"); } + } + + /// + /// Gets the scale. + /// + /// The scale. + public Vector2Node Scale + { + get { return ReferenceProperty("Scale"); } + } + + /// + /// Gets the transform matrix. + /// + /// The transform matrix. + public Matrix3x2Node TransformMatrix + { + get { return ReferenceProperty("TransformMatrix"); } + } + } +} diff --git a/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/VisualReferenceNode.cs b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/VisualReferenceNode.cs new file mode 100644 index 000000000..2eabb4f06 --- /dev/null +++ b/components/CompositionCollectionView/src/ExpressionsFork/Expressions/ReferenceNodes/VisualReferenceNode.cs @@ -0,0 +1,160 @@ +// 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. + +namespace Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork +{ + /// + /// Class VisualReferenceNode. This class cannot be inherited. + /// + /// + public sealed class VisualReferenceNode : ReferenceNode + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The v. + internal VisualReferenceNode(string paramName, Visual v = null) + : base(paramName, v) + { + } + + /// + /// Creates the target reference. + /// + /// VisualReferenceNode. + internal static VisualReferenceNode CreateTargetReference() + { + var node = new VisualReferenceNode(null); + node.NodeType = ExpressionNodeType.TargetReference; + + return node; + } + + /// + /// Gets the opacity. + /// + /// The opacity. + public ScalarNode Opacity + { + get { return ReferenceProperty("Opacity"); } + } + + /// + /// Gets the rotation angle. + /// + /// The rotation angle. + public ScalarNode RotationAngle + { + get { return ReferenceProperty("RotationAngle"); } + } + + /// + /// Gets the rotation angle in degrees. + /// + /// The rotation angle in degrees. + public ScalarNode RotationAngleInDegrees + { + get { return ReferenceProperty("RotationAngleInDegrees"); } + } + + /// + /// Gets the anchor point. + /// + /// The anchor point. + public Vector2Node AnchorPoint + { + get { return ReferenceProperty("AnchorPoint"); } + } + + /// + /// Gets the size of the relative. + /// + /// The size of the relative. + public Vector2Node RelativeSize + { + get { return ReferenceProperty("RelativeSize"); } + } + + /// + /// Gets the size. + /// + /// The size. + public Vector2Node Size + { + get { return ReferenceProperty("Size"); } + } + + /// + /// Gets the center point. + /// + /// The center point. + public Vector3Node CenterPoint + { + get { return ReferenceProperty("CenterPoint"); } + } + + /// + /// Gets the offset. + /// + /// The offset. + public Vector3Node Offset + { + get { return ReferenceProperty("Offset"); } + } + + /// + /// Gets the relative offset. + /// + /// The relative offset. + public Vector3Node RelativeOffset + { + get { return ReferenceProperty("RelativeOffset"); } + } + + /// + /// Gets the rotation axis. + /// + /// The rotation axis. + public Vector3Node RotationAxis + { + get { return ReferenceProperty("RotationAxis"); } + } + + /// + /// Gets the scale. + /// + /// The scale. + public Vector3Node Scale + { + get { return ReferenceProperty("Scale"); } + } + + /// + /// Gets the Translation. + /// + public Vector3Node Translation + { + get { return GetVector3Property("Translation"); } + } + + /// + /// Gets the orientation. + /// + /// The orientation. + public QuaternionNode Orientation + { + get { return ReferenceProperty("Orientation"); } + } + + /// + /// Gets the transform matrix. + /// + /// The transform matrix. + public Matrix4x4Node TransformMatrix + { + get { return ReferenceProperty("TransformMatrix"); } + } + } +} diff --git a/components/CompositionCollectionView/src/GlobalUsings_Local.cs b/components/CompositionCollectionView/src/GlobalUsings_Local.cs new file mode 100644 index 000000000..ec999951b --- /dev/null +++ b/components/CompositionCollectionView/src/GlobalUsings_Local.cs @@ -0,0 +1,5 @@ +// 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. + +global using Microsoft.Toolkit.Uwp.UI.Animations.ExpressionsFork; diff --git a/components/CompositionCollectionView/src/IsExternalInit.cs b/components/CompositionCollectionView/src/IsExternalInit.cs new file mode 100644 index 000000000..a02b1b7e1 --- /dev/null +++ b/components/CompositionCollectionView/src/IsExternalInit.cs @@ -0,0 +1,17 @@ +// 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.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + /// + /// This class is required to make Record types work in .NET Framework and .NET Core 3.1. + /// It's included in .NET 5. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class IsExternalInit + { + } +} diff --git a/components/CompositionCollectionView/src/Themes/Generic.xaml b/components/CompositionCollectionView/src/Themes/Generic.xaml new file mode 100644 index 000000000..e16094aca --- /dev/null +++ b/components/CompositionCollectionView/src/Themes/Generic.xaml @@ -0,0 +1,19 @@ + + + + diff --git a/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.projitems b/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.projitems new file mode 100644 index 000000000..4b6b90f72 --- /dev/null +++ b/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.projitems @@ -0,0 +1,23 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + A3D47776-AA76-4032-B5CC-68A25B06796C + + + CompositionCollectionViewExperiment.Tests + + + + + ExampleCompositionCollectionViewTestPage.xaml + + + + + Designer + MSBuild:Compile + + + \ No newline at end of file diff --git a/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.shproj b/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.shproj new file mode 100644 index 000000000..90c42dc27 --- /dev/null +++ b/components/CompositionCollectionView/tests/CompositionCollectionView.Tests.shproj @@ -0,0 +1,13 @@ + + + + A3D47776-AA76-4032-B5CC-68A25B06796C + 14.0 + + + + + + + + diff --git a/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestClass.cs b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestClass.cs new file mode 100644 index 000000000..e9aad792c --- /dev/null +++ b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestClass.cs @@ -0,0 +1,133 @@ +// 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 CommunityToolkit.Tooling.TestGen; +using CommunityToolkit.Tests; +using CommunityToolkit.Labs.WinUI; + +namespace CompositionCollectionViewExperiment.Tests; + +[TestClass] +public partial class ExampleCompositionCollectionViewTestClass : VisualUITestBase +{ + // If you don't need access to UI objects directly or async code, use this pattern. + [TestMethod] + public void SimpleSynchronousExampleTest() + { + var assembly = typeof(CompositionCollectionView).Assembly; + var type = assembly.GetType(typeof(CompositionCollectionView).FullName ?? string.Empty); + + Assert.IsNotNull(type, "Could not find CompositionCollectionView type."); + Assert.AreEqual(typeof(CompositionCollectionView), type, "Type of CompositionCollectionView does not match expected type."); + } + + // If you don't need access to UI objects directly, use this pattern. + [TestMethod] + public async Task SimpleAsyncExampleTest() + { + await Task.Delay(250); + + Assert.IsTrue(true); + } + + // Example that shows how to check for exception throwing. + [TestMethod] + public void SimpleExceptionCheckTest() + { + // If you need to check exceptions occur for invalid inputs, etc... + // Use Assert.ThrowsException to limit the scope to where you expect the error to occur. + // Otherwise, using the ExpectedException attribute could swallow or + // catch other issues in setup code. + Assert.ThrowsException(() => throw new NotImplementedException()); + } + + // The UIThreadTestMethod automatically dispatches to the UI for us to work with UI objects. + [UIThreadTestMethod] + public void SimpleUIAttributeExampleTest() + { + var component = new CompositionCollectionView(); + Assert.IsNotNull(component); + } + + // The UIThreadTestMethod can also easily grab a XAML Page for us by passing its type as a parameter. + // This lets us actually test a control as it would behave within an actual application. + // The page will already be loaded by the time your test is called. + [UIThreadTestMethod] + public void SimpleUIExamplePageTest(ExampleCompositionCollectionViewTestPage page) + { + // You can use the Toolkit Visual Tree helpers here to find the component by type or name: + var component = page.FindDescendant(); + + Assert.IsNotNull(component); + + var componentByName = page.FindDescendant("CompositionCollectionViewControl"); + + Assert.IsNotNull(componentByName); + } + + // You can still do async work with a UIThreadTestMethod as well. + [UIThreadTestMethod] + public async Task SimpleAsyncUIExamplePageTest(ExampleCompositionCollectionViewTestPage page) + { + // This helper can be used to wait for a rendering pass to complete. + await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { }); + + var component = page.FindDescendant(); + + Assert.IsNotNull(component); + } + + //// ----------------------------- ADVANCED TEST SCENARIOS ----------------------------- + + // If you need to use DataRow, you can use this pattern with the UI dispatch still. + // Otherwise, checkout the UIThreadTestMethod attribute above. + // See https://github.com/CommunityToolkit/Labs-Windows/issues/186 + [TestMethod] + public async Task ComplexAsyncUIExampleTest() + { + await EnqueueAsync(() => + { + var component = new CompositionCollectionView(); + Assert.IsNotNull(component); + }); + } + + // If you want to load other content not within a XAML page using the UIThreadTestMethod above. + // Then you can do that using the Load/UnloadTestContentAsync methods. + [TestMethod] + public async Task ComplexAsyncLoadUIExampleTest() + { + await EnqueueAsync(async () => + { + var component = new CompositionCollectionView(); + Assert.IsNotNull(component); + Assert.IsFalse(component.IsLoaded); + + await LoadTestContentAsync(component); + + Assert.IsTrue(component.IsLoaded); + + await UnloadTestContentAsync(component); + + Assert.IsFalse(component.IsLoaded); + }); + } + + // You can still use the UIThreadTestMethod to remove the extra layer for the dispatcher as well: + [UIThreadTestMethod] + public async Task ComplexAsyncLoadUIExampleWithoutDispatcherTest() + { + var component = new CompositionCollectionView(); + Assert.IsNotNull(component); + Assert.IsFalse(component.IsLoaded); + + await LoadTestContentAsync(component); + + Assert.IsTrue(component.IsLoaded); + + await UnloadTestContentAsync(component); + + Assert.IsFalse(component.IsLoaded); + } +} diff --git a/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml new file mode 100644 index 000000000..00f7c3114 --- /dev/null +++ b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml.cs b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml.cs new file mode 100644 index 000000000..a98600d02 --- /dev/null +++ b/components/CompositionCollectionView/tests/ExampleCompositionCollectionViewTestPage.xaml.cs @@ -0,0 +1,16 @@ +// 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. + +namespace CompositionCollectionViewExperiment.Tests; + +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class ExampleCompositionCollectionViewTestPage : Page +{ + public ExampleCompositionCollectionViewTestPage() + { + this.InitializeComponent(); + } +}