diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs index 0d3061f361..5bf46510dc 100644 --- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs +++ b/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 { diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs index cc4429f414..c81ba4fb17 100644 --- a/samples/ControlCatalog/Pages/GesturePage.cs +++ b/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(); - 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; } diff --git a/samples/GpuInterop/DrawingSurfaceDemoBase.cs b/samples/GpuInterop/DrawingSurfaceDemoBase.cs index aad813ea82..367436e5a5 100644 --- a/samples/GpuInterop/DrawingSurfaceDemoBase.cs +++ b/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) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6929f192c7..f96abcac96 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,4 +1,11 @@ + + + + + + + diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs b/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs index e19c0ff098..0df21c2c1d 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/Interpolators.cs +++ b/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 + { + public double Interpolate(double @from, double to, float progress) => @from + (to - @from) * progress; + + public static DoubleInterpolator Instance { get; } = new (); + } class Vector2Interpolator : IInterpolator { @@ -28,6 +34,15 @@ namespace Avalonia.Rendering.Composition.Animations public static Vector2Interpolator Instance { get; } = new Vector2Interpolator(); } + class VectorInterpolator : IInterpolator + { + 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 { 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 + { + 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 { public Vector4 Interpolate(Vector4 @from, Vector4 to, float progress) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 61a3f7e831..368743f1b5 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/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 { diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs b/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs index b7ed6fe612..598d4163d1 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionCustomVisualHandler.cs +++ b/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 { diff --git a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs b/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs index 91d2fb59de..121d11bf2a 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositionTarget.cs +++ b/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); } diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs index 896ae4fc3a..2fdf163153 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/BuiltInExpressionFfi.cs +++ b/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)}, diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs index c15487065c..ad8d370478 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/DelegateExpressionFfi.cs +++ b/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 countGroup, IReadOnlyList 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, diff --git a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs b/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs index 21f14283b5..a97b959fe1 100644 --- a/src/Avalonia.Base/Rendering/Composition/Expressions/ExpressionVariant.cs +++ b/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) diff --git a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs b/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs index 3d8d5bae78..714895cd71 100644 --- a/src/Avalonia.Base/Rendering/Composition/MatrixUtils.cs +++ b/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; } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs index b9e6833d21..c63e7917b3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs +++ b/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, diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 0eeffda123..3ff1bf2b98 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/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 /// public struct ReadbackData { - public Matrix4x4 Matrix; + public Matrix Matrix; public ulong Revision; public long TargetId; public bool Visible; diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs index 6d6818256a..f4f1f3e8d9 100644 --- a/src/Avalonia.Base/Rendering/Composition/Visual.cs +++ b/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; diff --git a/src/Avalonia.Base/Vector.cs b/src/Avalonia.Base/Vector.cs index 166ae6b93b..15722901a6 100644 --- a/src/Avalonia.Base/Vector.cs +++ b/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) + { + + } + + /// + /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. + /// + /// + public Vector Abs() => new(Math.Abs(X), Math.Abs(Y)); + + /// + /// Restricts a vector between a minimum and a maximum value. + /// + public static Vector Clamp(Vector value, Vector min, Vector max) => + Min(Max(value, min), max); + + /// + /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors + /// + public static Vector Max(Vector left, Vector right) => + new(Math.Max(left.X, right.X), Math.Max(left.Y, right.Y)); + + /// + /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors + /// + public static Vector Min(Vector left, Vector right) => + new(Math.Min(left.X, right.X), Math.Min(left.Y, right.Y)); + + /// + /// Computes the Euclidean distance between the two given points. + /// + public static double Distance(Vector value1, Vector value2) => Math.Sqrt(DistanceSquared(value1, value2)); + + /// + /// Returns the Euclidean distance squared between two specified points + /// + 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); } } diff --git a/src/Avalonia.Base/Vector3D.cs b/src/Avalonia.Base/Vector3D.cs new file mode 100644 index 0000000000..ec6463c987 --- /dev/null +++ b/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) +{ + /// + /// Parses a string. + /// + /// The string. + /// The . + 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); + + /// + /// Calculates the dot product of two vectors. + /// + public static double Dot(Vector3D vector1, Vector3D vector2) => + (vector1.X * vector2.X) + + (vector1.Y * vector2.Y) + + (vector1.Z * vector2.Z); + + /// + /// Adds the second to the first vector + /// + public static Vector3D Add(Vector3D left, Vector3D right) => + new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + + /// + /// Adds the second to the first vector + /// + public static Vector3D operator +(Vector3D left, Vector3D right) => Add(left, right); + + /// + /// Subtracts the second from the first vector + /// + public static Vector3D Substract(Vector3D left, Vector3D right) => + new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + + /// + /// Subtracts the second from the first vector + /// + public static Vector3D operator -(Vector3D left, Vector3D right) => Substract(left, right); + + /// + /// Negates the vector + /// + public static Vector3D operator -(Vector3D v) => new(-v.X, -v.Y, -v.Z); + + /// + /// Multiplies the first vector by the second. + /// + public static Vector3D Multiply(Vector3D left, Vector3D right) => + new(left.X * right.X, left.Y * right.Y, left.Z * right.Z); + + /// + /// Multiplies the vector by the given scalar. + /// + public static Vector3D Multiply(Vector3D left, double right) => + new(left.X * right, left.Y * right, left.Z * right); + + /// + /// Multiplies the vector by the given scalar. + /// + public static Vector3D operator *(Vector3D left, double right) => Multiply(left, right); + + /// + /// Divides the first vector by the second. + /// + public static Vector3D Divide(Vector3D left, Vector3D right) => + new(left.X / right.X, left.Y / right.Y, left.Z / right.Z); + + /// + /// Divides the vector by the given scalar. + /// + public static Vector3D Divide(Vector3D left, double right) => + new(left.X / right, left.Y / right, left.Z / right); + + /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. + public Vector3D Abs() => new(Math.Abs(X), Math.Abs(Y), Math.Abs(Z)); + + /// + /// Restricts a vector between a minimum and a maximum value. + /// + public static Vector3D Clamp(Vector3D value, Vector3D min, Vector3D max) => + Min(Max(value, min), max); + + /// + /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors + /// + 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)); + + /// + /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors + /// + 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)); + + /// + /// Length of the vector. + /// + public double Length => Math.Sqrt(Dot(this, this)); + + /// + /// Returns a normalized version of this vector. + /// + public static Vector3D Normalize(Vector3D value) => Divide(value, value.Length); + + /// + /// Computes the squared Euclidean distance between the two given points. + /// + public static double DistanceSquared(Vector3D value1, Vector3D value2) + { + var difference = Vector3D.Substract(value1, value2); + return Dot(difference, difference); + } + + /// + /// Computes the Euclidean distance between the two given points. + /// + public static double Distance(Vector3D value1, Vector3D value2) => Math.Sqrt(DistanceSquared(value1, value2)); +} \ No newline at end of file diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index 97b2686584..05aea3808e 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -21,14 +21,14 @@ - - - - + + + + - - + + @@ -57,10 +57,13 @@ + + + diff --git a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs b/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs index 39ff8e3a92..406b4443a6 100644 --- a/src/Avalonia.Controls/PullToRefresh/RefreshVisualizer.cs +++ b/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; diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs index 83bf795b03..cefc2a4c06 100644 --- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs +++ b/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(); }