├── screenshots └── screenshot.png ├── .gitmodules ├── Makefile ├── 3d.h ├── LICENSE ├── menu.h ├── README.md ├── menu.c └── 3d.c /screenshots/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/been-jamming/rubiks_cube/HEAD/screenshots/screenshot.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "CAM-curses-ascii-matcher"] 2 | path = CAM-curses-ascii-matcher 3 | url = https://github.com/been-jamming/CAM-curses-ascii-matcher 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(PREFIX),) 2 | PREFIX := /usr 3 | endif 4 | 5 | default: 3d.c CAM-curses-ascii-matcher/CAM.c menu.c 6 | cc 3d.c CAM-curses-ascii-matcher/CAM.c menu.c -Wall -O3 -ffast-math -lm -lncurses -o rubiks_cube 7 | 8 | install: rubiks_cube 9 | install -d $(DESTDIR)$(PREFIX)/bin 10 | install -C rubiks_cube $(DESTDIR)$(PREFIX)/bin/rubiks_cube 11 | install -d $(DESTDIR)$(PREFIX)/share/licenses/rubiks_cube 12 | install -C LICENSE $(DESTDIR)$(PREFIX)/share/licenses/rubiks_cube/LICENSE 13 | -------------------------------------------------------------------------------- /3d.h: -------------------------------------------------------------------------------- 1 | typedef struct vec2 vec2; 2 | 3 | struct vec2{ 4 | double x; 5 | double y; 6 | }; 7 | 8 | typedef struct vec3 vec3; 9 | 10 | struct vec3{ 11 | double x; 12 | double y; 13 | double z; 14 | }; 15 | 16 | typedef struct orientation orientation; 17 | 18 | struct orientation{ 19 | double real; 20 | double i; 21 | double j; 22 | double k; 23 | }; 24 | 25 | typedef struct triangle triangle; 26 | 27 | struct triangle{ 28 | vec3 p0; 29 | vec3 p1; 30 | vec3 p2; 31 | unsigned char color; 32 | }; 33 | 34 | typedef struct shape shape; 35 | 36 | struct shape{ 37 | unsigned int num_triangles; 38 | triangle *triangles; 39 | vec3 center; 40 | orientation shape_orientation; 41 | }; 42 | 43 | typedef struct rubiks_cube rubiks_cube; 44 | 45 | struct rubiks_cube{ 46 | shape cubies[26]; 47 | }; 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Benjamin Jones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /menu.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_INCLUDED 2 | #define MENU_INCLUDED 3 | typedef struct menu menu; 4 | 5 | struct menu{ 6 | char *title; 7 | char **items; 8 | unsigned int num_items; 9 | unsigned int current_choice; 10 | unsigned int width; 11 | unsigned char color; 12 | unsigned char select_color; 13 | unsigned char selected; 14 | WINDOW *win; 15 | }; 16 | 17 | typedef struct text_entry text_entry; 18 | 19 | struct text_entry{ 20 | char *title; 21 | unsigned char cursor_pos; 22 | unsigned int width; 23 | unsigned char color; 24 | unsigned char select_color; 25 | unsigned char done; 26 | WINDOW *win; 27 | }; 28 | 29 | menu create_menu(char *title, char **items, unsigned int num_items, unsigned char color, unsigned char select_color); 30 | 31 | text_entry create_text_entry(char *title, unsigned char color, unsigned char select_color); 32 | 33 | void free_menu(menu m); 34 | 35 | void free_text_entry(text_entry t); 36 | 37 | void render_menu(menu m); 38 | 39 | void render_text_entry(text_entry t, char *buffer); 40 | 41 | void do_menu(menu *m); 42 | 43 | void do_text_entry(text_entry *t, char *buffer, unsigned int buffer_size); 44 | 45 | void iterate_menu(menu *m); 46 | 47 | void iterate_text_entry(text_entry *t, char *buffer, unsigned int buffer_size); 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rubiks_cube 2 | A rubik's cube that runs in your terminal! 3 | 4 | ![alt text](screenshots/screenshot.png) 5 | 6 | ## Compilation: 7 | 8 | ### Depedencies: ncurses or curses 9 | 10 | Use 11 | ``` 12 | make 13 | ``` 14 | to build on Linux. 15 | 16 | On Windows, compilation can be done using a port of curses for Windows such as [pdcurses](https://pdcurses.org/). 17 | 18 | To install, use 19 | ``` 20 | make install 21 | ``` 22 | Which will install the binary to /usr/bin by default and the license to /usr/share/licenses/rubiks_cube/LICENSE. 23 | 24 | ## How to Use: 25 | Start the application in a terminal. Make sure that the terminal is the correct size before starting the application, as you cannot resize the terminal during runtime as of now. 26 | ### Controls 27 | - Press Left, Right, Up, Down, z, or Z to orient the cube 28 | - Left and Right rotate the cube around the y axis. 29 | - Up and Down rotate the cube around the x axis. 30 | - z and Z rotate the cube around the z axis. 31 | - Press f/F, b/B, l/L, r/R, u/U, or d/D to turn a face 32 | - f is forward, b back, l left, r right, u up, d down. Pressing Shift-face, ie F, B, L, R, U, or D will turn the face clockwise instead of counterclockwise. 33 | - Press Space to align the cube with the camera's perspective. 34 | - Press m to access the menu 35 | - The menu allows you to scramble the cube, save, load, or quit 36 | - An empty seed to the cube scrambler sets the seed to a random seed. 37 | 38 | ### Feedback is welcome! 39 | 40 | ### Possible upcoming features 41 | - The ability to use any nxn cube 42 | - An automatic, efficient solver 43 | -------------------------------------------------------------------------------- /menu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "menu.h" 6 | 7 | menu create_menu(char *title, char **items, unsigned int num_items, unsigned char color, unsigned char select_color){ 8 | menu output; 9 | 10 | output.title = title; 11 | output.items = items; 12 | output.num_items = num_items; 13 | output.current_choice = 0; 14 | output.win = newwin(5, COLS/2, (LINES - 6)/2, COLS/4); 15 | output.width = COLS/2; 16 | output.color = color; 17 | output.select_color = select_color; 18 | output.selected = 0; 19 | keypad(output.win, 1); 20 | wbkgd(output.win, COLOR_PAIR(color)); 21 | 22 | return output; 23 | } 24 | 25 | text_entry create_text_entry(char *title, unsigned char color, unsigned char select_color){ 26 | text_entry output; 27 | 28 | output.title = title; 29 | output.win = newwin(5, COLS/2, (LINES - 6)/2, COLS/4); 30 | output.width = COLS/2; 31 | output.color = color; 32 | output.select_color = select_color; 33 | output.done = 0; 34 | output.cursor_pos = 0; 35 | wbkgd(output.win, COLOR_PAIR(color)); 36 | 37 | return output; 38 | } 39 | 40 | void free_menu(menu m){ 41 | delwin(m.win); 42 | } 43 | 44 | void free_text_entry(text_entry t){ 45 | delwin(t.win); 46 | } 47 | 48 | void render_menu(menu m){ 49 | unsigned int i; 50 | 51 | wmove(m.win, 0, 0); 52 | wattron(m.win, COLOR_PAIR(m.color)); 53 | wprintw(m.win, "%s\n", m.title); 54 | for(i = 0; i < m.width; i++){ 55 | wprintw(m.win, "_"); 56 | } 57 | if(m.current_choice > 0){ 58 | wprintw(m.win, "%s\n", m.items[m.current_choice - 1]); 59 | } else { 60 | wprintw(m.win, "\n"); 61 | } 62 | wattron(m.win, COLOR_PAIR(m.select_color)); 63 | wprintw(m.win, "%s\n", m.items[m.current_choice]); 64 | wattron(m.win, COLOR_PAIR(m.color)); 65 | if(m.current_choice != m.num_items - 1){ 66 | wprintw(m.win, "%s\n", m.items[m.current_choice + 1]); 67 | } else { 68 | wprintw(m.win, "\n"); 69 | } 70 | } 71 | 72 | void render_text_entry(text_entry t, char *buffer){ 73 | unsigned int i; 74 | 75 | wmove(t.win, 0, 0); 76 | wattron(t.win, COLOR_PAIR(t.color)); 77 | wprintw(t.win, "%s\n", t.title); 78 | for(i = 0; i < t.width; i++){ 79 | wprintw(t.win, "_"); 80 | } 81 | wattron(t.win, COLOR_PAIR(t.select_color)); 82 | wprintw(t.win, "\n%s\n\n", buffer); 83 | wattron(t.win, COLOR_PAIR(t.color)); 84 | wrefresh(t.win); 85 | } 86 | 87 | void do_menu(menu *m){ 88 | nodelay(m->win, 0); 89 | while(1){ 90 | render_menu(*m); 91 | wrefresh(m->win); 92 | switch(wgetch(m->win)){ 93 | case KEY_UP: 94 | if(m->current_choice > 0){ 95 | m->current_choice--; 96 | } 97 | break; 98 | case KEY_DOWN: 99 | if(m->current_choice < m->num_items - 1){ 100 | m->current_choice++; 101 | } 102 | break; 103 | case '\n': 104 | return; 105 | } 106 | } 107 | } 108 | 109 | void do_text_entry(text_entry *t, char *buffer, unsigned int buffer_size){ 110 | int key; 111 | 112 | nodelay(t->win, 0); 113 | buffer[0] = '\0'; 114 | while(1){ 115 | render_text_entry(*t, buffer); 116 | key = wgetch(t->win); 117 | if((key == 8 || key == 127) && t->cursor_pos){ 118 | t->cursor_pos--; 119 | buffer[t->cursor_pos] = '\0'; 120 | } else if(key == '\n'){ 121 | t->done = 1; 122 | return; 123 | } else if(key >= ' ' && key < 127 && t->cursor_pos + 1 < buffer_size){ 124 | buffer[t->cursor_pos] = key; 125 | t->cursor_pos++; 126 | buffer[t->cursor_pos] = '\0'; 127 | } 128 | } 129 | } 130 | 131 | void iterate_menu(menu *m){ 132 | render_menu(*m); 133 | nodelay(m->win, 1); 134 | switch(wgetch(m->win)){ 135 | case KEY_UP: 136 | if(m->current_choice > 0){ 137 | m->current_choice--; 138 | } 139 | break; 140 | case KEY_DOWN: 141 | if(m->current_choice < m->num_items - 1){ 142 | m->current_choice++; 143 | } 144 | break; 145 | case '\n': 146 | m->selected = 1; 147 | } 148 | } 149 | 150 | void iterate_text_entry(text_entry *t, char *buffer, unsigned int buffer_size){ 151 | int key; 152 | 153 | werase(t->win); 154 | render_text_entry(*t, buffer); 155 | nodelay(t->win, 1); 156 | key = wgetch(t->win); 157 | if((key == 8 || key == 127) && t->cursor_pos){ 158 | t->cursor_pos--; 159 | buffer[t->cursor_pos] = 0; 160 | } else if(key == '\n'){ 161 | t->done = 1; 162 | } else if(key >= ' ' && key < 127 && t->cursor_pos + 1 < buffer_size){ 163 | buffer[t->cursor_pos] = key; 164 | t->cursor_pos++; 165 | buffer[t->cursor_pos] = 0; 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /3d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "CAM-curses-ascii-matcher/CAM.h" 10 | #include "menu.h" 11 | #include "3d.h" 12 | 13 | #define ORIENT_FRAMES 5 14 | 15 | #ifndef M_PI 16 | 17 | #define M_PI 3.14159265358979323846 18 | 19 | #endif 20 | 21 | struct sigaction curses_old_action; 22 | unsigned int z_buffer_cols; 23 | unsigned int z_buffer_lines; 24 | 25 | CAM_screen *screen; 26 | double **z_buffer; 27 | double normal_constant; 28 | double fov_constant; 29 | vec3 normal; 30 | rubiks_cube r_cube; 31 | unsigned int scramble_moves_left = 0; 32 | orientation current_orientation = (orientation) {.real = 0, .i = 0, .j = 0, .k = 1}; 33 | 34 | unsigned char faces[6][8] = {{0, 1, 2, 5, 8, 7, 6, 3}, {17, 20, 23, 24, 25, 22, 19, 18}, {23, 20, 17, 9, 0, 3, 6, 14}, {19, 22, 25, 16, 8, 5, 2, 11}, {0, 9, 17, 18, 19, 11, 2, 1}, {6, 7, 8, 16, 25, 24, 23, 14}}; 35 | unsigned char faces_middles[6][4] = {{1, 5, 7, 3}, {20, 24, 22, 18}, {9, 3, 14, 20}, {11, 22, 16, 5}, {1, 9, 18, 11}, {7, 16, 24, 14}}; 36 | unsigned char faces_corners[6][4] = {{0, 2, 8, 6}, {17, 23, 25, 19}, {6, 23, 17, 0}, {8, 2, 19, 25}, {2, 0, 17, 19}, {6, 8, 25, 23}}; 37 | unsigned char faces_centers[6] = {4, 21, 12, 13, 10, 15}; 38 | vec3 face_vectors[6] = {(vec3) {.x = 0, .y = 1, .z = 0}, (vec3) {.x = 0, .y = -1, .z = 0}, (vec3) {.x = 1, .y = 0, .z = 0}, (vec3) {.x = -1, .y = 0, .z = 0}, (vec3) {.x = 0, .y = 0, .z = -1}, (vec3) {.x = 0, .y = 0, .z = 1}}; 39 | unsigned char animation_face; 40 | unsigned char animation_frame; 41 | unsigned char animation_direction; 42 | 43 | unsigned char reorient_frame; 44 | orientation reorient_orientation; 45 | orientation original_orientation; 46 | 47 | vec3 corner_vectors[4] = { 48 | (vec3) {.x = -1, .y = 1, .z = -1}, 49 | (vec3) {.x = -1, .y = 1, .z = 1}, 50 | (vec3) {.x = 1, .y = 1, .z = -1}, 51 | (vec3) {.x = 1, .y = 1, .z = 1} 52 | }; 53 | 54 | vec3 edge_vectors[6] = { 55 | (vec3) {.x = 0, .y = 1, .z = -1}, 56 | (vec3) {.x = 0, .y = 1, .z = 1}, 57 | (vec3) {.x = 1, .y = 1, .z = 0}, 58 | (vec3) {.x = -1, .y = 1, .z = 0}, 59 | (vec3) {.x = 1, .y = 0, .z = -1}, 60 | (vec3) {.x = -1, .y = 0, .z = -1} 61 | }; 62 | 63 | vec3 cross(vec3 a, vec3 b){ 64 | vec3 output; 65 | 66 | output.x = a.y*b.z - a.z*b.y; 67 | output.y = a.z*b.x - a.x*b.z; 68 | output.z = a.x*b.y - a.y*b.x; 69 | 70 | return output; 71 | } 72 | 73 | double dot(vec3 a, vec3 b){ 74 | return a.x*b.x + a.y*b.y + a.z*b.z; 75 | } 76 | 77 | vec3 subtract(vec3 a, vec3 b){ 78 | return (vec3) {.x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z}; 79 | } 80 | 81 | vec3 add(vec3 a, vec3 b){ 82 | return (vec3) {.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z}; 83 | } 84 | 85 | orientation normalize_quaternion(orientation a){ 86 | orientation output; 87 | double length; 88 | 89 | length = sqrt(a.real*a.real + a.i*a.i + a.j*a.j + a.k*a.k); 90 | output.real = a.real/length; 91 | output.i = a.i/length; 92 | output.j = a.j/length; 93 | output.k = a.k/length; 94 | 95 | return output; 96 | } 97 | 98 | orientation positive_orientation(orientation a){ 99 | return (orientation) {.real = fabs(a.real), .i = fabs(a.i), .j = fabs(a.j), .k = fabs(a.k)}; 100 | } 101 | 102 | orientation multiply_quaternions(orientation a, orientation b){ 103 | orientation output; 104 | 105 | output.real = a.real*b.real - a.i*b.i - a.j*b.j - a.k*b.k; 106 | output.i = a.real*b.i + a.i*b.real + a.j*b.k - a.k*b.j; 107 | output.j = a.real*b.j - a.i*b.k + a.j*b.real + a.k*b.i; 108 | output.k = a.real*b.k + a.i*b.j - a.j*b.i + a.k*b.real; 109 | 110 | return output; 111 | } 112 | 113 | orientation inverse_quaternion(orientation a){ 114 | double d; 115 | 116 | d = a.real*a.real + a.i*a.i + a.j*a.j + a.k*a.k; 117 | return (orientation) {.real = a.real/d, .i = -a.i/d, .j = -a.j/d, .k = -a.k/d}; 118 | } 119 | 120 | vec3 apply_orientation(vec3 v, orientation q){ 121 | orientation p; 122 | orientation r; 123 | 124 | p.real = 0; 125 | p.i = v.x; 126 | p.j = v.y; 127 | p.k = v.z; 128 | r = multiply_quaternions(multiply_quaternions(q, p), inverse_quaternion(q)); 129 | return (vec3) {.x = r.i, .y = r.j, .z = r.k}; 130 | } 131 | 132 | orientation create_orientation(vec3 v, double angle){ 133 | double d; 134 | 135 | d = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); 136 | return (orientation) {.real = cos(angle/2), .i = v.x*sin(angle/2)/d, .j = v.y*sin(angle/2)/d, .k = v.z*sin(angle/2)/d}; 137 | } 138 | 139 | double orientation_inner_product(orientation a, orientation b){ 140 | return a.real*b.real + a.i*b.i + a.j*b.j + a.k*b.k; 141 | } 142 | 143 | double orientation_score(orientation a, orientation b){ 144 | double prod; 145 | 146 | prod = orientation_inner_product(a, b); 147 | return acos(2*prod*prod - 1); 148 | } 149 | 150 | orientation average_orientations(orientation a, orientation b, double weight){ 151 | return (orientation) {.real = a.real*weight + b.real*(1 - weight), .i = a.i*weight + b.i*(1 - weight), .j = a.j*weight + b.j*(1 - weight), .k = a.k*weight + b.k*(1 - weight)}; 152 | } 153 | 154 | orientation orientation_from_to(vec3 a, vec3 b){ 155 | orientation output; 156 | double a_len_squared; 157 | double b_len_squared; 158 | double d; 159 | vec3 c; 160 | 161 | d = dot(a, b); 162 | if(d < -0.9999) 163 | return (orientation) {.real = 0, .i = 1, .j = 0, .k = 0}; 164 | c = cross(a, b); 165 | output.i = c.x; 166 | output.j = c.y; 167 | output.k = c.z; 168 | a_len_squared = dot(a, a); 169 | b_len_squared = dot(b, b); 170 | output.real = sqrt(a_len_squared*b_len_squared) + d; 171 | 172 | return normalize_quaternion(output); 173 | } 174 | 175 | unsigned char create_z_buffer(){ 176 | unsigned int i; 177 | unsigned int j; 178 | 179 | z_buffer_cols = COLS; 180 | z_buffer_lines = LINES; 181 | 182 | z_buffer = malloc(sizeof(double *)*(z_buffer_cols - 1)*8); 183 | if(!z_buffer){ 184 | return 1; 185 | } 186 | for(i = 0; i < (z_buffer_cols - 1)*8; i++){ 187 | z_buffer[i] = malloc(sizeof(double)*z_buffer_lines*13); 188 | if(!z_buffer[i]){ 189 | for(j = 0; j < i; j++){ 190 | free(z_buffer[j]); 191 | } 192 | free(z_buffer); 193 | return 1; 194 | } 195 | for(j = 0; j < z_buffer_lines*13; j++){ 196 | z_buffer[i][j] = INFINITY; 197 | } 198 | } 199 | 200 | return 0; 201 | } 202 | 203 | void clear_z_buffer(){ 204 | unsigned int i; 205 | unsigned int j; 206 | 207 | for(i = 0; i < (z_buffer_cols - 1)*8; i++){ 208 | for(j = 0; j < z_buffer_lines*13; j++){ 209 | z_buffer[i][j] = INFINITY; 210 | } 211 | } 212 | } 213 | 214 | void free_z_buffer(){ 215 | unsigned int i; 216 | for(i = 0; i < (z_buffer_cols - 1)*8; i++){ 217 | free(z_buffer[i]); 218 | } 219 | free(z_buffer); 220 | } 221 | 222 | vec2 get_point_3D(CAM_screen *s, vec3 p){ 223 | vec2 output; 224 | 225 | output.x = p.x*fov_constant/p.z + ((double) s->width)/2; 226 | output.y = p.y*fov_constant/p.z + ((double) s->height)/2; 227 | 228 | return output; 229 | } 230 | 231 | unsigned char pixel_handler(unsigned int x, unsigned int y, unsigned char color){ 232 | double z3d; 233 | 234 | z3d = normal_constant*fov_constant/dot(normal, (vec3) {.x = x - ((double) screen->width)/2, .y = y - ((double) screen->height)/2, .z = fov_constant}); 235 | if(z_buffer[x][y] >= z3d){ 236 | z_buffer[x][y] = z3d; 237 | return 1; 238 | } else { 239 | return 0; 240 | } 241 | } 242 | 243 | void create_cube(shape *s, vec3 center, double width, unsigned char color0, unsigned char color1, unsigned char color2, unsigned char color3, unsigned char color4, unsigned char color5){ 244 | vec3 p0, p1, p2, p3, p4, p5, p6, p7; 245 | 246 | s->num_triangles = 12; 247 | s->triangles = malloc(sizeof(triangle)*12); 248 | if(!s->triangles){ 249 | return; 250 | } 251 | 252 | p0 = (vec3) {.x = -width/2, .y = width/2, .z = -width/2}; 253 | p1 = (vec3) {.x = width/2, .y = width/2, .z = -width/2}; 254 | p2 = (vec3) {.x = width/2, .y = width/2, .z = width/2}; 255 | p3 = (vec3) {.x = -width/2, .y = width/2, .z = width/2}; 256 | p4 = (vec3) {.x = -width/2, .y = -width/2, .z = -width/2}; 257 | p5 = (vec3) {.x = width/2, .y = -width/2, .z = -width/2}; 258 | p6 = (vec3) {.x = width/2, .y = -width/2, .z = width/2}; 259 | p7 = (vec3) {.x = -width/2, .y = -width/2, .z = width/2}; 260 | 261 | s->triangles[0] = (triangle) {.p0 = p1, .p1 = p0, .p2 = p2, .color = color0}; 262 | s->triangles[1] = (triangle) {.p0 = p3, .p1 = p2, .p2 = p0, .color = color0}; 263 | s->triangles[2] = (triangle) {.p0 = p4, .p1 = p5, .p2 = p6, .color = color1}; 264 | s->triangles[3] = (triangle) {.p0 = p6, .p1 = p7, .p2 = p4, .color = color1}; 265 | s->triangles[4] = (triangle) {.p0 = p0, .p1 = p1, .p2 = p4, .color = color4}; 266 | s->triangles[5] = (triangle) {.p0 = p1, .p1 = p5, .p2 = p4, .color = color4}; 267 | s->triangles[6] = (triangle) {.p0 = p3, .p1 = p7, .p2 = p2, .color = color5}; 268 | s->triangles[7] = (triangle) {.p0 = p7, .p1 = p6, .p2 = p2, .color = color5}; 269 | s->triangles[8] = (triangle) {.p0 = p0, .p1 = p4, .p2 = p7, .color = color2}; 270 | s->triangles[9] = (triangle) {.p0 = p7, .p1 = p3, .p2 = p0, .color = color2}; 271 | s->triangles[10] = (triangle) {.p0 = p1, .p1 = p2, .p2 = p5, .color = color3}; 272 | s->triangles[11] = (triangle) {.p0 = p5, .p1 = p2, .p2 = p6, .color = color3}; 273 | 274 | s->center = center; 275 | s->shape_orientation = (orientation) {.real = 1, .i = 0, .j = 0, .k = 0}; 276 | }; 277 | 278 | void apply_orientation_triangle(triangle *t, orientation p){ 279 | t->p0 = apply_orientation(t->p0, p); 280 | t->p1 = apply_orientation(t->p1, p); 281 | t->p2 = apply_orientation(t->p2, p); 282 | } 283 | 284 | void apply_orientation_shape(shape *s){ 285 | unsigned int i; 286 | 287 | for(i = 0; i < s->num_triangles; i++){ 288 | apply_orientation_triangle(s->triangles + i, s->shape_orientation); 289 | } 290 | 291 | s->shape_orientation = (orientation) {.real = 1, .i = 0, .j = 0, .k = 0}; 292 | } 293 | 294 | void draw_triangle_3D(CAM_screen *s, triangle t, orientation p, vec3 center){ 295 | vec3 p0; 296 | vec3 p1; 297 | vec3 p2; 298 | vec2 screen_p0; 299 | vec2 screen_p1; 300 | vec2 screen_p2; 301 | 302 | p0 = add(center, apply_orientation(t.p0, p)); 303 | p1 = add(center, apply_orientation(t.p1, p)); 304 | p2 = add(center, apply_orientation(t.p2, p)); 305 | 306 | screen_p0 = get_point_3D(s, p0); 307 | screen_p1 = get_point_3D(s, p1); 308 | screen_p2 = get_point_3D(s, p2); 309 | 310 | normal = cross(subtract(p0, p1), subtract(p0, p2)); 311 | if(dot(normal, p0) < 0){ 312 | normal_constant = dot(normal, p0); 313 | 314 | CAM_set_pixel_handler(pixel_handler); 315 | CAM_triangle(s, screen_p0.x, screen_p0.y, screen_p1.x, screen_p1.y, screen_p2.x, screen_p2.y, t.color); 316 | CAM_unset_pixel_handler(); 317 | } 318 | } 319 | 320 | void draw_shape_3D(CAM_screen *screen, shape s){ 321 | unsigned int i; 322 | 323 | for(i = 0; i < s.num_triangles; i++){ 324 | draw_triangle_3D(screen, s.triangles[i], s.shape_orientation, s.center); 325 | } 326 | } 327 | 328 | void translate_triangle(triangle *t, vec3 translation){ 329 | t->p0 = add(t->p0, translation); 330 | t->p1 = add(t->p1, translation); 331 | t->p2 = add(t->p2, translation); 332 | } 333 | 334 | void translate_shape(shape *s, vec3 translation){ 335 | unsigned int i; 336 | 337 | for(i = 0; i < s->num_triangles; i++){ 338 | translate_triangle(s->triangles + i, translation); 339 | } 340 | } 341 | 342 | void rotate_cube(orientation p){ 343 | unsigned char i; 344 | 345 | for(i = 0; i < 26; i++){ 346 | r_cube.cubies[i].shape_orientation = multiply_quaternions(p, r_cube.cubies[i].shape_orientation); 347 | } 348 | } 349 | 350 | void render_cube(){ 351 | unsigned char i; 352 | 353 | for(i = 0; i < 26; i++){ 354 | draw_shape_3D(screen, r_cube.cubies[i]); 355 | } 356 | } 357 | 358 | unsigned char get_face(orientation p, vec3 v){ 359 | vec3 transformed_vector; 360 | double best_product; 361 | double product; 362 | unsigned char best_face; 363 | unsigned char face; 364 | 365 | transformed_vector = apply_orientation(v, inverse_quaternion(p)); 366 | for(face = 0; face < 6; face++){ 367 | product = dot(transformed_vector, face_vectors[face]); 368 | if(!face || product < best_product){ 369 | best_face = face; 370 | best_product = product; 371 | } 372 | } 373 | 374 | return best_face; 375 | } 376 | 377 | void do_animation_frame(orientation current_orientation){ 378 | unsigned char i; 379 | orientation rotation; 380 | shape temp_shape; 381 | 382 | if(animation_frame < 10){ 383 | animation_frame++; 384 | if(!animation_direction){ 385 | rotation = create_orientation(apply_orientation(face_vectors[animation_face], current_orientation), M_PI/20); 386 | } else { 387 | rotation = create_orientation(apply_orientation(face_vectors[animation_face], current_orientation), -M_PI/20); 388 | } 389 | for(i = 0; i < 8; i++){ 390 | r_cube.cubies[faces[animation_face][i]].shape_orientation = multiply_quaternions(rotation, r_cube.cubies[faces[animation_face][i]].shape_orientation); 391 | } 392 | r_cube.cubies[faces_centers[animation_face]].shape_orientation = multiply_quaternions(rotation, r_cube.cubies[faces_centers[animation_face]].shape_orientation); 393 | if(animation_frame == 10){ 394 | if(!animation_direction){ 395 | temp_shape = r_cube.cubies[faces_middles[animation_face][3]]; 396 | for(i = 3; i > 0; i--){ 397 | r_cube.cubies[faces_middles[animation_face][i]] = r_cube.cubies[faces_middles[animation_face][i - 1]]; 398 | } 399 | r_cube.cubies[faces_middles[animation_face][0]] = temp_shape; 400 | 401 | temp_shape = r_cube.cubies[faces_corners[animation_face][3]]; 402 | for(i = 3; i > 0; i--){ 403 | r_cube.cubies[faces_corners[animation_face][i]] = r_cube.cubies[faces_corners[animation_face][i - 1]]; 404 | } 405 | r_cube.cubies[faces_corners[animation_face][0]] = temp_shape; 406 | } else { 407 | temp_shape = r_cube.cubies[faces_middles[animation_face][0]]; 408 | for(i = 0; i < 3; i++){ 409 | r_cube.cubies[faces_middles[animation_face][i]] = r_cube.cubies[faces_middles[animation_face][i + 1]]; 410 | } 411 | r_cube.cubies[faces_middles[animation_face][3]] = temp_shape; 412 | 413 | temp_shape = r_cube.cubies[faces_corners[animation_face][0]]; 414 | for(i = 0; i < 3; i++){ 415 | r_cube.cubies[faces_corners[animation_face][i]] = r_cube.cubies[faces_corners[animation_face][i + 1]]; 416 | } 417 | r_cube.cubies[faces_corners[animation_face][3]] = temp_shape; 418 | } 419 | } 420 | } 421 | } 422 | 423 | void start_reorientation(){ 424 | double best_score; 425 | double score; 426 | unsigned int i; 427 | unsigned int j; 428 | orientation check_orientation; 429 | 430 | original_orientation = current_orientation; 431 | reorient_orientation = (orientation) {.real = 1, .i = 0, .j = 0, .k = 0}; 432 | best_score = orientation_score(original_orientation, reorient_orientation); 433 | 434 | for(i = 0; i < 3; i++){ 435 | for(j = 1; j < 4; j++){ 436 | check_orientation = create_orientation(face_vectors[i*2], j*M_PI/2); 437 | score = orientation_score(original_orientation, check_orientation); 438 | if(score < best_score){ 439 | best_score = score; 440 | reorient_orientation = check_orientation; 441 | } 442 | } 443 | } 444 | 445 | for(i = 0; i < 4; i++){ 446 | for(j = 1; j < 3; j++){ 447 | check_orientation = create_orientation(corner_vectors[i], j*2*M_PI/3); 448 | score = orientation_score(original_orientation, check_orientation); 449 | if(score < best_score){ 450 | best_score = score; 451 | reorient_orientation = check_orientation; 452 | } 453 | } 454 | } 455 | 456 | for(i = 0; i < 6; i++){ 457 | check_orientation = create_orientation(edge_vectors[i], M_PI); 458 | score = orientation_score(original_orientation, check_orientation); 459 | if(score < best_score){ 460 | best_score = score; 461 | reorient_orientation = check_orientation; 462 | } 463 | } 464 | 465 | reorient_frame = 0; 466 | } 467 | 468 | void do_reorient_frame(){ 469 | orientation next_rotation; 470 | orientation total_rotation; 471 | vec3 vec; 472 | double total_angle; 473 | 474 | if(reorient_frame < ORIENT_FRAMES){ 475 | reorient_frame++; 476 | total_rotation = multiply_quaternions(reorient_orientation, inverse_quaternion(original_orientation)); 477 | vec = (vec3) {.x = total_rotation.i, .y = total_rotation.j, .z = total_rotation.k}; 478 | total_angle = 2*acos(total_rotation.real); 479 | if(total_angle > M_PI) 480 | total_angle = total_angle - 2*M_PI; 481 | next_rotation = create_orientation(vec, total_angle/ORIENT_FRAMES); 482 | current_orientation = multiply_quaternions(next_rotation, current_orientation); 483 | rotate_cube(next_rotation); 484 | } 485 | } 486 | 487 | unsigned char check_r_cube(){ 488 | unsigned int i; 489 | 490 | for(i = 0; i < 26; i++){ 491 | if(!r_cube.cubies[i].triangles) 492 | return 1; 493 | } 494 | 495 | return 0; 496 | } 497 | 498 | void free_r_cube(){ 499 | unsigned int i; 500 | 501 | for(i = 0; i < 26; i++){ 502 | free(r_cube.cubies[i].triangles); 503 | } 504 | } 505 | 506 | void create_r_cube(){ 507 | //Top slice 508 | create_cube(r_cube.cubies, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 509 | translate_shape(r_cube.cubies, (vec3) {.x = -1.1, .y = -1.1, .z = -1.1}); 510 | create_cube(r_cube.cubies + 1, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 511 | translate_shape(r_cube.cubies + 1, (vec3) {.x = 0, .y = -1.1, .z = -1.1}); 512 | create_cube(r_cube.cubies + 2, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_GREEN, COLOR_YELLOW, COLOR_BLACK); 513 | translate_shape(r_cube.cubies + 2, (vec3) {.x = 1.1, .y = -1.1, .z = -1.1}); 514 | create_cube(r_cube.cubies + 3, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); 515 | translate_shape(r_cube.cubies + 3, (vec3) {.x = -1.1, .y = -1.1, .z = 0}); 516 | create_cube(r_cube.cubies + 4, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); 517 | translate_shape(r_cube.cubies + 4, (vec3) {.x = 0, .y = -1.1, .z = 0}); 518 | create_cube(r_cube.cubies + 5, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_BLACK); 519 | translate_shape(r_cube.cubies + 5, (vec3) {.x = 1.1, .y = -1.1, .z = 0}); 520 | create_cube(r_cube.cubies + 6, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 521 | translate_shape(r_cube.cubies + 6, (vec3) {.x = -1.1, .y = -1.1, .z = 1.1}); 522 | create_cube(r_cube.cubies + 7, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 523 | translate_shape(r_cube.cubies + 7, (vec3) {.x = 0, .y = -1.1, .z = 1.1}); 524 | create_cube(r_cube.cubies + 8, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_RED, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_CYAN); 525 | translate_shape(r_cube.cubies + 8, (vec3) {.x = 1.1, .y = -1.1, .z = 1.1}); 526 | 527 | //Middle slice 528 | create_cube(r_cube.cubies + 9, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 529 | translate_shape(r_cube.cubies + 9, (vec3) {.x = -1.1, .y = 0, .z = -1.1}); 530 | create_cube(r_cube.cubies + 10, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 531 | translate_shape(r_cube.cubies + 10, (vec3) {.x = 0, .y = 0, .z = -1.1}); 532 | create_cube(r_cube.cubies + 11, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_YELLOW, COLOR_BLACK); 533 | translate_shape(r_cube.cubies + 11, (vec3) {.x = 1.1, .y = 0, .z = -1.1}); 534 | create_cube(r_cube.cubies + 12, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); 535 | translate_shape(r_cube.cubies + 12, (vec3) {.x = -1.1, .y = 0, .z = 0}); 536 | create_cube(r_cube.cubies + 13, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_BLACK); 537 | translate_shape(r_cube.cubies + 13, (vec3) {.x = 1.1, .y = 0, .z = 0}); 538 | create_cube(r_cube.cubies + 14, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 539 | translate_shape(r_cube.cubies + 14, (vec3) {.x = -1.1, .y = 0, .z = 1.1}); 540 | create_cube(r_cube.cubies + 15, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 541 | translate_shape(r_cube.cubies + 15, (vec3) {.x = 0, .y = 0, .z = 1.1}); 542 | create_cube(r_cube.cubies + 16, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_CYAN); 543 | translate_shape(r_cube.cubies + 16, (vec3) {.x = 1.1, .y = 0, .z = 1.1}); 544 | 545 | //Bottom slice 546 | create_cube(r_cube.cubies + 17, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 547 | translate_shape(r_cube.cubies + 17, (vec3) {.x = -1.1, .y = 1.1, .z = -1.1}); 548 | create_cube(r_cube.cubies + 18, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_YELLOW, COLOR_BLACK); 549 | translate_shape(r_cube.cubies + 18, (vec3) {.x = 0, .y = 1.1, .z = -1.1}); 550 | create_cube(r_cube.cubies + 19, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_YELLOW, COLOR_BLACK); 551 | translate_shape(r_cube.cubies + 19, (vec3) {.x = 1.1, .y = 1.1, .z = -1.1}); 552 | create_cube(r_cube.cubies + 20, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); 553 | translate_shape(r_cube.cubies + 20, (vec3) {.x = -1.1, .y = 1.1, .z = 0}); 554 | create_cube(r_cube.cubies + 21, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); 555 | translate_shape(r_cube.cubies + 21, (vec3) {.x = 0, .y = 1.1, .z = 0}); 556 | create_cube(r_cube.cubies + 22, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_BLACK); 557 | translate_shape(r_cube.cubies + 22, (vec3) {.x = 1.1, .y = 1.1, .z = 0}); 558 | create_cube(r_cube.cubies + 23, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 559 | translate_shape(r_cube.cubies + 23, (vec3) {.x = -1.1, .y = 1.1, .z = 1.1}); 560 | create_cube(r_cube.cubies + 24, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_CYAN); 561 | translate_shape(r_cube.cubies + 24, (vec3) {.x = 0, .y = 1.1, .z = 1.1}); 562 | create_cube(r_cube.cubies + 25, (vec3) {.x = 0, .y = 0, .z = 5.5}, 1, COLOR_MAGENTA, COLOR_BLACK, COLOR_BLACK, COLOR_GREEN, COLOR_BLACK, COLOR_CYAN); 563 | translate_shape(r_cube.cubies + 25, (vec3) {.x = 1.1, .y = 1.1, .z = 1.1}); 564 | 565 | if(check_r_cube()){ 566 | free_r_cube(); 567 | free_z_buffer(); 568 | CAM_screen_free(screen); 569 | endwin(); 570 | fprintf(stderr, "Error: not enough memory\n"); 571 | exit(1); 572 | } 573 | } 574 | 575 | orientation create_random_rotation(){ 576 | vec3 v; 577 | 578 | v = (vec3) {.x = rand()%100 - 50, .y = rand()%100 - 50, .z = rand()%100 - 50}; 579 | 580 | return create_orientation(v, 0.005); 581 | } 582 | 583 | unsigned int get_random_seed(){ 584 | struct timespec current_time; 585 | 586 | clock_gettime(CLOCK_MONOTONIC, ¤t_time); 587 | return current_time.tv_nsec%UINT_MAX; 588 | } 589 | 590 | unsigned int get_seed_from_string(char *string){ 591 | unsigned int seed = 0; 592 | 593 | while(*string){ 594 | seed = (seed<<7) | *string; 595 | string++; 596 | } 597 | 598 | return seed; 599 | } 600 | 601 | unsigned char write_shape(shape s, unsigned int max_triangles, FILE *fp){ 602 | if(!fwrite(&(s.num_triangles), sizeof(unsigned int), 1, fp) || s.num_triangles > max_triangles){ 603 | return 1; 604 | } 605 | if(fwrite(s.triangles, sizeof(triangle), s.num_triangles, fp) < s.num_triangles){ 606 | return 1; 607 | } 608 | if(!fwrite(&(s.center), sizeof(vec3), 1, fp)){ 609 | return 1; 610 | } 611 | if(!fwrite(&(s.shape_orientation), sizeof(orientation), 1, fp)){ 612 | return 1; 613 | } 614 | 615 | return 0; 616 | } 617 | 618 | unsigned char read_shape(shape *s, unsigned int max_triangles, FILE *fp){ 619 | if(!fread(&(s->num_triangles), sizeof(unsigned int), 1, fp) || s->num_triangles > max_triangles){ 620 | return 1; 621 | } 622 | if(fread(s->triangles, sizeof(triangle), s->num_triangles, fp) < s->num_triangles){ 623 | return 1; 624 | } 625 | if(!fread(&(s->center), sizeof(vec3), 1, fp)){ 626 | return 1; 627 | } 628 | if(!fread(&(s->shape_orientation), sizeof(orientation), 1, fp)){ 629 | return 1; 630 | } 631 | 632 | return 0; 633 | } 634 | 635 | unsigned char write_rubiks_cube(FILE *fp){ 636 | unsigned char i; 637 | 638 | for(i = 0; i < 26; i++){ 639 | if(write_shape(r_cube.cubies[i], 12, fp)){ 640 | return 1; 641 | } 642 | } 643 | if(!fwrite(¤t_orientation, sizeof(orientation), 1, fp)){ 644 | return 1; 645 | } 646 | 647 | return 0; 648 | } 649 | 650 | unsigned char read_rubiks_cube(FILE *fp){ 651 | unsigned char i; 652 | 653 | for(i = 0; i < 26; i++){ 654 | if(read_shape(r_cube.cubies + i, 12, fp)){ 655 | return 1; 656 | } 657 | } 658 | if(!fread(¤t_orientation, sizeof(orientation), 1, fp)){ 659 | return 1; 660 | } 661 | 662 | return 0; 663 | } 664 | 665 | unsigned char open_menu(){ 666 | menu main_menu; 667 | menu unable_menu; 668 | text_entry seed_entry; 669 | char *menu_items[5] = {"Resume", "Scramble", "Save", "Load", "Exit"}; 670 | char *ok_item = "Ok"; 671 | char input_buffer[32]; 672 | unsigned int seed; 673 | FILE *fp; 674 | 675 | main_menu = create_menu("Options", menu_items, 5, 1, 2); 676 | 677 | do_menu(&main_menu); 678 | 679 | switch(main_menu.current_choice){ 680 | case 0: 681 | free_menu(main_menu); 682 | break; 683 | case 1: 684 | free_menu(main_menu); 685 | seed_entry = create_text_entry("Enter Seed", 1, 2); 686 | do_text_entry(&seed_entry, input_buffer, 32); 687 | free_text_entry(seed_entry); 688 | if(input_buffer[0]){ 689 | seed = get_seed_from_string(input_buffer); 690 | } else { 691 | seed = get_random_seed(); 692 | } 693 | srand(seed); 694 | scramble_moves_left = 30; 695 | break; 696 | case 2: 697 | free_menu(main_menu); 698 | if(animation_frame < 10 || scramble_moves_left){ 699 | unable_menu = create_menu("Unable to save during animation", &ok_item, 1, 1, 2); 700 | do_menu(&unable_menu); 701 | free_menu(unable_menu); 702 | return 0; 703 | } 704 | seed_entry = create_text_entry("Enter save name", 1, 2); 705 | do_text_entry(&seed_entry, input_buffer, 32); 706 | free_text_entry(seed_entry); 707 | fp = fopen(input_buffer, "wb"); 708 | if(!fp){ 709 | main_menu = create_menu("Error saving file", menu_items, 1, 1, 2); 710 | do_menu(&main_menu); 711 | free_menu(main_menu); 712 | return 0; 713 | } 714 | if(write_rubiks_cube(fp)){ 715 | fclose(fp); 716 | main_menu = create_menu("Error saving file", menu_items, 1, 1, 2); 717 | do_menu(&main_menu); 718 | free_menu(main_menu); 719 | return 0; 720 | } 721 | fclose(fp); 722 | return 0; 723 | case 3: 724 | free_menu(main_menu); 725 | if(animation_frame < 10 || scramble_moves_left){ 726 | unable_menu = create_menu("Unable to load during animation", &ok_item, 1, 1, 2); 727 | do_menu(&unable_menu); 728 | free_menu(unable_menu); 729 | return 0; 730 | } 731 | seed_entry = create_text_entry("Enter load name", 1, 2); 732 | do_text_entry(&seed_entry, input_buffer, 32); 733 | free_text_entry(seed_entry); 734 | fp = fopen(input_buffer, "rb"); 735 | if(!fp){ 736 | main_menu = create_menu("Error loading file", menu_items, 1, 1, 2); 737 | do_menu(&main_menu); 738 | free_menu(main_menu); 739 | return 0; 740 | } 741 | if(read_rubiks_cube(fp)){ 742 | fclose(fp); 743 | free_r_cube(); 744 | create_r_cube(); 745 | main_menu = create_menu("Error loading file", menu_items, 1, 1, 2); 746 | do_menu(&main_menu); 747 | free_menu(main_menu); 748 | return 0; 749 | } 750 | fclose(fp); 751 | return 0; 752 | case 4: 753 | free_menu(main_menu); 754 | return 1; 755 | } 756 | 757 | return 0; 758 | } 759 | 760 | void resize(int sig, siginfo_t *sinfo, void *v){ 761 | if(curses_old_action.sa_flags&SA_SIGINFO) 762 | curses_old_action.sa_sigaction(sig, sinfo, v); 763 | else 764 | curses_old_action.sa_handler(sig); 765 | endwin(); 766 | refresh(); 767 | CAM_screen_free(screen); 768 | screen = CAM_screen_create(stdscr, COLS - 1, LINES); 769 | init_pair(1, COLOR_WHITE, COLOR_BLACK); 770 | init_pair(2, COLOR_BLACK, COLOR_CYAN); 771 | CAM_init(3); 772 | if(screen->width > screen->height){ 773 | fov_constant = screen->height*4/5; 774 | } else { 775 | fov_constant = screen->width*4/5; 776 | } 777 | free_z_buffer(); 778 | create_z_buffer(); 779 | } 780 | 781 | void install_resize_handler(){ 782 | struct sigaction new_action; 783 | 784 | new_action.sa_sigaction = resize; 785 | sigemptyset(&new_action.sa_mask); 786 | new_action.sa_flags = SA_SIGINFO; 787 | new_action.sa_restorer = NULL; 788 | 789 | sigaction(SIGWINCH, &new_action, &curses_old_action); 790 | } 791 | 792 | int main(int argc, char **argv){ 793 | orientation x_rotate; 794 | orientation y_rotate; 795 | orientation z_rotate; 796 | struct timespec current_time; 797 | struct timespec sleep_time; 798 | unsigned long int last_nanoseconds; 799 | unsigned long int current_nanoseconds; 800 | unsigned long int sleep_nanoseconds; 801 | unsigned char do_update; 802 | unsigned char quit = 0; 803 | unsigned char first_frame = 1; 804 | int key; 805 | sigset_t ignore_winch; 806 | 807 | sigemptyset(&ignore_winch); 808 | sigaddset(&ignore_winch, SIGWINCH); 809 | 810 | animation_frame = 10; 811 | reorient_frame = ORIENT_FRAMES; 812 | memset(&r_cube, 0, sizeof(rubiks_cube)); 813 | initscr(); 814 | cbreak(); 815 | curs_set(0); 816 | nodelay(stdscr, 1); 817 | keypad(stdscr, 1); 818 | start_color(); 819 | init_pair(1, COLOR_WHITE, COLOR_BLACK); 820 | init_pair(2, COLOR_BLACK, COLOR_CYAN); 821 | if(CAM_init(3)){ 822 | endwin(); 823 | fprintf(stderr, "Error: terminal does not support colors\n"); 824 | return 1; 825 | } 826 | 827 | if(create_z_buffer()){ 828 | endwin(); 829 | fprintf(stderr, "Error: not enough memory\n"); 830 | return 1; 831 | } 832 | create_r_cube(); 833 | 834 | x_rotate = create_orientation((vec3) {.x = 1, .y = 0, .z = 0}, M_PI/16); 835 | y_rotate = create_orientation((vec3) {.x = 0, .y = 1, .z = 0}, M_PI/16); 836 | z_rotate = create_orientation((vec3) {.x = 0, .y = 0, .z = 1}, M_PI/16); 837 | screen = CAM_screen_create(stdscr, COLS - 1, LINES); 838 | if(screen->width > screen->height){ 839 | fov_constant = screen->height*4/5; 840 | } else { 841 | fov_constant = screen->width*4/5; 842 | } 843 | 844 | install_resize_handler(); 845 | 846 | clock_gettime(CLOCK_MONOTONIC, ¤t_time); 847 | last_nanoseconds = current_time.tv_sec*1000000000 + current_time.tv_nsec; 848 | sigprocmask(SIG_BLOCK, &ignore_winch, NULL); 849 | while(!quit){ 850 | sigprocmask(SIG_UNBLOCK, &ignore_winch, NULL); 851 | sigprocmask(SIG_BLOCK, &ignore_winch, NULL); 852 | if(!first_frame){ 853 | do_update = 0; 854 | } else { 855 | do_update = 1; 856 | first_frame = 0; 857 | } 858 | key = getch(); 859 | switch(key){ 860 | case KEY_UP: 861 | if(reorient_frame >= ORIENT_FRAMES){ 862 | rotate_cube(x_rotate); 863 | current_orientation = multiply_quaternions(x_rotate, current_orientation); 864 | do_update = 1; 865 | } 866 | break; 867 | case KEY_DOWN: 868 | if(reorient_frame >= ORIENT_FRAMES){ 869 | rotate_cube(inverse_quaternion(x_rotate)); 870 | current_orientation = multiply_quaternions(inverse_quaternion(x_rotate), current_orientation); 871 | do_update = 1; 872 | } 873 | break; 874 | case KEY_LEFT: 875 | if(reorient_frame >= ORIENT_FRAMES){ 876 | rotate_cube(inverse_quaternion(y_rotate)); 877 | current_orientation = multiply_quaternions(inverse_quaternion(y_rotate), current_orientation); 878 | do_update = 1; 879 | } 880 | break; 881 | case KEY_RIGHT: 882 | if(reorient_frame >= ORIENT_FRAMES){ 883 | rotate_cube(y_rotate); 884 | current_orientation = multiply_quaternions(y_rotate, current_orientation); 885 | do_update = 1; 886 | } 887 | break; 888 | case 'z': 889 | rotate_cube(z_rotate); 890 | current_orientation = multiply_quaternions(z_rotate, current_orientation); 891 | do_update = 1; 892 | break; 893 | case 'Z': 894 | rotate_cube(inverse_quaternion(z_rotate)); 895 | current_orientation = multiply_quaternions(inverse_quaternion(z_rotate), current_orientation); 896 | do_update = 1; 897 | break; 898 | case ' ': 899 | if(reorient_frame >= ORIENT_FRAMES){ 900 | start_reorientation(); 901 | } 902 | break; 903 | case 'm': 904 | quit = open_menu(); 905 | werase(stdscr); 906 | do_update = 1; 907 | clock_gettime(CLOCK_MONOTONIC, ¤t_time); 908 | last_nanoseconds = current_time.tv_sec*1000000000 + current_time.tv_nsec; 909 | break; 910 | } 911 | if(scramble_moves_left){ 912 | if(animation_frame == 10){ 913 | animation_frame = 0; 914 | animation_face = (animation_face + rand()%5 + 1)%6; 915 | animation_direction = rand()&1; 916 | scramble_moves_left--; 917 | } 918 | } else { 919 | switch(key){ 920 | case 'f': 921 | if(animation_frame >= 10){ 922 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 0, .z = 1}); 923 | animation_frame = 0; 924 | animation_direction = 0; 925 | } 926 | break; 927 | case 'F': 928 | if(animation_frame >= 10){ 929 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 0, .z = 1}); 930 | animation_frame = 0; 931 | animation_direction = 1; 932 | } 933 | break; 934 | case 'u': 935 | if(animation_frame >= 10){ 936 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 1, .z = 0}); 937 | animation_frame = 0; 938 | animation_direction = 0; 939 | } 940 | break; 941 | case 'U': 942 | if(animation_frame >= 10){ 943 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 1, .z = 0}); 944 | animation_frame = 0; 945 | animation_direction = 1; 946 | } 947 | break; 948 | case 'd': 949 | if(animation_frame >= 10){ 950 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = -1, .z = 0}); 951 | animation_frame = 0; 952 | animation_direction = 0; 953 | } 954 | break; 955 | case 'D': 956 | if(animation_frame >= 10){ 957 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = -1, .z = 0}); 958 | animation_frame = 0; 959 | animation_direction = 1; 960 | } 961 | break; 962 | case 'l': 963 | if(animation_frame >= 10){ 964 | animation_face = get_face(current_orientation, (vec3) {.x = 1, .y = 0, .z = 0}); 965 | animation_frame = 0; 966 | animation_direction = 0; 967 | } 968 | break; 969 | case 'L': 970 | if(animation_frame >= 10){ 971 | animation_face = get_face(current_orientation, (vec3) {.x = 1, .y = 0, .z = 0}); 972 | animation_frame = 0; 973 | animation_direction = 1; 974 | } 975 | break; 976 | case 'r': 977 | if(animation_frame >= 10){ 978 | animation_face = get_face(current_orientation, (vec3) {.x = -1, .y = 0, .z = 0}); 979 | animation_frame = 0; 980 | animation_direction = 0; 981 | } 982 | break; 983 | case 'R': 984 | if(animation_frame >= 10){ 985 | animation_face = get_face(current_orientation, (vec3) {.x = -1, .y = 0, .z = 0}); 986 | animation_frame = 0; 987 | animation_direction = 1; 988 | } 989 | break; 990 | case 'b': 991 | if(animation_frame >= 10){ 992 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 0, .z = -1}); 993 | animation_frame = 0; 994 | animation_direction = 0; 995 | } 996 | break; 997 | case 'B': 998 | if(animation_frame >= 10){ 999 | animation_face = get_face(current_orientation, (vec3) {.x = 0, .y = 0, .z = -1}); 1000 | animation_frame = 0; 1001 | animation_direction = 1; 1002 | } 1003 | break; 1004 | } 1005 | } 1006 | if(animation_frame < 10 || reorient_frame < ORIENT_FRAMES){ 1007 | do_update = 1; 1008 | } 1009 | do_animation_frame(current_orientation); 1010 | do_reorient_frame(); 1011 | if(do_update){ 1012 | clear_z_buffer(); 1013 | CAM_fill(screen, COLOR_WHITE); 1014 | render_cube(); 1015 | CAM_update(screen); 1016 | refresh(); 1017 | } 1018 | 1019 | clock_gettime(CLOCK_MONOTONIC, ¤t_time); 1020 | current_nanoseconds = current_time.tv_sec*1000000000 + current_time.tv_nsec; 1021 | if(current_nanoseconds - last_nanoseconds < 50000000){ 1022 | sleep_nanoseconds = 50000000 + last_nanoseconds - current_nanoseconds; 1023 | sleep_time.tv_sec = sleep_nanoseconds/1000000000; 1024 | sleep_time.tv_nsec = sleep_nanoseconds - sleep_time.tv_sec*1000000000; 1025 | nanosleep(&sleep_time, NULL); 1026 | } 1027 | last_nanoseconds += 50000000; 1028 | } 1029 | 1030 | CAM_screen_free(screen); 1031 | endwin(); 1032 | free_z_buffer(); 1033 | free_r_cube(); 1034 | 1035 | return 0; 1036 | } 1037 | 1038 | --------------------------------------------------------------------------------