1 /* 2 DSFML - The Simple and Fast Multimedia Library for D 3 4 Copyright (c) <2013 - 2015> <Jeremy DeHaan> 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.transformable; 21 22 import dsfml.system.vector2; 23 24 //public import so that people don't have to worry about 25 //importing transform when they import transformable 26 public import dsfml.graphics.transform; 27 28 /++ 29 + Decomposed transform defined by a position, a rotation, and a scale. 30 + 31 + This interface is provided for convenience, on top of Transform. 32 + 33 + Authors: Laurent Gomila, Jeremy DeHaan 34 + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Transformable.php#details 35 +/ 36 interface Transformable 37 { 38 /** 39 * The local origin of the object. 40 * 41 * The origin of an object defines the center point for all transformations (position, scale, ratation). 42 * 43 * The coordinates of this point must be relative to the top-left corner of the object, and ignore all transformations (position, scale, rotation). The default origin of a transformable object is (0, 0). 44 */ 45 @property 46 { 47 Vector2f origin(Vector2f newOrigin); 48 Vector2f origin() const; 49 } 50 51 /// The position of the object. The default is (0, 0). 52 @property 53 { 54 Vector2f position(Vector2f newPosition); 55 Vector2f position() const; 56 } 57 58 /// The orientation of the object, in degrees. The default is 0 degrees. 59 @property 60 { 61 float rotation(float newRotation); 62 float rotation() const; 63 } 64 65 /// The scale factors of the object. The default is (1, 1). 66 @property 67 { 68 Vector2f scale(Vector2f newScale); 69 Vector2f scale() const; 70 } 71 72 /** 73 * Get the inverse of the combined transform of the object. 74 * 75 * Returns: Inverse of the combined transformations applied to the object. 76 */ 77 const(Transform) getTransform(); 78 79 /** 80 * Get the combined transform of the object. 81 * 82 * Returns: Transform combining the position/rotation/scale/origin of the object. 83 */ 84 const(Transform) getInverseTransform(); 85 86 87 /** 88 *Move the object by a given offset. 89 * 90 *This function adds to the current position of the object, unlike the position property which overwrites it. 91 * 92 * Params: 93 * offset = The offset. 94 */ 95 void move(Vector2f offset); 96 97 } 98 99 /++ 100 + Decomposed transform defined by a position, a rotation, and a scale. 101 + 102 + This template is provided for convenience, on top of Transformable (and Transform). 103 + 104 + Transform, as a low-level class, offers a great level of flexibility but it is not always convenient to manage. Indeed, one can easily combine any kind of operation, such as a translation followed by a rotation followed by a scaling, but once the result transform is built, there's no way to go backward and, let's say, change only the rotation without modifying the translation and scaling. 105 + 106 + The entire transform must be recomputed, which means that you need to retrieve the initial translation and scale factors as well, and combine them the same way you did before updating the rotation. This is a tedious operation, and it requires to store all the individual components of the final transform. 107 + 108 + That's exactly what Transformable was written for: it hides these variables and the composed transform behind an easy to use interface. You can set or get any of the individual components without worrying about the others. It also provides the composed transform (as a Transform), and keeps it up-to-date. 109 + 110 + Authors: Laurent Gomila, Jeremy DeHaan 111 + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Transformable.php#details 112 +/ 113 mixin template NormalTransformable() 114 { 115 private 116 { 117 Vector2f m_origin = Vector2f(0,0); ///< Origin of translation/rotation/scaling of the object 118 Vector2f m_position = Vector2f(0,0); ///< Position of the object in the 2D world 119 float m_rotation = 0; ///< Orientation of the object, in degrees 120 Vector2f m_scale = Vector2f(1,1); ///< Scale of the object 121 Transform m_transform; ///< Combined transformation of the object 122 bool m_transformNeedUpdate; ///< Does the transform need to be recomputed? 123 Transform m_inverseTransform; ///< Combined transformation of the object 124 bool m_inverseTransformNeedUpdate; ///< Does the transform need to be recomputed? 125 } 126 127 /** 128 * The local origin of the object. 129 * 130 * The origin of an object defines the center point for all transformations (position, scale, ratation). 131 * 132 * The coordinates of this point must be relative to the top-left corner of the object, and ignore all transformations (position, scale, rotation). The default origin of a transformable object is (0, 0). 133 */ 134 @property 135 { 136 Vector2f origin(Vector2f newOrigin) 137 { 138 m_origin = newOrigin; 139 m_transformNeedUpdate = true; 140 m_inverseTransformNeedUpdate = true; 141 return newOrigin; 142 } 143 144 Vector2f origin() const 145 { 146 return m_origin; 147 } 148 } 149 150 /// The position of the object. The default is (0, 0). 151 @property 152 { 153 Vector2f position(Vector2f newPosition) 154 { 155 m_position = newPosition; 156 m_transformNeedUpdate = true; 157 m_inverseTransformNeedUpdate = true; 158 return newPosition; 159 } 160 161 Vector2f position() const 162 { 163 return m_position; 164 } 165 } 166 167 /// The orientation of the object, in degrees. The default is 0 degrees. 168 @property 169 { 170 float rotation(float newRotation) 171 { 172 m_rotation = cast(float)fmod(newRotation, 360); 173 if(m_rotation < 0) 174 { 175 m_rotation += 360; 176 } 177 m_transformNeedUpdate = true; 178 m_inverseTransformNeedUpdate = true; 179 return newRotation; 180 } 181 182 float rotation() const 183 { 184 return m_rotation; 185 } 186 } 187 188 /// The scale factors of the object. The default is (1, 1). 189 @property 190 { 191 Vector2f scale(Vector2f newScale) 192 { 193 m_scale = newScale; 194 m_transformNeedUpdate = true; 195 m_inverseTransformNeedUpdate = true; 196 return newScale; 197 } 198 199 Vector2f scale() const 200 { 201 return m_scale; 202 } 203 } 204 205 /** 206 * Get the inverse of the combined transform of the object. 207 * 208 * Returns: Inverse of the combined transformations applied to the object. 209 */ 210 const(Transform) getInverseTransform() 211 { 212 if (m_inverseTransformNeedUpdate) 213 { 214 m_inverseTransform = getTransform().getInverse(); 215 m_inverseTransformNeedUpdate = false; 216 } 217 218 return m_inverseTransform; 219 } 220 221 /** 222 * Get the combined transform of the object. 223 * 224 * Returns: Transform combining the position/rotation/scale/origin of the object. 225 */ 226 const(Transform) getTransform() 227 { 228 229 if (m_transformNeedUpdate) 230 { 231 float angle = -m_rotation * 3.141592654f / 180f; 232 float cosine = cast(float)(cos(angle)); 233 float sine = cast(float)(sin(angle)); 234 float sxc = m_scale.x * cosine; 235 float syc = m_scale.y * cosine; 236 float sxs = m_scale.x * sine; 237 float sys = m_scale.y * sine; 238 float tx = -m_origin.x * sxc - m_origin.y * sys + m_position.x; 239 float ty = m_origin.x * sxs - m_origin.y * syc + m_position.y; 240 241 m_transform = Transform( sxc, sys, tx, 242 -sxs, syc, ty, 243 0f, 0f, 1f); 244 m_transformNeedUpdate = false; 245 } 246 247 248 return m_transform; 249 } 250 251 /** 252 *Move the object by a given offset. 253 * 254 *This function adds to the current position of the object, unlike the position property which overwrites it. 255 * 256 * Params: 257 * offset = The offset. 258 */ 259 void move(Vector2f offset) 260 { 261 position = position + offset; 262 } 263 }