From b952d03277312f1ef419c70aa591715402581a27 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Wed, 31 Mar 2021 19:39:31 +0200 Subject: [PATCH] rogueviz:: added hyperbolic-analogs --- rogueviz/hyperbolic-analogs.cpp | 424 ++++++++++++++++++++++++++++++++ rogueviz/rogueviz-all.cpp | 1 + 2 files changed, 425 insertions(+) create mode 100644 rogueviz/hyperbolic-analogs.cpp diff --git a/rogueviz/hyperbolic-analogs.cpp b/rogueviz/hyperbolic-analogs.cpp new file mode 100644 index 00000000..1a7333ae --- /dev/null +++ b/rogueviz/hyperbolic-analogs.cpp @@ -0,0 +1,424 @@ +#include "rogueviz.h" + +// used in: https://www.youtube.com/watch?v=H7NKhKTjHVE&feature=youtu.be +// run: -analogs + +namespace hr { + +namespace analogs { + +patterns::ePattern wp; +flagtype wpf; + +texture::texture_data earth; + +bool loaded; + +bool textured = true; + +basic_textureinfo tv; + +int earthpart; + +string spherename = "A"; +string hypername = "B"; + +vector models_to_use = { + [] { + if(sphere) { + pmodel = mdDisk; + pconf.alpha = 1000; + pconf.scale *= pconf.alpha; + View = cspin(1, 2, 20 * degree) * View; + } + else { + pmodel = mdHyperboloid; + pconf.top_z = 4; + pconf.ballangle = -20; + pconf.scale = .75; + } + spherename = "Euclidean sphere"; + hypername = "Minkowski hyperboloid"; + }, + [] { + pmodel = mdDisk; + pconf.alpha = 1; + pconf.scale = .9; + spherename = "stereographic projection"; + if(sphere) pconf.scale *= .75; + hypername = "Poincaré disk model"; + }, + [] { + pmodel = mdDisk; + pconf.alpha = 0; + pconf.scale = sphere ? 0.25 : .9; + spherename = "gnomonic projection"; + hypername = "Beltrami-Klein disk model"; + }, + [] { + pmodel = mdDisk; + pconf.alpha = 1000; + pconf.scale *= pconf.alpha; + if(hyperbolic) pconf.scale *= .25; + spherename = "orthographic projection"; + hypername = "Gans model"; + }, + [] { + pmodel = mdEquidistant; + spherename = "azimuthal equidistant"; + }, + [] { + pmodel = mdEquiarea; + spherename = "azimuthal equi-area"; + }, + [] { + pmodel = mdBandEquidistant; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "equirectangular projection"; + hypername = "Lobachevsky coordinates"; + }, + [] { + pmodel = mdBand; + pconf.scale = .5; + if(sphere) pconf.scale *= 0.9; + spherename = "Mercator projection"; + hypername = "band model"; + }, + [] { + pmodel = mdBandEquiarea; + pconf.scale = .5; + pconf.stretch = M_PI; + spherename = "cylindrical equal-area"; + }, + [] { + pmodel = mdCentralCyl; + pconf.scale = .5; + spherename = "central cylindrical"; + }, + [] { + pmodel = mdGallStereographic; + pconf.scale = .5; + pconf.scale *= 1.8; + spherename = "Gall stereographic"; + }, + [] { + pmodel = mdMiller; + pconf.scale = .5; + spherename = "Miller cylindrical"; + }, + [] { + pmodel = mdLoximuthal; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "loximuthal projection"; + pconf.loximuthal_parameter = 15 * degree; + }, + [] { + pmodel = mdSinusoidal; + pconf.scale = .5; + if(sphere) pconf.scale *= 1.5; + spherename = "(co)sinusoidal projection"; + }, + [] { + pmodel = mdMollweide; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "Mollweide projection"; + }, + [] { + pmodel = mdCollignon; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "Collignon projection"; + }, + [] { + pmodel = mdTwoPoint; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "two-point equidistant"; + }, + [] { + pmodel = mdSimulatedPerspective; + pconf.scale = .5; + spherename = "two-point azimuthal"; + }, + [] { + pmodel = mdAitoff; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "Aitoff projection"; + }, + [] { + pmodel = mdHammer; + pconf.scale = .5; + if(sphere) pconf.scale *= 1.3; + spherename = "Hammer projection"; + }, + [] { + pmodel = mdWinkelTripel; + pconf.scale = .5; + if(sphere) pconf.scale *= 2; + spherename = "Winkel tripel projection"; + }, + [] { + pmodel = mdWerner; + pconf.scale = .3; + spherename = "Werner projection"; + }, + }; + +ld prec = 5; + +void draw_earth() { + if(textured && !loaded) { + earth.twidth = earth.theight = 0; earth.stretched = true; + // earth.readtexture("extra/to-earth.png"); + earth.readtexture(file_exists("textures/earth.png") ? "textures/earth.png" : "textures/earth320.png"); + earth.loadTextureGL(); + loaded = true; + } + + auto tform = [] (hyperpoint euc) { + return xpush(euc[0] * degree) * ypush(euc[1] * degree) * C0; + }; + + if(sphere && textured) { + + shiftmatrix S = ggmatrix(currentmap->gamestart()); + tv.tvertices.clear(); + + if(prec < .5) prec = 1; + + for(ld x=-180; x<180; x+=prec) + for(ld y=-90; y<90; y+=prec) { + + vector 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) + }; + + bool ok = true; + + if(pmodel == mdSimulatedPerspective) for(hyperpoint base: bases) { + hyperpoint h = S.T * tform(base); + if(h[2] <= 0.1) ok = false; + } + + if(among(pmodel, mdEquidistant, mdEquiarea)) for(hyperpoint base: bases) { + hyperpoint h = S.T * tform(base); + if(h[2] <= -0.999) ok = false; + } + + if(ok) for(auto base: bases) { + hyperpoint h = base; + hyperpoint h1 = tform(h); + curvepoint(h1); + + hyperpoint vi = point31((h[0] + 180) / 360., (h[1] + 90) / 180., 0); + tv.tvertices.push_back(glhr::pointtogl(vi)); + + color_t col = earth.get_texture_pixel(vi[0]*earth.twidth, vi[1]*earth.theight); + col &= 0xFFFFFF; + addaura(S*h1, col, 0); + } + } + + if(isize(tv.tvertices)) { + color_t full = 0xFFFFFFFF; + part(full, 0) = earthpart; + + auto& poly = queuecurve(ggmatrix(currentmap->gamestart()), 0, full, PPR::LINE); + poly.flags |= POLY_TRIANGLES; + poly.tinf = &tv; + poly.offset_texture = 0; + tv.texture_id = earth.textureid; + } + } + } + +bool cycle_models = false; + +bool animate = true; + +EX void compare() { + if(!animate) return; + centerover = cwt.at; + spherename = ""; + hypername = ""; + ld t = ticks * 1. / anims::period; + t = frac(t); + + if(cycle_models) { + int mtu = isize(models_to_use); + t *= mtu; + if(anims::period < 100) { + t = ticks % mtu; + println(hlog, "t = ", t); + } + int index = t; + t = frac(t); + pconf.alpha = 1; + pconf.scale = .9; + pconf.stretch = 1; + models_to_use[index](); + } + View = Id; + ld t4 = t * 5; + int at4 = t4; + t4 -= at4; + t4 = t4 * t4 * (3 - 2 * t4); + earthpart = 192; + if(at4 == 0) + earthpart = lerp(255, earthpart, t4); + else if(at4 == 1) + View = spin(t4 * 180 * degree) * View; + else if(at4 == 2) + View = xpush(t4 * M_PI) * spin(M_PI) * View; + else if(at4 == 3) + View = ypush(t4 * M_PI) * xpush(M_PI) * spin(M_PI) * View; + else if(at4 == 4) { + View = ypush(M_PI) * xpush(M_PI) * spin(M_PI) * View; + earthpart = lerp(255, earthpart, 1-t4); + } + anims::moved(); + no_find_player = true; + vid.cells_drawn_limit = 20000; + pconf.twopoint_param = 0.5; + neon_nofill = true; + if(hyperbolic && pmodel == mdWerner) + neon_mode = eNeon::neon; + else + neon_mode = eNeon::none; + vid.smart_area_based = (pmodel != mdHyperboloid); + + if(!inHighQual) { + vid.cells_drawn_limit = 1000; + } + } + +EX bool ourStats() { + draw_centerover = false; + + displayfr(10, 10 + 2 * vid.fsize, 2, vid.fsize * 2, spherename, 0xFFFFFF, 0); + + displayfr(vid.xres - 10, vid.yres - (10 + 2 * vid.fsize), 2, vid.fsize * 2, hypername, 0xFFFFFF, 16); + + nohelp = true; + nomenukey = true; + clearMessages(); + + glflush(); + + hide_hud = false; + + return true; + } + +bool restrict_cell(cell *c, const shiftmatrix& V) { + if(hyperbolic && zebra40(c) >= 40) + c->landparam = 0x0080C0; + if(hyperbolic && pmodel == mdWerner && hdist0(V.T * C0) > 3) + return true; + return false; + } + +int current_index = -1; + +void choose_projection() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(0); + dialog::init(XLAT("choose projection"), 0xFFFFFFFF, 150, 0); + for(int i=0; i vp(pconf, pconf); + dynamicval v(View, View); + models_to_use[i](); + } + dialog::addBoolItem(hypername == "" ? spherename : spherename + " / " + hypername, i == current_index, 'a'+i); + dialog::add_action([i] { + current_index = i; + dual::switch_to(0); + models_to_use[i](); + dual::switch_to(1); + models_to_use[i](); + }); + } + dialog::addBack(); + dialog::display(); + } + +void show() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(0); + dialog::init(XLAT("hyperbolic analogs"), 0xFFFFFFFF, 150, 0); + add_edit(prec); + dialog::addItem("choose a projection", 'p'); + dialog::add_action_push(choose_projection); + add_edit(earthpart); + add_edit(textured); + add_edit(animate); + add_edit(cycle_models); + + dialog::addBack(); + dialog::display(); + } + +void enable() { + using rogueviz::rv_hook; + + vid.linequality = 4; + firstland = specialland = laCanvas; + patterns::whichCanvas = 'F'; + + colortables['F'][0] = 0x80C080; + colortables['F'][1] = 0x80A080; + pconf.scale = .3; + + vid.use_smart_range = 2; + vid.smart_range_detail = 2; + + mapeditor::drawplayer = false; + showstartmenu = false; + + dual::enable(); + + dual::switch_to(0); + set_geometry(gSphere); + set_variation(eVariation::pure); + firstland = specialland = laCanvas; + + dual::switch_to(1); + set_geometry(gNormal); + set_variation(eVariation::pure); + firstland = specialland = laCanvas; + + rv_hook(hooks_frame, 100, draw_earth); + rv_hook(hooks_drawcell, 100, restrict_cell); + rv_hook(anims::hooks_anim, 100, compare); + rv_hook(hooks_prestats, 100, ourStats); + rv_hook(hooks_o_key, 80, [] (o_funcs& v) { v.push_back(named_dialog("hyperbolic analogs", show)); }); + } + +auto msc = arg::add3("-analogs", enable) + + addHook(hooks_configfile, 100, [] { + param_f(prec, "analogs_precision") + ->editable(0, 30, .5, "precision", "larger values are less precise", 'p'); + param_b(animate, "analogs_animate") + ->editable("animate", 'a'); + param_b(cycle_models, "analogs_cycle") + ->editable("cycle models in the animation", 'm'); + param_i(earthpart, "earthpart") + ->editable(0, 255, 15, "Earth transparency", "", 't'); + param_b(textured, "analogs_texture") + ->editable("draw Earth", 'T'); + }); + +} +} + diff --git a/rogueviz/rogueviz-all.cpp b/rogueviz/rogueviz-all.cpp index 4482a57e..da7f9b01 100644 --- a/rogueviz/rogueviz-all.cpp +++ b/rogueviz/rogueviz-all.cpp @@ -40,6 +40,7 @@ #include "inner-maps.cpp" #include "planets.cpp" +#include "hyperbolic-analogs.cpp" #include "simple-impossible.cpp" #include "ascending-descending.cpp"