File indexing completed on 2024-05-12 15:58:15

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  *
0006  *  adopted from here http://web.archive.org/web/20090728150504/http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx
0007  */
0008 
0009 #include "kis_fast_math.h"
0010 
0011 #include <cmath>
0012 #include <cstdio>
0013 #include <cstdlib>
0014 #include <QtGlobal>
0015 #include <QGlobalStatic>
0016 
0017 // Algorithm from http://web.archive.org/web/20090728150504/http://www.snippetcenter.org/en/a-fast-atan2-function-s1868.aspx
0018 const qreal MAX_SECOND_DERIV_IN_RANGE = 0.6495;
0019 
0020 /// precision
0021 const qreal MAX_ERROR = 0.0001;
0022 
0023 struct KisATanTable {
0024 
0025     KisATanTable() {
0026         qreal nf = ::sqrt(MAX_SECOND_DERIV_IN_RANGE / (8 * MAX_ERROR));
0027         NUM_ATAN_ENTRIES = int(nf) + 1;
0028         // Build table
0029         qreal y = 10.0;
0030         qreal x;
0031         ATanTable = new qreal[NUM_ATAN_ENTRIES + 1];
0032         ATanTable[0] = 0.0;
0033         for (quint32 i = 1; i <= NUM_ATAN_ENTRIES; i++) {
0034             x = (y / i) * NUM_ATAN_ENTRIES;
0035             ATanTable[i] = (qreal)::atan2(y, x);
0036         }
0037 
0038     }
0039 
0040     ~KisATanTable() {
0041         delete [] ATanTable;
0042     }
0043 
0044     quint32 NUM_ATAN_ENTRIES;
0045     qreal* ATanTable;
0046 };
0047 
0048 Q_GLOBAL_STATIC(KisATanTable, kisATanTable)
0049 
0050 /// private functions
0051 
0052 inline qreal interp(qreal r, qreal a, qreal b)
0053 {
0054     return r*(b - a) + a;
0055 }
0056 
0057 inline qreal calcAngle(qreal x, qreal y)
0058 {
0059     static qreal af = kisATanTable->NUM_ATAN_ENTRIES;
0060     static int ai = kisATanTable->NUM_ATAN_ENTRIES;
0061     static qreal* ATanTable = kisATanTable->ATanTable;
0062     qreal di = (y / x) * af;
0063     int i = (int)(di);
0064     if (i >= ai) return ::atan2(y, x);
0065     return interp(di - i, ATanTable[i], ATanTable[i+1]);
0066 }
0067 
0068 qreal KisFastMath::atan2(qreal y, qreal x)
0069 {
0070 
0071     if (y == 0.0) { // the line is horizontal
0072         if (x >= 0.0) { // towards the right
0073             return(0.0);// the angle is 0
0074         }
0075         // toward the left
0076         return qreal(M_PI);
0077     } // we now know that y is not 0 check x
0078     if (x == 0.0) { // the line is vertical
0079         if (y > 0.0) {
0080             return M_PI_2;
0081         }
0082         return -M_PI_2;
0083     }
0084     // from here on we know that niether x nor y is 0
0085     if (x > 0.0) {
0086         // we are in quadrant 1 or 4
0087         if (y > 0.0) {
0088             // we are in quadrant 1
0089             // now figure out which side of the 45 degree line
0090             if (x > y) {
0091                 return(calcAngle(x, y));
0092             }
0093             return(M_PI_2 - calcAngle(y, x));
0094         }
0095         // we are in quadrant 4
0096         y = -y;
0097         // now figure out which side of the 45 degree line
0098         if (x > y) {
0099             return(-calcAngle(x, y));
0100         }
0101         return(-M_PI_2 + calcAngle(y, x));
0102     }
0103     // we are in quadrant 2 or 3
0104     x = -x;
0105     // flip x so we can use it as a positive
0106     if (y > 0.0) {
0107         // we are in quadrant 2
0108         // now figure out which side of the 45 degree line
0109         if (x > y) {
0110             return(M_PI - calcAngle(x, y));
0111         } return(M_PI_2 + calcAngle(y, x));
0112     }
0113     // we are in quadrant 3
0114     y = -y;
0115     // flip y so we can use it as a positive
0116     // now figure out which side of the 45 degree line
0117     if (x > y) {
0118         return(-M_PI + calcAngle(x, y));
0119     } return(-M_PI_2 - calcAngle(y, x));
0120 }