/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 43): * vi@violette.town wrote this file. You can do whatever you want with this * stuff. If we meet some day, and you think this stuff is worth it, * you can buy me a beer in return. Violette Paulin * ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include "json.h" #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 32 #define CUBE_WIDTH 10 enum faces { FACE_FRONT = 0, FACE_LEFT, FACE_RIGHT, FACE_BOTTOM, FACE_TOP, FACE_BACK, NUM_FACES, }; #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 SQ(n) n * n #define GET_ROTATE_X_Q(a) ({ double _a = (a) ; struct Quaternions q = {}; q.w = cos(_a/2); q.x = sin(_a/2); q; }) #define GET_ROTATE_Y_Q(a) ({ double _a = (a) ; struct Quaternions q = {}; q.w = cos(_a/2); q.y = sin(_a/2); q; }) #define GET_ROTATE_Z_Q(a) ({ double _a = (a) ; struct Quaternions q = {}; q.w = cos(_a/2); q.z = sin(_a/2); q; }) //TODO Idle animations #define IS_IDLE (Idle.x || Idle.y || Idle.z) #define RESET_IDLE {Idle.x = 0; Idle.y = 0; Idle.z = 0;} struct { char x; char y; char z; } Idle; struct Quaternions { double w; double x; double y; double z; } Target, Current, Last; double interpolationStep = 0; double zBuffer[SCREEN_HEIGHT * SCREEN_WIDTH]; char output[SCREEN_HEIGHT * SCREEN_WIDTH]; #ifdef VBUF char buf[SCREEN_HEIGHT * SCREEN_WIDTH * 7 * 2]; // stdout buffer #endif static volatile char shouldBreak = 1; static volatile char currentlyMoving = 0; static struct termios originalTerm = {}; void normalize(struct Quaternions *q) { double n = sqrt(SQ(q->w) + SQ(q->x) + SQ(q->y) + SQ(q->z)); if (n == 0) return; q->w /= n; q->x /= n; q->y /= n; q->z /= n; } struct Quaternions mult(struct Quaternions q, double x, double y, double z) { //p = q * p * qbar struct Quaternions res; res.w = 0; res.x = x * (SQ(q.w) + SQ(q.x) - SQ(q.y) - SQ(q.z)) + 2 * y * (q.x * q.y - q.w * q.z) + 2 * z * (q.x * q.z + q.w * q.y); res.y = 2 * x * (q.x * q.y + q.w * q.z) + y * (SQ(q.w) - SQ(q.x) + SQ(q.y) - SQ(q.z)) + 2 * z * (q.y * q.z - q.w * q.x); res.z = 2 * x * (q.x * q.z - q.w * q.y) + 2 * y * (q.y * q.z + q.w * q.x) + z * (SQ(q.w) - SQ(q.x) - SQ(q.y) + SQ(q.z)); return res; } struct Quaternions multQ(struct Quaternions p, struct Quaternions q) { if (p.x <= 0.001 && p.x >= -0.001 && p.y <= 0.001 && p.y >= -0.001 && p.z <= 0.001 && p.z >= -0.001) return q; if (q.x <= 0.001 && q.x >= -0.001 && q.y <= 0.001 && q.y >= -0.001 && q.z <= 0.001 && q.z >= -0.001) return p; struct Quaternions res = { .w = p.w * q.w - p.x * q.x - p.y * q.y - p.z * q.z, .x = p.w * q.x + p.x * q.w + p.y * q.z - p.z * q.y, .y = p.w * q.y - p.x * q.z + p.y * q.w + p.z * q.x, .z = p.w * q.z + p.x * q.y - p.y * q.x + p.z * q.w, }; return res; } char * chooseColor(char c) { switch (c) { case FACE_FRONT: return "\033[31;1;4m#\033[0m"; case FACE_BACK: return "\033[32;1;4m0\033[0m"; case FACE_BOTTOM: return "\033[33;1;4m!\033[0m"; case FACE_LEFT: return "\033[34;1;4m%\033[0m"; case FACE_RIGHT: return "\033[35;1;4m@\033[0m"; case FACE_TOP: return "\033[36;1;4m-\033[0m"; case ' ': return " "; default: //unused, but issues no warning return "\033[0m"; } } char chooseMainFace() { int total = 0; int faces[NUM_FACES] = {0}; for (int k = 0; k < SCREEN_WIDTH * SCREEN_HEIGHT; ++k) if (output[k] >= 0 && output[k] < NUM_FACES) { faces[output[k]]++; ++total; } int max = 0, idx = 0; for (int k = 0; k < NUM_FACES; ++k) if (faces[k] > max) { max = faces[k]; idx = k; } return max > total * 0.9 ? idx : -1; } void printFrontFace(struct json_object_element_s *elt, int row) { printf("\033[%dCContacts:\r\n", row); struct json_object_element_s *obj = json_value_as_object(elt->value)->start; while (obj != NULL) { struct json_string_s *str = json_value_as_string(obj->value); printf("\033[%dC%s: %s\r\n", row + 4, obj->name->string, str->string); obj = obj->next; } } void printLefttFace(struct json_object_element_s *elt, int row) { printf("\033[%dCSkills:\r\n", row); struct json_array_element_s *arr = json_value_as_array(elt->value)->start; while (arr != NULL) { struct json_object_element_s *obj = json_value_as_object(arr->value)->start; printf("\033[%dC%s\r\n", row + 4, json_value_as_string(obj->value)->string); struct json_array_element_s *arr2 = json_value_as_array(obj->next->value)->start; while (arr2 != NULL) { struct json_string_s *str = json_value_as_string(arr2->value); printf("\033[%dC%s\r\n", row + 8, str->string); arr2 = arr2->next; } arr = arr->next; } } void printRightFace(struct json_object_element_s *elt, int row) { printf("\033[%dCLanguages:\r\n", row); struct json_array_element_s *arr = json_value_as_array(elt->value)->start; while (arr != NULL) { struct json_object_element_s *obj = json_value_as_object(arr->value)->start; printf("\033[%dC%s: %s / 5\r\n", row + 4, json_value_as_string(obj->value)->string, json_value_as_number(obj->next->value)->number); arr = arr->next; } } void printBottomFace(struct json_object_element_s *elt, int row) { printf("\033[%dCProfessional experience:\r\n", row); struct json_array_element_s *arr = json_value_as_array(elt->value)->start; while (arr != NULL) { struct json_object_element_s *obj = json_value_as_object(arr->value)->start; struct json_string_s *position = json_value_as_string(obj->value); struct json_object_element_s *company = json_value_as_object(obj->next->value)->start; struct json_string_s *from = json_value_as_string(obj->next->next->value); struct json_string_s *to = json_value_as_string(obj->next->next->next->value); struct json_array_element_s *descr = json_value_as_array(obj->next->next->next->next->value)->start; printf("\033[%dC%s: %s ; %s -> %s\r\n", row + 4, json_value_as_string(company->value)->string, position->string, from->string, to->string); while (descr != NULL) { printf("\033[%dC%s\r\n", row + 8, json_value_as_string(descr->value)->string); descr = descr->next; } printf("\r\n"); arr = arr->next; } } void printBackFace(struct json_object_element_s *elt, int row) { printf("\033[%dCProjects:\r\n", row); struct json_object_element_s *obj = json_value_as_object(elt->value)->start; //ignore personnal projects printf("\033[%dC- This cube! its made using Quaternion for 3d rotation, " "from scratch (except the CV text), in C\r\n", row + 4); printf("\r\n"); struct json_array_element_s *arr = json_value_as_array(obj->next->value)->start; while (arr != NULL) { struct json_string_s *main = json_value_as_string(json_value_as_object(arr->value)->start->value); struct json_string_s *link = json_value_as_string(json_value_as_object(arr->value)->start->next->value); struct json_array_element_s *descr = json_value_as_array(json_value_as_object(arr->value)->start->next->value)->start; struct json_number_s *year = json_value_as_number(json_value_as_object(arr->value)->start->next->next->next->value); printf("\033[%dC- %s, %s\r\n", row + 4, json_value_as_string(descr->value)->string, year->number); while (descr != NULL) { printf("\033[%dCo %s\r\n", row + 8, json_value_as_string(descr->value)->string); descr = descr->next; } printf("\r\n"); arr = arr->next; } } void printTopFace(struct json_object_element_s *elt, int row) { printf("\033[%dCEducation:\r\n", row); struct json_array_element_s *arr = json_value_as_array(elt->value)->start; while (arr != NULL) { struct json_object_element_s *obj = json_value_as_object(arr->value)->start; struct json_string_s *univ_name = json_value_as_string(json_value_as_object(obj->value)->start->value); struct json_string_s *degree = json_value_as_string(obj->next->value); struct json_string_s *major = json_value_as_string(obj->next->next->value); struct json_string_s *track = json_value_as_string(obj->next->next->next->value); struct json_string_s *from = json_value_as_string(obj->next->next->next->next->value); struct json_string_s *to = json_value_as_string(obj->next->next->next->next->next->value); printf("\033[%dC%s: %s -> %s \r\n", row + 4, univ_name->string, from->string, to->string); if (track == NULL || strcmp(track->string, "None") == 0) printf("\033[%dC%s, %s\r\n", row + 8, degree->string, major->string); else printf("\033[%dC%s %s, %s\r\n", row + 8, degree->string, major->string, track->string); printf("\r\n"); arr = arr->next; } } void printCV(char face, struct json_object_element_s *r, int row) { struct json_object_element_s start = *r; struct json_object_element_s *next = &start; char k = 0; if (face >= 0) for (; k < face; ++k) next = next->next; char *res; switch (face) { case FACE_FRONT: printFrontFace(next, row); break; case FACE_LEFT: printLefttFace(next, row); break; case FACE_RIGHT: printRightFace(next, row); break; case FACE_BOTTOM: printBottomFace(next, row); break; case FACE_BACK: printBackFace(next, row); break; case FACE_TOP: printTopFace(next, row); break; default: break; } } void printAscii(struct json_value_s *json) { char face = chooseMainFace(); struct json_object_element_s *root = json_value_as_object(json)->start; struct winsize w; ioctl(0, TIOCGWINSZ, &w); printCV(face, root, w.ws_row); printf("\033[%d;%dH", (int)((w.ws_row - SCREEN_HEIGHT) * 1.5 / 2), (w.ws_col - SCREEN_WIDTH) / 2); for (int k = 0; k < SCREEN_WIDTH * SCREEN_HEIGHT; ++k) { if (k % SCREEN_WIDTH) printf("%s", chooseColor(output[k])); else printf("\r\n\033[%dC", w.ws_row); } fflush(stdout); } void rotateCube(double cubeX, double cubeY, double cubeZ, char ch) { struct Quaternions q = mult(Current, cubeX, cubeY, cubeZ); double x = q.x; double y = q.y; double invZ = 1 / (q.z + 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); printf("\033[2J\033[?1049l"); // escape fullscreen mode } void printBanner(char keyboardInput) { printf("Last input character: %c, " "Current: w:%f x:%f y%f z:%f | " "Last: w:%f x:%f y%f z:%f | " "Target: w:%f x:%f y%f z:%f\r\n", keyboardInput, Current.w, Current.x, Current.y, Current.z, Last.w, Last.x, Last.y, Last.z, Target.w, Target.x, Target.y, Target.z); } struct Quaternions interpolate(struct Quaternions qa, struct Quaternions qb) { struct Quaternions res; double cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z; //if qa = qb or qa = -qb then theta = 0 and we can return qa if (fabs(cosHalfTheta) >= 1.0) { res.w = qa.w; res.x = qa.x; res.y = qa.y; res.z = qa.z; return res; } if (cosHalfTheta < 0) { qb.w = -qb.w; qb.x = -qb.x; qb.y = -qb.y; qb.z = qb.z; cosHalfTheta = -cosHalfTheta; } double halfTheta = acos(cosHalfTheta); double sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta); //if theta = 180 degrees then result is not fully defined // we could rotate around any axis normal to qa or qb if (fabs(sinHalfTheta) < 0.001) { res.w = (qa.w * 0.5 + qb.w * 0.5); res.x = (qa.x * 0.5 + qb.x * 0.5); res.y = (qa.y * 0.5 + qb.y * 0.5); res.z = (qa.z * 0.5 + qb.z * 0.5); return res; } interpolationStep += ACTION_STEP; double ratioA = sin((1 - interpolationStep) * halfTheta) / sinHalfTheta; double ratioB = sin(interpolationStep * halfTheta) / sinHalfTheta; res.w = (qa.w * ratioA + qb.w * ratioB); res.x = (qa.x * ratioA + qb.x * ratioB); res.y = (qa.y * ratioA + qb.y * ratioB); res.z = (qa.z * ratioA + qb.z * ratioB); return res; } void handleAngle(char *input) { if (!currentlyMoving) { currentlyMoving = *input; Last = Current; switch (*input) { case 'w': case 'W': Target = multQ(GET_ROTATE_X_Q(M_PI_2), Target); break; case 'a': case 'A': Target = multQ(GET_ROTATE_Y_Q(-M_PI_2), Target); break; case 's': case 'S': Target = multQ(GET_ROTATE_X_Q(-M_PI_2), Target); break; case 'd': case 'D': Target = multQ(GET_ROTATE_Y_Q(M_PI_2), Target); break; case 'q': case 'Q': Target = multQ(GET_ROTATE_Z_Q(-M_PI_2), Target); break; case 'e': case 'E': Target = multQ(GET_ROTATE_Z_Q(M_PI_2), Target); break; default: currentlyMoving = 0; //TODO idle movement } normalize(&Target); } else { if (interpolationStep < 1 - ACTION_STEP) Current = interpolate(Last, Target); else { //RESET_IDLE; Current = Target; *input = 0; interpolationStep = 0; currentlyMoving = 0; } normalize(&Current); } } char getInput(char *keyboardInput) { fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK); char c = getchar(); if (c == '\033') { if ((c = getchar()) == '[') switch (c = getchar()) { 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 // fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) & ~O_NONBLOCK); handleAngle(keyboardInput); return c; } struct json_value_s * readJson() { int size; FILE *fp = fopen("./cv.json", "r"); char *json; if (fp) { fseek(fp, 0, SEEK_END); size = ftell(fp); fseek(fp, 0, SEEK_SET); json = (char *)malloc(sizeof(char) * size); fread(json, 1, size, fp); } return json_parse(json, strlen(json)); } int main() { printf("\033[?1049h\033[H"); // enter fullscreen mode char keyboardInput = 0; signal(SIGINT, intHandler); tcgetattr(STDIN_FILENO, &originalTerm); struct termios t = {}; cfmakeraw(&t); t.c_lflag |= ISIG; t.c_cc[VINTR] = 3; tcsetattr(STDIN_FILENO, TCSANOW, &t); #ifdef VBUF setvbuf(stdout, buf, _IOFBF, sizeof(buf)); #endif Current = GET_ROTATE_Z_Q(0); struct json_value_s *json = readJson(); while (shouldBreak) { #ifdef VBUF memset(buf, ' ', sizeof(buf)); #endif memset(output, ' ', SCREEN_WIDTH * SCREEN_HEIGHT); memset(zBuffer, DBL_MIN, 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); rotateCube(cubeX, cubeY, CUBE_WIDTH, FACE_BACK); rotateCube(CUBE_WIDTH, cubeX, cubeY, FACE_RIGHT); rotateCube(-CUBE_WIDTH, cubeX, cubeY, FACE_LEFT); rotateCube(cubeX, CUBE_WIDTH, cubeY, FACE_BOTTOM); rotateCube(cubeX, -CUBE_WIDTH, cubeY, FACE_TOP); } } printf("\033[1;1H\033[2J"); printAscii(json); getInput(&keyboardInput); usleep(1000000 / 60); // 60fps max } }