Browse Source

Use double for compositor types

Nikita Tsukanov 2 years ago
parent
commit
700b199602

+ 5 - 5
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@@ -144,8 +144,8 @@ public partial class CompositionPage : UserControl
         {
             if(_solidVisual == null)
                 return;
-            _solidVisual.Size = new Vector2((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3);
-            _solidVisual.Offset = new Vector3((float)v.Bounds.Width / 3, (float)v.Bounds.Height / 3, 0);
+            _solidVisual.Size = new (v.Bounds.Width / 3, v.Bounds.Height / 3);
+            _solidVisual.Offset = new (v.Bounds.Width / 3, v.Bounds.Height / 3, 0);
         }
         v.AttachedToVisualTree += delegate
         {
@@ -164,7 +164,7 @@ public partial class CompositionPage : UserControl
             animation.Direction = PlaybackDirection.Alternate;
             _solidVisual.StartAnimation("Color", animation);
 
-            _solidVisual.AnchorPoint = new Vector2(0, 0);
+            _solidVisual.AnchorPoint = new (0, 0);
 
             var scale = _solidVisual.Compositor.CreateVector3KeyFrameAnimation();
             scale.Duration = TimeSpan.FromSeconds(5);
@@ -195,8 +195,8 @@ public partial class CompositionPage : UserControl
             if (_customVisual == null)
                 return;
             var h = (float)Math.Min(v.Bounds.Height, v.Bounds.Width / 3);
-            _customVisual.Size = new Vector2((float)v.Bounds.Width, h);
-            _customVisual.Offset = new Vector3(0, (float)(v.Bounds.Height - h) / 2, 0);
+            _customVisual.Size = new (v.Bounds.Width, h);
+            _customVisual.Offset = new (0, (v.Bounds.Height - h) / 2, 0);
         }
         v.AttachedToVisualTree += delegate
         {

+ 8 - 8
samples/ControlCatalog/Pages/GesturePage.cs

@@ -13,7 +13,7 @@ namespace ControlCatalog.Pages
     public class GesturePage : UserControl
     {
         private bool _isInit;
-        private float _currentScale;
+        private double _currentScale;
 
         public GesturePage()
         {
@@ -53,7 +53,7 @@ namespace ControlCatalog.Pages
                 if(compositionVisual!= null)
                 {
                     _currentScale = 1;
-                    compositionVisual.Scale = new Vector3(1,1,1);
+                    compositionVisual.Scale = new (1,1,1);
                     compositionVisual.Offset = default;
                     image.InvalidateMeasure();
                 }
@@ -69,7 +69,7 @@ namespace ControlCatalog.Pages
             }
 
             _currentScale = 1;
-            Vector3 currentOffset = default;
+            Vector3D currentOffset = default;
 
             CompositionVisual? compositionVisual = null;
 
@@ -133,11 +133,11 @@ namespace ControlCatalog.Pages
 
                 if (compositionVisual != null && _currentScale != 1)
                 {
-                    currentOffset += new Vector3((float)e.Delta.X, (float)e.Delta.Y, 0);
+                    currentOffset += new Vector3D(e.Delta.X, e.Delta.Y, 0);
 
                     var currentSize = control.Bounds.Size * _currentScale;
 
-                    currentOffset = new Vector3((float)MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
+                    currentOffset = new Vector3D(MathUtilities.Clamp(currentOffset.X, 0, currentSize.Width - control.Bounds.Width),
                         (float)MathUtilities.Clamp(currentOffset.Y, 0, currentSize.Height - control.Bounds.Height),
                         0);
 
@@ -157,7 +157,7 @@ namespace ControlCatalog.Pages
 
             var ball = control.FindLogicalDescendantOfType<Border>();
 
-            Vector3 defaultOffset = default;
+            Vector3D defaultOffset = default;
 
             CompositionVisual? ballCompositionVisual = null;
 
@@ -181,11 +181,11 @@ namespace ControlCatalog.Pages
 
             control.AddHandler(Gestures.PullGestureEvent, (s, e) =>
             {
-                Vector3 center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
+                Vector3D center = new((float)control.Bounds.Center.X, (float)control.Bounds.Center.Y, 0);
                 InitComposition(ball!);
                 if (ballCompositionVisual != null)
                 {
-                    ballCompositionVisual.Offset = defaultOffset + new System.Numerics.Vector3((float)e.Delta.X * 0.4f, (float)e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
+                    ballCompositionVisual.Offset = defaultOffset + new Vector3D(e.Delta.X * 0.4f, e.Delta.Y * 0.4f, 0) * (inverse ? -1 : 1);
 
                     e.Handled = true;
                 }

+ 2 - 2
samples/GpuInterop/DrawingSurfaceDemoBase.cs

@@ -48,7 +48,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
             
             Surface = _compositor.CreateDrawingSurface();
             _visual = _compositor.CreateSurfaceVisual();
-            _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+            _visual.Size = new (Bounds.Width, Bounds.Height);
             _visual.Surface = Surface;
             ElementComposition.SetElementChildVisual(this, _visual);
             var (res, info) = await DoInitialize(_compositor, Surface);
@@ -72,7 +72,7 @@ public abstract class DrawingSurfaceDemoBase : Control, IGpuDemo
         if (root == null)
             return;
         
-        _visual!.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+        _visual!.Size = new (Bounds.Width, Bounds.Height);
         var size = PixelSize.FromSize(Bounds.Size, root.RenderScaling);
         RenderFrame(size);
         if (SupportsDisco && Disco > 0)

+ 7 - 0
samples/Sandbox/MainWindow.axaml

@@ -1,4 +1,11 @@
 <Window xmlns="https://github.com/avaloniaui"
         xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
         x:Class="Sandbox.MainWindow">
+        
+        <ScrollViewer>
+                <StackPanel>
+                        <Button Margin="0 100000000000000000 0 0">0</Button>
+                        <Button>1</Button>
+                </StackPanel>
+        </ScrollViewer>
 </Window>

+ 25 - 0
src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs

@@ -19,6 +19,12 @@ namespace Avalonia.Rendering.Composition.Animations
         
         public static ScalarInterpolator Instance { get; } = new ScalarInterpolator();
     }
+    class DoubleInterpolator : IInterpolator<double>
+    {
+        public double Interpolate(double @from, double to, float progress) => @from + (to - @from) * progress;
+        
+        public static DoubleInterpolator Instance { get; } = new ();
+    }
 
     class Vector2Interpolator : IInterpolator<Vector2>
     {
@@ -28,6 +34,15 @@ namespace Avalonia.Rendering.Composition.Animations
         public static Vector2Interpolator Instance { get; } = new Vector2Interpolator();
     }
     
+    class VectorInterpolator : IInterpolator<Vector>
+    {
+        public Vector Interpolate(Vector @from, Vector to, float progress)
+            => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress),
+                DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress));
+        
+        public static VectorInterpolator Instance { get; } = new ();
+    }
+    
     class Vector3Interpolator : IInterpolator<Vector3>
     {
         public Vector3 Interpolate(Vector3 @from, Vector3 to, float progress) 
@@ -36,6 +51,16 @@ namespace Avalonia.Rendering.Composition.Animations
         public static Vector3Interpolator Instance { get; } = new Vector3Interpolator();
     }
     
+    class Vector3DInterpolator : IInterpolator<Vector3D>
+    {
+        public Vector3D Interpolate(Vector3D @from, Vector3D to, float progress)
+            => new(DoubleInterpolator.Instance.Interpolate(from.X, to.X, progress),
+                DoubleInterpolator.Instance.Interpolate(from.Y, to.Y, progress),
+                DoubleInterpolator.Instance.Interpolate(from.Z, to.Z, progress));
+        
+        public static Vector3DInterpolator Instance { get; } = new ();
+    }
+    
     class Vector4Interpolator : IInterpolator<Vector4>
     {
         public Vector4 Interpolate(Vector4 @from, Vector4 to, float progress) 

+ 3 - 3
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@@ -241,8 +241,8 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
                 continue;
             
             // TODO: Optimize all of that by moving to the Visual itself, so we won't have to recalculate every time
-            comp.Offset = new Vector3((float)visual.Bounds.Left, (float)visual.Bounds.Top, 0);
-            comp.Size = new Vector2((float)visual.Bounds.Width, (float)visual.Bounds.Height);
+            comp.Offset = new (visual.Bounds.Left, visual.Bounds.Top, 0);
+            comp.Size = new (visual.Bounds.Width, visual.Bounds.Height);
             comp.Visible = visual.IsVisible;
             comp.Opacity = (float)visual.Opacity;
             comp.ClipToBounds = visual.ClipToBounds;
@@ -269,7 +269,7 @@ internal class CompositingRenderer : IRendererWithCompositor, IHitTester
                 renderTransform *= (-offset) * visual.RenderTransform.Value * (offset);
             }
 
-            comp.TransformMatrix = MatrixUtils.ToMatrix4x4(renderTransform);
+            comp.TransformMatrix = renderTransform;
 
             try
             {

+ 1 - 1
src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs

@@ -28,7 +28,7 @@ public abstract class CompositionCustomVisualHandler
         _host.Compositor.VerifyAccess();
     }
 
-    protected Vector2 EffectiveSize
+    protected Vector EffectiveSize
     {
         get
         {

+ 1 - 1
src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs

@@ -70,7 +70,7 @@ namespace Avalonia.Rendering.Composition
                 return false;
             }
 
-            var m33 = MatrixUtils.ToMatrix(m.Value);
+            var m33 = m.Value;
             return m33.TryInvert(out matrix);
         }
 

+ 57 - 2
src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs

@@ -16,6 +16,7 @@ namespace Avalonia.Rendering.Composition.Expressions
         private readonly DelegateExpressionFfi _registry;
 
         static float Lerp(float a, float b, float p) => p * (b - a) + a;
+        static double Lerp(double a, double b, double p) => p * (b - a) + a;
 
         static Matrix3x2 Inverse(Matrix3x2 m)
         {
@@ -34,6 +35,13 @@ namespace Avalonia.Rendering.Composition.Expressions
             var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
             return t * t * (3.0f - 2.0f * t);
         }
+        
+        static double SmoothStep(double edge0, double edge1, double x)
+        {
+            var t = MathUtilities.Clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+            return t * t * (3.0f - 2.0f * t);
+        }
+
 
         static Vector2 SmoothStep(Vector2 edge0, Vector2 edge1, Vector2 x)
         {
@@ -43,6 +51,15 @@ namespace Avalonia.Rendering.Composition.Expressions
                 
                 );
         }
+        
+        static Vector SmoothStep(Vector edge0, Vector edge1, Vector x)
+        {
+            return new (
+                SmoothStep(edge0.X, edge1.X, x.X),
+                SmoothStep(edge0.Y, edge1.Y, x.Y)
+            );
+        }
+        
         static Vector3 SmoothStep(Vector3 edge0, Vector3 edge1, Vector3 x)
         {
             return new Vector3(
@@ -52,6 +69,15 @@ namespace Avalonia.Rendering.Composition.Expressions
                 
                 );
         }
+        
+        static Vector3D SmoothStep(Vector3D edge0, Vector3D edge1, Vector3D x)
+        {
+            return new (
+                SmoothStep(edge0.X, edge1.X, x.X),
+                SmoothStep(edge0.Y, edge1.Y, x.Y),
+                SmoothStep(edge0.Z, edge1.Z, x.Z)
+            );
+        }
 
         static Vector4 SmoothStep(Vector4 edge0, Vector4 edge1, Vector4 x)
         {
@@ -69,7 +95,9 @@ namespace Avalonia.Rendering.Composition.Expressions
             {
                 {"Abs", (float f) => Math.Abs(f)},
                 {"Abs", (Vector2 v) => Vector2.Abs(v)},
+                {"Abs", (Vector v) => v.Abs()},
                 {"Abs", (Vector3 v) => Vector3.Abs(v)},
+                {"Abs", (Vector3D v) => v.Abs()},
                 {"Abs", (Vector4 v) => Vector4.Abs(v)},
 
                 {"ACos", (float f) => (float) Math.Acos(f)},
@@ -79,7 +107,9 @@ namespace Avalonia.Rendering.Composition.Expressions
 
                 {"Clamp", (float a1, float a2, float a3) => MathUtilities.Clamp(a1, a2, a3)},
                 {"Clamp", (Vector2 a1, Vector2 a2, Vector2 a3) => Vector2.Clamp(a1, a2, a3)},
+                {"Clamp", (Vector a1, Vector a2, Vector a3) => Vector.Clamp(a1, a2, a3)},
                 {"Clamp", (Vector3 a1, Vector3 a2, Vector3 a3) => Vector3.Clamp(a1, a2, a3)},
+                {"Clamp", (Vector3D a1, Vector3D a2, Vector3D a3) => Vector3D.Clamp(a1, a2, a3)},
                 {"Clamp", (Vector4 a1, Vector4 a2, Vector4 a3) => Vector4.Clamp(a1, a2, a3)},
 
                 {"Concatenate", (Quaternion a1, Quaternion a2) => Quaternion.Concatenate(a1, a2)},
@@ -109,11 +139,15 @@ namespace Avalonia.Rendering.Composition.Expressions
                 },
 
                 {"Distance", (Vector2 a1, Vector2 a2) => Vector2.Distance(a1, a2)},
+                {"Distance", (Vector a1, Vector a2) => Vector.Distance(a1, a2)},
                 {"Distance", (Vector3 a1, Vector3 a2) => Vector3.Distance(a1, a2)},
+                {"Distance", (Vector3D a1, Vector3D a2) => Vector3D.Distance(a1, a2)},
                 {"Distance", (Vector4 a1, Vector4 a2) => Vector4.Distance(a1, a2)},
 
                 {"DistanceSquared", (Vector2 a1, Vector2 a2) => Vector2.DistanceSquared(a1, a2)},
+                {"DistanceSquared", (Vector a1, Vector a2) => Vector.DistanceSquared(a1, a2)},
                 {"DistanceSquared", (Vector3 a1, Vector3 a2) => Vector3.DistanceSquared(a1, a2)},
+                {"DistanceSquared", (Vector3D a1, Vector3D a2) => Vector3D.DistanceSquared(a1, a2)},
                 {"DistanceSquared", (Vector4 a1, Vector4 a2) => Vector4.DistanceSquared(a1, a2)},
 
                 {"Floor", (float v) => (float) Math.Floor(v)},
@@ -123,18 +157,24 @@ namespace Avalonia.Rendering.Composition.Expressions
 
 
                 {"Length", (Vector2 a1) => a1.Length()},
+                {"Length", (Vector a1) => a1.Length},
                 {"Length", (Vector3 a1) => a1.Length()},
+                {"Length", (Vector3D a1) => a1.Length},
                 {"Length", (Vector4 a1) => a1.Length()},
                 {"Length", (Quaternion a1) => a1.Length()},
 
                 {"LengthSquared", (Vector2 a1) => a1.LengthSquared()},
+                {"LengthSquared", (Vector a1) => a1*a1},
                 {"LengthSquared", (Vector3 a1) => a1.LengthSquared()},
+                {"LengthSquared", (Vector3D a1) => Vector3D.Dot(a1, a1)},
                 {"LengthSquared", (Vector4 a1) => a1.LengthSquared()},
                 {"LengthSquared", (Quaternion a1) => a1.LengthSquared()},
 
                 {"Lerp", (float a1, float a2, float a3) => Lerp(a1, a2, a3)},
                 {"Lerp", (Vector2 a1, Vector2 a2, float a3) => Vector2.Lerp(a1, a2, a3)},
+                {"Lerp", (Vector a1, Vector a2, float a3) => new Vector(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3))},
                 {"Lerp", (Vector3 a1, Vector3 a2, float a3) => Vector3.Lerp(a1, a2, a3)},
+                {"Lerp", (Vector3D a1, Vector3D a2, float a3) => new Vector3D(Lerp(a1.X, a2.X, a3), Lerp(a1.Y, a2.Y, a3),  Lerp(a1.Z, a2.Z, a3))},
                 {"Lerp", (Vector4 a1, Vector4 a2, float a3) => Vector4.Lerp(a1, a2, a3)},
 
 
@@ -173,24 +213,31 @@ namespace Avalonia.Rendering.Composition.Expressions
 
                 {"Max", (float a1, float a2) => Math.Max(a1, a2)},
                 {"Max", (Vector2 a1, Vector2 a2) => Vector2.Max(a1, a2)},
+                {"Max", (Vector a1, Vector a2) => Vector.Max(a1, a2)},
                 {"Max", (Vector3 a1, Vector3 a2) => Vector3.Max(a1, a2)},
+                {"Max", (Vector3D a1, Vector3D a2) => Vector3D.Max(a1, a2)},
                 {"Max", (Vector4 a1, Vector4 a2) => Vector4.Max(a1, a2)},
 
 
                 {"Min", (float a1, float a2) => Math.Min(a1, a2)},
                 {"Min", (Vector2 a1, Vector2 a2) => Vector2.Min(a1, a2)},
+                {"Min", (Vector a1, Vector a2) => Vector.Min(a1, a2)},
                 {"Min", (Vector3 a1, Vector3 a2) => Vector3.Min(a1, a2)},
+                {"Min", (Vector3D a1, Vector3D a2) => Vector3D.Min(a1, a2)},
                 {"Min", (Vector4 a1, Vector4 a2) => Vector4.Min(a1, a2)},
 
                 {"Mod", (float a, float b) => a % b},
 
                 {"Normalize", (Quaternion a) => Quaternion.Normalize(a)},
                 {"Normalize", (Vector2 a) => Vector2.Normalize(a)},
+                {"Normalize", (Vector a) => Vector.Normalize(a)},
                 {"Normalize", (Vector3 a) => Vector3.Normalize(a)},
+                {"Normalize", (Vector3D a) => Vector3D.Normalize(a)},
                 {"Normalize", (Vector4 a) => Vector4.Normalize(a)},
 
                 {"Pow", (float a, float b) => (float) Math.Pow(a, b)},
                 {"Quaternion.CreateFromAxisAngle", (Vector3 a, float b) => Quaternion.CreateFromAxisAngle(a, b)},
+                {"Quaternion.CreateFromAxisAngle", (Vector3D a, float b) => Quaternion.CreateFromAxisAngle(a.ToVector3(), b)},
                 {"Quaternion", (float a, float b, float c, float d) => new Quaternion(a, b, c, d)},
 
                 {"Round", (float a) => (float) Math.Round(a)},
@@ -198,14 +245,18 @@ namespace Avalonia.Rendering.Composition.Expressions
                 {"Scale", (Matrix3x2 a, float b) => a * b},
                 {"Scale", (Matrix4x4 a, float b) => a * b},
                 {"Scale", (Vector2 a, float b) => a * b},
+                {"Scale", (Vector a, float b) => a * b},
                 {"Scale", (Vector3 a, float b) => a * b},
+                {"Scale", (Vector3D a, float b) => Vector3D.Multiply(a, b)},
                 {"Scale", (Vector4 a, float b) => a * b},
 
                 {"Sin", (float a) => (float) Math.Sin(a)},
 
                 {"SmoothStep", (float a1, float a2, float a3) => SmoothStep(a1, a2, a3)},
                 {"SmoothStep", (Vector2 a1, Vector2 a2, Vector2 a3) => SmoothStep(a1, a2, a3)},
+                {"SmoothStep", (Vector a1, Vector a2, Vector a3) => SmoothStep(a1, a2, a3)},
                 {"SmoothStep", (Vector3 a1, Vector3 a2, Vector3 a3) => SmoothStep(a1, a2, a3)},
+                {"SmoothStep", (Vector3D a1, Vector3D a2, Vector3D a3) => SmoothStep(a1, a2, a3)},
                 {"SmoothStep", (Vector4 a1, Vector4 a2, Vector4 a3) => SmoothStep(a1, a2, a3)},
 
                 // I have no idea how to do a spherical interpolation for a scalar value, so we are doing a linear one
@@ -222,9 +273,13 @@ namespace Avalonia.Rendering.Composition.Expressions
                 {"Transform", (Vector2 a, Matrix3x2 b) => Vector2.Transform(a, b)},
                 {"Transform", (Vector3 a, Matrix4x4 b) => Vector3.Transform(a, b)},
 
-                {"Vector2", (float a, float b) => new Vector2(a, b)},
-                {"Vector3", (float a, float b, float c) => new Vector3(a, b, c)},
+                {"Vector2", (float a, float b) => new Vector(a, b)},
+                {"Vector2", (double a, double b) => new Vector(a, b)},
+                {"Vector3", (float a, float b, float c) => new Vector3D(a, b, c)},
+                {"Vector3", (double a, double b, double c) => new Vector3D(a, b, c)},
                 {"Vector3", (Vector2 v2, float z) => new Vector3(v2, z)},
+                {"Vector3", (Vector v2, float z) => new Vector3D(v2.X, v2.Y, z)},
+                {"Vector3", (Vector v2, double z) => new Vector3D(v2.X, v2.Y, z)},
                 {"Vector4", (float a, float b, float c, float d) => new Vector4(a, b, c, d)},
                 {"Vector4", (Vector2 v2, float z, float w) => new Vector4(v2, z, w)},
                 {"Vector4", (Vector3 v3, float w) => new Vector4(v3, w)},

+ 43 - 0
src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs

@@ -49,6 +49,46 @@ namespace Avalonia.Rendering.Composition.Expressions
                 }
             }
 
+            return CallWithCast(countGroup, arguments, out result, false);
+        }
+        
+        bool CallWithCast(List<FfiRecord> countGroup, IReadOnlyList<ExpressionVariant> arguments, out ExpressionVariant result, bool anyCast)
+        {
+            result = default;
+            foreach (var record in countGroup)
+            {
+                var match = true;
+                for (var c = 0; c < arguments.Count; c++)
+                {
+                    var parameter = record.Types[c];
+                    var arg = arguments[c].Type;
+                    if (parameter != arg)
+                    {
+                        var canCast = (parameter == VariantType.Double && arg == VariantType.Scalar)
+                                      || (parameter == VariantType.Vector3D && arg == VariantType.Vector3)
+                                      || (parameter == VariantType.Vector && arg == VariantType.Vector2)
+                                      || (anyCast && (
+                                          (arg == VariantType.Double && parameter == VariantType.Scalar)
+                                          || (arg == VariantType.Vector3D && parameter == VariantType.Vector3)
+                                          || (arg == VariantType.Vector && parameter == VariantType.Vector2)
+                                      ));
+                        if (!canCast)
+                        {
+                            match = false;
+                            break;
+                        }
+                    }
+                }
+
+                if (match)
+                {
+                    result = record.Delegate(arguments);
+                    return true;
+                }
+            }
+
+            if (anyCast == false)
+                return CallWithCast(countGroup, arguments, out result, true);
             return false;
         }
 
