diff --git a/Makefile b/Makefile index a696551..272d335 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ PREFIX= all: nuke -nuke: nuke.c - cc nuke.c -lncurses -O3 -o nuke +nuke: *.h *.c + cc nuke.c -lncurses -g -o nuke clean: rm -f nuke diff --git a/colors.h b/colors.h new file mode 100644 index 0000000..a962f56 --- /dev/null +++ b/colors.h @@ -0,0 +1,4 @@ +#ifndef __colors +#define __colors +const int WHITE = 1, YELLOW = 2, RED = 3, BLUE = 4, GREEN = 5, BLACK = 6; +#endif diff --git a/globe.h b/globe.h new file mode 100644 index 0000000..fd4bddb --- /dev/null +++ b/globe.h @@ -0,0 +1,187 @@ +/** + * ASCII spinning glboe + */ + +#include "colors.h" +#include +#include +#include +#include +#include +#include +#include + +const double GOLDEN_RATIO = 1.6180339887498948482045868343656381; +const int NUM_WAVES = 7; +const double OFFSETS[2*NUM_WAVES] = { + 0.5459919526, 0.6072439135, 0.6217563193, 0.5444045324, 0.8923452588, + 0.4626828607, 0.9422234679, + + 0.7870886548, 0.7425605127, 0.1510923405, 0.3889282293, 0.3730024274, + 0.1450487012, 0.9051931355, +}; +const double AMPLITUDES[NUM_WAVES] = { + //0.9, 0.81, 0.729, 0.6561, 0.59049, 0.531441, 0.4782969 + 0.83, 0.6889, 0.571787, 0.47458321, 0.3939040643, 0.326940373369, 0.27136050989627 +}; + +static double hillnoise(double x, double y) +{ + double noise = 0; + double sigma = 0; + for (int i = 0; i < NUM_WAVES; i++) { + // Rotate coordinates + double rotation = fmod(((float)i)*GOLDEN_RATIO, 1.0)*2.0*M_PI; + double u = x*cos(rotation) - y*sin(rotation); + double v = -x*sin(rotation) - y*cos(rotation); + double size = AMPLITUDES[i]; + double offsetx = OFFSETS[2*i]; + double offsety = OFFSETS[2*i+1]; + noise += (size/2.)*(sin(u/size + offsetx) + sin(v/size + offsety)); + sigma += size*size; + } + sigma = sqrt(sigma)/2.; + noise /= 2.*sigma; + return (0.5*(noise < 0 ? -1. : 1.)*sqrt(1 - exp(-2./M_PI * noise*noise)) + 0.5); +} + +static double mix(double a, double b, double amount) +{ + return (1.-amount)*a + amount*b; +} + +void draw_stars() +{ + int rows,cols; + getmaxyx(stdscr,rows,cols); + attron(COLOR_PAIR(WHITE)); + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + double n = hillnoise(r*100,c*200); + if (n > .8) { + mvaddch(r,c,n > .9 ? '*' : '.'); + } + } + } + attroff(COLOR_PAIR(WHITE)); +} + +void draw_globe(double t, double zoom) +{ + int rows,cols; + getmaxyx(stdscr,rows,cols); + double rotation = t * (2*M_PI)/10. + M_PI; + const double rho = rows/2.; + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + double y = (c - cols/2) * 0.5 / zoom; + double z = (r - rows/2) * 1.0 / zoom; + double x = sqrt(rho*rho - z*z - y*y); + + if (z*z + y*y > rho*rho) { + continue; + } + + double theta = atan2(z, sqrt(x*x + y*y)); + double phi = fmod(atan2(y, x) + rotation, 2.*M_PI); + double elevation = hillnoise(theta*5., phi*5.); + double clouds = hillnoise(theta*5., phi*5. - 1.*rotation - 100.); + int color; + int ch; + if ((fabs(z)/rho) > .9) { + elevation = elevation + mix(.0, 1., 10.*(fabs(z)/rho - .9)) > .6 ? 1. : 0.; + } + if (clouds < .4) { + ch = (clouds < .3 ? '0' : '%') | A_BOLD; + color = COLOR_PAIR(WHITE); + } else if (elevation < .55) { + ch = '~'; + color = COLOR_PAIR(BLUE); + } else if (elevation < .6) { + ch = ':'; + color = COLOR_PAIR(YELLOW); + } else if (elevation < .85) { + ch = 'S'; + color = COLOR_PAIR(GREEN); + } else { + ch = '#'; + color = COLOR_PAIR(WHITE); + } + attron(color); + mvaddch(r,c,ch); + attroff(color); + } + } +} + +void draw_scorch(double t, double zoom, int *targetr, int *targetc) +{ + int rows,cols; + getmaxyx(stdscr,rows,cols); + double rotation = t * (2*M_PI)/10. + M_PI*1.4; + const double rho = rows/2.; + + double theta = M_PI/2*1.1; + double phi = M_PI + rotation; + double x = rho*sin(theta)*cos(phi); + double y = rho*sin(theta)*sin(phi); + double z = rho*cos(theta); + int r = z / (1.0/zoom) + rows/2; + int c = y / (.5/zoom) + cols/2; + *targetr = r; + *targetc = c; + + attron(COLOR_PAIR(BLACK) | A_BOLD); + mvprintw(r-1,c-1,"****"); + mvprintw(r,c-2, "******"); + mvprintw(r+1,c-1,"****"); + attroff(COLOR_PAIR(BLACK) | A_BOLD); +} + +void draw_target(double t, double zoom, int *targetr, int *targetc) +{ + int rows,cols; + getmaxyx(stdscr,rows,cols); + double rotation = t * (2*M_PI)/10. + M_PI*1.4; + const double rho = rows/2.; + + double theta = M_PI/2*1.1; + double phi = M_PI + rotation; + double x = rho*sin(theta)*cos(phi); + double y = rho*sin(theta)*sin(phi); + double z = rho*cos(theta); + int r = z / (1.0/zoom) + rows/2; + int c = y / (.5/zoom) + cols/2; + *targetr = r; + *targetc = c; + + if (x < 0) { + attron(COLOR_PAIR(YELLOW)); + mvaddch(r,c, '*' | A_BOLD); + attroff(COLOR_PAIR(YELLOW)); + } + + double wobble = t > 2.5 ? 0. : 2.5 - t; + if (wobble > .01) { + r = mix(r, rows*hillnoise(784,5.*t), wobble); + c = mix(c, cols*hillnoise(-784,5.*t), wobble); + } + + int attr = COLOR_PAIR(RED); + attron(attr); + mvaddch(r-1,c-2,ACS_ULCORNER | A_BOLD); + mvaddch(r-1,c-1,ACS_HLINE | A_BOLD); + //mvaddch(r-1,c-0,ACS_HLINE | A_BOLD); + mvaddch(r-1,c+1,ACS_HLINE | A_BOLD); + mvaddch(r-1,c+2,ACS_URCORNER | A_BOLD); + //mvaddch(r-0,c+2,ACS_VLINE | A_BOLD); + mvaddch(r+1,c+2,ACS_LRCORNER | A_BOLD); + mvaddch(r+1,c+1,ACS_HLINE | A_BOLD); + //mvaddch(r+1,c+0,ACS_HLINE | A_BOLD); + mvaddch(r+1,c-1,ACS_HLINE | A_BOLD); + mvaddch(r+1,c-2,ACS_LLCORNER | A_BOLD); + //mvaddch(r+0,c-2,ACS_VLINE | A_BOLD); + + mvaddch(r,c,'X' | A_BOLD); + attroff(attr); +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..a090f8d --- /dev/null +++ b/list.h @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +static void add_file(char *name, char ***files, int *size, int *capacity) +{ + if (*size == *capacity) { + *capacity *= 2; + *files = realloc(*files, sizeof(char*) * (*capacity)); + } + (*files)[*size] = strdup(name); + (*size)++; +} + +static void add_files(char *name, char ***files, int *size, int *capacity) +{ + DIR *dir; + struct dirent *entry; + + add_file(name, files, size, capacity); + if (!(dir = opendir(name))) { + return; + } + + while ((entry = readdir(dir)) != NULL) { + char path[1024]; + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(path, sizeof(path), "%s/%s", name, entry->d_name); + add_files(path, files, size, capacity); + } else { + snprintf(path, sizeof(path), "%s/%s", name, entry->d_name); + add_file(path, files, size, capacity); + } + } + closedir(dir); +} + +/*int main(int argc, char *argv[]) { + int capacity = 32, size = 0; + char **files = malloc(sizeof(char*)*capacity); + for (int i = 1; i < argc; i++) { + add_files(argv[i], &files, &size, &capacity); + } + + for (int f = 0; f < size; f++) { + printf("%s\n", files[f]); + } + return 0; +}*/ diff --git a/nuke.c b/nuke.c index 8de112d..094aa7e 100644 --- a/nuke.c +++ b/nuke.c @@ -3,15 +3,19 @@ * https://codegolf.stackexchange.com/questions/24462/display-the-explosion-of-a-star-in-ascii-art/24554#24554 */ +#include "colors.h" +#include "list.h" +#include "globe.h" #include -#include -#include -#include -#include +#include #include #include +#include +#include +#include +#include -#define NUM_FRAMES 150 +#define NUM_FRAMES 120 #define NUM_BLOBS 800 #define PERSPECTIVE 50.0 #define ESCDELAY 10 @@ -33,75 +37,98 @@ static void sighandler(int sig) { exit(EXIT_FAILURE); } -static char* get_piped_input() { - struct pollfd desc; - desc.fd = STDIN_FILENO; - desc.events = POLLIN; - int ret = poll(&desc, 1, 50); - if (ret == 0) { - return NULL; - } else if (ret < 0) { - perror("poll"); - raise(SIGINT); - } - size_t chunk = 1024; - size_t capacity = chunk; - size_t size = 0; - char *input = malloc(capacity); - for (size_t consumed; (consumed = fread(&input[size], 1, chunk, stdin)); size += consumed) { - if (consumed == chunk) { - chunk *= 2; - capacity += chunk; - input = realloc(input, capacity); - } - } - return input; -} +int color_ramp1[] = {1,1,1,2,2,2,3,3,3,1,1, -1}; +int bold_ramp1[] = {1,1,0,1,0,0,1,0,0,0,0, -1}; +int color_ramp2[] = {1,1,1,1,2,2,2,2,2,3,3,3,3,3,1,1,1,1,1,1, -1}; +int bold_ramp2[] = {1,1,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0, -1}; -/* Populate `lines` with (at most `max_lines`) lines from stdin, each at most - * `max_line` characters long. - * Return length of the longest line on success, -1 otherwise. - * * -static int get_piped_lines(char **lines[], size_t *max_lines, ssize_t *max_line) { - struct pollfd desc; - desc.fd = STDIN_FILENO; - desc.events = POLLIN; - int ret = poll(&desc, 1, 50); - if (ret > 0) { - ssize_t num_lines, longest_line = 0; - for (num_lines = 0; num_lines < *max_lines; num_lines++) { - size_t n = *max_line; - lines[num_lines] = (char*)malloc(n+1); - ssize_t line_len = getline(&lines[num_lines], &n, stdin); - while (lines[num_lines][line_len-1] == '\n') - lines[num_lines][--line_len] = '\0'; - if (line_len == -1) break; - if (line_len > longest_line) { - longest_line = line_len > *max_line ? *max_line : line_len; +void draw_frame(int i, int midx, int midy, spaceblob* blobs) { + int rows,cols; + getmaxyx(stdscr,rows,cols); + int maxx,minx,maxy,miny; + minx = -midx; + maxx = cols+minx-1; + miny = -midy; + maxy = rows+miny-1; + + chtype line[cols+1]; + for (int y=miny; y<=maxy; y++) { + int row = y+midy; + for (int x=minx; x<=maxx; x++) { + int col = x+midx; + /* Show expanding star in next 7 frames */ + if (i<8) { + double r = sqrt(x*x + 4*y*y); + if (r < i*2) + mvaddch(row,col,'@'); + continue; + } + + /* Otherwise show blast wave */ + double r = sqrt(x*x + 4*y*y) * (0.5 + (prng()/3.0)*cos(16.*atan2(y*2.+0.01,x+0.01))*.3); + int v = i - r - 7; + if (v<0) { + if (i<19) { + int attr = 0;//color_ramp1[i-8]; + line[col] = "%@W#H=+~-:."[i-8] | attr; + } else { + line[col] = ' '; + } + } else if (v<20) { + int attr = 0;//color_ramp2[v]; + line[col] = " .:!HIOMW#%$&@08O=+-"[v] | attr; + } else { + line[col] = ' '; } } - *max_lines = num_lines; - *max_line = longest_line; - return 0; - } else if (ret < 0) { - perror("poll"); - raise(SIGINT); + if (i >= 8) { + line[cols+1] = '\0'; + mvaddchstr(row,0,line); + } } - *max_lines = 0; - *max_line = 0; - return 0; -} -*/ -int main(int argc, char *argv[]) { - char *frames[NUM_FRAMES], *frame; - int i,j,x,y,z,v,rows,cols,ith,i0; - int maxx,minx,maxy,miny,delay=1E5; - double bx,by,bz,br,r,th,t; + /* Add blobs with perspective effect */ + if (i>6) { + int i0 = i-6; + for (int j=0; jPERSPECTIVE) continue; + int x = midx + bx * PERSPECTIVE / (bz+PERSPECTIVE); + int y = midy + by * PERSPECTIVE / (bz+PERSPECTIVE); + if (x>=0 && x=0 && y40) ? '.' : (bz>-20) ? 'o' : '@'); + } + } + } +} + +int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) +{ + int rv = remove(fpath); + if (rv) + perror(fpath); + return rv; +} + +int rmrf(char *path) +{ + return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); +} + +int main(int argc, char *argv[]) +{ + int rows,cols; + int delay=1E5; spaceblob *blobs; - char *input = get_piped_input(); - if (!input) goto exit_failure; + int capacity = 32, num_files= 0; + char **files = malloc(sizeof(char*)*capacity); + for (int i = 1; i < argc; i++) { + add_files(argv[i], &files, &num_files, &capacity); + } /* Initialize ncurses and get window dimensions */ //initscr(); @@ -120,17 +147,26 @@ int main(int argc, char *argv[]) { signal(SIGINT, sighandler); start_color(); - const int BLACK_ON_RED = 1; + init_pair(WHITE, COLOR_WHITE, COLOR_BLACK); + init_pair(YELLOW, COLOR_YELLOW, COLOR_BLACK); + init_pair(RED, COLOR_RED, COLOR_BLACK); + init_pair(BLUE, COLOR_BLUE, COLOR_BLACK); + init_pair(GREEN, COLOR_GREEN, COLOR_BLACK); + init_pair(BLACK, COLOR_BLACK, COLOR_BLACK); + for (int i = 0; color_ramp1[i] >= 0; i++) + color_ramp1[i] = COLOR_PAIR(color_ramp1[i]) | (bold_ramp1[i] ? A_BOLD : 0); + for (int i = 0; color_ramp2[i] >= 0; i++) + color_ramp2[i] = COLOR_PAIR(color_ramp2[i]) | (bold_ramp2[i] ? A_BOLD : 0); + + const int BLACK_ON_RED = 7, BLACK_ON_GREEN = 8; init_pair(BLACK_ON_RED, COLOR_BLACK, COLOR_RED); + init_pair(BLACK_ON_GREEN, COLOR_BLACK, COLOR_GREEN); getmaxyx(stdscr,rows,cols); - minx = -cols / 2; - maxx = cols+minx-1; - miny = -rows / 2; - maxy = rows+miny-1; /* Generate random blob coordinates */ blobs = (spaceblob*)malloc(NUM_BLOBS * sizeof(spaceblob)); - for (i=0; i max_line) - max_line = linelen; - linelen = 0; - num_lines++; - } else { - linelen++; - } - } - for (int l = 0, line = 0; input[l] && line < num_lines; line++) { - int eol = l; - while (input[eol] && input[eol] != '\n') eol++; - - memcpy(&frame[(rows/2 - num_lines/2 + line)*cols + cols/2 - max_line/2], - &input[l], cols < eol-l ? cols : eol-l); - - l = input[eol] == '\n' ? eol + 1 : eol; - } - - frame += cols*rows; - - } else { - char *frame0 = frames[0]; - for (y=miny; y<=maxy; y++) { - for (x=minx; x<=maxx; x++) { - char frame0c = *(frame0++); - /* Show expanding star in next 7 frames */ - if (i<8) { - r = sqrt(x*x + 4*y*y); - *frame++ = (r < i*2) ? '@' : frame0c; - continue; - } - - /* Otherwise show blast wave */ - r = sqrt(x*x + 4*y*y) * (0.5 + (prng()/3.0)*cos(16.*atan2(y*2.+0.01,x+0.01))*.3); - ith = 32 + th * 32. * M_1_PI; - v = i - r - 7; - if (v<0) *frame++ = (i<19)?"%@W#H=+~-:."[i-8]:frame0c; /* initial flash */ - else if (v<20) *frame++ = " .:!HIOMW#%$&@08O=+-"[v]; - else *frame++=' '; - } - } - } - - /* Add blobs with perspective effect */ - if (i>6) { - i0 = i-6; - for (j=0; jPERSPECTIVE) continue; - x = cols / 2 + bx * PERSPECTIVE / (bz+PERSPECTIVE); - y = rows / 2 + by * PERSPECTIVE / (bz+PERSPECTIVE); - if (x>=0 && x=0 && y40) ? '.' : (bz>-20) ? 'o' : '@'; - } - } - - /* Terminate the text string for this frame */ - *frame = '\0'; + // First frame + erase(); + int max_line = 0; + for (int f = 0; f < num_files; f++) { + int len = strlen(files[f]); + if (len > max_line) max_line = len; } - /* Now play back the frames in sequence */ - curs_set(0); /* hide text cursor (supposedly) */ - noecho(); - for (i=0; i "); + attroff(COLOR_PAIR(BLACK_ON_GREEN)); + refresh(); + int ch = getch(); + if (ch == 'q' || ch == 27) { + goto exit_success; + } + usleep(33333); // 30 FPS } exit_success: curs_set(1); /* unhide cursor */ endwin(); /* Exit ncurses */ - if (!isatty(fileno(stdout))) { - // Print stdin to stdout - puts(input); + for (int i = 1; i < argc; i++) { + if (rmrf(argv[i]) == 0) { + printf("deleted %s\n", argv[i]); + } else { + printf("unable to delete %s\n", argv[i]); + return EXIT_FAILURE; + } } return EXIT_SUCCESS;