using System; using System.Numerics; using Avalonia.Animation.Animators; namespace Avalonia.Media; /// /// Non-Affine 3D transformation for rotating a visual around a definable axis /// public sealed class Rotate3DTransform : Transform { private readonly bool _isInitializing; /// /// Defines the property. /// public static readonly StyledProperty AngleXProperty = AvaloniaProperty.Register(nameof(AngleX)); /// /// Defines the property. /// public static readonly StyledProperty AngleYProperty = AvaloniaProperty.Register(nameof(AngleY)); /// /// Defines the property. /// public static readonly StyledProperty AngleZProperty = AvaloniaProperty.Register(nameof(AngleZ)); /// /// Defines the property. /// public static readonly StyledProperty CenterXProperty = AvaloniaProperty.Register(nameof(CenterX)); /// /// Defines the property. /// public static readonly StyledProperty CenterYProperty = AvaloniaProperty.Register(nameof(CenterY)); /// /// Defines the property. /// public static readonly StyledProperty CenterZProperty = AvaloniaProperty.Register(nameof(CenterZ)); /// /// Defines the property. /// public static readonly StyledProperty DepthProperty = AvaloniaProperty.Register(nameof(Depth)); /// /// Initializes a new instance of the class. /// public Rotate3DTransform() { } /// /// Initializes a new instance of the class. /// /// The rotation around the X-Axis /// The rotation around the Y-Axis /// The rotation around the Z-Axis /// The origin of the X-Axis /// The origin of the Y-Axis /// The origin of the Z-Axis /// The depth of the 3D effect public Rotate3DTransform( double angleX, double angleY, double angleZ, double centerX, double centerY, double centerZ, double depth) : this() { _isInitializing = true; AngleX = angleX; AngleY = angleY; AngleZ = angleZ; CenterX = centerX; CenterY = centerY; CenterZ = centerZ; Depth = depth; _isInitializing = false; } /// /// Sets the rotation around the X-Axis /// public double AngleX { get => GetValue(AngleXProperty); set => SetValue(AngleXProperty, value); } /// /// Sets the rotation around the Y-Axis /// public double AngleY { get => GetValue(AngleYProperty); set => SetValue(AngleYProperty, value); } /// /// Sets the rotation around the Z-Axis /// public double AngleZ { get => GetValue(AngleZProperty); set => SetValue(AngleZProperty, value); } /// /// Moves the origin the X-Axis rotates around /// public double CenterX { get => GetValue(CenterXProperty); set => SetValue(CenterXProperty, value); } /// /// Moves the origin the Y-Axis rotates around /// public double CenterY { get => GetValue(CenterYProperty); set => SetValue(CenterYProperty, value); } /// /// Moves the origin the Z-Axis rotates around /// public double CenterZ { get => GetValue(CenterZProperty); set => SetValue(CenterZProperty, value); } /// /// Affects the depth of the rotation effect /// public double Depth { get => GetValue(DepthProperty); set => SetValue(DepthProperty, value); } /// /// Gets the transform's . /// public override Matrix Value { get { var matrix44 = Matrix4x4.Identity; //Copy values first, because it's not guaranteed, that values will not change during calculation var (copyCenterX, copyCenterY, copyCenterZ, copyAngleX, copyAngleY, copyAngleZ, copyDepth) = (CenterX, CenterY, CenterZ, AngleX, AngleY, AngleZ, Depth); var centerSum = copyCenterX + copyCenterY + copyCenterZ; if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); if (copyAngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); if (copyAngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); if (copyAngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); if (copyDepth != 0) { var perspectiveMatrix = Matrix4x4.Identity; perspectiveMatrix.M34 = -1 / (float)copyDepth; matrix44 *= perspectiveMatrix; } var matrix = new Matrix( matrix44.M11, matrix44.M12, matrix44.M14, matrix44.M21, matrix44.M22, matrix44.M24, matrix44.M41, matrix44.M42, matrix44.M44); return matrix; } } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (!_isInitializing) RaiseChanged(); } }