1 /++
2 Module for working with mathematical angles. Contains the translation of
3 measurement systems, work in the form of determining the angle,
4 vector rotation and more.
5 
6 Macros:
7     LREF = <a href="#$1">$1</a>
8     HREF = <a href="$1">$2</a>
9 
10 Authors: $(HREF https://github.com/TodNaz,TodNaz)
11 Copyright: Copyright (c) 2020 - 2021, TodNaz.
12 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
13 +/
14 module tida.angle;
15 
16 import std.math : PI;
17 
18 enum Radians = 0; /// Radians
19 enum Degrees = 1; /// Degrees
20 enum Turns = 2; /// Turns
21 enum Gons = 3; /// Gons
22 
23 /++
24 Maximum angle value.
25 
26 Params:
27     Type = Type angle.
28     
29 Example:
30 ---
31 auto maxRad = max!Radians;
32 ---
33 +/
34 template max(ubyte Type)
35 {
36     static if (Type == Radians) 
37     {
38         enum max = 2 * PI;
39     }else
40     static if (Type == Degrees)
41     {
42         enum max = 360.0f;
43     }else
44     static if (Type == Turns)
45     {
46         enum max = 1.0f;
47     }else
48     static if (Type == Gons)
49     {
50         enum max = 400.0f;
51     }
52 }
53 
54 alias perigon = max; /// perigon
55 
56 /++
57 Returns the right angle.
58 
59 Params:
60     Type = Type angle.
61     
62 Example:
63 ---
64 auto riaRad = rightAngle!Radians;
65 ---
66 +/
67 template rightAngle(ubyte Type)
68 {
69     static if (Type == Radians)
70     {
71         enum rightAngle = PI / 2;
72     }else
73     static if (Type == Degrees)
74     {
75         enum rightAngle = 90.0f;
76     }else
77     static if (Type == Turns)
78     {
79         enum rightAngle = 1 / 4;
80     }else
81     static if (Type == Gons)
82     {
83         enum rightAngle = 100;
84     }else
85         static assert(null, "Unknown angle type!");
86 }
87 
88 /++
89 Returns straight angle.
90 
91 Params:
92     Type = Type angle.
93     
94 Example:
95 ---
96 auto strRad = straight!Radians;
97 ---
98 +/
99 template straight(ubyte Type)
100 {   
101     static if (Type == Radians)
102     {
103         enum straight = PI;
104     }else
105     static if (Type == Degrees) 
106     {
107         enum straight =  180;
108     }else
109     static if (Type == Turns)
110     {
111         enum straight =  0.5;
112     }else
113     static if (Type == Gons)
114     {
115         enum straight =  200;
116     }else
117         static assert(null,"Unknown angle type!");
118 }
119 
120 /++
121 Translate one system of angles into another.
122 
123 Params:
124     What = What to translate.
125     In = What to translate.
126     value = Value.
127 
128 Example:
129 ---
130 from(Degrees,Radians)(45);
131 ---
132 +/
133 float conv(ubyte What,ubyte In)(float value) @safe nothrow pure
134 {
135     import std.math;
136     
137     static if (What == In) return value;
138     else
139     static if (In == Turns) return value / max!What;
140     else
141     static if (What == Turns) return max!In * value;
142     else
143     static if (What == Radians)
144     {
145         static if (In == Degrees)
146         {
147             return value * 180 / PI;
148         }else
149         static if (In == Gons)
150         {
151             return value * 200 / PI;
152         }
153     }else
154     static if (What == Degrees)
155     {
156         static if (In == Radians)
157         {
158             return value * PI / 180;
159         }else
160         static if (In == Gons)
161         {
162             return value * 200 / 180;
163         }
164     }else
165     static if (What == Gons)
166     {
167         static if (In == Radians)
168         {
169             return value * PI / 200;
170         }else
171         static if (In == Degrees)
172         {
173             return value * 180 / 200;
174         }
175     }
176 }
177 
178 alias from = conv; // old name saved.
179 
180 /++
181 Converts degrees to radians.
182 
183 See_Also:
184     $(LREF conv)
185 +/
186 alias degToRad = conv!(Degrees, Radians);
187 
188 /++
189 Convert radians to degrees.
190 
191 
192 See_Also:
193     $(LREF conv)
194 +/
195 alias radToDeg = conv!(Radians, Degrees);
196 
197 /++
198 Brings the angle back to normal.
199 
200 Params:
201     Type = Angle type.
202     angle = Angle.
203 
204 Example:
205 ---
206 assert(375.minimize!Degrees == 15);
207     ---
208 +/
209 float minimize(ubyte Type)(float angle) @safe nothrow pure
210 {
211     int k = cast(int) (angle / max!Type);
212     float sign = angle >= 0 ? 1 : -1;
213 
214     return angle - ((max!Type * cast(float) k) * sign);
215 }
216 
217 unittest
218 {
219     assert(minimize!Degrees(361) == 1);
220     assert(minimize!Degrees(470) == (470 - 360));
221     assert(minimize!Degrees(848) == 128);
222 }
223 
224 /++
225 Finds the angle between two angel's. Accepted in any angle change systems.
226 
227 Params:
228     a = First angle.
229     b = Second angle.
230     
231 Returns: Angle between two angel's.
232 +/
233 float betweenAngle(float a, float b) @safe nothrow pure
234 {
235     return (a + b) / 2;
236 }
237 
238 unittest
239 {
240     assert(max!Radians.from!(Radians,Degrees) == (max!Degrees));
241     assert(max!Degrees.from!(Degrees,Gons) == (max!Gons));
242     assert(max!Gons.from!(Gons,Turns) == (max!Turns));
243     
244     assert(rightAngle!Radians.from!(Radians,Degrees) == (rightAngle!Degrees));
245     assert(rightAngle!Degrees.from!(Degrees,Gons) == (rightAngle!Gons));
246 }
247 
248 import tida.vector;
249 
250 /++
251 Returns the angle between two vectors in radians.
252 
253 Params:
254     a = First point.
255     b = Second point.
256     
257 Returns: Angle in radians.
258 
259 To convert, for example, to degrees, use the function like this:
260 ---
261 pointDirection(...).from!(Radians,Degrees);
262     ---
263 +/
264 T pointDirection(T)(Vector!T a, Vector!T b) @safe nothrow pure
265 {
266     import std.math : atan2;
267 
268     T result = atan2(b.y - a.y, b.x - a.x);
269     return result;
270 }
271 
272 /++
273 Rotates the point by the specified number of degrees.
274 
275 Params:
276     vec = Point.
277     angle = Angle of rotation.
278     center = Center of rotation.
279 +/
280 Vecf rotate(Vecf vec, float angle, Vecf center = vecf(0, 0)) @safe nothrow pure
281 {
282     import std.math : sin, cos;
283 
284     float ca = cos(angle);
285     float sa = sin(angle);
286 
287     Vecf result;
288 
289     vec -= center;
290 
291     result.x = vec.x * ca - vec.y * sa;
292     result.y = vec.x * sa + vec.y * ca;
293 
294     return result + center;
295 }
296 
297 /++
298 Convert angle to direction vector. Use when moving an object at a given angle.
299 
300 Params:
301     angle = Angle.
302 
303 Example:
304 ---
305 Vecf vecd = vectorDirection(45.from!(Degrees,Radians));
306 position += vecd * 5; // Move the object 45 degrees at a given speed.
307     ---
308 +/
309 Vector!T vectorDirection(T)(T angle) @safe nothrow pure
310 {
311     import std.math : cos, sin;
312 
313     return vec!T(cos(angle), sin(angle));
314 }