diff --git a/source/maths/Fixed.cpp b/source/maths/Fixed.cpp
index e9e3c8c..d2ecf33 100644
a
|
b
|
CStr8 CFixed_15_16::ToString() const
|
143 | 143 | return r.str(); |
144 | 144 | } |
145 | 145 | |
146 | | // Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization |
147 | 146 | CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x) |
148 | 147 | { |
149 | | CFixed_15_16 zero; |
| 148 | // Error <= arctan(1/16)/2 ~ 1.788 degrees. |
150 | 149 | |
151 | | // Special case to avoid division-by-zero |
152 | | if (x.IsZero() && y.IsZero()) |
153 | | return zero; |
| 150 | // Binary search using the following angles: |
| 151 | // 45, 26.565, 14.036, 7.125, 3.576 degrees (= arctan(1/2^n)) |
| 152 | // Idea: [1, 2^(-n); -2^(-n), 1] are multiples of the rotation matrices for these angles. |
| 153 | i32 tan_angle_table_rad[5] = {51472, 30386, 16055, 8150, 4091}; |
154 | 154 | |
155 | | CFixed_15_16 c1; |
156 | | c1.SetInternalValue(51472); // pi/4 << 16 |
| 155 | i32 angleint = 2045; // = 1/2*arctan(1/16) << 16 |
| 156 | i32 xint = x.GetInternalValue(), yint = y.GetInternalValue(), tmpint; |
157 | 157 | |
158 | | CFixed_15_16 c2; |
159 | | c2.SetInternalValue(154415); // 3*pi/4 << 16 |
| 158 | // Handle the case (0,0). |
| 159 | if (xint == 0 && yint == 0) |
| 160 | return CFixed_15_16::FromInt(0); |
160 | 161 | |
161 | | CFixed_15_16 abs_y = y.Absolute(); |
| 162 | // Reduce the problem to the case x,y >= 0, i.e. 0 <= angle <= pi/2 |
| 163 | if (yint >= 0) // (0, pi) |
| 164 | { |
| 165 | if (xint < 0) // (pi/2, pi) |
| 166 | { |
| 167 | // (x, y) -> (y, -x) |
| 168 | tmpint = xint; |
| 169 | xint = yint; |
| 170 | yint = -tmpint; |
| 171 | angleint = 102944+2045; // pi/2 << 16 |
| 172 | } |
| 173 | } |
| 174 | else // (-pi, 0) |
| 175 | { |
| 176 | if (xint < 0) // (-pi, -pi/2) |
| 177 | { |
| 178 | // (x, y) -> (-x, -y) |
| 179 | xint *= -1; |
| 180 | yint *= -1; |
| 181 | angleint = -205887+2045; // -pi << 16 |
| 182 | } |
| 183 | else // (-pi/2, 0) |
| 184 | { |
| 185 | // (x, y) -> (-y, x) |
| 186 | tmpint = xint; |
| 187 | xint = -yint; |
| 188 | yint = tmpint; |
| 189 | angleint = -102944+2045; // -pi/2 << 16 |
| 190 | } |
| 191 | } |
162 | 192 | |
163 | | CFixed_15_16 angle; |
164 | | if (x >= zero) |
| 193 | // Scale up / down to prevent massive rounding errors / overflows. |
| 194 | if (xint < 64 && yint < 64) |
| 195 | { |
| 196 | xint <<= 6; |
| 197 | yint <<= 6; |
| 198 | } |
| 199 | else if (xint >= 8192 || yint >= 8192) |
165 | 200 | { |
166 | | CFixed_15_16 r = (x - abs_y) / (x + abs_y); |
167 | | angle = c1 - c1.Multiply(r); |
| 201 | xint >>= 4; |
| 202 | yint >>= 4; |
168 | 203 | } |
169 | | else |
| 204 | for (u8 loop_var = 0; loop_var < 5; ++loop_var) |
170 | 205 | { |
171 | | CFixed_15_16 r = (x + abs_y) / (abs_y - x); |
172 | | angle = c2 - c1.Multiply(r); |
| 206 | tmpint = xint >> loop_var; |
| 207 | // If remaining angle > arctan(1/2^n) "rotate" counterclockwise. |
| 208 | if (yint >= tmpint) |
| 209 | { |
| 210 | // (x,y) -> (x,y) + 1/2^n * (-y,x) |
| 211 | xint += yint >> loop_var; |
| 212 | yint -= tmpint; |
| 213 | angleint += tan_angle_table_rad[loop_var]; |
| 214 | } |
173 | 215 | } |
| 216 | // Remaining angle is in [0,arctan(1/16)], arctan(1/16)/2 is the best possible approximation. |
174 | 217 | |
175 | | if (y < zero) |
176 | | return -angle; |
177 | | else |
178 | | return angle; |
| 218 | // If needed, get the value into the desired interval of length 2*pi. |
| 219 | |
| 220 | CFixed_15_16 angle; |
| 221 | angle.SetInternalValue(angleint); |
| 222 | return angle; |
179 | 223 | } |
180 | 224 | |
181 | 225 | template<> |