1 /*
2 DSFML - The Simple and Fast Multimedia Library for D
3 
4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5 
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose, including commercial applications,
10 and to alter it and redistribute it freely, subject to the following restrictions:
11 
12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
14 
15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
16 
17 3. This notice may not be removed or altered from any source distribution
18 */
19 
20 module dsfml.graphics.transform;
21 
22 import dsfml.system.vector2;
23 import dsfml.graphics.rect;
24 public import std.math;
25 
26 
27 /++
28  + Define a 3x3 transform matrix.
29  + 
30  + A Transform specifies how to translate, rotate, scale, shear, project, whatever things.
31  + 
32  + In mathematical terms, it defines how to transform a coordinate system into another.
33  + 
34  + For example, if you apply a rotation transform to a sprite, the result will be a rotated sprite. And anything that is transformed by this rotation transform will be rotated the same way, according to its initial position.
35  + 
36  + Transforms are typically used for drawing. But they can also be used for any computation that requires to transform points between the local and global coordinate systems of an entity (like collision detection).
37  + 
38  + Authors: Laurent Gomila, Jeremy DeHaan
39  + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Transform.php#details
40  +/
41 struct Transform
42 {
43 	float[9] m_matrix = [1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f];
44 
45 	/**
46 	 * Construct a 3x3 matrix.
47 	 * 
48 	 * Params:
49 	 * 		a00	= Element (0, 0) of the matrix
50 	 * 		a01	= Element (0, 1) of the matrix
51 	 * 		a02	= Element (0, 2) of the matrix
52 	 * 		a10	= Element (1, 0) of the matrix
53 	 * 		a11	= Element (1, 1) of the matrix
54 	 * 		a12	= Element (1, 2) of the matrix
55 	 * 		a20	= Element (2, 0) of the matrix
56 	 * 		a21	= Element (2, 1) of the matrix
57 	 * 		a22	= Element (2, 2) of the matrix
58 	 */
59 	this(float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22)
60 	{
61 		m_matrix = [a00, a01, a02, a10, a11, a12, a20, a21, a22];
62 	}
63 	
64 	this(float[9] newMatrix)
65 	{
66 		m_matrix = newMatrix.dup;
67 	}
68 
69 	/**
70 	 * Return the inverse of the transform.
71 	 * 
72 	 * If the inverse cannot be computed, an identity transform is returned.
73 	 * 
74 	 * Returns: A new transform which is the inverse of self.
75 	 */
76 	Transform getInverse() const
77 	{
78 		float[9] temp;
79 		sfTransform_getInverse(m_matrix.ptr,temp.ptr);
80 		return Transform(temp);
81 	}
82 
83 	/**
84 	 * Return the transform as a 4x4 matrix.
85 	 * 
86 	 * This function returns a pointer to an array of 16 floats containing the transform elements as a 4x4 matrix, which is directly compatible with OpenGL functions.
87 	 * 
88 	 * Returns: A 4x4 matrix.
89 	 */
90 	const(float)[] getMatrix()
91 	{
92 		static float[16] temp;
93 		
94 		sfTransform_getMatrix(m_matrix.ptr, temp.ptr);
95 		
96 		return temp.dup;
97 	}
98 
99 	/**
100 	 * Combine the current transform with another one.
101 	 * 
102 	 * The result is a transform that is equivalent to applying this followed by transform. Mathematically, it is equivalent to a matrix multiplication.
103 	 * 
104 	 * Params:
105 	 * 		transform	= Transform to combine with this one.
106 	 * 
107 	 * Returns: Reference to this.
108 	 */
109 	void combine(Transform otherTransform)
110 	{
111 		sfTransform_combine(m_matrix.ptr, otherTransform.m_matrix.ptr);
112 	}
113 
114 	/**
115 	 * Transform a 2D point.
116 	 * 
117 	 * Params:
118 	 * 		x	= X coordinate of the point to transform.
119 	 * 		y	= Y coordinate of the point to transform.
120 	 * 
121 	 * Returns: Transformed point.
122 	 */
123 	Vector2f transformPoint(Vector2f point) const
124 	{
125 		Vector2f temp;
126 		sfTransform_transformPoint(m_matrix.ptr,point.x, point.y, &temp.x, &temp.y);
127 		return temp;	
128 	}
129 
130 	/**
131 	 * Transform a rectangle.
132 	 * 
133 	 * 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.
134 	 * 
135 	 * Params:
136 	 * 		rectangle	= Rectangle to transform.
137 	 * 
138 	 * Returns: Transformed rectangle.
139 	 */
140 	FloatRect transformRect(const(FloatRect) rect)const
141 	{
142 		FloatRect temp;
143 		sfTransform_transformRect(m_matrix.ptr,rect.left, rect.top, rect.width, rect.height, &temp.left, &temp.top, &temp.width, &temp.height);
144 		return temp;
145 	}
146 
147 	//TODO: These functions should probably return this; like the documentation states.
148 	/**
149 	 * Combine the current transform with a translation.
150 	 * 
151 	 * This function returns a reference to this, so that calls can be chained.
152 	 * 
153 	 * Params:
154 	 * 		offset	= Translation offset to apply.
155 	 * 
156 	 * Returns: this
157 	 */
158 	void translate(float x, float y)
159 	{
160 		sfTransform_translate(m_matrix.ptr, x, y);
161 	}
162 
163 	/**
164 	 * Combine the current transform with a rotation.
165 	 * 
166 	 * This function returns a reference to this, so that calls can be chained.
167 	 * 
168 	 * Params:
169 	 * 		angle	= Rotation angle, in degrees.
170 	 * 
171 	 * Returns: this
172 	 */
173 	void rotate(float angle)
174 	{
175 		sfTransform_rotate(m_matrix.ptr, angle);
176 	}
177 
178 	/**
179 	 * Combine the current transform with a rotation.
180 	 * 
181 	 * 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).
182 	 * 
183 	 * This function returns a reference to this, so that calls can be chained.
184 	 * 
185 	 * Params:
186 	 * 		angle	= Rotation angle, in degrees.
187 	 * 		center	= Center of rotation
188 	 * 
189 	 * Returns: this
190 	 */
191 	void rotate(float angle, float centerX, float centerY)
192 	{
193 		sfTransform_rotateWithCenter(m_matrix.ptr, angle, centerX, centerY);
194 	}
195 
196 	/**
197 	 * Combine the current transform with a scaling.
198 	 * 
199 	 * This function returns a reference to this, so that calls can be chained.
200 	 * 
201 	 * Params:
202 	 * 		scaleX	= Scaling factor on the X-axis.
203 	 * 		scaleY	= Scaling factor on the Y-axis.
204 	 * 
205 	 * Returns: this
206 	 */
207 	void scale(float scaleX, float scaleY)
208 	{
209 		sfTransform_scale(m_matrix.ptr, scaleX, scaleY);	
210 	}
211 
212 	/**
213 	 * Combine the current transform with a scaling.
214 	 * 
215 	 * 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).
216 	 * 
217 	 * This function returns a reference to this, so that calls can be chained.
218 	 * 
219 	 * Params:
220 	 * 		scaleX	= Scaling factor on the X-axis.
221 	 * 		scaleY	= Scaling factor on the Y-axis.
222 	 * 		centerX	= X coordinate of the center of scaling
223 	 * 		centerY	= Y coordinate of the center of scaling
224 	 * 
225 	 * Returns: this
226 	 */
227 	void scale(float scaleX, float scaleY, float centerX, float centerY)
228 	{
229 		sfTransform_scaleWithCenter(m_matrix.ptr, scaleX, scaleY, centerX, centerY);
230 	}
231 
232 	string toString()
233 	{
234 		return "";//text(InternalsfTransform.matrix);
235 	}
236 
237 	Transform opBinary(string op)(Transform rhs)
238 		if(op == "*")
239 	{
240 		Transform temp = this;//Transform(InternalsfTransform);
241 		temp.combine(rhs);
242 		return temp;
243 	}
244 	
245 	ref Transform opOpAssign(string op)(Transform rhs)
246 		if(op == "*")
247 	{
248 		
249 		this.combine(rhs);
250 		return this;
251 	}
252 	
253 	Transform opBinary(string op)(Vector2f vector)
254 		if(op == "*")
255 	{
256 		return transformPoint(vector);
257 	}
258 
259 	/// Indentity transform (does nothing).
260 	static const(Transform) Identity;
261 }
262 
263 private extern(C):
264 
265 //Return the 4x4 matrix of a transform
266 void sfTransform_getMatrix(const float* transform, float* matrix);
267 
268 //Return the inverse of a transform
269 void sfTransform_getInverse(const float* transform, float* inverse);
270 
271 //Apply a transform to a 2D point
272 void sfTransform_transformPoint(const float* transform, float xIn, float yIn, float* xOut, float* yOut);
273 
274 //Apply a transform to a rectangle
275 void sfTransform_transformRect(const float* transform, float leftIn, float topIn, float widthIn, float heightIn, float* leftOut, float* topOut, float* widthOut, float* heightOut);
276 
277 //Combine two transforms
278 void sfTransform_combine(float* transform, const float* other);
279 
280 //Combine a transform with a translation
281 void sfTransform_translate(float* transform, float x, float y);
282 
283 //Combine the current transform with a rotation
284 void sfTransform_rotate(float* transform, float angle);
285 
286 //Combine the current transform with a rotation
287 void sfTransform_rotateWithCenter(float* transform, float angle, float centerX, float centerY);
288 
289 //Combine the current transform with a scaling
290 void sfTransform_scale(float* transform, float scaleX, float scaleY);
291 
292 //Combine the current transform with a scaling
293 void sfTransform_scaleWithCenter(float* transform, float scaleX, float scaleY, float centerX, float centerY);
294 
295