Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | |||
8 | /* |
||
9 | * |
||
10 | * C version of fixed point library |
||
11 | * |
||
12 | */ |
||
13 | |||
14 | #include <cstdint> |
||
15 | #include <stdlib.h> |
||
16 | #include <math.h> |
||
17 | |||
18 | #include "dxxerror.h" |
||
19 | #include "maths.h" |
||
20 | |||
21 | namespace dcx { |
||
22 | |||
23 | #define EPSILON (F1_0/100) |
||
24 | |||
25 | namespace { |
||
26 | |||
27 | class fix_sincos_input |
||
28 | { |
||
29 | public: |
||
30 | const uint8_t m_idx; |
||
31 | const signed m_mul; |
||
32 | fix_sincos_input(fix a) : |
||
33 | m_idx(static_cast<uint8_t>(a >> 8)), |
||
34 | m_mul(static_cast<uint8_t>(a)) |
||
35 | { |
||
36 | } |
||
37 | }; |
||
38 | |||
39 | } |
||
40 | |||
41 | fix64 fixmul64(fix a, fix b) |
||
42 | { |
||
43 | const fix64 a64 = a; |
||
44 | const fix64 b64 = b; |
||
45 | return (a64 * b64) / 65536; |
||
46 | } |
||
47 | |||
48 | fix fixdiv(fix a, fix b) |
||
49 | { |
||
50 | if (!b) |
||
51 | return 1; |
||
52 | const fix64 a64 = a; |
||
53 | return static_cast<fix>((a64 * 65536) / b); |
||
54 | } |
||
55 | |||
56 | fix fixmuldiv(fix a, fix b, fix c) |
||
57 | { |
||
58 | if (!c) |
||
59 | return 1; |
||
60 | const fix64 a64 = a; |
||
61 | return static_cast<fix>((a64 * b) / c); |
||
62 | } |
||
63 | |||
64 | //given cos & sin of an angle, return that angle. |
||
65 | //parms need not be normalized, that is, the ratio of the parms cos/sin must |
||
66 | //equal the ratio of the actual cos & sin for the result angle, but the parms |
||
67 | //need not be the actual cos & sin. |
||
68 | //NOTE: this is different from the standard C atan2, since it is left-handed. |
||
69 | |||
70 | fixang fix_atan2(fix cos,fix sin) |
||
71 | { |
||
72 | fixang t; |
||
73 | |||
74 | //Assert(!(cos==0 && sin==0)); |
||
75 | |||
76 | //find smaller of two |
||
77 | |||
78 | const auto dsin = static_cast<double>(sin); |
||
79 | const auto dcos = static_cast<double>(cos); |
||
80 | double d; |
||
81 | d = sqrt((dsin * dsin) + (dcos * dcos)); |
||
82 | |||
83 | if (d==0.0) |
||
84 | return 0; |
||
85 | |||
86 | if (labs(sin) < labs(cos)) { //sin is smaller, use arcsin |
||
87 | t = fix_asin(static_cast<fix>((dsin / d) * 65536.0)); |
||
88 | if (cos<0) |
||
89 | t = 0x8000 - t; |
||
90 | return t; |
||
91 | } |
||
92 | else { |
||
93 | t = fix_acos(static_cast<fix>((dcos / d) * 65536.0)); |
||
94 | if (sin<0) |
||
95 | t = -t; |
||
96 | return t; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | static unsigned int fixdivquadlongu(quadint n, uint64_t d) |
||
101 | { |
||
102 | return n.q / d; |
||
103 | } |
||
104 | |||
105 | uint32_t quad_sqrt(const quadint iq) |
||
106 | { |
||
107 | const uint32_t low = iq.low; |
||
108 | const int32_t high = iq.high; |
||
109 | int i, cnt; |
||
110 | uint32_t r,old_r,t; |
||
111 | |||
112 | if (high<0) |
||
113 | return 0; |
||
114 | |||
115 | if (high==0 && static_cast<int32_t>(low)>=0) |
||
116 | return long_sqrt(static_cast<int32_t>(low)); |
||
117 | |||
118 | if ((i = high >> 24)) { |
||
119 | cnt=12+16; |
||
120 | } |
||
121 | else if ((i = high >> 16)) |
||
122 | { |
||
123 | cnt=8+16; |
||
124 | } |
||
125 | else if ((i = high >> 8)) |
||
126 | { |
||
127 | cnt=4+16; |
||
128 | } else { |
||
129 | cnt=0+16; i = high; |
||
130 | } |
||
131 | |||
132 | r = guess_table[i]<<cnt; |
||
133 | |||
134 | //quad loop usually executed 4 times |
||
135 | |||
136 | r = fixdivquadlongu(iq,r)/2 + r/2; |
||
137 | r = fixdivquadlongu(iq,r)/2 + r/2; |
||
138 | r = fixdivquadlongu(iq,r)/2 + r/2; |
||
139 | |||
140 | do { |
||
141 | |||
142 | old_r = r; |
||
143 | t = fixdivquadlongu(iq,r); |
||
144 | |||
145 | if (t==r) //got it! |
||
146 | return r; |
||
147 | |||
148 | r = t/2 + r/2; |
||
149 | |||
150 | } while (!(r==t || r==old_r)); |
||
151 | |||
152 | t = fixdivquadlongu(iq,r); |
||
153 | quadint tq; |
||
154 | //edited 05/17/99 Matt Mueller - tq.high is undefined here.. so set them to = 0 |
||
155 | tq.q = 0; |
||
156 | //end edit -MM |
||
157 | fixmulaccum(&tq,r,t); |
||
158 | if (tq.q != iq.q) |
||
159 | r++; |
||
160 | |||
161 | return r; |
||
162 | } |
||
163 | |||
164 | //computes the square root of a long, returning a short |
||
165 | ushort long_sqrt(int32_t a) |
||
166 | { |
||
167 | int cnt,r,old_r,t; |
||
168 | |||
169 | if (a<=0) |
||
170 | return 0; |
||
171 | |||
172 | if (a & 0xff000000) |
||
173 | cnt=12; |
||
174 | else if (a & 0xff0000) |
||
175 | cnt=8; |
||
176 | else if (a & 0xff00) |
||
177 | cnt=4; |
||
178 | else |
||
179 | cnt=0; |
||
180 | |||
181 | r = guess_table[(a>>cnt)&0xff]<<cnt; |
||
182 | |||
183 | //the loop nearly always executes 3 times, so we'll unroll it 2 times and |
||
184 | //not do any checking until after the third time. By my calcutations, the |
||
185 | //loop is executed 2 times in 99.97% of cases, 3 times in 93.65% of cases, |
||
186 | //four times in 16.18% of cases, and five times in 0.44% of cases. It never |
||
187 | //executes more than five times. By timing, I determined that is is faster |
||
188 | //to always execute three times and not check for termination the first two |
||
189 | //times through. This means that in 93.65% of cases, we save 6 cmp/jcc pairs, |
||
190 | //and in 6.35% of cases we do an extra divide. In real life, these numbers |
||
191 | //might not be the same. |
||
192 | |||
193 | r = ((a/r)+r)/2; |
||
194 | r = ((a/r)+r)/2; |
||
195 | |||
196 | do { |
||
197 | |||
198 | old_r = r; |
||
199 | t = a/r; |
||
200 | |||
201 | if (t==r) //got it! |
||
202 | return r; |
||
203 | |||
204 | r = (t+r)/2; |
||
205 | |||
206 | } while (!(r==t || r==old_r)); |
||
207 | |||
208 | if (a % r) |
||
209 | r++; |
||
210 | |||
211 | return r; |
||
212 | } |
||
213 | |||
214 | //computes the square root of a fix, returning a fix |
||
215 | fix fix_sqrt(fix a) |
||
216 | { |
||
217 | return static_cast<fix>(long_sqrt(a)) << 8; |
||
218 | } |
||
219 | |||
220 | static fix fix_sincos(const uint8_t idx0, const signed mul) |
||
221 | { |
||
222 | const fix t0 = sincos_table[idx0]; |
||
223 | const uint8_t idx1 = idx0 + 1; |
||
224 | const fix t1 = sincos_table[idx1]; |
||
225 | return (t0 + (((t1 - t0) * mul) >> 8)) << 2; |
||
226 | } |
||
227 | |||
228 | static fix fix_sin(const fix_sincos_input sci) |
||
229 | { |
||
230 | return fix_sincos(sci.m_idx, sci.m_mul); |
||
231 | } |
||
232 | |||
233 | __attribute_warn_unused_result |
||
234 | static fix fix_cos(const fix_sincos_input sci) |
||
235 | { |
||
236 | return fix_sincos(static_cast<uint8_t>(sci.m_idx + 64), sci.m_mul); |
||
237 | } |
||
238 | |||
239 | //compute sine and cosine of an angle, filling in the variables |
||
240 | //either of the pointers can be NULL |
||
241 | //with interpolation |
||
242 | fix_sincos_result fix_sincos(fix a) |
||
243 | { |
||
244 | fix_sincos_input i(a); |
||
245 | return {fix_sin(i), fix_cos(i)}; |
||
246 | } |
||
247 | |||
248 | fix fix_sin(fix a) |
||
249 | { |
||
250 | return fix_sin(fix_sincos_input{a}); |
||
251 | } |
||
252 | |||
253 | fix fix_cos(fix a) |
||
254 | { |
||
255 | return fix_cos(fix_sincos_input{a}); |
||
256 | } |
||
257 | |||
258 | //compute sine and cosine of an angle, filling in the variables |
||
259 | //either of the pointers can be NULL |
||
260 | //no interpolation |
||
261 | fix fix_fastsin(fix a) |
||
262 | { |
||
263 | const uint8_t i = static_cast<uint8_t>(a >> 8); |
||
264 | return sincos_table[i] << 2; |
||
265 | } |
||
266 | |||
267 | //compute inverse sine |
||
268 | fixang fix_asin(fix v) |
||
269 | { |
||
270 | fix vv; |
||
271 | int i,f,aa; |
||
272 | |||
273 | vv = labs(v); |
||
274 | |||
275 | if (vv >= f1_0) //check for out of range |
||
276 | return 0x4000; |
||
277 | |||
278 | i = (vv>>8)&0xff; |
||
279 | f = vv&0xff; |
||
280 | |||
281 | aa = asin_table[i]; |
||
282 | aa = aa + (((asin_table[i+1] - aa) * f)>>8); |
||
283 | |||
284 | if (v < 0) |
||
285 | aa = -aa; |
||
286 | |||
287 | return aa; |
||
288 | } |
||
289 | |||
290 | //compute inverse cosine |
||
291 | fixang fix_acos(fix v) |
||
292 | { |
||
293 | fix vv; |
||
294 | int i,f,aa; |
||
295 | |||
296 | vv = labs(v); |
||
297 | |||
298 | if (vv >= f1_0) //check for out of range |
||
299 | return 0; |
||
300 | |||
301 | i = (vv>>8)&0xff; |
||
302 | f = vv&0xff; |
||
303 | |||
304 | aa = acos_table[i]; |
||
305 | aa = aa + (((acos_table[i+1] - aa) * f)>>8); |
||
306 | |||
307 | if (v < 0) |
||
308 | aa = 0x8000 - aa; |
||
309 | |||
310 | return aa; |
||
311 | } |
||
312 | |||
313 | } |