Download
186 lines 5.4 KiBCModified 2019-02-05 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
/**
  * 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);
}