@@ -75,8 +115,11 @@ namespace Avalonia.Rendering.Composition.Expressions
         {
             [typeof(bool)] = VariantType.Boolean,
             [typeof(float)] = VariantType.Scalar,
+            [typeof(double)] = VariantType.Double,
             [typeof(Vector2)] = VariantType.Vector2,
+            [typeof(Vector)] = VariantType.Vector,
             [typeof(Vector3)] = VariantType.Vector3,
+            [typeof(Vector3D)] = VariantType.Vector3D,
             [typeof(Vector4)] = VariantType.Vector4,
             [typeof(Matrix3x2)] = VariantType.Matrix3x2,
             [typeof(Matrix4x4)] = VariantType.Matrix4x4,

+ 176 - 2
src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs

@@ -17,6 +17,8 @@ namespace Avalonia.Rendering.Composition.Expressions
         Vector2,
         Vector3,
         Vector4,
+        Vector,
+        Vector3D,
         AvaloniaMatrix,
         Matrix3x2,
         Matrix4x4,
@@ -38,6 +40,8 @@ namespace Avalonia.Rendering.Composition.Expressions
         [FieldOffset(4)] public Vector2 Vector2;
         [FieldOffset(4)] public Vector3 Vector3;
         [FieldOffset(4)] public Vector4 Vector4;
+        [FieldOffset(4)] public Vector Vector;
+        [FieldOffset(4)] public Vector3D Vector3D;
         [FieldOffset(4)] public Matrix AvaloniaMatrix;
         [FieldOffset(4)] public Matrix3x2 Matrix3x2;
         [FieldOffset(4)] public Matrix4x4 Matrix4x4;
@@ -55,6 +59,15 @@ namespace Avalonia.Rendering.Composition.Expressions
                     return Vector2.Y;
                 return default;
             }
