#include <vectrex.h> #include "controller.h" /* Determine the direction that the vectrex joystick 'bat' is pointing, to use it as a spinner-like pointing device, returning a sector in the range 0:15. Note that the segments we want to determine straddle the compass points, eg segment 0 is from 1/32 left of north to 1/32 right of north. So we're interested in 16 segments, but they're all on boundaries that are rotated by 1/32 of a circle (i.e. 11.25 degrees) (It would have been much easier with unrotated octants! Just test x>y as tan 45 = 1 ...) 349.75d 11.25d, tan=0.2034523 \ / \ Sector / \ 0 / 22.5d tan = ?2 - 1 15 | 1 33.75 | / 45d, tan = 1 14 | 2 _56.25 | / 67.5d, tan = 1 + ?2 13 | 3 | __ 78.75 | 12---------------+----------------4 90d tan = infty | __ 101.25 | 11 | 5 | 10 | 6 | 9 | 7 8 */ // use signs to map sectors: static const int map[4][5] = { /* +n means n >= 0, -n means n < 0 */ /* 0: +x +y */ {0, 1, 2, 3, 4}, /* 1: +x -y */ {8, 7, 6, 5, 4}, /* 2: -x +y */ {0, 15, 14, 13, 12}, /* 3: -x -y */ {8, 9, 10, 11, 12} }; int sector(int x, int y) { // x,y signed in range -128:127, result 0:15 from north, clockwise. long tangent; // 16 bits int quadrant = 0; if (x > 0) x = -x; else quadrant |= 2; // make both negative avoids issue with negating -128 if (y > 0) y = -y; else quadrant |= 1; if (y != 0) { // The primary cost of this algorithm is five 16-bit multiplies. tangent = (long)x*32L; // worst case y = 1, tangent = 255*32 so fits in 2 bytes. /* determine base sector using abs(x)/abs(y). in segment: 0 if 0 <= x/y < tan 11.25 -- centered around 0 N 1 if tan 11.25 <= x/y < tan 33.75 -- 22.5 NxNE 2 if tan 33.75 <= x/y < tan 56.25 -- 45 NE 3 if tan 56.25 <= x/y < tan 78.75 -- 67.5 ExNE 4 if tan 78.75 <= x/y < tan 90 -- 90 E */ if (tangent > y*6L ) return map[quadrant][0]; // tan(11.25)*32 if (tangent > y*21L ) return map[quadrant][1]; // tan(33.75)*32 if (tangent > y*47L ) return map[quadrant][2]; // tan(56.25)*32 if (tangent > y*160L) return map[quadrant][3]; // tan(78.75)*32 // last case is the potentially infinite tan(90) but we never check that. } return map[quadrant][4]; } const int circle_x[16] = { 0,40,80,100, 120,100,80,40, 0,-40,-80,-100, -120,-100,-80,-40 }; const int circle_y[16] = { 120,100,80,40, 0, -40, -80, -100, -120, -100, -80, -40, 0, 40, 80, 100 }; int main(void) { int x,y,compass; enable_controller_1_x(); enable_controller_1_y(); for (;;) { Joy_Analog(); Wait_Recal(); Intensity_a(0x7f); Print_Str_d(120, -90, " NEAREST COMPASS \x80"); Print_Str_d(100, -90, "POINT TO JOYSTICK \x80"); // Only 8 will be selectable with digital joysticks, // All 16 reachable with analog values, which will // help tell some emulators from the real machine... // (an unintended side-effect of this code) x = joystick_1_x(); y = joystick_1_y(); if (x > 0 ) { Print_Str_d(-120, -120, "X > 0, \x80"); } else if (x < 0) { Print_Str_d(-120, -120, "X < 0, \x80"); } else { Print_Str_d(-120, -120, "X = 0, \x80"); } if (y > 0 ) { Print_Str_d(-120, -40, "Y > 0 \x80"); } else if (y < 0) { Print_Str_d(-120, -40, "Y < 0 \x80"); } else { Print_Str_d(-120, -40, "Y = 0 \x80"); } compass = sector(x,y); switch(compass) { case 0: Print_Str_d(y, x, "* 0 N \x80"); break; case 1: Print_Str_d(y, x, "* 1 N*NE\x80"); break; case 2: Print_Str_d(y, x, "* 2 NE\x80"); break; case 3: Print_Str_d(y, x, "* 3 E*NE\x80"); break; case 4: Print_Str_d(y, x, "* 4 E \x80"); break; case 5: Print_Str_d(y, x, "* 5 E*SE\x80"); break; case 6: Print_Str_d(y, x, "* 6 SE\x80"); break; case 7: Print_Str_d(y, x, "* 7 S*SE\x80"); break; case 8: Print_Str_d(y, x, "* 8 S \x80"); break; case 9: Print_Str_d(y, x, "* 9 S*SW\x80"); break; case 10: Print_Str_d(y, x, "* 10 SW\x80"); break; case 11: Print_Str_d(y, x, "* 11 W*SW\x80"); break; case 12: Print_Str_d(y, x, "* 12 W \x80"); break; case 13: Print_Str_d(y, x, "* 13 W*NW\x80"); break; case 14: Print_Str_d(y, x, "* 14 NW\x80"); break; case 15: Print_Str_d(y, x, "* 15 N*NW\x80"); break; } Reset0Ref(); Intensity_a(0x7f); Moveto_d(0, 0); Draw_Line_d(y, x); Reset0Ref(); Intensity_a(0x3f); Moveto_d(0, 0); Draw_Line_d(circle_y[compass], circle_x[compass]); // for this demo, no special handling of the dead zone around 0,0 // while testing robustness of x/y check. } return 0; }