Added globe and lots of fanciness.

This commit is contained in:
Bruce Hill 2019-01-03 01:51:33 -08:00
parent 6b20ca721f
commit 0adbd9b687
5 changed files with 454 additions and 172 deletions

View File

@ -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

4
colors.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef __colors
#define __colors
const int WHITE = 1, YELLOW = 2, RED = 3, BLUE = 4, GREEN = 5, BLACK = 6;
#endif

187
globe.h Normal file
View File

@ -0,0 +1,187 @@
/**
* ASCII spinning glboe
*/
#include "colors.h"
#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <math.h>
#include <poll.h>
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);
}

52
list.h Normal file
View File

@ -0,0 +1,52 @@
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
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;
}*/

379
nuke.c
View File

@ -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 <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <ftw.h>
#include <math.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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; j<NUM_BLOBS; j++) {
double bx = blobs[j].x * i0;
double by = blobs[j].y * i0;
double bz = blobs[j].z * i0;
if (bz<5-PERSPECTIVE || bz>PERSPECTIVE) continue;
int x = midx + bx * PERSPECTIVE / (bz+PERSPECTIVE);
int y = midy + by * PERSPECTIVE / (bz+PERSPECTIVE);
if (x>=0 && x<cols && y>=0 && y<rows) {
int row = y, col = x;
mvaddch(row,col,(bz>40) ? '.' : (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<NUM_BLOBS; i++) {
for (int i=0; i<NUM_BLOBS; i++) {
double bx,by,bz,br;
bx = prng();
by = prng();
bz = prng();
@ -140,118 +176,121 @@ int main(int argc, char *argv[]) {
blobs[i].z = (bz / br) * (1.3 + 0.2 * prng());;
}
/* Generate animation frames */
for (i=0; i<NUM_FRAMES; i++) {
t = (1. * i) / NUM_FRAMES;
frame = frames[i] = (char*)malloc(cols * rows + 1);
curs_set(0); /* hide text cursor */
noecho();
if (i == 0) {
memset(frame, ' ', cols*rows);
int num_lines = 1, max_line = 0;
int linelen = 0;
for (char *p = input; *p && (num_lines < rows); ++p) {
if (*p == '\n') {
if (linelen > 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; j<NUM_BLOBS; j++) {
bx = blobs[j].x * i0;
by = blobs[j].y * i0;
bz = blobs[j].z * i0;
if (bz<5-PERSPECTIVE || bz>PERSPECTIVE) continue;
x = cols / 2 + bx * PERSPECTIVE / (bz+PERSPECTIVE);
y = rows / 2 + by * PERSPECTIVE / (bz+PERSPECTIVE);
if (x>=0 && x<cols && y>=0 && y<rows)
frames[i][x+y*cols] = (bz>40) ? '.' : (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<NUM_FRAMES; i++) {
erase();
mvaddstr(0,0,frames[i]);
refresh();
if (i == 0) {
/* Display confirmation */
attron(COLOR_PAIR(BLACK_ON_RED));
const char confirm[] = " FIRE NUKE? y/n ";
mvprintw((rows*3)/4, cols/2 - strlen(confirm)/2, "%s", confirm);
attroff(COLOR_PAIR(BLACK_ON_RED));
refresh();
if (getch() != 'y') {
goto exit_failure;
}
erase();
mvaddstr(0,0,frames[i]);
refresh();
timeout(10);
for (int f = 0; f < num_files; f++) {
mvprintw(rows/2-num_files/2+f, cols/2-max_line/2, "%s", files[f]);
}
refresh();
/* Display confirmation */
attron(COLOR_PAIR(BLACK_ON_RED));
const char confirm[] = " FIRE NUKE? y/n ";
mvprintw((rows*3)/4, cols/2 - strlen(confirm)/2, "%s", confirm);
attroff(COLOR_PAIR(BLACK_ON_RED));
refresh();
double zoom = .8; // percent of viewport globe will fill
/*
timeout(10);
chtype line[cols+1];
for (int f = 0; 1; f = (f + 1) % num_files) {
int i;
for (i = 0; files[f][i]; i++) {
line[i] = files[f][i];
}
for (; i < max_line; i++) line[i] = ' ';
line[i] = '\0';
//mvprintw(rows/2, cols/2-max_line/2, "%s", files[f]);
mvaddchstr(rows/2, cols/2-max_line/2,line);
refresh();
int ch = getch();
if (ch == -1)
usleep(33333); // 30 FPS
else if (ch != 'y')
goto exit_failure;
else break;
}
*/
if (getch() != 'y') {
goto exit_failure;
}
timeout(10);
int f = 0;
int targetr, targetc;
for (int i=0; i<30*3; i++) {
erase();
draw_stars();
draw_globe((double)i/30., zoom);
draw_target((double)i/30, zoom, &targetr, &targetc);
attron(COLOR_PAIR(RED));
mvaddch(targetr-1,targetc+1,'/');
attroff(COLOR_PAIR(RED));
attron(COLOR_PAIR(BLACK_ON_RED));
mvprintw(targetr-2,targetc+2,"%s", argv[f+1]);
attroff(COLOR_PAIR(BLACK_ON_RED));
f = (f + 1) % (argc - 1);
refresh();
int ch = getch();
if (ch == 'q' || ch == 27) {
goto exit_success;
}
usleep(33333); // 30 FPS
}
for (int i=1; i<NUM_FRAMES; i++) {
draw_frame(i, targetc, targetr, blobs);
refresh();
/* Quit early? */
int ch = getch();
if (ch == 'q' || ch == 27) {
goto exit_success;
}
usleep(33333); // 30 FPS
}
usleep(delay);
delay=16666; /* Change to 60fps after first frame */
for (int i=30*3.5; i < 30*5; i++) {
erase();
draw_stars();
draw_globe((double)i/30., zoom);
draw_scorch((double)i/30, zoom, &targetr, &targetc);
attron(COLOR_PAIR(GREEN));
mvaddch(targetr-1,targetc+1,'/');
attroff(COLOR_PAIR(GREEN));
attron(COLOR_PAIR(BLACK_ON_GREEN));
mvprintw(targetr-2,targetc+2," <GONE> ");
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;