274 lines
6.1 KiB
C
274 lines
6.1 KiB
C
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
|
|
#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.05
|
|
#define YAW_STEP 0.05
|
|
|
|
#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.cosC * Angle.cosB +
|
|
y * Angle.sinC * Angle.cosB
|
|
-z * Angle.sinB;
|
|
}
|
|
|
|
double calcY(double x, double y, double z){
|
|
return
|
|
x * (Angle.cosC * Angle.sinB * Angle.sinA - Angle.sinC * Angle.cosA) +
|
|
y * (Angle.sinC * Angle.sinB * Angle.sinA + Angle.cosC * Angle.cosA) +
|
|
z * Angle.cosB * Angle.sinA;
|
|
}
|
|
|
|
double calcZ(double x, double y, double z){
|
|
return
|
|
x * (Angle.cosC * Angle.sinB * Angle.cosA + Angle.sinC * Angle.sinA) +
|
|
y * (Angle.sinC * Angle.sinB * Angle.cosA - Angle.cosC * Angle.sinA) +
|
|
z * Angle.cosB * Angle.cosA;
|
|
}
|
|
|
|
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.ATarget = Angle.A - M_PI_2;
|
|
break;
|
|
case 'A':
|
|
Angle.BTarget = Angle.B + M_PI_2;
|
|
break;
|
|
case 'S':
|
|
Angle.ATarget = Angle.A + M_PI_2;
|
|
break;
|
|
case 'D':
|
|
Angle.BTarget = Angle.B - 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.A -= ACTION_STEP;
|
|
if (Angle.A <= Angle.ATarget) RESET_A_ANGLE(*input);
|
|
break;
|
|
case 'A':
|
|
Angle.B += ACTION_STEP;
|
|
if (Angle.B >= Angle.BTarget) RESET_B_ANGLE(*input);
|
|
break;
|
|
case 'S':
|
|
Angle.A += ACTION_STEP;
|
|
if (Angle.A >= Angle.ATarget) RESET_A_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 + STEP ; cubeX < CUBE_WIDTH - STEP ; cubeX += STEP) {
|
|
for(double cubeY = -CUBE_WIDTH + STEP ; cubeY < CUBE_WIDTH - STEP ; 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);
|
|
}
|
|
}
|