+            
+            if (Type == VariantType.Vector)
+            {
+                if (ReferenceEquals(property, "X"))
+                    return Vector.X;
+                if (ReferenceEquals(property, "Y"))
+                    return Vector.Y;
+                return default;
+            }
 
             if (Type == VariantType.Vector3)
             {
@@ -78,6 +91,29 @@ namespace Avalonia.Rendering.Composition.Expressions
                     return new Vector2(Vector3.Z, Vector3.Y);
                 return default;
             }
+            
+            if (Type == VariantType.Vector3D)
+            {
+                if (ReferenceEquals(property, "X"))
+                    return Vector3D.X;
+                if (ReferenceEquals(property, "Y"))
+                    return Vector3D.Y;
+                if (ReferenceEquals(property, "Z"))
+                    return Vector3D.Z;
+                if(ReferenceEquals(property, "XY"))
+                    return new Vector(Vector3D.X, Vector3D.Y);
+                if(ReferenceEquals(property, "YX"))
+                    return new Vector(Vector3D.Y, Vector3D.X);
+                if(ReferenceEquals(property, "XZ"))
+                    return new Vector(Vector3D.X, Vector3D.Z);
+                if(ReferenceEquals(property, "ZX"))
+                    return new Vector(Vector3D.Z, Vector3D.X);
+                if(ReferenceEquals(property, "YZ"))
+                    return new Vector(Vector3D.Y, Vector3D.Z);
+                if(ReferenceEquals(property, "ZY"))
+                    return new Vector(Vector3D.Z, Vector3D.Y);
+                return default;
+            }
 
             if (Type == VariantType.Vector4)
             {
@@ -115,14 +151,20 @@ namespace Avalonia.Rendering.Composition.Expressions
                     return AvaloniaMatrix.M11;
                 if (ReferenceEquals(property, "M12"))
                     return AvaloniaMatrix.M12;
+                if (ReferenceEquals(property, "M13"))
+                    return AvaloniaMatrix.M13;
                 if (ReferenceEquals(property, "M21"))
                     return AvaloniaMatrix.M21;
                 if (ReferenceEquals(property, "M22"))
                     return AvaloniaMatrix.M22;
+                if (ReferenceEquals(property, "M23"))
+                    return AvaloniaMatrix.M23;
                 if (ReferenceEquals(property, "M31"))
                     return AvaloniaMatrix.M31;
                 if (ReferenceEquals(property, "M32"))
                     return AvaloniaMatrix.M32;
+                if (ReferenceEquals(property, "M33"))
+                    return AvaloniaMatrix.M33;
                 return default;
             }
 
