#include "rogueviz.h" namespace hr { #if CAP_MODELS namespace ply { using namespace rogueviz::objmodels; bool animated; hyperpoint A = point31(9.7, -38.1, 10.116); hyperpoint B = point31(17.3, -38.1, 10.95); hyperpoint C = point31(17.3, -46.1, 11.75); hyperpoint D = point31(12.95, -46.08, 12.28); hyperpoint E = point31(12.86, -44.53, 12.4); ld radius = (12.86 - 11.11) / 2; transmatrix nilform; map > cache; bool debugnil = false; bool usecache = true; pair nilize(hyperpoint h) { if(euclid) return {0, h - A + C0}; hyperpoint hc = h; for(int i=0; i<4; i++) hc[i] = floor(h[i] * 1000 + .5); if(usecache && cache.count(hc)) return cache[hc]; hyperpoint at = A; hyperpoint result = C0; auto move_coord_full = [&] (hyperpoint tgt) { for(int i=0; i<100; i++) { result = nisot::translate(result) * nilform * ((tgt - at) / 100 + C0); } at = tgt; }; auto move_coord = [&] (int c, hyperpoint upto) { ld part = (h[c] - at[c]) / (upto[c] - at[c]); if(part > 1) part = 1; move_coord_full(at + part * (upto-at)); }; int cat = 0; if(h[1] > -39) { move_coord(0, B); cat = 1; goto finish; } move_coord_full(B); if(h[0] > 17.2) { move_coord(1, C); cat = 2; goto finish; } move_coord_full(C); if(h[1] < -45.5) { move_coord(0, D); cat = 3; goto finish; } cat = 4; move_coord_full(D); move_coord(1, E); finish: move_coord_full(h); return cache[hc] = {cat, result}; } ld vperiod; pair trace_path(ld v) { ld vorig = v; hyperpoint lctr = A; ld angle = 0; ld arclen = radius * 90._deg; auto change_angle = [&] (ld x) { if(v == 0) return; else if(v >= arclen) v -= arclen, angle += 1; else angle += v / arclen, v = 0; }; auto shift_to = [&] (hyperpoint h, bool last = false) { ld seglen = hypot_d(3, h - lctr); if(v == 0) return; else if(v >= seglen && !last) v -= seglen, lctr = h; else lctr = lerp(lctr, h, v / seglen), v = 0; }; change_angle(1); shift_to(B); change_angle(2); shift_to(C); change_angle(3); shift_to(D); change_angle(4); shift_to(E, true); angle *= 90._deg; if(v > 0) vperiod = vorig - v; return { lctr + point3(radius * -cos(angle), radius * sin(angle), 0), point3(-sin(angle), -cos(angle), 0) }; } transmatrix scaleby(ld x) { transmatrix res = Id; for(int i=0; i route(1000), forwards(1000), overroute(1000); hyperpoint interpolate_at(vector& v, ld x) { while(x > 1000) x -= 1000; return lerp(v[x], v[(int(x)+1) % 1000], frac(x)); } ld advance = 2; ld over = 1.5; ld over2 = 1; void make_routes() { for(int i=0; i<1000; i++) { ld v = vperiod * i / 1000; route[i] = nilize(trace_path(v).first + point3(0,0,over)).second; ld vb = v + advance; if(vb > vperiod) vb -= vperiod; forwards[i] = nilize(trace_path(vb).first).second; overroute[i] = nilize(trace_path(v).first + point3(0,0,over+over2)).second; } auto smoothen = [&] (vector& v) { for(int it=0; it<100; it++) { vector smoother; for(int i=0; i<1000; i++) { hyperpoint& a = v[i]; hyperpoint& b = v[(i+1) % 1000]; hyperpoint& c = v[(i+2) % 1000]; smoother.push_back((a + b + c) / 3); } v = smoother; } }; smoothen(route); smoothen(forwards); smoothen(overroute); } bool prepared; void prepare_nilform() { if(prepared) return; prepared = true; hyperpoint axis = (E - A); transmatrix rotator = Id; rotator[2] = axis; println(hlog, " = ", (rotator[2] | rotator[2])); rotator[2] /= sqrt(rotator[2] | rotator[2]); rotator[1] -= (rotator[1] | rotator[2]) * rotator[2]; rotator[1] /= sqrt(rotator[1] | rotator[1]); rotator[0] -= (rotator[0] | rotator[2]) * rotator[2]; rotator[0] -= (rotator[0] | rotator[1]) * rotator[1]; rotator[0] /= sqrt(rotator[0] | rotator[0]); println(hlog, "rotator = ", kz(rotator)); rotator = inverse(transpose(rotator)); println(hlog, "rotator = ", kz(rotator)); ld minscale = 0.5; // positive ld maxscale = 1.5; // negative ld scale; for(int it=0; it<100; it++) { scale = (minscale + maxscale) / 2; nilform = rotator * scaleby(scale); cache.clear(); hyperpoint nE = nilize(E).second; if(nE[2] < 0) maxscale = scale; else minscale = scale; } println(hlog, "scale = ", scale); println(hlog, nilize(E).second); vperiod = radius * TAU + hypot_d(3, B-A) + hypot_d(3, C-B) + hypot_d(3, D-C) + hypot_d(3, E-D); println(hlog, "vperiod = ", vperiod); make_routes(); } struct nilmodel : model { hyperpoint transform(hyperpoint h) override { return nilize(h).second; } void process_triangle(vector& hys, vector& tot, bool textured, object *co) override { auto n0 = nilize(hys[0]).first; auto n1 = nilize(hys[1]).first; auto n2 = nilize(hys[2]).first; auto mi = min(n0, min(n1, n2)); auto ma = max(n0, max(n1, n2)); if(ma - mi > 1) return; model::process_triangle(hys, tot, textured, co); } }; nilmodel staircase; bool draw_ply() { if(nil) prepare_nilform(); staircase.render(ggmatrix(currentmap->gamestart())); return false; } void show() { cmode = sm::SIDE | sm::MAYDARK; gamescreen(); dialog::init(XLAT("Ascending & Descending"), 0xFFFFFFFF, 150, 0); dialog::addSelItem("advance", fts(advance), 'a'); dialog::add_action([]() { dialog::editNumber(advance, 0, 100, 1, 1, "advance", "").reaction = make_routes; }); dialog::addSelItem("over", fts(over), 'o'); dialog::add_action([]() { dialog::editNumber(over, 0, 100, 1, 1, "over", "").reaction = make_routes; }); dialog::addSelItem("over2", fts(over2), 'p'); dialog::add_action([]() { dialog::editNumber(over2, 0, 100, 1, 1, "over2", "").reaction = make_routes; }); dialog::addBoolItem_action("animated", animated, 'a'); add_edit(prec); dialog::addBack(); dialog::display(); } void o_key(o_funcs& v) { v.push_back(named_dialog("Ascending & Descending", show)); } void enable() { rogueviz::rv_hook(hooks_frame, 100, draw_ply); rogueviz::rv_hook(hooks_o_key, 80, o_key); rogueviz::rv_hook(anims::hooks_anim, 100, [] { if(!animated) return; usecache = false; ld t = ticks * 1. / anims::period; t = t - floor(t); t *= 1000; centerover = currentmap->gamestart(); #if CAP_VR if(vrhr::active()) View = gpushxto0(interpolate_at(route, t)); else #endif set_view( interpolate_at(route, t), interpolate_at(forwards, t), interpolate_at(overroute, t) ); anims::moved(); }); } auto plyhook = arg::add3("-asd", enable) + addHook(hooks_configfile, 100, [] { param_b(animated, "ad_animated"); }); } #endif }