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 }