@@ -220,7 +262,13 @@ namespace Avalonia.Rendering.Composition.Expressions
                 Type = VariantType.Vector2,
                 Vector2 = value
             };
-
+        
+        public static implicit operator ExpressionVariant(Vector value) =>
+            new ExpressionVariant
+            {
+                Type = VariantType.Vector,
+                Vector = value
+            };
 
         public static implicit operator ExpressionVariant(Vector3 value) =>
             new ExpressionVariant
@@ -228,6 +276,13 @@ namespace Avalonia.Rendering.Composition.Expressions
                 Type = VariantType.Vector3,
                 Vector3 = value
             };
+        
+        public static implicit operator ExpressionVariant(Vector3D value) =>
+            new ExpressionVariant
+            {
+                Type = VariantType.Vector3D,
+                Vector3D = value
+            };
 
 
         public static implicit operator ExpressionVariant(Vector4 value) =>
@@ -285,10 +340,16 @@ namespace Avalonia.Rendering.Composition.Expressions
 
             if (left.Type == VariantType.Vector2)
                 return left.Vector2 + right.Vector2;
+            
+            if (left.Type == VariantType.Vector)
+                return left.Vector + right.Vector;
 
             if (left.Type == VariantType.Vector3)
                 return left.Vector3 + right.Vector3;
 
+            if (left.Type == VariantType.Vector3D)
+                return Avalonia.Vector3D.Add(left.Vector3D, right.Vector3D);
+
             if (left.Type == VariantType.Vector4)
                 return left.Vector4 + right.Vector4;
             
@@ -317,10 +378,16 @@ namespace Avalonia.Rendering.Composition.Expressions
 
             if (left.Type == VariantType.Vector2)
                 return left.Vector2 - right.Vector2;
+            
+            if (left.Type == VariantType.Vector)
+                return left.Vector - right.Vector;
 
             if (left.Type == VariantType.Vector3)
                 return left.Vector3 - right.Vector3;
 
+            if (left.Type == VariantType.Vector3D)
+                return Vector3D.Add(left.Vector3D, -right.Vector3D);
+
             if (left.Type == VariantType.Vector4)
                 return left.Vector4 - right.Vector4;
             
