Skip to content

Instantly share code, notes, and snippets.

@DavidKarlas
Created May 28, 2013 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DavidKarlas/931b090e2c8a05c52aa9 to your computer and use it in GitHub Desktop.
Save DavidKarlas/931b090e2c8a05c52aa9 to your computer and use it in GitHub Desktop.
MyTransform.cs
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