bb
157c6af18e
Avoid cases of filenames Update years in terms and other legal(ish) documents Don't update years in license headers, since change is not meaningful Will add linter rule in seperate commit Happy recompiling everyone! Original Patch By: Nescio Comment By: Gallaecio Differential Revision: D2620 This was SVN commit r27786.
237 lines
5.1 KiB
C++
237 lines
5.1 KiB
C++
/* Copyright (C) 2010 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include "Fixed.h"
|
|
|
|
#include "ps/CStr.h"
|
|
|
|
#include <sstream>
|
|
|
|
template<>
|
|
CFixed_15_16 CFixed_15_16::FromString(const CStr8& s)
|
|
{
|
|
// Parse a superset of the xsd:decimal syntax: [-+]?\d*(\.\d*)?
|
|
|
|
if (s.empty())
|
|
return CFixed_15_16::Zero();
|
|
|
|
bool neg = false;
|
|
CFixed_15_16 r;
|
|
const char* c = &s[0];
|
|
|
|
if (*c == '+')
|
|
{
|
|
++c;
|
|
}
|
|
else if (*c == '-')
|
|
{
|
|
++c;
|
|
neg = true;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
// Integer part:
|
|
if (*c >= '0' && *c <= '9')
|
|
{
|
|
r = r * 10; // TODO: handle overflow gracefully, maybe
|
|
r += CFixed_15_16::FromInt(*c - '0');
|
|
++c;
|
|
}
|
|
else if (*c == '.')
|
|
{
|
|
++c;
|
|
u32 frac = 0;
|
|
u32 div = 1;
|
|
// Fractional part
|
|
while (*c >= '0' && *c <= '9')
|
|
{
|
|
frac *= 10;
|
|
frac += (*c - '0');
|
|
div *= 10;
|
|
++c;
|
|
if (div >= 100000)
|
|
{
|
|
// any further digits will be too small to have any major effect
|
|
break;
|
|
}
|
|
}
|
|
// too many digits or invalid character or end of string - add the fractional part and stop
|
|
r += CFixed_15_16(((u64)frac << 16) / div);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// invalid character or end of string
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (neg ? -r : r);
|
|
}
|
|
|
|
template<>
|
|
CFixed_15_16 CFixed_15_16::FromString(const CStrW& s)
|
|
{
|
|
return FromString(s.ToUTF8());
|
|
}
|
|
|
|
template<>
|
|
CStr8 CFixed_15_16::ToString() const
|
|
{
|
|
std::stringstream r;
|
|
|
|
u32 posvalue = abs(value);
|
|
if (value < 0)
|
|
r << "-";
|
|
|
|
r << (posvalue >> fract_bits);
|
|
|
|
u16 fraction = posvalue & ((1 << fract_bits) - 1);
|
|
if (fraction)
|
|
{
|
|
r << ".";
|
|
|
|
u32 frac = 0;
|
|
u32 div = 1;
|
|
|
|
// Do the inverse of FromString: Keep adding digits until (frac<<16)/div == expected fraction
|
|
while (true)
|
|
{
|
|
frac *= 10;
|
|
div *= 10;
|
|
|
|
// Low estimate of d such that ((frac+d)<<16)/div == fraction
|
|
u32 digit = (((u64)fraction*div) >> 16) - frac;
|
|
frac += digit;
|
|
|
|
// If this gives the exact target, then add the digit and stop
|
|
if (((u64)frac << 16) / div == fraction)
|
|
{
|
|
r << digit;
|
|
break;
|
|
}
|
|
|
|
// If the next higher digit gives the exact target, then add that digit and stop
|
|
if (digit <= 8 && (((u64)frac+1) << 16) / div == fraction)
|
|
{
|
|
r << digit+1;
|
|
break;
|
|
}
|
|
|
|
// Otherwise add the digit and continue
|
|
r << digit;
|
|
}
|
|
}
|
|
|
|
return r.str();
|
|
}
|
|
|
|
// Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
|
|
CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
|
|
{
|
|
CFixed_15_16 zero;
|
|
|
|
// Special case to avoid division-by-zero
|
|
if (x.IsZero() && y.IsZero())
|
|
return zero;
|
|
|
|
CFixed_15_16 c1;
|
|
c1.SetInternalValue(51472); // pi/4 << 16
|
|
|
|
CFixed_15_16 c2;
|
|
c2.SetInternalValue(154415); // 3*pi/4 << 16
|
|
|
|
CFixed_15_16 abs_y = y.Absolute();
|
|
|
|
CFixed_15_16 angle;
|
|
if (x >= zero)
|
|
{
|
|
CFixed_15_16 r = (x - abs_y) / (x + abs_y);
|
|
angle = c1 - c1.Multiply(r);
|
|
}
|
|
else
|
|
{
|
|
CFixed_15_16 r = (x + abs_y) / (abs_y - x);
|
|
angle = c2 - c1.Multiply(r);
|
|
}
|
|
|
|
if (y < zero)
|
|
return -angle;
|
|
else
|
|
return angle;
|
|
}
|
|
|
|
template<>
|
|
CFixed_15_16 CFixed_15_16::Pi()
|
|
{
|
|
return CFixed_15_16(205887); // = pi << 16
|
|
}
|
|
|
|
void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out)
|
|
{
|
|
// Based on http://www.coranac.com/2009/07/sines/
|
|
|
|
// TODO: this could be made a bit more precise by being careful about scaling
|
|
|
|
typedef CFixed_15_16 fixed;
|
|
|
|
fixed c2_pi;
|
|
c2_pi.SetInternalValue(41721); // = 2/pi << 16
|
|
|
|
// Map radians onto the range [0, 4)
|
|
fixed z = a.Multiply(c2_pi) % fixed::FromInt(4);
|
|
|
|
// Map z onto the range [-1, +1] for sin, and the same with z+1 to compute cos
|
|
fixed sz, cz;
|
|
if (z >= fixed::FromInt(3)) // [3, 4)
|
|
{
|
|
sz = z - fixed::FromInt(4);
|
|
cz = z - fixed::FromInt(3);
|
|
}
|
|
else if (z >= fixed::FromInt(2)) // [2, 3)
|
|
{
|
|
sz = fixed::FromInt(2) - z;
|
|
cz = z - fixed::FromInt(3);
|
|
}
|
|
else if (z >= fixed::FromInt(1)) // [1, 2)
|
|
{
|
|
sz = fixed::FromInt(2) - z;
|
|
cz = fixed::FromInt(1) - z;
|
|
}
|
|
else // [0, 1)
|
|
{
|
|
sz = z;
|
|
cz = fixed::FromInt(1) - z;
|
|
}
|
|
|
|
// Third-order (max absolute error ~0.02)
|
|
|
|
// sin_out = (sz / 2).Multiply(fixed::FromInt(3) - sz.Multiply(sz));
|
|
// cos_out = (cz / 2).Multiply(fixed::FromInt(3) - cz.Multiply(cz));
|
|
|
|
// Fifth-order (max absolute error ~0.0005)
|
|
|
|
fixed sz2 = sz.Multiply(sz);
|
|
sin_out = sz.Multiply(fixed::Pi() - sz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - sz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
|
|
|
|
fixed cz2 = cz.Multiply(cz);
|
|
cos_out = cz.Multiply(fixed::Pi() - cz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - cz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
|
|
}
|