@@ -347,9 +414,15 @@ namespace Avalonia.Rendering.Composition.Expressions
 
             if (left.Type == VariantType.Vector2)
                 return -left.Vector2;
+            
+            if (left.Type == VariantType.Vector)
+                return -left.Vector;
 
             if (left.Type == VariantType.Vector3)
                 return -left.Vector3;
+            
+            if (left.Type == VariantType.Vector3D)
+                return -left.Vector3D;
 
             if (left.Type == VariantType.Vector4)
                 return -left.Vector4;
@@ -383,14 +456,29 @@ namespace Avalonia.Rendering.Composition.Expressions
             if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2)
                 return left.Vector2 * right.Vector2;
 
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Vector)
+                return Vector.Multiply(left.Vector, right.Vector);
+
             if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar)
                 return left.Vector2 * right.Scalar;
+            
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar)
+                return left.Vector * right.Scalar;
+            
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Double)
+                return left.Vector * right.Double;
 
             if (left.Type == VariantType.Vector3 && right.Type == VariantType.Vector3)
                 return left.Vector3 * right.Vector3;
+            
+            if (left.Type == VariantType.Vector3D && right.Type == VariantType.Vector3D)
+                return Vector3D.Multiply(left.Vector3D, right.Vector3D);
 
             if (left.Type == VariantType.Vector3 && right.Type == VariantType.Scalar)
                 return left.Vector3 * right.Scalar;
+            
+            if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar)
+                return Vector3D.Multiply(left.Vector3D, right.Scalar);
 
             if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4)
                 return left.Vector4 * right.Vector4;
@@ -436,15 +524,33 @@ namespace Avalonia.Rendering.Composition.Expressions
             if (left.Type == VariantType.Vector2 && right.Type == VariantType.Vector2)
                 return left.Vector2 / right.Vector2;
 
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Vector)
+                return Vector.Divide(left.Vector, right.Vector);
+
             if (left.Type == VariantType.Vector2 && right.Type == VariantType.Scalar)
                 return left.Vector2 / right.Scalar;
+            
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Scalar)
+                return left.Vector / right.Scalar;
+            
+            if (left.Type == VariantType.Vector && right.Type == VariantType.Double)
+                return left.Vector / right.Scalar;
 
             if (left.Type == VariantType.Vector3 && right.Type == VariantType.Vector3)
                 return left.Vector3 / right.Vector3;
 
+            if (left.Type == VariantType.Vector3D && right.Type == VariantType.Vector3D)
+                return Vector3D.Divide(left.Vector3D, right.Vector3D);
+
             if (left.Type == VariantType.Vector3 && right.Type == VariantType.Scalar)
                 return left.Vector3 / right.Scalar;
 
+            if (left.Type == VariantType.Vector3D && right.Type == VariantType.Scalar)
+                return Avalonia.Vector3D.Divide(left.Vector3D, right.Scalar);
+            
+            if (left.Type == VariantType.Vector3D && right.Type == VariantType.Double)
+                return Avalonia.Vector3D.Divide(left.Vector3D, right.Double);
+
             if (left.Type == VariantType.Vector4 && right.Type == VariantType.Vector4)
                 return left.Vector4 / right.Vector4;
 
@@ -471,9 +577,15 @@ namespace Avalonia.Rendering.Composition.Expressions
 
             if (Type == VariantType.Vector2)
                 return Vector2 == right.Vector2;
-
+            
+            if (Type == VariantType.Vector)
+                return Vector == right.Vector;
+            
             if (Type == VariantType.Vector3)
                 return Vector3 == right.Vector3;
+            
+            if (Type == VariantType.Vector3D)
+                return Vector3D == right.Vector3D;
 
             if (Type == VariantType.Vector4)
                 return Vector4 == right.Vector4;
@@ -571,6 +683,11 @@ namespace Avalonia.Rendering.Composition.Expressions
                     res = (T) (object) Scalar;
                     return true;
                 }
+                if (Type == VariantType.Double)
+                {
+                    res = (T)(object)Scalar;
+                    return true;
+                }
             }
             
             if (typeof(T) == typeof(double))
@@ -580,6 +697,12 @@ namespace Avalonia.Rendering.Composition.Expressions
                     res = (T) (object) Double;
                     return true;
                 }
+
+                if (Type == VariantType.Scalar)
+                {
+                    res = (T)(object)(float)Double;
+                    return true;
+                }
             }
 
             if (typeof(T) == typeof(Vector2))
@@ -589,6 +712,27 @@ namespace Avalonia.Rendering.Composition.Expressions
                     res = (T) (object) Vector2;
                     return true;
                 }
+
+                if (Type == VariantType.Vector)
+                {
+                    res = (T) (object) Vector.ToVector2();
+                    return true;
+                }
+            }
+            
+            if (typeof(T) == typeof(Vector))
+            {
+                if (Type == VariantType.Vector)
+                {
+                    res = (T) (object) Vector;
+                    return true;
+                }
+
+                if (Type == VariantType.Vector2)
+                {
+                    res = (T)(object)new Vector(Vector2);
+                    return true;
+                }
             }
 
             if (typeof(T) == typeof(Vector3))
@@ -598,6 +742,26 @@ namespace Avalonia.Rendering.Composition.Expressions
                     res = (T) (object) Vector3;
                     return true;
                 }
+                if (Type == VariantType.Vector3D)
+                {
+                    res = (T) (object) Vector3D.ToVector3();
+                    return true;
+                }
+            }
+            
+            if (typeof(T) == typeof(Vector3D))
+            {
+                if (Type == VariantType.Vector3D)
+                {
+                    res = (T) (object) Vector3D;
+                    return true;
+                }
+                
+                if (Type == VariantType.Vector3)
+                {
+                    res = (T)(object)new Vector3D(Vector3);
+                    return true;
+                }
             }
 
             if (typeof(T) == typeof(Vector4))
@@ -668,9 +832,15 @@ namespace Avalonia.Rendering.Composition.Expressions
 
             if (typeof(T) == typeof(Vector2))
                 return (Vector2) (object) v;
+            
+            if (typeof(T) == typeof(Vector))
+                return (Vector) (object) v;
 
             if (typeof(T) == typeof(Vector3))
                 return (Vector3) (object) v;
+            
+            if (typeof(T) == typeof(Vector3D))
+                return (Vector3D) (object) v;
 
             if (typeof(T) == typeof(Vector4))
                 return (Vector4) (object) v;
@@ -709,8 +879,12 @@ namespace Avalonia.Rendering.Composition.Expressions
                 return Double.ToString(CultureInfo.InvariantCulture);
             if (Type == VariantType.Vector2)
                 return Vector2.ToString();
+            if (Type == VariantType.Vector)
+                return Vector.ToString();
             if (Type == VariantType.Vector3)
                 return Vector3.ToString();
+            if (Type == VariantType.Vector3D)
+                return Vector3D.ToString();
             if (Type == VariantType.Vector4)
                 return Vector4.ToString();
             if (Type == VariantType.Quaternion)

+ 19 - 14
src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs

