mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-11-03 23:33:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			552 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			552 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "rogueviz.h"
 | 
						|
 | 
						|
namespace hr {
 | 
						|
 | 
						|
#if CAP_TEXTURE
 | 
						|
namespace planets {
 | 
						|
 | 
						|
texture::texture_data earth, neptune, mars, moon, sun;
 | 
						|
 | 
						|
map<void*, ld> radius;
 | 
						|
map<void*, string> pname;
 | 
						|
map<void*, char> key;
 | 
						|
 | 
						|
ld longit = 0, latit = 0;
 | 
						|
 | 
						|
bool loaded;
 | 
						|
 | 
						|
void load_planets() {
 | 
						|
  if(!loaded) {
 | 
						|
    earth.twidth = earth.theight = 0; earth.stretched = true;
 | 
						|
    if(file_exists("textures/earth.png"))
 | 
						|
      earth.readtexture("textures/earth.png");
 | 
						|
    else
 | 
						|
      earth.readtexture("textures/earth320.png");
 | 
						|
    earth.loadTextureGL();
 | 
						|
    radius[&earth] = 6371;
 | 
						|
    pname[&earth] = "Earth";
 | 
						|
    key[&earth] = 'e';
 | 
						|
 | 
						|
    neptune.twidth = neptune.theight = 0; neptune.stretched = true;
 | 
						|
    neptune.readtexture("textures/neptune.png");
 | 
						|
    neptune.loadTextureGL();
 | 
						|
    radius[&neptune] = 24622;
 | 
						|
    pname[&neptune] = "Neptune";
 | 
						|
    key[&neptune] = 'n';
 | 
						|
 | 
						|
    sun.twidth = sun.theight = 0; sun.stretched = true;
 | 
						|
    sun.readtexture("textures/sun.png");
 | 
						|
    sun.loadTextureGL();
 | 
						|
    radius[&sun] = 696340;
 | 
						|
    pname[&sun] = "Sun";
 | 
						|
    key[&sun] = 's';
 | 
						|
 | 
						|
    moon.twidth = moon.theight = 0; moon.stretched = true;
 | 
						|
    moon.readtexture("textures/moon.png");
 | 
						|
    moon.loadTextureGL();
 | 
						|
    radius[&moon] = 1371.1;
 | 
						|
    pname[&moon] = "Moon";
 | 
						|
    key[&moon] = 'm';
 | 
						|
 | 
						|
    mars.twidth = mars.theight = 0; mars.stretched = true;
 | 
						|
    mars.readtexture("textures/mars.png");
 | 
						|
    mars.loadTextureGL();
 | 
						|
    radius[&mars] = 3389.5;
 | 
						|
    pname[&mars] = "Mars";
 | 
						|
    key[&mars] = 'x';
 | 
						|
 | 
						|
    loaded = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
basic_textureinfo tv, tv1;
 | 
						|
 | 
						|
bool anim = false;
 | 
						|
 | 
						|
int index = 0;
 | 
						|
 | 
						|
string mname;
 | 
						|
 | 
						|
void rev_band_conformal(ld& x, ld& y) {
 | 
						|
  x /= 2; y /= 2;
 | 
						|
  y = asin(tanh(y));
 | 
						|
  }
 | 
						|
 | 
						|
template<class T> void reverse_band(hyperpoint h, hyperpoint& h1, const T& f) {
 | 
						|
  ld x = h[0] * M_PI;
 | 
						|
  ld y = h[1] * M_PI;
 | 
						|
  f(x, y);
 | 
						|
  h1 = xpush(x) * ypush(y) * C0;
 | 
						|
  }
 | 
						|
 | 
						|
void reverse_model(hyperpoint h, hyperpoint& h1, eModel md) {
 | 
						|
  switch(pmodel) {
 | 
						|
    case mdDisk:
 | 
						|
      h1 = perspective_to_space(h);
 | 
						|
      return;
 | 
						|
 | 
						|
    case mdEquidistant:
 | 
						|
      h[0] *= M_PI; h[1] *= M_PI;
 | 
						|
      h1 = direct_exp(h);
 | 
						|
      break;
 | 
						|
 | 
						|
    case mdEquiarea: {
 | 
						|
 | 
						|
      ld d = hypot_d(2, h);
 | 
						|
      // d = sqrt(2*(1 - cos(d0))) / 2;
 | 
						|
      ld d0 = acos(1 - pow(2 * d, 2) / 2);
 | 
						|
 | 
						|
      h[0] *= d0 / d; h[1] *= d0 / d;
 | 
						|
      h1 = direct_exp(h);
 | 
						|
      break;
 | 
						|
      }
 | 
						|
    
 | 
						|
    case mdBand: {
 | 
						|
      reverse_band(h, h1, rev_band_conformal);
 | 
						|
      break;
 | 
						|
      }
 | 
						|
 | 
						|
    case mdBandEquiarea: {
 | 
						|
      reverse_band(h, h1, [] (ld& x, ld& y) { y = asin_auto(y); });
 | 
						|
      break;
 | 
						|
      }
 | 
						|
 | 
						|
    case mdBandEquidistant: {
 | 
						|
      reverse_band(h, h1, [] (ld& x, ld& y) {});
 | 
						|
      break;
 | 
						|
      }
 | 
						|
    
 | 
						|
    case mdSinusoidal: {
 | 
						|
      reverse_band(h, h1, [] (ld& x, ld& y) { x /= cos_auto(y); });
 | 
						|
      break;
 | 
						|
      }
 | 
						|
 | 
						|
    default:
 | 
						|
      throw "unknown";
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
bool kill_off(hyperpoint h1, hyperpoint h2, hyperpoint& h3) {
 | 
						|
  if(pmodel == mdEquidistant) {
 | 
						|
    if(hypot_d(2, h2) > 1) {
 | 
						|
      h3 = point3(0, 0, -1); 
 | 
						|
      return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  if(pmodel == mdEquiarea) {
 | 
						|
    if(hypot_d(2, h2) > 1) {
 | 
						|
      h3 = point3(0, 0, -1); 
 | 
						|
      return true;
 | 
						|
      }     
 | 
						|
    }
 | 
						|
  if(pmodel == mdDisk && pconf.alpha == 0) {
 | 
						|
    if(abs(h1[2]) < 1e-6) return true;
 | 
						|
    if(h1[2] < 0) h3 = -h3;
 | 
						|
    }
 | 
						|
  if(pmodel == mdDisk && pconf.alpha > 1) {
 | 
						|
    if(hypot_d(2, h2) > 1 / pconf.alpha) {
 | 
						|
      h2 = h2 * .999 / pconf.alpha / hypot_d(2, h2);
 | 
						|
      return true;
 | 
						|
      }
 | 
						|
    if(h1[2] < 0) h3[2] = -h3[2];
 | 
						|
    }
 | 
						|
  if(among(pmodel, mdBand, mdBandEquidistant, mdBandEquiarea)) {
 | 
						|
    if(abs(h1[0]) < 1e-4 && h1[2] < 0) return true;
 | 
						|
    if(pmodel == mdBand && (h2[0] < -2 || h2[0] > 2)) return true;
 | 
						|
    if(pmodel != mdBand && (h2[0] < -1 || h2[0] > 1)) return true;
 | 
						|
    if(pmodel == mdBandEquidistant && (h2[1] < -.5 || h2[1] > .5)) return true;
 | 
						|
    }
 | 
						|
  return false;
 | 
						|
  }
 | 
						|
 | 
						|
int bad;
 | 
						|
 | 
						|
void test_reverse() {
 | 
						|
  int ok = 0, killed = 0;
 | 
						|
  bad = 0;
 | 
						|
  for(int a=0; a<100; a++) {
 | 
						|
    hyperpoint h = random_spin3() * C0;
 | 
						|
    hyperpoint h1;
 | 
						|
    hyperpoint h2;
 | 
						|
    applymodel(shiftless(h), h1);
 | 
						|
    reverse_model(h1, h2, pmodel);
 | 
						|
    bool ko = kill_off(h, h1, h2);
 | 
						|
    if(hdist(h, h2) < 1e-5)
 | 
						|
      ok++;
 | 
						|
    else if(ko)
 | 
						|
      killed++;
 | 
						|
    else {
 | 
						|
      bad++;
 | 
						|
      println(hlog, h, " -> ", h1, " -> ", h2);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  // println(hlog, "ok ", ok, " bad ", bad, " killed ", killed);
 | 
						|
  }
 | 
						|
 | 
						|
texture::texture_data *tgt_planet = &moon;
 | 
						|
texture::texture_data *src_planet = &earth;
 | 
						|
 | 
						|
int alpha = 255;
 | 
						|
 | 
						|
void pick_model() {
 | 
						|
  switch(index) {
 | 
						|
    case 0: 
 | 
						|
      mname = "azimuthal equidistant";
 | 
						|
      pmodel = mdEquidistant;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 1: 
 | 
						|
      mname = "azimuthal equal area";
 | 
						|
      pmodel = mdEquiarea;
 | 
						|
      break;
 | 
						|
    
 | 
						|
    case 2:
 | 
						|
      mname = "gnomonic";
 | 
						|
      pmodel = mdDisk;
 | 
						|
      pconf.alpha = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 3:
 | 
						|
      mname = "stereographic";
 | 
						|
      pmodel = mdDisk;
 | 
						|
      pconf.alpha = 1;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 4:
 | 
						|
      mname = "orthographic";
 | 
						|
      pmodel = mdDisk;
 | 
						|
      pconf.alpha = 999999;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 5:
 | 
						|
      mname = "Mercator projection";
 | 
						|
      pmodel = mdBand;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 6:
 | 
						|
      mname = "equirectangular projection";
 | 
						|
      pmodel = mdBandEquidistant;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 7:
 | 
						|
      mname = "cylindrical equal area";
 | 
						|
      pmodel = mdBandEquiarea;
 | 
						|
      break;
 | 
						|
    }    
 | 
						|
  }
 | 
						|
 | 
						|
ld prec = 5;
 | 
						|
 | 
						|
void draw_earth() {
 | 
						|
  load_planets();
 | 
						|
 | 
						|
  shiftmatrix S = ggmatrix(currentmap->gamestart()) * spin90();
 | 
						|
 | 
						|
  ld mte = radius[src_planet] / radius[tgt_planet];
 | 
						|
  
 | 
						|
  if(true) {
 | 
						|
    tv1.tvertices.clear();
 | 
						|
    if(prec<0.5) prec=1;
 | 
						|
    for(int x=-180; x<180; x+=prec)
 | 
						|
    for(int y=-90; y<90; y+=prec) {
 | 
						|
    
 | 
						|
      vector<hyperpoint> bases = {
 | 
						|
        point31(x, y, 0), 
 | 
						|
        point31(x+prec, y, 0), 
 | 
						|
        point31(x, y+prec, 0), 
 | 
						|
        point31(x+prec, y, 0), 
 | 
						|
        point31(x, y+prec, 0), 
 | 
						|
        point31(x+prec, y+prec, 0)
 | 
						|
        };
 | 
						|
      
 | 
						|
      auto tform = [&] (hyperpoint euc) {
 | 
						|
        return xpush(euc[0] * degree) * ypush(euc[1] * degree) * C0;
 | 
						|
        };  
 | 
						|
      
 | 
						|
      for(int i=0; i<6; i++) {
 | 
						|
  
 | 
						|
        hyperpoint h = bases[i];
 | 
						|
        hyperpoint h1 = tform(h);
 | 
						|
        curvepoint(h1);
 | 
						|
        hyperpoint vi = point31((h[0] + 180) / 360., (h[1] + 90) / 180., 0);
 | 
						|
        tv1.tvertices.push_back(glhr::pointtogl(vi));
 | 
						|
  
 | 
						|
        color_t col = tgt_planet->get_texture_pixel(vi[0]*tgt_planet->twidth, vi[1]*tgt_planet->theight);
 | 
						|
        col &= 0xFFFFFF;
 | 
						|
        addaura(S*h1, col, 0);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    
 | 
						|
    if(isize(tv1.tvertices)) {
 | 
						|
      color_t full = 0xFFFFFFFF;
 | 
						|
      part(full, 0) = 255;
 | 
						|
        
 | 
						|
      auto& poly = queuecurve(S, 0, full, PPR::LINE);
 | 
						|
      poly.flags |= POLY_TRIANGLES;
 | 
						|
      poly.tinf = &tv1;
 | 
						|
      poly.offset_texture = 0;
 | 
						|
      tv1.texture_id = tgt_planet->textureid;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  
 | 
						|
  if(true) {
 | 
						|
 | 
						|
    tv.tvertices.clear();
 | 
						|
    
 | 
						|
    dynamicval<eModel> md(pmodel, pmodel);
 | 
						|
    dynamicval<ld> ma(pconf.alpha, pconf.alpha);
 | 
						|
    
 | 
						|
    pick_model();
 | 
						|
    
 | 
						|
    test_reverse(); 
 | 
						|
    
 | 
						|
    bool twoside = (pmodel == mdDisk && pconf.alpha > 100);
 | 
						|
    
 | 
						|
    for(int x=-180; x<180; x+=prec)
 | 
						|
    for(int y=-90; y<90; y+=prec) 
 | 
						|
    for(int si: {-1, 1}) {
 | 
						|
    
 | 
						|
      if(si == 1 && !twoside) continue;
 | 
						|
    
 | 
						|
      vector<hyperpoint> bases = {
 | 
						|
        point31(x, y, 0), 
 | 
						|
        point31(x+prec, y, 0), 
 | 
						|
        point31(x, y+prec, 0), 
 | 
						|
        point31(x+prec, y, 0), 
 | 
						|
        point31(x, y+prec, 0), 
 | 
						|
        point31(x+prec, y+prec, 0)
 | 
						|
        };
 | 
						|
      
 | 
						|
      int errs = 0;
 | 
						|
    
 | 
						|
      for(int i=0; i<6; i+=3) {
 | 
						|
  
 | 
						|
        array<hyperpoint, 3> tforms;
 | 
						|
        
 | 
						|
        errs = 0;
 | 
						|
  
 | 
						|
        for(int j=0; j<3; j++) {
 | 
						|
          hyperpoint euc = bases[i+j];
 | 
						|
          hyperpoint h = xpush(euc[0] * degree) * ypush(euc[1] * degree) * C0;
 | 
						|
 | 
						|
          h = cspin(1, 2, latit*degree) * cspin(0, 2, -longit*degree) * h;
 | 
						|
          
 | 
						|
          hyperpoint h1, h2;
 | 
						|
          applymodel(shiftless(h), h1);
 | 
						|
          h1[0] *= mte;
 | 
						|
          h1[1] *= mte;
 | 
						|
          
 | 
						|
          if(twoside) {
 | 
						|
            if(h[2] * si < 0) errs++;
 | 
						|
            else if(hypot_d(2, h1) > 1 / pconf.alpha) {
 | 
						|
              h1 = h1 * .999999 / hypot_d(2, h1) / pconf.alpha;
 | 
						|
              errs++;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          
 | 
						|
          reverse_model(h1, h2, pmodel);
 | 
						|
          
 | 
						|
          if(kill_off(h, h1, h2)) errs++;
 | 
						|
          
 | 
						|
          tforms[j] = h2;
 | 
						|
          }
 | 
						|
        
 | 
						|
        if(twoside && errs && mte < 1) continue;
 | 
						|
        
 | 
						|
        if(errs < 3) {
 | 
						|
          for(int j=0; j<3; j++) {
 | 
						|
            hyperpoint h = bases[i+j];
 | 
						|
            hyperpoint h1 = tforms[j];
 | 
						|
            curvepoint(h1);
 | 
						|
            hyperpoint vi = point31((h[0] + 180) / 360., (h[1] + 90) / 180., 0);
 | 
						|
            tv.tvertices.push_back(glhr::pointtogl(vi));
 | 
						|
  
 | 
						|
            color_t col = src_planet->get_texture_pixel(vi[0]*src_planet->twidth, vi[1]*src_planet->theight);
 | 
						|
            col &= 0xFFFFFF;
 | 
						|
            addaura(S*h1, col, 0);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      
 | 
						|
    if(isize(tv.tvertices)) {
 | 
						|
      color_t full = 0xFFFFFFFF;
 | 
						|
      part(full, 0) = alpha;
 | 
						|
        
 | 
						|
      auto& poly = queuecurve(S, 0, full, PPR::LINE);
 | 
						|
      poly.flags |= POLY_TRIANGLES;
 | 
						|
      poly.tinf = &tv;
 | 
						|
      poly.offset_texture = 0;
 | 
						|
      tv.texture_id = src_planet->textureid;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
EX bool ourStats() {
 | 
						|
  draw_centerover = false;
 | 
						|
 | 
						|
  displayfr(10, 10 + 2 * vid.fsize, 2, vid.fsize * 2, mname, 0xFFFFFF, 0);
 | 
						|
  displayfr(vid.xres - 10, vid.yres - 10 - 2 * vid.fsize, 2, vid.fsize * 2, pname[src_planet] + " to " + pname[tgt_planet], 0xFFFFFF, 16);
 | 
						|
 | 
						|
  // nohelp = true;
 | 
						|
  // nomenukey = true;
 | 
						|
  clearMessages();
 | 
						|
 | 
						|
  glflush();
 | 
						|
  
 | 
						|
  // hide_hud = false;
 | 
						|
 | 
						|
  return true;
 | 
						|
  }
 | 
						|
 | 
						|
texture::texture_data* get_planet(char ch) {
 | 
						|
  if(ch == 'a') return &mars;
 | 
						|
  if(ch == 'm') return &moon;
 | 
						|
  if(ch == 's') return &sun;
 | 
						|
  if(ch == 'n') return &neptune;
 | 
						|
  if(ch == 'e') return &earth;
 | 
						|
  return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
ld max_alpha = 0;
 | 
						|
 | 
						|
EX void compare() {
 | 
						|
  if(max_alpha) {
 | 
						|
    ld t = ticks * 1. / anims::period;
 | 
						|
    t = t - floor(t);
 | 
						|
    static ld lrot = 0;
 | 
						|
    ld rot = 0;
 | 
						|
    ld dark = 1;
 | 
						|
    if(t > .1 && t < .9) {
 | 
						|
      rot = (t - .1) / .8;
 | 
						|
      rot = rot * rot * (3-2*rot);
 | 
						|
      }
 | 
						|
    else {
 | 
						|
      dark = t < .5 ? 10 * t : 10 * (1 - t);
 | 
						|
      dark = dark * dark * (3-2*dark);
 | 
						|
      }
 | 
						|
    alpha = dark * max_alpha;
 | 
						|
    View = cspin(0, 2, (rot - lrot) * TAU) * View;
 | 
						|
    lrot = rot;
 | 
						|
    anims::moved();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
void choose_projection() {
 | 
						|
  cmode = sm::SIDE | sm::MAYDARK;
 | 
						|
  gamescreen();
 | 
						|
  dialog::init(XLAT("choose projection"), 0xFFFFFFFF, 150, 0);
 | 
						|
  dynamicval<int> di(index);
 | 
						|
  for(int i=0; i<8; i++) {
 | 
						|
    index = i;
 | 
						|
    dynamicval<eModel> md(pmodel, pmodel);
 | 
						|
    dynamicval<ld> ma(pconf.alpha, pconf.alpha);
 | 
						|
    pick_model();
 | 
						|
    dialog::addItem(mname, 'a'+i);
 | 
						|
    dialog::add_action([i] { index = i; popScreen(); });
 | 
						|
    }
 | 
						|
  dialog::addBack();
 | 
						|
  dialog::display();
 | 
						|
  }
 | 
						|
 | 
						|
void choose_planet(texture::texture_data *& t) {
 | 
						|
  cmode = sm::SIDE | sm::MAYDARK;
 | 
						|
  gamescreen();
 | 
						|
  dialog::init(XLAT("choose the planet"), 0xFFFFFFFF, 150, 0);
 | 
						|
  for(auto opt: {&earth, &moon, &sun, &mars, &neptune}) {
 | 
						|
    dialog::addSelItem(pname[opt], its(radius[opt]) + " km", key[opt]);
 | 
						|
    dialog::add_action([&t, opt] { t = opt; });
 | 
						|
    }
 | 
						|
  dialog::addBack();
 | 
						|
  dialog::display();  
 | 
						|
  }
 | 
						|
 | 
						|
void show() {
 | 
						|
  cmode = sm::SIDE | sm::MAYDARK;
 | 
						|
  gamescreen();
 | 
						|
  dialog::init(XLAT("projections between planets"), 0xFFFFFFFF, 150, 0);
 | 
						|
  add_edit(alpha);
 | 
						|
  add_edit(latit);
 | 
						|
  add_edit(longit);
 | 
						|
  add_edit(max_alpha);
 | 
						|
  add_edit(prec);
 | 
						|
  dialog::addSelItem("projection used", mname, 'p');
 | 
						|
  dialog::add_action_push(choose_projection);
 | 
						|
  dialog::addSelItem("source planet", pname[src_planet], 's');
 | 
						|
  dialog::add_action_push([] { choose_planet(src_planet); });
 | 
						|
  dialog::addSelItem("target planet", pname[tgt_planet], 't');
 | 
						|
  dialog::add_action_push([] { choose_planet(tgt_planet); });
 | 
						|
  
 | 
						|
  if(bad) dialog::addInfo("BAD", 0xFF0000);
 | 
						|
  dialog::addBack();
 | 
						|
  dialog::display();
 | 
						|
  }
 | 
						|
 | 
						|
void enable() {
 | 
						|
  using rogueviz::rv_hook;
 | 
						|
  rv_hook(hooks_frame, 100, draw_earth);
 | 
						|
  rv_hook(hooks_prestats, 100, ourStats);
 | 
						|
  rv_hook(hooks_o_key, 80, [] (o_funcs& v) { v.push_back(named_dialog("planet projections", show)); });
 | 
						|
  }
 | 
						|
 | 
						|
auto msc =   
 | 
						|
  arg::add3("-moon", enable)
 | 
						|
  + arg::add3("-ebody", [] {
 | 
						|
    string s = arg::shift_args();
 | 
						|
    src_planet = get_planet(s[0]);
 | 
						|
    tgt_planet = get_planet(s[1]);
 | 
						|
    if(!src_planet) src_planet = &earth;
 | 
						|
    if(!tgt_planet) tgt_planet = &moon;
 | 
						|
    })
 | 
						|
  + addHook(anims::hooks_anim, 100, compare)
 | 
						|
  + addHook(hooks_configfile, 100, [] {
 | 
						|
    param_i(alpha, "moon_alpha")
 | 
						|
    ->editable(0, 255, 15, "alpha (transparency)", "", 'a');
 | 
						|
    param_i(index, "moon_index")
 | 
						|
    ->editable(0, 7, 1, "projection index", "", 'i');
 | 
						|
    param_f(latit, "moon_lat")
 | 
						|
    ->editable(0, 7, 1, "latitude of the reference point", "", 'l');
 | 
						|
    param_f(longit, "moon_long")
 | 
						|
    ->editable(0, 7, 1, "longitude of the reference point", "", 'L');
 | 
						|
    param_f(max_alpha, "moon_max_alpha")
 | 
						|
    ->editable(0, 255, 15, "animation alpha max", "", 'm');
 | 
						|
    param_f(prec, "moon_precision")
 | 
						|
    ->editable(0, 30, .5, "precision", "larger values are less precise", 'p');
 | 
						|
    })
 | 
						|
  + addHook_rvslides(51, [] (string s, vector<tour::slide>& v) {
 | 
						|
      if(s != "projections") return;
 | 
						|
      using namespace tour;
 | 
						|
 | 
						|
      v.push_back(slide{
 | 
						|
        "projections/sphere to sphere", 10, LEGAL::NONE | QUICKGEO,
 | 
						|
 | 
						|
        "We can also project a sphere to a sphere of different curvature. For example, what about the azimuthal equidistant projection from Earth to Moon? "
 | 
						|
        "This projection correctly maps the angles and distances from a chosen point at Earth. "
 | 
						|
        "Press '5' to use the place on Earth you are in as the chosen point, try other projections, or change the other settings!"
 | 
						|
        ,
 | 
						|
        [] (presmode mode) {
 | 
						|
          slide_url(mode, 't', "Twitter link (with description)", "https://twitter.com/ZenoRogue/status/1339946298460483589");
 | 
						|
          setPlainCanvas(mode, [] {
 | 
						|
            set_geometry(gSphere);
 | 
						|
            });
 | 
						|
          
 | 
						|
          if(mode == pmStart) {
 | 
						|
            enable();
 | 
						|
            slide_backup(canvas_default_wall, waInvisibleFloor);
 | 
						|
            slide_backup(pmodel, mdDisk);
 | 
						|
            slide_backup(pconf.scale, 1000);
 | 
						|
            slide_backup(pconf.alpha, 1000);
 | 
						|
            slide_backup(mapeditor::drawplayer, false);
 | 
						|
            start_game();
 | 
						|
            slide_backup(max_alpha, 192);
 | 
						|
            }
 | 
						|
          slidecommand = "options";
 | 
						|
          if(mode == tour::pmKey) pushScreen(show);
 | 
						|
          }});
 | 
						|
      });
 | 
						|
 | 
						|
}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 |