/* * This file is part of the DXX-Rebirth project . * It is copyright by its individual contributors, as recorded in the * project's Git history. See COPYING.txt at the top level for license * terms and a link to the Git history. */ /* * * C version of fixed point library * */ #include #include #include #include "dxxerror.h" #include "maths.h" namespace dcx { #define EPSILON (F1_0/100) namespace { class fix_sincos_input { public: const uint8_t m_idx; const signed m_mul; fix_sincos_input(const fixang a) : m_idx(static_cast(a >> 8)), m_mul(static_cast(a)) { } }; } fix64 fixmul64(fix a, fix b) { const fix64 a64 = a; const fix64 b64 = b; return (a64 * b64) / 65536; } fix fixdiv(fix a, fix b) { if (!b) return 1; const fix64 a64 = a; return static_cast((a64 * 65536) / b); } fix fixmuldiv(fix a, fix b, fix c) { if (!c) return 1; const fix64 a64 = a; return static_cast((a64 * b) / c); } //given cos & sin of an angle, return that angle. //parms need not be normalized, that is, the ratio of the parms cos/sin must //equal the ratio of the actual cos & sin for the result angle, but the parms //need not be the actual cos & sin. //NOTE: this is different from the standard C atan2, since it is left-handed. fixang fix_atan2(fix cos,fix sin) { fixang t; //Assert(!(cos==0 && sin==0)); //find smaller of two const auto dsin = static_cast(sin); const auto dcos = static_cast(cos); double d; d = sqrt((dsin * dsin) + (dcos * dcos)); if (d==0.0) return 0; if (labs(sin) < labs(cos)) { //sin is smaller, use arcsin t = fix_asin(static_cast((dsin / d) * 65536.0)); if (cos<0) t = 0x8000 - t; return t; } else { t = fix_acos(static_cast((dcos / d) * 65536.0)); if (sin<0) t = -t; return t; } } [[nodiscard]] static unsigned fixdivquadlongu(quadint n, uint64_t d) { return n.q / d; } uint32_t quad_sqrt(const quadint iq) { const uint32_t low = static_cast(iq.q); const int32_t high = static_cast(iq.q >> 32); int i, cnt; uint32_t r,old_r,t; if (high<0) return 0; if (high==0 && static_cast(low)>=0) return long_sqrt(static_cast(low)); if ((i = high >> 24)) { cnt=12+16; } else if ((i = high >> 16)) { cnt=8+16; } else if ((i = high >> 8)) { cnt=4+16; } else { cnt=0+16; i = high; } r = guess_table[i]<>cnt)&0xff]<(long_sqrt(a)) << 8; } [[nodiscard]] static fix fix_sincos(const uint8_t idx0, const signed mul) { const fix t0 = sincos_table[idx0]; /* `idx1` is `uint8_t` to truncate the value, since sincos_table is * only 256 elements long. */ const uint8_t idx1 = idx0 + 1; const fix t1 = sincos_table[idx1]; return (t0 + (((t1 - t0) * mul) >> 8)) << 2; } [[nodiscard]] static fix fix_sin(const fix_sincos_input sci) { return fix_sincos(sci.m_idx, sci.m_mul); } [[nodiscard]] static fix fix_cos(const fix_sincos_input sci) { return fix_sincos(static_cast(sci.m_idx + 64), sci.m_mul); } //compute sine and cosine of an angle, filling in the variables //either of the pointers can be NULL //with interpolation fix_sincos_result fix_sincos(const fixang a) { fix_sincos_input i(a); return {fix_sin(i), fix_cos(i)}; } fix fix_sin(const fixang a) { return fix_sin(fix_sincos_input{a}); } fix fix_cos(const fixang a) { return fix_cos(fix_sincos_input{a}); } //compute sine and cosine of an angle, filling in the variables //no interpolation fix fix_fastsin(const fixang a) { const uint8_t i = static_cast(a >> 8); return sincos_table[i] << 2; } //compute inverse sine fixang fix_asin(fix v) { fix vv; int i,f,aa; vv = labs(v); if (vv >= f1_0) //check for out of range return 0x4000; i = (vv>>8)&0xff; f = vv&0xff; aa = asin_table[i]; aa = aa + (((asin_table[i+1] - aa) * f)>>8); if (v < 0) aa = -aa; return aa; } //compute inverse cosine fixang fix_acos(fix v) { fix vv; int i,f,aa; vv = labs(v); if (vv >= f1_0) //check for out of range return 0; i = (vv>>8)&0xff; f = vv&0xff; aa = acos_table[i]; aa = aa + (((acos_table[i+1] - aa) * f)>>8); if (v < 0) aa = 0x8000 - aa; return aa; } }