@@ -6,41 +6,46 @@ namespace Avalonia.Rendering.Composition
 {
     static class MatrixUtils
     {
-        public static Matrix4x4 ComputeTransform(Vector2 size, Vector2 anchorPoint, Vector3 centerPoint,
-            Matrix4x4 transformMatrix, Vector3 scale, float rotationAngle, Quaternion orientation, Vector3 offset)
+        public static Matrix ComputeTransform(Vector size, Vector anchorPoint, Vector3D centerPoint,
+            Matrix transformMatrix, Vector3D scale, float rotationAngle, Quaternion orientation, Vector3D offset)
         {
             // The math here follows the *observed* UWP behavior since there are no docs on how it's supposed to work
-            
-            var anchor = size * anchorPoint;
-            var  mat = Matrix4x4.CreateTranslation(-anchor.X, -anchor.Y, 0);
 
-            var center = new Vector3(centerPoint.X, centerPoint.Y, centerPoint.Z);
+            var anchor = Vector.Multiply(size, anchorPoint);
+            var  mat = Matrix.CreateTranslation(-anchor.X, -anchor.Y);
+
+            var center = new Vector3D(centerPoint.X, centerPoint.Y, centerPoint.Z);
 
             if (!transformMatrix.IsIdentity)
                 mat = transformMatrix * mat;
 
 
-            if (scale != new Vector3(1, 1, 1))
-                mat *= Matrix4x4.CreateScale(scale, center);
+            if (scale != new Vector3D(1, 1, 1))
+                mat *= ToMatrix(Matrix4x4.CreateScale(scale.ToVector3(), center.ToVector3()));
 
             //TODO: RotationAxis support
             if (rotationAngle != 0)
-                mat *= Matrix4x4.CreateRotationZ(rotationAngle, center);
+                mat *= ToMatrix(Matrix4x4.CreateRotationZ(rotationAngle, center.ToVector3()));
 
             if (orientation != Quaternion.Identity)
             {
                 if (centerPoint != default)
                 {
-                    mat *= Matrix4x4.CreateTranslation(-center)
-                           * Matrix4x4.CreateFromQuaternion(orientation)
-                           * Matrix4x4.CreateTranslation(center);
+                    mat *= ToMatrix(Matrix4x4.CreateTranslation(-center.ToVector3())
+                                    * Matrix4x4.CreateFromQuaternion(orientation)
+                                    * Matrix4x4.CreateTranslation(center.ToVector3()));
                 }
                 else
-                    mat *= Matrix4x4.CreateFromQuaternion(orientation);
+                    mat *= ToMatrix(Matrix4x4.CreateFromQuaternion(orientation));
             }
 
             if (offset != default)
-                mat *= Matrix4x4.CreateTranslation(offset);
+            {
+                if (offset.Z == 0)
+                    mat *= Matrix.CreateTranslation(offset.X, offset.Y);
+                else
+                    mat *= ToMatrix(Matrix4x4.CreateTranslation(offset.ToVector3()));
+            }
 
             return mat;
         }

+ 3 - 4
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs

@@ -81,10 +81,9 @@ namespace Avalonia.Rendering.Composition.Server
             // If we only have translation and scale, just scale the padding
             if (CombinedTransformMatrix is
                 {
-                    M12: 0, M13: 0, M14: 0,
-                    M21: 0, M23: 0, M24: 0,
-                    M31: 0, M32: 0,  M34: 0,
-                    M43: 0, M44: 1
+                    M12: 0, M13: 0,
+                    M21: 0, M23: 0,
+                    M31: 0, M32: 0
                 })
                 padding = new Thickness(padding.Left * CombinedTransformMatrix.M11,
                     padding.Top * CombinedTransformMatrix.M22,

+ 10 - 10
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@@ -51,7 +51,7 @@ namespace Avalonia.Rendering.Composition.Server
                     canvas.PushClip(AdornedVisual._combinedTransformedClipBounds);
             }
             var transform = GlobalTransformMatrix;
-            canvas.PostTransform = MatrixUtils.ToMatrix(transform);
+            canvas.PostTransform = transform;
             canvas.Transform = Matrix.Identity;
 
             if (Effect != null)
@@ -71,7 +71,7 @@ namespace Avalonia.Rendering.Composition.Server
             RenderCore(canvas, currentTransformedClip);
             
             // Hack to force invalidation of SKMatrix
-            canvas.PostTransform = MatrixUtils.ToMatrix(transform);
+            canvas.PostTransform = transform;
             canvas.Transform = Matrix.Identity;
 
             if (OpacityMaskBrush != null)
@@ -106,8 +106,8 @@ namespace Avalonia.Rendering.Composition.Server
             return ref _readback2;
         }
 
-        public Matrix4x4 CombinedTransformMatrix { get; private set; } = Matrix4x4.Identity;
-        public Matrix4x4 GlobalTransformMatrix { get; private set; }
+        public Matrix CombinedTransformMatrix { get; private set; } = Matrix.Identity;
+        public Matrix GlobalTransformMatrix { get; private set; }
 
         public record struct UpdateResult(Rect? Bounds, bool InvalidatedOld, bool InvalidatedNew)
         {
@@ -134,12 +134,12 @@ namespace Avalonia.Rendering.Composition.Server
             {
                 CombinedTransformMatrix = MatrixUtils.ComputeTransform(Size, AnchorPoint, CenterPoint,
                     // HACK: Ignore RenderTransform set by the adorner layer
-                    AdornedVisual != null ? Matrix4x4.Identity : TransformMatrix,
+                    AdornedVisual != null ? Matrix.Identity : TransformMatrix,
                     Scale, RotationAngle, Orientation, Offset);
                 _combinedTransformDirty = false;
             }
 
-            var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix4x4.Identity;
+            var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix.Identity;
 
             var newTransform = CombinedTransformMatrix * parentTransform;
 
@@ -148,7 +148,7 @@ namespace Avalonia.Rendering.Composition.Server
             if (GlobalTransformMatrix != newTransform)
             {
                 _isBackface = Vector3.Transform(
-                    new Vector3(0, 0, float.PositiveInfinity), GlobalTransformMatrix).Z <= 0;
+                    new Vector3(0, 0, float.PositiveInfinity), MatrixUtils.ToMatrix4x4(GlobalTransformMatrix)).Z <= 0;
                 positionChanged = true;
             }
 
@@ -179,14 +179,14 @@ namespace Avalonia.Rendering.Composition.Server
                     TransformedOwnContentBounds = default;
                 else
                     TransformedOwnContentBounds =
-                        ownBounds.TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix));
+                        ownBounds.TransformToAABB(GlobalTransformMatrix);
             }
 
             if (_clipSizeDirty || positionChanged)
             {
                 _transformedClipBounds = ClipToBounds
                     ? new Rect(new Size(Size.X, Size.Y))
-                        .TransformToAABB(MatrixUtils.ToMatrix(GlobalTransformMatrix))
+                        .TransformToAABB(GlobalTransformMatrix)
                     : null;
                 
                 _clipSizeDirty = false;
@@ -249,7 +249,7 @@ namespace Avalonia.Rendering.Composition.Server
         /// </summary>
         public struct ReadbackData
         {
-            public Matrix4x4 Matrix;
+            public Matrix Matrix;
             public ulong Revision;
             public long TargetId;
             public bool Visible;

+ 1 - 1
src/Avalonia.Base/Rendering/Composition/Visual.cs

@@ -33,7 +33,7 @@ namespace Avalonia.Rendering.Composition
             }
         }
 
-        internal Matrix4x4? TryGetServerGlobalTransform()
+        internal Matrix? TryGetServerGlobalTransform()
         {
             if (Root == null)
                 return null;

+ 48 - 0
src/Avalonia.Base/Vector.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Globalization;
+using System.Numerics;
 #if !BUILDTASK
 using Avalonia.Animation.Animators;
 #endif
@@ -353,5 +354,52 @@ namespace Avalonia
             x = this._x;
             y = this._y;
         }
