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 = 0.5 * PI;
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 Brings the angle back to normal.
182 
183 Params:
184     Type = Angle type.
185     angle = Angle.
186 
187 Example:
188 ---
189 assert(375.minimize!Degrees == 15);
190     ---
191 +/
192 float minimize(ubyte Type)(float angle) @safe nothrow pure
193 {
194     int k = cast(int) (angle / max!Type);
195     float sign = angle >= 0 ? 1 : -1;
196 
197     return angle - ((max!Type * cast(float) k) * sign);
198 }
199 
200 /++
201 Finds the angle between two angel's. Accepted in any angle change systems.
202 
203 Params:
204     a = First angle.
205     b = Second angle.
206     
207 Returns: Angle between two angel's.
208 +/
209 float betweenAngle(float a, float b) @safe nothrow pure
210 {
211     return (a + b) / 2;
212 }
213 
214 unittest
215 {
216     assert(max!Radians.from!(Radians,Degrees) == max!Degrees);
217     assert(max!Degrees.from!(Degrees,Gons) == max!Gons);
218     assert(max!Gons.from!(Gons,Turns) == max!Turns);
219     
220     assert(rightAngle!Radians.from!(Radians,Degrees) == rightAngle!Degrees);
221     assert(rightAngle!Degrees.from!(Degrees,Gons) == rightAngle!Gons);
222 }
223 
224 import tida.vector;
225 
226 /++
227 Returns the angle between two vectors in radians.
228 
229 Params:
230     a = First point.
231     b = Second point.
232     
233 Returns: Angle in radians.
234 
235 To convert, for example, to degrees, use the function like this:
236 ---
237 pointDirection(...).from!(Radians,Degrees);
238     ---
239 +/
240 T pointDirection(T)(Vector!T a, Vector!T b) @safe nothrow pure
241 {
242     import std.math : atan2;
243 
244     T result = atan2(b.y - a.y, b.x - a.x);
245     return result;
246 }
247 
248 /++
249 Rotates the point by the specified number of degrees.
250 
251 Params:
252     vec = Point.
253     angle = Angle of rotation.
254     center = Center of rotation.
255 +/
256 Vecf rotate(Vecf vec, float angle, Vecf center = vecf(0, 0)) @safe nothrow pure
257 {
258     import std.math : sin, cos;
259 
260     float ca = cos(angle);
261     float sa = sin(angle);
262 
263     Vecf result;
264 
265     vec -= center;
266 
267     result.x = vec.x * ca - vec.y * sa;
268     result.y = vec.x * sa + vec.y * ca;
269 
270     return result + center;
271 }
272 
273 /++
274 Convert angle to direction vector. Use when moving an object at a given angle.
275 
276 Params:
277     angle = Angle.
278 
279 Example:
280 ---
281 Vecf vecd = vectorDirection(45.from!(Degrees,Radians));
282 position += vecd * 5; // Move the object 45 degrees at a given speed.
283     ---
284 +/
285 Vector!T vectorDirection(T)(T angle) @safe nothrow pure
286 {
287     import std.math : cos, sin;
288 
289     return vec!T(cos(angle), sin(angle));
290 }