diff options
-rw-r--r-- | engines/sci/engine/kmath.cpp | 82 |
1 files changed, 70 insertions, 12 deletions
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp index 14a20b8da1..b2aaa01b45 100644 --- a/engines/sci/engine/kmath.cpp +++ b/engines/sci/engine/kmath.cpp @@ -77,18 +77,7 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) { return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16()))); } -/** - * Returns the angle (in degrees) between the two points determined by (x1, y1) - * and (x2, y2). The angle ranges from 0 to 359 degrees. - * What this function does is pretty simple but apparently the original is not - * accurate. - */ -uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { - // SCI1 games (QFG2 and newer) use a simple atan implementation. SCI0 games - // use a somewhat less accurate calculation (below). - if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) - return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 180 / M_PI + 0.5) % 360; - +uint16 kGetAngle_SCI0(int16 x1, int16 y1, int16 x2, int16 y2) { int16 xRel = x2 - x1; int16 yRel = y1 - y2; // y-axis is mirrored. int16 angle; @@ -118,6 +107,75 @@ uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { return angle; } +// atan2 for first octant, x >= y >= 0. Returns [0,45] (inclusive) +int kGetAngle_SCI1_atan2_base(int y, int x) { + if (x == 0) + return 0; + + // fixed point tan(a) + int tan_fp = 10000 * y / x; + + if ( tan_fp >= 1000 ) { + // For tan(a) >= 0.1, interpolate between multiples of 5 degrees + + // 10000 * tan([5, 10, 15, 20, 25, 30, 35, 40, 45]) + const int tan_table[] = { 875, 1763, 2679, 3640, 4663, 5774, + 7002, 8391, 10000 }; + + // Look up tan(a) in our table + int i = 1; + while (tan_fp > tan_table[i]) ++i; + + // The angle a is between 5*i and 5*(i+1). We linearly interpolate. + int dist = tan_table[i] - tan_table[i-1]; + int interp = (5 * (tan_fp - tan_table[i-1]) + dist/2) / dist; + return 5*i + interp; + } else { + // for tan(a) < 0.1, tan(a) is approximately linear in a. + // tan'(0) = 1, so in degrees the slope of atan is 180/pi = 57.29... + return (57 * y + x/2) / x; + } +} + +int kGetAngle_SCI1_atan2(int y, int x) { + if (y < 0) { + int a = kGetAngle_SCI1_atan2(-y, -x); + if (a == 180) + return 0; + else + return 180 + a; + } + if (x < 0) + return 90 + kGetAngle_SCI1_atan2(-x, y); + if (y > x) + return 90 - kGetAngle_SCI1_atan2_base(x, y); + else + return kGetAngle_SCI1_atan2_base(y, x); + +} + +uint16 kGetAngle_SCI1(int16 x1, int16 y1, int16 x2, int16 y2) { + // We flip things around to get into the standard atan2 coordinate system + return kGetAngle_SCI1_atan2(x2 - x1, y1 - y2); + +} + +/** + * Returns the angle (in degrees) between the two points determined by (x1, y1) + * and (x2, y2). The angle ranges from 0 to 359 degrees. + * What this function does is pretty simple but apparently the original is not + * accurate. + */ + +uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) + return kGetAngle_SCI1(x1, y1, x2, y2); + else + return kGetAngle_SCI0(x1, y1, x2, y2); +} + + + reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) { // Based on behavior observed with a test program created with // SCI Studio. |