1 module nudsfml.graphics.transform;
2 
3 import bindbc.sfml.graphics;
4 import bindbc.sfml.system;
5 
6 import nudsfml.system.vector2;
7 import nudsfml.graphics.rect;
8 
9 
10 /**
11  * Define a 3x3 transform matrix.
12  */
13 struct Transform {
14 	/// 4x4 matrix defining the transformation.
15 	package float[16] m_matrix = [1.0f, 0.0f, 0.0f, 0.0f,
16 						  		  0.0f, 1.0f, 0.0f, 0.0f,
17 						  		  0.0f, 0.0f, 1.0f, 0.0f,
18 						  		  0.0f, 0.0f, 0.0f, 1.0f];
19 
20 	/**
21 	 * Construct a transform from a 3x3 matrix.
22 	 *
23 	 * Params:
24 	 * 		a00	= Element (0, 0) of the matrix
25 	 * 		a01	= Element (0, 1) of the matrix
26 	 * 		a02	= Element (0, 2) of the matrix
27 	 * 		a10	= Element (1, 0) of the matrix
28 	 * 		a11	= Element (1, 1) of the matrix
29 	 * 		a12	= Element (1, 2) of the matrix
30 	 * 		a20	= Element (2, 0) of the matrix
31 	 * 		a21	= Element (2, 1) of the matrix
32 	 * 		a22	= Element (2, 2) of the matrix
33 	 */
34 	this(float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22) {
35 		m_matrix = [ a00,  a10, 0.0f,  a20,
36     			     a01,  a11, 0.0f,  a21,
37     				0.0f, 0.0f, 1.0f, 0.0f,
38     				 a02,  a12, 0.0f,  a22];
39 	}
40 
41 	/// Construct a transform from a float array describing a 3x3 matrix.
42 	this(float[9] matrix) {
43 		m_matrix = [matrix[0], matrix[3], 0.0f, matrix[6],
44     			    matrix[1], matrix[4], 0.0f, matrix[7],
45     				     0.0f,      0.0f, 1.0f,      0.0f,
46     				matrix[2], matrix[5], 0.0f, matrix[8]];
47 	}
48 
49 	/**
50 	 * Return the inverse of the transform.
51 	 *
52 	 * If the inverse cannot be computed, an identity transform is returned.
53 	 *
54 	 * Returns: A new transform which is the inverse of self.
55 	 */
56 	Transform getInverse() const {
57 		sfTransform matrix ;
58 		matrix.matrix = [m_matrix[0], m_matrix[1], m_matrix[3],
59 								m_matrix[4], m_matrix[5], m_matrix[7],
60 								m_matrix[12], m_matrix[13], m_matrix[15]];
61 		auto temp = sfTransform_getInverse(&matrix );
62 		return Transform(temp.matrix);
63 	}
64 
65 	/**
66 	 * Return the transform as a 4x4 matrix.
67 	 *
68 	 * This function returns a pointer to an array of 16 floats containing the
69 	 * transform elements as a 4x4 matrix, which is directly compatible with
70 	 * OpenGL functions.
71 	 *
72 	 * Returns: A 4x4 matrix.
73 	 */
74 	const(float)[] getMatrix() const {
75 		return m_matrix;
76 	}
77 
78 	/**
79 	 * Combine the current transform with another one.
80 	 *
81 	 * The result is a transform that is equivalent to applying this followed by
82 	 * transform. Mathematically, it is equivalent to a matrix multiplication.
83 	 *
84 	 * Params:
85 	 * 		otherTransform	= Transform to combine with this one
86 	 *
87 	 * Returns: Reference to this.
88 	 */
89 	ref Transform combine(Transform otherTransform) {
90 		sfTransform thisTransform = getFromTransform(this);
91 		sfTransform other = getFromTransform(otherTransform);
92 
93 		sfTransform_combine(&thisTransform, &other);
94 		m_matrix = get4x4FromTransform(thisTransform);
95 		return this;
96 	}
97 
98 	/**
99 	 * Transform a 2D point.
100 	 *
101 	 * Params:
102 	 *		x 	= X coordinate of the point to transform
103 	 * 		y	= Y coordinate of the point to transform
104 	 *
105 	 * Returns: Transformed point.
106 	 */
107 	Vector2f transformPoint(float x, float y) const {
108 		Vector2f temp;
109 		sfVector2f p;
110 		p.x = x;
111 		p.y = y;
112 		auto m = getFromTransform(this);
113 		auto point = sfTransform_transformPoint(&m, p);
114 		temp.x = point.x;
115 		temp.y = point.y;
116 		return temp;
117 	}
118 
119 	/**
120 	 * Transform a 2D point.
121 	 *
122 	 * Params:
123 	 *		point 	= the point to transform
124 	 *
125 	 * Returns: Transformed point.
126 	 */
127 	Vector2f transformPoint(Vector2f point) const {
128 		return transformPoint(point.x, point.y);
129 	}
130 
131 	/**
132 	 * Transform a rectangle.
133 	 *
134 	 * Since SFML doesn't provide support for oriented rectangles, the result of
135 	 * this function is always an axis-aligned rectangle. Which means that if
136 	 * the transform contains a rotation, the bounding rectangle of the
137 	 * transformed rectangle is returned.
138 	 *
139 	 * Params:
140 	 * 		rect	= Rectangle to transform
141 	 *
142 	 * Returns: Transformed rectangle.
143 	 */
144 	FloatRect transformRect(const(FloatRect) rect)const {
145 		FloatRect temp;
146 		sfFloatRect sent;
147 		sent.height = rect.height;
148 		sent.left = rect.left;
149 		sent.top = rect.top;
150 		sent.width = rect.width;
151 		auto m = getFromTransform(this);
152 		auto retval = sfTransform_transformRect(&m,sent);
153 		temp.height = retval.height;
154 		temp.left = retval.left;
155 		temp.top = retval.top;
156 		temp.width = retval.width;
157 		return temp;
158 	}
159 
160 	/**
161 	 * Combine the current transform with a translation.
162 	 *
163 	 * This function returns a reference to this, so that calls can be chained.
164 	 *
165 	 * Params:
166 	 * 		offset	= Translation offset to apply
167 	 *
168 	 * Returns: this
169 	 */
170 	ref Transform translate(Vector2f offset) {
171 
172 		sfTransform m = getFromTransform(this);
173 		sfTransform_translate(&m, offset.x, offset.y);
174 		m_matrix = get4x4FromTransform(m);
175 
176 		return this;
177 	}
178 
179 	/**
180 	 * Combine the current transform with a translation.
181 	 *
182 	 * This function returns a reference to this, so that calls can be chained.
183 	 *
184 	 * Params:
185 	 * 		x	= Offset to apply on X axis
186 	 *		y	= Offset to apply on Y axis
187 	 *
188 	 * Returns: this
189 	 */
190 	ref Transform translate(float x, float y) {
191 		sfTransform m = getFromTransform(this);
192 		sfTransform_translate(&m, x, y);
193 		m_matrix = get4x4FromTransform(m);
194 
195 		return this;
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 	ref Transform rotate(float angle) {
209 
210 		sfTransform m = getFromTransform(this);
211 		sfTransform_rotate(&m, angle);
212 		m_matrix = get4x4FromTransform(m);
213 		return this;
214 	}
215 
216 	/**
217 	 * Combine the current transform with a rotation.
218 	 *
219 	 * The center of rotation is provided for convenience as a second argument,
220 	 * so that you can build rotations around arbitrary points more easily (and
221 	 * efficiently) than the usual
222 	 * translate(-center).rotate(angle).translate(center).
223 	 *
224 	 * This function returns a reference to this, so that calls can be chained.
225 	 *
226 	 * Params:
227 	 * 		angle	= Rotation angle, in degrees
228 	 * 		centerX	= X coordinate of the center of rotation
229 	 *		centerY = Y coordinate of the center of rotation
230 	 *
231 	 * Returns: this
232 	 */
233 	ref Transform rotate(float angle, float centerX, float centerY) {
234 		sfTransform m = getFromTransform(this);
235 		sfTransform_rotateWithCenter(&m, angle, centerX, centerY);
236 		m_matrix = get4x4FromTransform(m);
237 		return this;
238 	}
239 
240 	/**
241 	 * Combine the current transform with a rotation.
242 	 *
243 	 * The center of rotation is provided for convenience as a second argument,
244 	 * so that you can build rotations around arbitrary points more easily (and
245 	 * efficiently) than the usual
246 	 * translate(-center).rotate(angle).translate(center).
247 	 *
248 	 * This function returns a reference to this, so that calls can be chained.
249 	 *
250 	 * Params:
251 	 * 		angle	= Rotation angle, in degrees
252 	 * 		center	= Center of rotation
253 	 *
254 	 * Returns: this
255 	 */
256 	ref Transform rotate(float angle, Vector2f center) {
257 		sfTransform m = getFromTransform(this);
258 		sfTransform_rotateWithCenter(&m, angle, center.x, center.y);
259 		m_matrix = get4x4FromTransform(m);
260 		return this;
261 	}
262 
263 	/**
264 	 * Combine the current transform with a scaling.
265 	 *
266 	 * This function returns a reference to this, so that calls can be chained.
267 	 *
268 	 * Params:
269 	 * 		scaleX	= Scaling factor on the X-axis.
270 	 * 		scaleY	= Scaling factor on the Y-axis.
271 	 *
272 	 * Returns: this
273 	 */
274 	ref Transform scale(float scaleX, float scaleY) {
275 		sfTransform m = getFromTransform(this);
276 		sfTransform_scale(&m, scaleX, scaleY);
277 		m_matrix = get4x4FromTransform(m);
278 
279 		return this;
280 	}
281 
282 	/**
283 	 * Combine the current transform with a scaling.
284 	 *
285 	 * This function returns a reference to this, so that calls can be chained.
286 	 *
287 	 * Params:
288 	 * 		factors	= Scaling factors
289 	 *
290 	 * Returns: this
291 	 */
292 	ref Transform scale(Vector2f factors) {
293 		sfTransform m = getFromTransform(this);
294 		sfTransform_scale(&m, factors.x, factors.y);
295 		m_matrix = get4x4FromTransform(m);
296 
297 		return this;
298 	}
299 
300 	/**
301 	 * Combine the current transform with a scaling.
302 	 *
303 	 * The center of scaling is provided for convenience as a second argument,
304 	 * so that you can build scaling around arbitrary points more easily
305 	 * (and efficiently) than the usual
306 	 * translate(-center).scale(factors).translate(center).
307 	 *
308 	 * This function returns a reference to this, so that calls can be chained.
309 	 *
310 	 * Params:
311 	 * 		scaleX	= Scaling factor on the X-axis
312 	 * 		scaleY	= Scaling factor on the Y-axis
313 	 * 		centerX	= X coordinate of the center of scaling
314 	 * 		centerY	= Y coordinate of the center of scaling
315 	 *
316 	 * Returns: this
317 	 */
318 	ref Transform scale(float scaleX, float scaleY, float centerX, float centerY) {
319 
320 		sfTransform m = getFromTransform(this);
321 		sfTransform_scaleWithCenter(&m, scaleX, scaleY, centerX, centerY);
322 		m_matrix = get4x4FromTransform(m);
323 
324 		return this;
325 	}
326 
327 	/**
328 	 * Combine the current transform with a scaling.
329 	 *
330 	 * The center of scaling is provided for convenience as a second argument,
331 	 * so that you can build scaling around arbitrary points more easily
332 	 * (and efficiently) than the usual
333 	 * translate(-center).scale(factors).translate(center).
334 	 *
335 	 * This function returns a reference to this, so that calls can be chained.
336 	 *
337 	 * Params:
338 	 * 		factors	= Scaling factors
339 	 * 		center	= Center of scaling
340 	 *
341 	 * Returns: this
342 	 */
343 	ref Transform scale(Vector2f factors, Vector2f center) {
344 		sfTransform m = getFromTransform(this);
345 		sfTransform_scaleWithCenter(&m, factors.x, factors.y, center.x, center.y);		
346 		m_matrix = get4x4FromTransform(m);
347 
348 		return this;
349 	}
350 
351 	string toString() const {
352 		return "";//text(InternalsfTransform.matrix);
353 	}
354 
355 	/**
356 	 * Overload of binary operator `*` to combine two transforms.
357 	 *
358 	 * This call is equivalent to:
359 	 * ---
360 	 * Transform combined = transform;
361 	 * combined.combine(rhs);
362 	 * ---
363 	 *
364 	 * Params:
365 	 * rhs = the second transform to be combined with the first
366 	 *
367 	 * Returns: New combined transform.
368 	 */
369 	Transform opBinary(string op)(Transform rhs)
370 		if(op == "*")
371 	{
372 		Transform temp = this;
373 		return temp.combine(rhs);
374 	}
375 
376 	/**
377 	 * Overload of assignment operator `*=` to combine two transforms.
378 	 *
379 	 * This call is equivalent to calling `transform.combine(rhs)`.
380 	 *
381 	 * Params:
382 	 * rhs = the second transform to be combined with the first
383 	 *
384 	 * Returns: The combined transform.
385 	 */
386 	ref Transform opOpAssign(string op)(Transform rhs)
387 		if(op == "*")
388 	{
389 		return this.combine(rhs);
390 	}
391 
392 	/**
393 	* Overload of binary operator * to transform a point
394 	*
395 	* This call is equivalent to calling `transform.transformPoint(vector)`.
396 	*
397 	* Params:
398 	* vector = the point to transform
399 	*
400 	* Returns: New transformed point.
401 	*/
402 	Vextor2f opBinary(string op)(Vector2f vector)
403 		if(op == "*")
404 	{
405 		return transformPoint(vector);
406 	}
407 
408 	/// Indentity transform (does nothing).
409 	static const(Transform) Identity;
410 }
411 
412 
413 
414 
415 sfTransform getFromTransform ( Transform t ) {
416 	sfTransform matrix ;
417 	matrix.matrix = [t.m_matrix[0], t.m_matrix[1], t.m_matrix[3],
418 					t.m_matrix[4], t.m_matrix[5], t.m_matrix[7],
419 					t.m_matrix[12], t.m_matrix[13], t.m_matrix[15]];
420 	
421 	return matrix;
422 }
423 
424 float [16] get4x4FromTransform ( sfTransform t ) {
425 
426 	return  [t.matrix[0], t.matrix[3], 0.0f, t.matrix[6],
427         	t.matrix[1], t.matrix[4], 0.0f, t.matrix[7],
428          	0.0f,      0.0f, 1.0f,      0.0f,
429     		t.matrix[2], t.matrix[5], 0.0f, t.matrix[8]];
430 }