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 }