-
-
Save DavidKarlas/931b090e2c8a05c52aa9 to your computer and use it in GitHub Desktop.
MyTransform.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using System.Security; | |
using SFML.Window; | |
namespace SFML | |
{ | |
namespace Graphics | |
{ | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Define a 3x3 transform matrix | |
/// </summary> | |
//////////////////////////////////////////////////////////// | |
[StructLayout(LayoutKind.Sequential)] | |
public struct MyTransform | |
{ | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Construct a transform from a 3x3 matrix | |
/// </summary> | |
/// <param name="a00">Element (0, 0) of the matrix</param> | |
/// <param name="a01">Element (0, 1) of the matrix</param> | |
/// <param name="a02">Element (0, 2) of the matrix</param> | |
/// <param name="a10">Element (1, 0) of the matrix</param> | |
/// <param name="a11">Element (1, 1) of the matrix</param> | |
/// <param name="a12">Element (1, 2) of the matrix</param> | |
/// <param name="a20">Element (2, 0) of the matrix</param> | |
/// <param name="a21">Element (2, 1) of the matrix</param> | |
/// <param name="a22">Element (2, 2) of the matrix</param> | |
//////////////////////////////////////////////////////////// | |
public MyTransform(float a00, float a01, float a02, | |
float a10, float a11, float a12, | |
float a20, float a21, float a22) | |
{ | |
m00 = a00; m01 = a01; m02 = a02; | |
m10 = a10; m11 = a11; m12 = a12; | |
m20 = a20; m21 = a21; m22 = a22; | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Return the inverse of the transform. | |
/// | |
/// If the inverse cannot be computed, an identity transform | |
/// is returned. | |
/// </summary> | |
/// <returns>A new transform which is the inverse of self</returns> | |
//////////////////////////////////////////////////////////// | |
public MyTransform GetInverse() | |
{ | |
//// Compute the determinant | |
float det = m00 * (m22 * m11 - m21 * m12) - | |
m10 * (m22 * m01 - m21 * m02) + | |
m20 * (m12 * m01 - m11 * m02); | |
//// Compute the inverse if the determinant is not zero | |
//// (don't use an epsilon because the determinant may *really* be tiny) | |
if (det != 0.0f) | |
{ | |
return new MyTransform((m22 * m11 - m21 * m12) / det, | |
-(m22 * m01 - m21 * m02) / det, | |
(m12 * m01 - m11 * m02) / det, | |
-(m22 * m10 - m20 * m12) / det, | |
(m22 * m00 - m20 * m02) / det, | |
-(m12 * m00 - m10 * m02) / det, | |
(m21 * m10 - m20 * m11) / det, | |
-(m21 * m00 - m20 * m01) / det, | |
(m11 * m00 - m10 * m01) / det); | |
} | |
else | |
{ | |
return Identity; | |
} | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// MyTransform a 2D point. | |
/// </summary> | |
/// <param name="x">X coordinate of the point to transform</param> | |
/// <param name="y">Y coordinate of the point to transform</param> | |
/// <returns>Transformed point</returns> | |
//////////////////////////////////////////////////////////// | |
public Vector2f TransformPoint(float x, float y) | |
{ | |
return new Vector2f(m00 * x + m01 * y + m02, | |
m10 * x + m11 * y + m12); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// MyTransform a 2D point. | |
/// </summary> | |
/// <param name="point">Point to transform</param> | |
/// <returns>Transformed point</returns> | |
//////////////////////////////////////////////////////////// | |
public Vector2f TransformPoint(Vector2f point) | |
{ | |
return new Vector2f(m00 * point.X + m01 * point.Y + m02, | |
m10 * point.X + m11 * point.Y + m12); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// MyTransform a rectangle. | |
/// | |
/// Since SFML doesn't provide support for oriented rectangles, | |
/// the result of this function is always an axis-aligned | |
/// rectangle. Which means that if the transform contains a | |
/// rotation, the bounding rectangle of the transformed rectangle | |
/// is returned. | |
/// </summary> | |
/// <param name="rectangle">Rectangle to transform</param> | |
/// <returns>Transformed rectangle</returns> | |
//////////////////////////////////////////////////////////// | |
public FloatRect TransformRect(FloatRect rectangle) | |
{ | |
// Transform the 4 corners of the rectangle | |
float left = rectangle.Left; | |
float top = rectangle.Top; | |
float right = rectangle.Left + rectangle.Width; | |
float bottom = rectangle.Top + rectangle.Height; | |
float point1X = m00 * left + m01 * top + m02; | |
float point1Y = m10 * left + m11 * top + m12; | |
float point2X = m00 * left + m01 * bottom + m02; | |
float point2Y = m10 * left + m11 * bottom + m12; | |
float point3X = m00 * right + m01 * top + m02; | |
float point3Y = m10 * right + m11 * top + m12; | |
float point4X = m00 * right + m01 * bottom + m02; | |
float point4Y = m10 * right + m11 * bottom + m12; | |
// Compute the bounding rectangle of the transformed points | |
left = point1X; | |
top = point1Y; | |
right = point1X; | |
bottom = point1Y; | |
if (point2X < left) | |
left = point2X; | |
else if (point2X > right) | |
right = point2X; | |
if (point2Y < top) | |
top = point2Y; | |
else if (point2Y > bottom) | |
bottom = point2Y; | |
if (point3X < left) | |
left = point3X; | |
else if (point3X > right) | |
right = point3X; | |
if (point3Y < top) | |
top = point3Y; | |
else if (point3Y > bottom) | |
bottom = point3Y; | |
if (point4X < left) | |
left = point4X; | |
else if (point4X > right) | |
right = point4X; | |
if (point4Y < top) | |
top = point4Y; | |
else if (point4Y > bottom) | |
bottom = point4Y; | |
return new FloatRect(left, top, right - left, bottom - top); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with another one. | |
/// | |
/// The result is a transform that is equivalent to applying | |
/// this followed by transform. Mathematically, it is | |
/// equivalent to a matrix multiplication. | |
/// </summary> | |
/// <param name="transform">MyTransform to combine to this transform</param> | |
//////////////////////////////////////////////////////////// | |
public void Combine(MyTransform transform) | |
{ | |
float a00 = m00 * transform.m00 + m01 * transform.m10 + m02 * transform.m20; | |
float a01 = m00 * transform.m01 + m01 * transform.m11 + m02 * transform.m21; | |
float a02 = m00 * transform.m02 + m01 * transform.m12 + m02 * transform.m22; | |
float a10 = m10 * transform.m00 + m11 * transform.m10 + m12 * transform.m20; | |
float a11 = m10 * transform.m01 + m11 * transform.m11 + m12 * transform.m21; | |
float a12 = m10 * transform.m02 + m11 * transform.m12 + m12 * transform.m22; | |
float a20 = m20 * transform.m00 + m21 * transform.m10 + m22 * transform.m20; | |
float a21 = m20 * transform.m01 + m21 * transform.m11 + m22 * transform.m21; | |
float a22 = m20 * transform.m02 + m21 * transform.m12 + m22 * transform.m22; | |
m00 = a00; m01 = a01; m02 = a02; | |
m10 = a10; m11 = a11; m12 = a12; | |
m20 = a20; m21 = a21; m22 = a22; | |
} | |
public void Combine(float b00, float b01, float b02, | |
float b10, float b11, float b12, | |
float b20, float b21, float b22) | |
{ | |
float a00 = m00 * b00 + m01 * b10 + m02 * b20; | |
float a01 = m00 * b01 + m01 * b11 + m02 * b21; | |
float a02 = m00 * b02 + m01 * b12 + m02 * b22; | |
float a10 = m10 * b00 + m11 * b10 + m12 * b20; | |
float a11 = m10 * b01 + m11 * b11 + m12 * b21; | |
float a12 = m10 * b02 + m11 * b12 + m12 * b22; | |
float a20 = m20 * b00 + m21 * b10 + m22 * b20; | |
float a21 = m20 * b01 + m21 * b11 + m22 * b21; | |
float a22 = m20 * b02 + m21 * b12 + m22 * b22; | |
m00 = a00; m01 = a01; m02 = a02; | |
m10 = a10; m11 = a11; m12 = a12; | |
m20 = a20; m21 = a21; m22 = a22; | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a translation. | |
/// </summary> | |
/// <param name="x">Offset to apply on X axis</param> | |
/// <param name="y">Offset to apply on Y axis</param> | |
//////////////////////////////////////////////////////////// | |
public void Translate(float x, float y) | |
{ | |
Combine(1, 0, x, | |
0, 1, y, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a translation. | |
/// </summary> | |
/// <param name="offset">Translation offset to apply</param> | |
//////////////////////////////////////////////////////////// | |
public void Translate(Vector2f offset) | |
{ | |
Combine(1, 0, offset.X, | |
0, 1, offset.Y, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a rotation. | |
/// </summary> | |
/// <param name="angle">Rotation angle, in degrees</param> | |
//////////////////////////////////////////////////////////// | |
public void Rotate(float angle) | |
{ | |
double rad = angle * Math.PI / 180.0; | |
float cos = (float)Math.Cos(rad); | |
float sin = (float)Math.Sin(rad); | |
Combine(cos, -sin, 0, | |
sin, cos, 0, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a rotation. | |
/// | |
/// The center of rotation is provided for convenience as a second | |
/// argument, so that you can build rotations around arbitrary points | |
/// more easily (and efficiently) than the usual | |
/// Translate(-center); Rotate(angle); Translate(center). | |
/// </summary> | |
/// <param name="angle">Rotation angle, in degrees</param> | |
/// <param name="centerX">X coordinate of the center of rotation</param> | |
/// <param name="centerY">Y coordinate of the center of rotation</param> | |
//////////////////////////////////////////////////////////// | |
public void Rotate(float angle, float centerX, float centerY) | |
{ | |
double rad = angle * Math.PI / 180.0; | |
float cos = (float)Math.Cos(rad); | |
float sin = (float)Math.Sin(rad); | |
Combine(cos, -sin, centerX * (1 - cos) + centerY * sin, | |
sin, cos, centerY * (1 - cos) - centerX * sin, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a rotation. | |
/// | |
/// The center of rotation is provided for convenience as a second | |
/// argument, so that you can build rotations around arbitrary points | |
/// more easily (and efficiently) than the usual | |
/// Translate(-center); Rotate(angle); Translate(center). | |
/// </summary> | |
/// <param name="angle">Rotation angle, in degrees</param> | |
/// <param name="center">Center of rotation</param> | |
//////////////////////////////////////////////////////////// | |
public void Rotate(float angle, Vector2f center) | |
{ | |
double rad = angle * Math.PI / 180.0; | |
float cos = (float)Math.Cos(rad); | |
float sin = (float)Math.Sin(rad); | |
Combine(cos, -sin, center.X * (1 - cos) + center.Y * sin, | |
sin, cos, center.Y * (1 - cos) - center.X * sin, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a scaling. | |
/// </summary> | |
/// <param name="scaleX">Scaling factor on the X axis</param> | |
/// <param name="scaleY">Scaling factor on the Y axis</param> | |
//////////////////////////////////////////////////////////// | |
public void Scale(float scaleX, float scaleY) | |
{ | |
Combine(scaleX, 0, 0, | |
0, scaleY, 0, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a scaling. | |
/// | |
/// The center of scaling is provided for convenience as a second | |
/// argument, so that you can build scaling around arbitrary points | |
/// more easily (and efficiently) than the usual | |
/// Translate(-center); Scale(factors); Translate(center). | |
/// </summary> | |
/// <param name="scaleX">Scaling factor on X axis</param> | |
/// <param name="scaleY">Scaling factor on Y axis</param> | |
/// <param name="centerX">X coordinate of the center of scaling</param> | |
/// <param name="centerY">Y coordinate of the center of scaling</param> | |
//////////////////////////////////////////////////////////// | |
public void Scale(float scaleX, float scaleY, float centerX, float centerY) | |
{ | |
Combine(scaleX, 0, centerX * (1 - scaleX), | |
0, scaleY, centerY * (1 - scaleY), | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a scaling. | |
/// </summary> | |
/// <param name="factors">Scaling factors</param> | |
//////////////////////////////////////////////////////////// | |
public void Scale(Vector2f factors) | |
{ | |
Combine(factors.X, 0, 0, | |
0, factors.Y, 0, | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Combine the current transform with a scaling. | |
/// | |
/// The center of scaling is provided for convenience as a second | |
/// argument, so that you can build scaling around arbitrary points | |
/// more easily (and efficiently) than the usual | |
/// Translate(-center); Scale(factors); Translate(center). | |
/// </summary> | |
/// <param name="factors">Scaling factors</param> | |
/// <param name="center">Center of scaling</param> | |
//////////////////////////////////////////////////////////// | |
public void Scale(Vector2f factors, Vector2f center) | |
{ | |
Combine(factors.X, 0, center.X * (1 - factors.X), | |
0, factors.Y, center.Y * (1 - factors.Y), | |
0, 0, 1); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Overload of binary operator * to combine two transforms. | |
/// This call is equivalent to calling new MyTransform(left).Combine(right). | |
/// </summary> | |
/// <param name="left">Left operand (the first transform)</param> | |
/// <param name="right">Right operand (the second transform)</param> | |
/// <returns>New combined transform</returns> | |
//////////////////////////////////////////////////////////// | |
public static MyTransform operator *(MyTransform left, MyTransform right) | |
{ | |
left.Combine(right); | |
return left; | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Overload of binary operator * to transform a point. | |
/// This call is equivalent to calling left.TransformPoint(right). | |
/// </summary> | |
/// <param name="left">Left operand (the transform)</param> | |
/// <param name="right">Right operand (the point to transform)</param> | |
/// <returns>New transformed point</returns> | |
//////////////////////////////////////////////////////////// | |
public static Vector2f operator *(MyTransform left, Vector2f right) | |
{ | |
return left.TransformPoint(right); | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary>The identity transform (does nothing)</summary> | |
//////////////////////////////////////////////////////////// | |
public static MyTransform Identity | |
{ | |
get | |
{ | |
return new MyTransform(1, 0, 0, | |
0, 1, 0, | |
0, 0, 1); | |
} | |
} | |
//////////////////////////////////////////////////////////// | |
/// <summary> | |
/// Provide a string describing the object | |
/// </summary> | |
/// <returns>String description of the object</returns> | |
//////////////////////////////////////////////////////////// | |
public override string ToString() | |
{ | |
return "[Transform]" + | |
" Matrix(" + | |
m00 + ", " + m01 + ", " + m02 + | |
m10 + ", " + m11 + ", " + m12 + | |
m20 + ", " + m21 + ", " + m22 + | |
")"; | |
} | |
float m00, m01, m02; | |
float m10, m11, m12; | |
float m20, m21, m22; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment