From ca9f2d4c033ae2bdff8bedb3560e653195c7bf9a Mon Sep 17 00:00:00 2001 From: violette Date: Sun, 27 Apr 2025 15:38:09 -0400 Subject: [PATCH] Tait Bryan angles, yaw pitch roll --- main.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 main.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..19af0ed --- /dev/null +++ b/main.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 100 +#define SCREEN_HEIGHT 50 +#define CUBE_WIDTH 10 + +#define FACE_FRONT '#' +#define FACE_BACK '0' +#define FACE_BOTTOM '!' +#define FACE_LEFT '%' +#define FACE_RIGHT '@' +#define FACE_TOP '-' + +#define STEP 0.2 + +#define ACTION_STEP 0.1 + +#define PITCH_STEP 0.08 +#define ROLL_STEP 0.00 +#define YAW_STEP 0.01 + +#define K1 100 +#define K2 50 + +#define _RESET_ANGLE(input) do {\ + Angle.currentTarget = 0;\ + input = 0; \ + } while (0); + +#define RESET_A_ANGLE(input) do {\ + Angle.A = Angle.ATarget; \ + _RESET_ANGLE(input); \ + } while (0); +#define RESET_B_ANGLE(input) do {\ + Angle.B = Angle.BTarget; \ + _RESET_ANGLE(input); \ + } while (0); +#define RESET_C_ANGLE(input) do {\ + Angle.C = Angle.CTarget; \ + _RESET_ANGLE(input); \ + } while (0); + +struct { + double A, B, C; + double ATarget, BTarget, CTarget; + char currentTarget; + double cosA, sinA; + double cosB, sinB; + double cosC, sinC; +} Angle; + +void initAngles() { + Angle.A = 0, Angle.B = 0, Angle.C = 0; + Angle.ATarget = 0, Angle.BTarget = 0, Angle.CTarget = 0; + Angle.currentTarget = 0; +} + +double zBuffer[SCREEN_HEIGHT * SCREEN_WIDTH]; +char output[SCREEN_HEIGHT * SCREEN_WIDTH]; + +static volatile char shouldBreak = 1; +static struct termios originalTerm = {}; + +double calcX(double x, double y, double z){ + return + x * Angle.cosA * Angle.cosB + + y * Angle.sinA * Angle.cosB + -z * Angle.sinB; +} + +double calcY(double x, double y, double z){ + return + x * (Angle.cosA * Angle.sinB * Angle.sinC - Angle.sinA * Angle.cosC) + + y * (Angle.sinA * Angle.sinB * Angle.sinC + Angle.cosA * Angle.cosC) + + z * Angle.cosB * Angle.sinC; +} + +double calcZ(double x, double y, double z){ + return + x * (Angle.cosA * Angle.sinB * Angle.cosC + Angle.sinA * Angle.sinC) + + y * (Angle.sinA * Angle.sinB * Angle.cosC - Angle.cosA * Angle.sinC) + + z * Angle.cosB * Angle.cosC; +} + +void calcAngle(){ + Angle.cosA = cos(Angle.A); + Angle.sinA = sin(Angle.A); + Angle.cosB = cos(Angle.B); + Angle.sinB = sin(Angle.B); + Angle.cosC = cos(Angle.C); + Angle.sinC = sin(Angle.C); +} + +char *chooseColor(char c){ + switch(c) { + case '#': + return "\033[31;1;4m#\033[0m"; + case '0': + return "\033[32;1;4m0\033[0m"; + case '!': + return "\033[33;1;4m!\033[0m"; + case '%': + return "\033[34;1;4m%\033[0m"; + case '@': + return "\033[35;1;4m@\033[0m"; + case '-': + return "\033[36;1;4m-\033[0m"; + case ' ': + return " "; + default: // unused, but issues no warning + return ""; + } +} + +void printAscii() { +#ifdef __APPLE__ + fcntl(STDOUT_FILENO, F_SETFL, ~O_NONBLOCK); +#endif + for (int k = 0 ; k < SCREEN_WIDTH * SCREEN_HEIGHT ; ++k) + printf("%s", k % SCREEN_WIDTH ? chooseColor(output[k]) : "\r\n"); + printf("\r\n"); +#ifdef __APPLE__ + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); +#endif +} + +void rotateCube(double cubeX, double cubeY, double cubeZ, char ch){ + calcAngle(); + double x = calcX(cubeX, cubeY, cubeZ); + double y = calcY(cubeX, cubeY, cubeZ); + double invZ = 1 / (calcZ(cubeX, cubeY, cubeZ) + K2); + + int screenX = (int)(SCREEN_WIDTH / 2) + floor((x) * K1) * invZ; + int screenY = (int)(SCREEN_HEIGHT / 2) + floor(((y) * K1 / 2 ) * invZ); + // TODO luminescence + + if (screenX > SCREEN_WIDTH || screenX < 0) return; + + int idx = screenY * SCREEN_WIDTH + screenX; + if(idx >= 0 && idx < SCREEN_WIDTH * SCREEN_HEIGHT) + if(zBuffer[idx] < invZ) { + zBuffer[idx] = invZ; + output[idx] = ch; + } +} + +void intHandler(int unused) { + shouldBreak = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &originalTerm); +} + +void printBanner(char keyboardInput) { + printf("Last input character: %c", keyboardInput); +} + +void handleAngle(char *input) { + if (!Angle.currentTarget) { + Angle.currentTarget = *input; + switch (*input) { + case 'W': + Angle.CTarget -= M_PI_2; + // pitch angle +pi/2 + break; + case 'A': + // yaw angle -pi/2 + Angle.BTarget -= M_PI_2; + break; + case 'S': + // pitch angle -pi/2 + Angle.CTarget += M_PI_2; + break; + case 'D': + // yaw angle +pi/2 + Angle.BTarget += M_PI_2; + break; + default: + Angle.currentTarget = 0; + } + + // idle animation + //Angle.A += ROLL_STEP; + //Angle.B += YAW_STEP; + //Angle.C += PITCH_STEP; + } + + else + switch(Angle.currentTarget) { + case 'W': + Angle.C -= ACTION_STEP; + if (Angle.C <= Angle.CTarget) RESET_C_ANGLE(*input); + break; + case 'A': + Angle.B -= ACTION_STEP; + if (Angle.B <= Angle.BTarget) RESET_B_ANGLE(*input); + break; + case 'S': + Angle.C += ACTION_STEP; + if (Angle.C >= Angle.CTarget) RESET_C_ANGLE(*input); + break; + case 'D': + Angle.B += ACTION_STEP; + if (Angle.B >= Angle.BTarget) RESET_B_ANGLE(*input); + break; + } +} + +char getInput(char *keyboardInput) { + char c = getchar(); + if (c == '\033') { // if the first value is esc + if ((c = getchar()) == '[') + switch(c = getchar()) { // the real value + case 'A': + c = 'W'; + break; + case 'B': + c = 'S'; + break; + case 'C': + c = 'D'; + break; + case 'D': + c = 'A'; + break; + } + } + + if (c != EOF) + *keyboardInput = c; + while((c = getchar()) != '\n' && c != EOF) {} // clean stdin + + handleAngle(keyboardInput); + return c; +} + +int main(){ + char keyboardInput = 0; + signal(SIGINT, intHandler); + tcgetattr(STDIN_FILENO, &originalTerm); +#ifndef __APPLE__ + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); +#endif + + struct termios t = {}; + cfmakeraw(&t); + t.c_lflag |= ISIG; + t.c_cc[VINTR] = 3; + tcsetattr(STDIN_FILENO, TCSANOW, &t); + + initAngles(); + + while(shouldBreak) { + memset(output, ' ', SCREEN_WIDTH * SCREEN_HEIGHT); + memset(zBuffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(double)); + + for(double cubeX = -CUBE_WIDTH ; cubeX < CUBE_WIDTH ; cubeX += STEP) { + for(double cubeY = -CUBE_WIDTH ; cubeY < CUBE_WIDTH ; cubeY += STEP) { + rotateCube(cubeX, cubeY, -CUBE_WIDTH, FACE_FRONT);//front + rotateCube(cubeX, cubeY, CUBE_WIDTH, FACE_BACK);//back + rotateCube(CUBE_WIDTH, -cubeX, cubeY, FACE_RIGHT);//right + rotateCube(-CUBE_WIDTH, cubeX, cubeY, FACE_LEFT);//left + rotateCube(cubeX, CUBE_WIDTH, cubeY, FACE_BOTTOM);//bottom + rotateCube(cubeX, -CUBE_WIDTH, cubeY, FACE_TOP);//top + } + } + + printf("\x1b[2J"); + printBanner(keyboardInput); + printAscii(); + getInput(&keyboardInput); + usleep(100000); + } +}