nuke/globe.h

187 lines
5.4 KiB
C

/**
* 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>
static const double GOLDEN_RATIO = 1.6180339887498948482045868343656381;
#define NUM_WAVES 7
// Randomly generated x/y offsets:
static 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,
};
// 0.83^n amplitudes
static const double AMPLITUDES[NUM_WAVES] = {
0.83, 0.6889, 0.571787, 0.47458321, 0.3939040643, 0.326940373369, 0.27136050989627
};
// See: https://blog.bruce-hill.com/hill-noise/
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(double t)
{
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*1,c*2+(t*.04));
if (n > .8) {
mvaddch(r,c,n > .9 ? '*' : '.');
}
}
}
attroff(COLOR_PAIR(WHITE));
}
static double get_rotation(double t)
{
return t * (2*M_PI)/60. + M_PI;
}
void draw_globe(double t, double zoom)
{
int rows,cols;
getmaxyx(stdscr,rows,cols);
double rotation = get_rotation(t);
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*4., phi*4.);
double clouds = hillnoise(theta*12., phi*6. - 1.*rotation - 140.);
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) {
continue;
} else if (elevation < .55) {
// Water
ch = '~';
color = COLOR_PAIR(BLUE);
} else if (elevation < .65) {
// Sand
ch = ':';
color = COLOR_PAIR(YELLOW);
} else if (elevation < .75) {
// Grass
ch = ',' | A_BOLD;
color = COLOR_PAIR(GREEN);
} else if (elevation < .85) {
// Forest
ch = '&';
color = COLOR_PAIR(GREEN);
} else {
// Mountain
ch = '#';
color = COLOR_PAIR(WHITE);
}
attron(color);
mvaddch(r,c,ch);
attroff(color);
}
}
}
void draw_clouds(double t, double zoom)
{
int rows,cols;
getmaxyx(stdscr,rows,cols);
double rotation = get_rotation(t);
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 clouds = hillnoise(theta*12., phi*6. - 1.*rotation - 140.);
int color;
int ch;
if (clouds < .4) {
ch = (clouds < .3 ? '0' : '%') | A_BOLD;
color = COLOR_PAIR(WHITE);
attron(color);
mvaddch(r,c,ch);
attroff(color);
}
}
}
}
int latlon_to_rc(double t, double zoom, double lat, double lon, int *r, int *c)
{
int rows,cols;
getmaxyx(stdscr,rows,cols);
const double rho = rows/2.;
double theta = lat;
double phi = lon + get_rotation(t);
double x = rho*sin(theta)*cos(phi);
double y = rho*sin(theta)*sin(phi);
double z = rho*cos(theta);
*r = z / (1.0/zoom) + rows/2;
*c = y / (.5/zoom) + cols/2;
return x < 0;
}
const double target_lat = M_PI/2*1.01;
const double target_lon = M_PI + 2.6;
// return 1 if visible, else 0
int get_target_pos(double t, double zoom, int *targetr, int *targetc)
{
return latlon_to_rc(t, zoom, target_lat, target_lon, targetr, targetc);
}