1//------------------------------------------------------------------------------
2//	Copyright (c) 2001-2002, Haiku, Inc.
3//
4//	Permission is hereby granted, free of charge, to any person obtaining a
5//	copy of this software and associated documentation files (the "Software"),
6//	to deal in the Software without restriction, including without limitation
7//	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8//	and/or sell copies of the Software, and to permit persons to whom the
9//	Software is furnished to do so, subject to the following conditions:
10//
11//	The above copyright notice and this permission notice shall be included in
12//	all copies or substantial portions of the Software.
13//
14//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19//	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20//	DEALINGS IN THE SOFTWARE.
21//
22//	File Name:		Angle.cpp
23//	Author:			DarkWyrm <bpmagic@columbus.rr.com>
24//	Description:	Angle class for speeding up trig functions
25//
26//------------------------------------------------------------------------------
27#include "Angle.h"
28#include <math.h>
29
30#ifndef ANGLE_PI
31	#define ANGLE_PI 3.14159265358979323846
32#endif
33
34static bool sTablesInitialized = false;
35static float sSinTable[360];
36static float sCosTable[360];
37static float sTanTable[360];
38
39/*!
40	\brief Constructor
41	\param angle Value in degrees
42*/
43Angle::Angle(float angle)
44	: fAngleValue(angle)
45{
46	_InitTrigTables();
47}
48
49//! Constructor
50Angle::Angle()
51	: fAngleValue(0)
52{
53	_InitTrigTables();
54}
55
56//! Empty destructor
57Angle::~Angle()
58{
59}
60
61//! Constrains angle to 0 <= angle <= 360
62void
63Angle::Normalize()
64{
65	// if the value of the angle is >=360 or <0, make it so that it is
66	// within those bounds
67    fAngleValue = fmodf(fAngleValue, 360);
68    if (fAngleValue < 0)
69        fAngleValue += 360;
70}
71
72/*!
73	\brief Obtains the sine of the angle
74	\return The sine of the angle
75*/
76float
77Angle::Sine()
78{
79	return sSinTable[(int)fAngleValue];
80}
81
82/*!
83	\brief Calculates an angle given a float value
84	\param value Number between 0 and 1 inclusive
85	\return The angle obtained or 0 if value passed was invalid
86*/
87Angle
88Angle::InvSine(float value)
89{
90	// Returns the inverse sine of a value in the range 0 <= value <= 1 via
91	//	reverse-lookup any value out of range causes the function to return 0
92
93	// Filter out bad values
94	value = fabs(value);
95
96	if (value > 1)
97		return Angle(0);
98
99	uint16 i = 90;
100	while (value < sSinTable[i])
101		i--;
102
103	// current sSinTable[i] is less than value. Pick the degree value which is closer
104	// to the passed value
105	if ((value - sSinTable[i]) > (sSinTable[i + 1] - value))
106		return Angle(i + 1);
107
108	return Angle(i);		// value is closer to previous
109}
110
111
112/*!
113	\brief Obtains the cosine of the angle
114	\return The cosine of the angle
115*/
116float
117Angle::Cosine(void)
118{
119	return sCosTable[(int)fAngleValue];
120}
121
122/*!
123	\brief Calculates an angle given a float value
124	\param value Number between 0 and 1 inclusive
125	\return The angle obtained or 0 if value passed was invalid
126*/
127Angle
128Angle::InvCosine(float value)
129{
130	// Returns the inverse cosine of a value in the range 0 <= value <= 1 via
131	//	reverse-lookup any value out of range causes the function to return 0
132
133	// Filter out bad values
134	value = fabs(value);
135
136	if (value > 1)
137		return 0;
138
139	uint16 i = 90;
140	while (value > sCosTable[i])
141		i--;
142
143	// current sCosTable[i] is less than value. Pick the degree value which is closer
144	// to the passed value
145	if ((value - sCosTable[i]) < (sCosTable[i + 1] - value))
146		return Angle(i + 1);
147
148	return Angle(i);		// value is closer to previous
149}
150
151/*!
152	\brief Obtains the tangent of the angle
153	\return The tangent of the angle
154*/
155float
156Angle::Tangent(int *status)
157{
158	if (fAngleValue == 90 || fAngleValue == 270) {
159		if (status)
160			*status = 0;
161		return 0.0;
162	}
163
164	return sTanTable[(int)fAngleValue];
165}
166
167/*!
168	\brief Returns the inverse tangent of a value given
169	\param value Number between 0 and 1 inclusive
170	\return The angle found or 0 if value was invalid
171*/
172Angle
173Angle::InvTangent(float value)
174{
175	// Filter out bad values
176	value = fabs(value);
177
178	if (value > 1)
179		return Angle(0);
180
181	uint16 i = 90;
182	while (value > sTanTable[i])
183		i--;
184
185	if ((value - sTanTable[i]) < (sTanTable[i+1] - value))
186		return Angle(i+1);
187
188	return Angle(i);		// value is closer to previous
189}
190
191/*!
192	\brief Returns a value based on what quadrant the angle is in
193	\return
194	- \c 1: 0 <= angle <90
195	- \c 2: 90 <= angle < 180
196	- \c 3: 180 <= angle < 270
197	- \c 4: 270 <= angle < 360
198*/
199uint8
200Angle::Quadrant()
201{
202	// We can get away with not doing extra value checks because of the order in
203	// which the checks are done.
204	if (fAngleValue < 90)
205		return 1;
206
207	if (fAngleValue < 180)
208		return 2;
209
210	if (fAngleValue < 270)
211		return 3;
212
213	return 4;
214}
215
216/*!
217	\brief Obtains the angle constrained to between 0 and 180 inclusive
218	\return The constrained value
219*/
220Angle
221Angle::Constrain180()
222{
223	// Constrains angle to 0 <= angle < 180
224	if (fAngleValue < 180)
225		return Angle(fAngleValue);
226
227	float value = fmodf(fAngleValue, 180);;
228	if (value < 0)
229		value += 180;
230	return Angle(value);
231}
232
233/*!
234	\brief Obtains the angle constrained to between 0 and 90 inclusive
235	\return The constrained value
236*/
237Angle
238Angle::Constrain90()
239{
240	// Constrains angle to 0 <= angle < 90
241	if (fAngleValue < 90)
242		return Angle(fAngleValue);
243
244	float value = fmodf(fAngleValue, 90);;
245	if (value < 0)
246		value += 90;
247	return Angle(value);
248}
249
250/*!
251	\brief Sets the angle's value and normalizes the value
252	\param angle Value in degrees
253*/
254void
255Angle::SetValue(float angle)
256{
257	fAngleValue = angle;
258	Normalize();
259}
260
261
262float
263Angle::Value() const
264{
265	return fAngleValue;
266}
267
268//! Initializes the global trig tables
269void
270Angle::_InitTrigTables()
271{
272	if (sTablesInitialized)
273		return;
274	sTablesInitialized = true;
275
276	for(int32 i = 0; i < 90; i++) {
277		double currentRadian = (i * ANGLE_PI) / 180.0;
278
279		// Get these so that we can do some superfast assignments
280		double sinValue = sin(currentRadian);
281		double cosValue = cos(currentRadian);
282
283		// Do 4 assignments, taking advantage of sin/cos symmetry
284		sSinTable[i] = sinValue;
285		sSinTable[i + 90] = cosValue;
286		sSinTable[i + 180] = sinValue * -1;
287		sSinTable[i + 270] = cosValue * -1;
288
289		sCosTable[i] = cosValue;
290		sCosTable[i + 90] = sinValue * -1;
291		sCosTable[i + 180] = cosValue * -1;
292		sCosTable[i + 270] = sinValue;
293
294		double tanValue = sinValue / cosValue;
295
296		sTanTable[i] = tanValue;
297		sTanTable[i + 90] = tanValue;
298		sTanTable[i + 180] = tanValue;
299		sTanTable[i + 270] = tanValue;
300	}
301}
302
303
304Angle&
305Angle::operator=(const Angle &from)
306{
307	fAngleValue = from.fAngleValue;
308	return *this;
309}
310
311
312Angle&
313Angle::operator=(const float &from)
314{
315	fAngleValue = from;
316	return *this;
317}
318
319
320Angle&
321Angle::operator=(const long &from)
322{
323	fAngleValue = (float)from;
324	return *this;
325}
326
327
328Angle&
329Angle::operator=(const int &from)
330{
331	fAngleValue = (float)from;
332	return *this;
333}
334
335
336bool
337Angle::operator==(const Angle &from)
338{
339	return (fAngleValue == from.fAngleValue);
340}
341
342
343bool
344Angle::operator!=(const Angle &from)
345{
346	return (fAngleValue != from.fAngleValue);
347}
348
349
350bool
351Angle::operator>(const Angle &from)
352{
353	return (fAngleValue > from.fAngleValue);
354}
355
356
357bool
358Angle::operator<(const Angle &from)
359{
360	return (fAngleValue < from.fAngleValue);
361}
362
363
364bool
365Angle::operator>=(const Angle &from)
366{
367	return (fAngleValue >= from.fAngleValue);
368}
369
370
371bool
372Angle::operator<=(const Angle &from)
373{
374	return (fAngleValue <= from.fAngleValue);
375}
376