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 | } |