1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 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
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  * A $(U Transform) specifies how to translate, rotate, scale, shear, project,
27  * whatever things. In mathematical terms, it defines how to transform a
28  * coordinate system into another.
29  *
30  * For example, if you apply a rotation transform to a sprite, the result will
31  * be a rotated sprite. And anything that is transformed by this rotation
32  * transform will be rotated the same way, according to its initial position.
33  *
34  * Transforms are typically used for drawing. But they can also be used for any
35  * computation that requires to transform points between the local and global
36  * coordinate systems of an entity (like collision detection).
37  *
38  * Example:
39  * ---
40  * // define a translation transform
41  * Transform translation;
42  * translation.translate(20, 50);
43  *
44  * // define a rotation transform
45  * Transform rotation;
46  * rotation.rotate(45);
47  *
48  * // combine them
49  * Transform transform = translation * rotation;
50  *
51  * // use the result to transform stuff...
52  * Vector2f point = transform.transformPoint(Vector2f(10, 20));
53  * FloatRect rect = transform.transformRect(FloatRect(0, 0, 10, 100));
54  * ---
55  *
56  * See_Also:
57  * $(TRANSFORMABLE_LINK), $(RENDERSTATES_LINK)
58  */
59 module dsfml.graphics.transform;
60 
61 import dsfml.system.vector2;
62 import dsfml.graphics.rect;
63 public import std.math;
64 
65 
66 /**
67  * Define a 3x3 transform matrix.
68  */
69 struct Transform
70 {
71 	float[9] m_matrix = [1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f];
72 
73 	/**
74 	 * Construct a 3x3 matrix.
75 	 *
76 	 * Params:
77 	 * 		a00	= Element (0, 0) of the matrix
78 	 * 		a01	= Element (0, 1) of the matrix
79 	 * 		a02	= Element (0, 2) of the matrix
80 	 * 		a10	= Element (1, 0) of the matrix
81 	 * 		a11	= Element (1, 1) of the matrix
82 	 * 		a12	= Element (1, 2) of the matrix
83 	 * 		a20	= Element (2, 0) of the matrix
84 	 * 		a21	= Element (2, 1) of the matrix
85 	 * 		a22	= Element (2, 2) of the matrix
86 	 */
87 	this(float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22)
88 	{
89 		m_matrix = [a00, a01, a02, a10, a11, a12, a20, a21, a22];
90 	}
91 
92 	/// Construct a 3x3 matrix from a float array.
93 	this(float[9] newMatrix)
94 	{
95 		m_matrix = newMatrix.dup;
96 	}
97 
98 	/**
99 	 * Return the inverse of the transform.
100 	 *
101 	 * If the inverse cannot be computed, an identity transform is returned.
102 	 *
103 	 * Returns: A new transform which is the inverse of self.
104 	 */
105 	Transform getInverse() const
106 	{
107 		float[9] temp;
108 		sfTransform_getInverse(m_matrix.ptr,temp.ptr);
109 		return Transform(temp);
110 	}
111 
112 	/**
113 	 * Return the transform as a 4x4 matrix.
114 	 *
115 	 * This function returns a pointer to an array of 16 floats containing the
116 	 * transform elements as a 4x4 matrix, which is directly compatible with
117 	 * OpenGL functions.
118 	 *
119 	 * Returns: A 4x4 matrix.
120 	 */
121 	const(float)[] getMatrix() const
122 	{
123 		static float[16] temp;
124 
125 		sfTransform_getMatrix(m_matrix.ptr, temp.ptr);
126 
127 		return temp.dup;
128 	}
129 
130 	/**
131 	 * Combine the current transform with another one.
132 	 *
133 	 * The result is a transform that is equivalent to applying this followed by
134 	 * transform. Mathematically, it is equivalent to a matrix multiplication.
135 	 *
136 	 * Params:
137 	 * 		transform	= Transform to combine with this one
138 	 *
139 	 * Returns: Reference to this.
140 	 */
141 	void combine(Transform otherTransform)
142 	{
143 		sfTransform_combine(m_matrix.ptr, otherTransform.m_matrix.ptr);
144 	}
145 
146 	/**
147 	 * Transform a 2D point.
148 	 *
149 	 * Params:
150 	 * 		x	= X coordinate of the point to transform
151 	 * 		y	= Y coordinate of the point to transform
152 	 *
153 	 * Returns: Transformed point.
154 	 */
155 	Vector2f transformPoint(Vector2f point) const
156 	{
157 		Vector2f temp;
158 		sfTransform_transformPoint(m_matrix.ptr,point.x, point.y, &temp.x, &temp.y);
159 		return temp;
160 	}
161 
162 	/**
163 	 * Transform a rectangle.
164 	 *
165 	 * Since SFML doesn't provide support for oriented rectangles, the result of
166 	 * this function is always an axis-aligned rectangle. Which means that if
167 	 * the transform contains a rotation, the bounding rectangle of the
168 	 * transformed rectangle is returned.
169 	 *
170 	 * Params:
171 	 * 		rect	= Rectangle to transform
172 	 *
173 	 * Returns: Transformed rectangle.
174 	 */
175 	FloatRect transformRect(const(FloatRect) rect)const
176 	{
177 		FloatRect temp;
178 		sfTransform_transformRect(m_matrix.ptr,rect.left, rect.top, rect.width, rect.height, &temp.left, &temp.top, &temp.width, &temp.height);
179 		return temp;
180 	}
181 
182 	//TODO: These functions should probably return this; like the documentation states.
183 	/**
184 	 * Combine the current transform with a translation.
185 	 *
186 	 * This function returns a reference to this, so that calls can be chained.
187 	 *
188 	 * Params:
189 	 * 		offset	= Translation offset to apply.
190 	 *
191 	 * Returns: this
192 	 */
193 	void translate(float x, float y)
194 	{
195 		sfTransform_translate(m_matrix.ptr, x, y);
196 	}
197 
198 	/**
199 	 * Combine the current transform with a rotation.
200 	 *
201 	 * This function returns a reference to this, so that calls can be chained.
202 	 *
203 	 * Params:
204 	 * 		angle	= Rotation angle, in degrees
205 	 *
206 	 * Returns: this
207 	 */
208 	void rotate(float angle)
209 	{
210 		sfTransform_rotate(m_matrix.ptr, angle);
211 	}
212 
213 	/**
214 	 * Combine the current transform with a rotation.
215 	 *
216 	 * The center of rotation is provided for convenience as a second argument,
217 	 * so that you can build rotations around arbitrary points more easily (and
218 	 * efficiently) than the usual
219 	 * translate(-center).rotate(angle).translate(center).
220 	 *
221 	 * This function returns a reference to this, so that calls can be chained.
222 	 *
223 	 * Params:
224 	 * 		angle	= Rotation angle, in degrees
225 	 * 		center	= Center of rotation
226 	 *
227 	 * Returns: this
228 	 */
229 	void rotate(float angle, float centerX, float centerY)
230 	{
231 		sfTransform_rotateWithCenter(m_matrix.ptr, angle, centerX, centerY);
232 	}
233 
234 	/**
235 	 * Combine the current transform with a scaling.
236 	 *
237 	 * This function returns a reference to this, so that calls can be chained.
238 	 *
239 	 * Params:
240 	 * 		scaleX	= Scaling factor on the X-axis.
241 	 * 		scaleY	= Scaling factor on the Y-axis.
242 	 *
243 	 * Returns: this
244 	 */
245 	void scale(float scaleX, float scaleY)
246 	{
247 		sfTransform_scale(m_matrix.ptr, scaleX, scaleY);
248 	}
249 
250 	/**
251 	 * Combine the current transform with a scaling.
252 	 *
253 	 * The center of scaling is provided for convenience as a second argument,
254 	 * so that you can build scaling around arbitrary points more easily
255 	 * (and efficiently) than the usual
256 	 * translate(-center).scale(factors).translate(center).
257 	 *
258 	 * This function returns a reference to this, so that calls can be chained.
259 	 *
260 	 * Params:
261 	 * 		scaleX	= Scaling factor on the X-axis
262 	 * 		scaleY	= Scaling factor on the Y-axis
263 	 * 		centerX	= X coordinate of the center of scaling
264 	 * 		centerY	= Y coordinate of the center of scaling
265 	 *
266 	 * Returns: this
267 	 */
268 	void scale(float scaleX, float scaleY, float centerX, float centerY)
269 	{
270 		sfTransform_scaleWithCenter(m_matrix.ptr, scaleX, scaleY, centerX, centerY);
271 	}
272 
273 	string toString()
274 	{
275 		return "";//text(InternalsfTransform.matrix);
276 	}
277 
278 	/**
279 	 * Overload of binary operator `*` to combine two transforms.
280 	 *
281 	 * This call is equivalent to:
282 	 * ---
283 	 * Transform combined = transform;
284 	 * combined.combine(rhs);
285 	 * ---
286 	 *
287 	 * Params:
288 	 * rhs = the second transform to be combined with the first
289 	 *
290 	 * Returns: New combined transform.
291 	 */
292 	Transform opBinary(string op)(Transform rhs)
293 		if(op == "*")
294 	{
295 		Transform temp = this;//Transform(InternalsfTransform);
296 		temp.combine(rhs);
297 		return temp;
298 	}
299 
300 	/**
301 	 * Overload of assignment operator `*=` to combine two transforms.
302 	 *
303 	 * This call is equivalent to calling `transform.combine(rhs)`.
304 	 *
305 	 * Params:
306 	 * rhs = the second transform to be combined with the first
307 	 *
308 	 * Returns: The combined transform.
309 	 */
310 	ref Transform opOpAssign(string op)(Transform rhs)
311 		if(op == "*")
312 	{
313 
314 		this.combine(rhs);
315 		return this;
316 	}
317 
318 	/**
319 	* Overload of binary operator * to transform a point
320 	*
321 	* This call is equivalent to calling `transform.transformPoint(vector)`.
322 	*
323 	* Params:
324 	* vector = the point to transform
325 	*
326 	* Returns: New transformed point.
327 	*/
328 	Vextor2f opBinary(string op)(Vector2f vector)
329 		if(op == "*")
330 	{
331 		return transformPoint(vector);
332 	}
333 
334 	/// Indentity transform (does nothing).
335 	static const(Transform) Identity;
336 }
337 
338 private extern(C):
339 
340 //Return the 4x4 matrix of a transform
341 void sfTransform_getMatrix(const float* transform, float* matrix);
342 
343 //Return the inverse of a transform
344 void sfTransform_getInverse(const float* transform, float* inverse);
345 
346 //Apply a transform to a 2D point
347 void sfTransform_transformPoint(const float* transform, float xIn, float yIn, float* xOut, float* yOut);
348 
349 //Apply a transform to a rectangle
350 void sfTransform_transformRect(const float* transform, float leftIn, float topIn, float widthIn, float heightIn, float* leftOut, float* topOut, float* widthOut, float* heightOut);
351 
352 //Combine two transforms
353 void sfTransform_combine(float* transform, const float* other);
354 
355 //Combine a transform with a translation
356 void sfTransform_translate(float* transform, float x, float y);
357 
358 //Combine the current transform with a rotation
359 void sfTransform_rotate(float* transform, float angle);
360 
361 //Combine the current transform with a rotation
362 void sfTransform_rotateWithCenter(float* transform, float angle, float centerX, float centerY);
363 
364 //Combine the current transform with a scaling
365 void sfTransform_scale(float* transform, float scaleX, float scaleY);
366 
367 //Combine the current transform with a scaling
368 void sfTransform_scaleWithCenter(float* transform, float scaleX, float scaleY, float centerX, float centerY);