+
+        internal Vector2 ToVector2() => new Vector2((float)X, (float)Y);
+
+        internal Vector(Vector2 v) : this(v.X, v.Y)
+        {
+            
+        }
+
+        /// <summary>
+        /// Returns a vector whose elements are the absolute values of each of the specified vector's elements.
+        /// </summary>
+        /// <returns></returns>
+        public Vector Abs() => new(Math.Abs(X), Math.Abs(Y));
+
+        /// <summary>
+        /// Restricts a vector between a minimum and a maximum value.
+        /// </summary>
+        public static Vector Clamp(Vector value, Vector min, Vector max) => 
+            Min(Max(value, min), max);
+
+        /// <summary>
+        /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors
+        /// </summary>
+        public static Vector Max(Vector left, Vector right) =>
+            new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y));
+        
+        /// <summary>
+        /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
+        /// </summary>
+        public static Vector Min(Vector left, Vector right) =>
+            new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y));
+        
+        /// <summary>
+        /// Computes the Euclidean distance between the two given points.
+        /// </summary>
+        public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2));
+        
+        /// <summary>
+        /// Returns the Euclidean distance squared between two specified points
+        /// </summary>
+        public static double DistanceSquared(Vector value1, Vector value2)
+        {
+            var difference = value1 - value2;
+            return Dot(difference, difference);
+        }
+
+        public static implicit operator Vector(Vector2 v) => new(v);
     }
 }

+ 145 - 0
src/Avalonia.Base/Vector3D.cs

@@ -0,0 +1,145 @@
+using System;
+using System.Globalization;
+using System.Numerics;
+using Avalonia.Rendering.Composition.Expressions;
+using Avalonia.Utilities;
+
+namespace Avalonia;
+
+public readonly record struct Vector3D(double X, double Y, double Z)
+{
+    /// <summary>
+    /// Parses a <see cref="Vector"/> string.
+    /// </summary>
+    /// <param name="s">The string.</param>
+    /// <returns>The <see cref="Vector3D"/>.</returns>
+    public static Vector3D Parse(string s)
+    {
+        using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Vector."))
+        {
+            return new Vector3D(
+                tokenizer.ReadDouble(),
+                tokenizer.ReadDouble(),
+                tokenizer.ReadDouble()
+            );
+        }
+    }
+
+    internal Vector3 ToVector3() => new Vector3((float)X, (float)Y, (float)Z);
+
+    internal Vector3D(Vector3 v) : this(v.X, v.Y, v.Z)
+    {
+        
+    }
+
+    public static implicit operator Vector3D(Vector3 vector) => new(vector);
+    
+    /// <summary>
+    /// Calculates the dot product of two vectors.
+    /// </summary>
+    public static double Dot(Vector3D vector1, Vector3D vector2) =>
+        (vector1.X * vector2.X)
+        + (vector1.Y * vector2.Y)
+        + (vector1.Z * vector2.Z);
+
+    /// <summary>
+    /// Adds the second to the first vector
+    /// </summary>
+    public static Vector3D Add(Vector3D left, Vector3D right) =>
+        new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
+
+    /// <summary>
+    /// Adds the second to the first vector
+    /// </summary>
+    public static Vector3D operator +(Vector3D left, Vector3D right) => Add(left, right);
+
+    /// <summary>
+    /// Subtracts the second from the first vector
+    /// </summary>
+    public static Vector3D Substract(Vector3D left, Vector3D right) =>
+        new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
+    
+    /// <summary>
+    /// Subtracts the second from the first vector
+    /// </summary>
+    public static Vector3D operator -(Vector3D left, Vector3D right) => Substract(left, right);
+
+    /// <summary>
+    /// Negates the vector
+    /// </summary>
+    public static Vector3D operator -(Vector3D v) => new(-v.X, -v.Y, -v.Z);
+
+    /// <summary>
+    /// Multiplies the first vector by the second.
+    /// </summary>
+    public static Vector3D Multiply(Vector3D left, Vector3D right) =>
+        new(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
+
+    /// <summary>
+    /// Multiplies the vector by the given scalar.
+    /// </summary>
+    public static Vector3D Multiply(Vector3D left, double right) =>
+        new(left.X * right, left.Y * right, left.Z * right);
+
+    /// <summary>
+    /// Multiplies the vector by the given scalar.
+    /// </summary>
+    public static Vector3D operator *(Vector3D left, double right) => Multiply(left, right);
+    
+    /// <summary>
+    /// Divides the first vector by the second.
+    /// </summary>
+    public static Vector3D Divide(Vector3D left, Vector3D right) =>
+        new(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
+    
+    /// <summary>
+    /// Divides the vector by the given scalar.
+    /// </summary>
+    public static Vector3D Divide(Vector3D left, double right) =>
+        new(left.X / right, left.Y / right, left.Z / right);
+
+    /// <summary>Returns a vector whose elements are the absolute values of each of the specified vector's elements.</summary>
+    public Vector3D Abs() => new(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
+
+    /// <summary>
+    /// Restricts a vector between a minimum and a maximum value.
+    /// </summary>
+    public static Vector3D Clamp(Vector3D value, Vector3D min, Vector3D max) => 
+        Min(Max(value, min), max);
+
+    /// <summary>
+    /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors
+    /// </summary>
+    public static Vector3D Max(Vector3D left, Vector3D right) =>
+        new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y), Math.Max(left.Z, right.Z));
+        
+    /// <summary>
+    /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors
+    /// </summary>
+    public static Vector3D Min(Vector3D left, Vector3D right) =>
+        new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y), Math.Min(left.Z, right.Z));
+
+    /// <summary>
+    /// Length of the vector.
+    /// </summary>
+    public double Length => Math.Sqrt(Dot(this, this));
+    
+    /// <summary>
+    /// Returns a normalized version of this vector.
+    /// </summary>
+    public static Vector3D Normalize(Vector3D value) => Divide(value, value.Length);
+    
+    /// <summary>
+    /// Computes the squared Euclidean distance between the two given points.
+    /// </summary>
+    public static double DistanceSquared(Vector3D value1, Vector3D value2)
+    {
+        var difference = Vector3D.Substract(value1, value2);
+        return Dot(difference, difference);
+    }
+    
+    /// <summary>
+    /// Computes the Euclidean distance between the two given points.
+    /// </summary>
+    public static double Distance(Vector3D value1, Vector3D value2) => Math.Sqrt(DistanceSquared(value1, value2));
+}

+ 9 - 6
src/Avalonia.Base/composition-schema.xml

@@ -21,14 +21,14 @@
         
         <Property Name="Clip" Type="Avalonia.Platform.IGeometryImpl?" Internal="true" />
         <Property Name="ClipToBounds" Type="bool" Animated="true" DefaultValue="true"/>
-        <Property Name="Offset" Type="Vector3" Animated="true"/>
-        <Property Name="Size" Type="Vector2" Animated="true"/>
-        <Property Name="AnchorPoint" Type="Vector2" Animated="true"/>
-        <Property Name="CenterPoint" Type="Vector3" Animated="true"/>
+        <Property Name="Offset" Type="Vector3D" Animated="true"/>
+        <Property Name="Size" Type="Vector" Animated="true"/>
+        <Property Name="AnchorPoint" Type="Vector" Animated="true"/>
+        <Property Name="CenterPoint" Type="Vector3D" Animated="true"/>
         <Property Name="RotationAngle" Type="float" Animated="true"/>
         <Property Name="Orientation" Type="Quaternion" DefaultValue="Quaternion.Identity" Animated="true"/>
