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();
}
}