-        <Property Name="Scale" Type="Vector3" DefaultValue="new Vector3(1, 1, 1)" Animated="true"/>
-        <Property Name="TransformMatrix" Type="Matrix4x4" DefaultValue="Matrix4x4.Identity" Animated="true"/>
+        <Property Name="Scale" Type="Vector3D" DefaultValue="new Avalonia.Vector3D(1, 1, 1)" Animated="true"/>
+        <Property Name="TransformMatrix" Type="Avalonia.Matrix" DefaultValue="Avalonia.Matrix.Identity" Animated="true" Internal="true"/>
         <Property Name="AdornedVisual" Type="CompositionVisual?" Internal="true" />
         <Property Name="AdornerIsClipped" Type="bool" Internal="true" />
         <Property Name="OpacityMaskBrush" Type="Avalonia.Media.IImmutableBrush?" Internal="true" />
@@ -57,10 +57,13 @@
         <Property Name="Size" Type="Size" />
     </Object>
     <KeyFrameAnimation Name="Scalar" Type="float"/>
+    <KeyFrameAnimation Name="Double" Type="double"/>
     <KeyFrameAnimation Name="Boolean" Type="bool"/>
     <KeyFrameAnimation Name="Color" Type="Avalonia.Media.Color"/>
+    <KeyFrameAnimation Type="Vector"/>
     <KeyFrameAnimation Type="Vector2"/>
     <KeyFrameAnimation Type="Vector3"/>
+    <KeyFrameAnimation Type="Vector3D"/>
     <KeyFrameAnimation Type="Vector4"/>
     <KeyFrameAnimation Type="Quaternion"/>
     <Object Name="CompositionSimpleTransform" Internal="true" ServerOnly="true" ServerBase="SimpleServerRenderResource">

+ 18 - 18
src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs

@@ -223,7 +223,7 @@ namespace Avalonia.Controls
                 var visualizerVisual = ElementComposition.GetElementVisual(this);
                 if (visual != null && contentVisual != null && visualizerVisual != null)
                 {
-                    contentVisual.CenterPoint = new Vector3((float)(_content.Bounds.Width / 2), (float)(_content.Bounds.Height / 2), 0);
+                    contentVisual.CenterPoint = new Vector3D((_content.Bounds.Width / 2), (_content.Bounds.Height / 2), 0);
                     switch (RefreshVisualizerState)
                     {
                         case RefreshVisualizerState.Idle:
@@ -236,43 +236,43 @@ namespace Avalonia.Controls
                             contentVisual.Opacity = MinimumIndicatorOpacity;
                             contentVisual.RotationAngle = _startingRotationAngle;
                             visualizerVisual.Offset = IsPullDirectionVertical ?
-                                new Vector3(visualizerVisual.Offset.X, 0, 0) :
-                                new Vector3(0, visualizerVisual.Offset.Y, 0);
+                                new Vector3D(visualizerVisual.Offset.X, 0, 0) :
+                                new Vector3D(0, visualizerVisual.Offset.Y, 0);
                             _content.InvalidateMeasure();
                             break;
                         case RefreshVisualizerState.Interacting:
                             _played = false;
                             contentVisual.Opacity = MinimumIndicatorOpacity;
                             contentVisual.RotationAngle = (float)(_startingRotationAngle + _interactionRatio * 2 * Math.PI);
-                            Vector3 offset = default;
+                            Vector3D offset = default;
                             if (IsPullDirectionVertical)
                             {
-                                offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
+                                offset = new Vector3D(0, (_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
                             }
                             else
                             {
-                                offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
+                                offset = new Vector3D((_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
                             }
                             visual.Offset = offset;
                             visualizerVisual.Offset = IsPullDirectionVertical ? 
-                                new Vector3(visualizerVisual.Offset.X, offset.Y, 0) :
-                                new Vector3(offset.X, visualizerVisual.Offset.Y, 0);
+                                new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) :
+                                new Vector3D(offset.X, visualizerVisual.Offset.Y, 0);
                             break;
                         case RefreshVisualizerState.Pending:
                             contentVisual.Opacity = 1;
                             contentVisual.RotationAngle = _startingRotationAngle + (float)(2 * Math.PI);
                             if (IsPullDirectionVertical)
                             {
-                                offset = new Vector3(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
+                                offset = new Vector3D(0, (float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
                             }
                             else
                             {
-                                offset = new Vector3((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
+                                offset = new Vector3D((float)(_interactionRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
                             }
                             visual.Offset = offset;
                             visualizerVisual.Offset = IsPullDirectionVertical ? 
-                                new Vector3(visualizerVisual.Offset.X, offset.Y, 0) : 
-                                new Vector3(offset.X, visualizerVisual.Offset.Y, 0);
+                                new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) : 
+                                new Vector3D(offset.X, visualizerVisual.Offset.Y, 0);
 
                             if (!_played)
                             {
@@ -301,18 +301,18 @@ namespace Avalonia.Controls
                                 * (IsPullDirectionFar ? -1f : 1f);
                             if (IsPullDirectionVertical)
                             {
-                                offset = new Vector3(0, (float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
+                                offset = new Vector3D(0, (_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Height), 0);
                             }
                             else
                             {
-                                offset = new Vector3((float)(_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
+                                offset = new Vector3D((_executingRatio * (IsPullDirectionFar ? -1 : 1) * root.Bounds.Width), 0, 0);
                             }
                             visual.Offset = offset;
-                            contentVisual.Offset += IsPullDirectionVertical ? new Vector3(0, (float)(translationRatio * root.Bounds.Height), 0) :
-                                new Vector3((float)(translationRatio * root.Bounds.Width), 0, 0);
+                            contentVisual.Offset += IsPullDirectionVertical ? new Vector3D(0, (translationRatio * root.Bounds.Height), 0) :
+                                new Vector3D((translationRatio * root.Bounds.Width), 0, 0);
                             visualizerVisual.Offset = IsPullDirectionVertical ?
-                                new Vector3(visualizerVisual.Offset.X, offset.Y, 0) :
-                                new Vector3(offset.X, visualizerVisual.Offset.Y, 0);
+                                new Vector3D(visualizerVisual.Offset.X, offset.Y, 0) :
+                                new Vector3D(offset.X, visualizerVisual.Offset.Y, 0);
                             break;
                         case RefreshVisualizerState.Peeking:
                             contentVisual.Opacity = 1;

+ 2 - 2
src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@@ -105,7 +105,7 @@ namespace Avalonia.OpenGL.Controls
             }
             
             _visual = _compositor.CreateSurfaceVisual();
-            _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+            _visual.Size = new Vector(Bounds.Width, Bounds.Height);
             _visual.Surface = _resources.Surface;
             ElementComposition.SetElementChildVisual(this, _visual);
             using (_resources.Context.MakeCurrent())
@@ -118,7 +118,7 @@ namespace Avalonia.OpenGL.Controls
         {
             if (_visual != null && change.Property == BoundsProperty)
             {
-                _visual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height);
+                _visual.Size = new Vector(Bounds.Width, Bounds.Height);
                 RequestNextFrameRendering();
             }