diff --git a/3d-models.cpp b/3d-models.cpp index 4f911df5..87a74758 100644 --- a/3d-models.cpp +++ b/3d-models.cpp @@ -623,16 +623,16 @@ void geometry_information::slimetriangle(hyperpoint a, hyperpoint b, hyperpoint texture_order([&] (ld x, ld y) { ld z = 1-x-y; ld r = scalefactor * hcrossf7 * (0 + pow(max(x,max(y,z)), .3) * 0.8) * (hybri ? .5 : 1); - hyperpoint h = direct_exp(tangent_length(a*x+b*y+c*z, r), 10); + hyperpoint h = direct_exp(tangent_length(a*x+b*y+c*z, r)); hpcpush(h); }); } void geometry_information::balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) { if(lev == 0) { - hpcpush(direct_exp(a, 10)); - hpcpush(direct_exp(b, 10)); - hpcpush(direct_exp(c, 10)); + hpcpush(direct_exp(a)); + hpcpush(direct_exp(b)); + hpcpush(direct_exp(c)); } else { auto midpoint = [&] (hyperpoint h1, hyperpoint h2) { @@ -1254,7 +1254,16 @@ hpcshape& geometry_information::generate_pipe(ld length, ld width) { } } - last->flags |= POLY_TRIANGLES; + for(int a=0; aflags |= POLY_TRIANGLES | POLY_PRINTABLE; add_texture(*last); finishshape(); extra_vertices(); diff --git a/arbitrile.cpp b/arbitrile.cpp index 3c4b0b03..ab7af344 100644 --- a/arbitrile.cpp +++ b/arbitrile.cpp @@ -78,7 +78,7 @@ void shape::build_from_angles_edges() { bool correct_index(int index, int size) { return index >= 0 && index < size; } template bool correct_index(int index, const T& v) { return correct_index(index, isize(v)); } -template void verify_index(int index, const T& v) { if(!correct_index(index, v)) throw hr_parse_exception("bad index"); } +template void verify_index(int index, const T& v, exp_parser& ep) { if(!correct_index(index, v)) throw hr_parse_exception("bad index: " + its(index) + " at " + ep.where()); } string unnamed = "unnamed"; @@ -102,7 +102,7 @@ EX void load(const string& fname) { int ai; if(ep.next() == ')') ai = isize(c.shapes)-1; else ai = ep.iparse(); - verify_index(ai, c.shapes); + verify_index(ai, c.shapes, ep); c.shapes[ai].flags |= f; ep.force_eat(")"); }; @@ -171,7 +171,12 @@ EX void load(const string& fname) { else if(ep.eat(")")) break; else throw hr_parse_exception("expecting , or )"); } - cc.build_from_angles_edges(); + try { + cc.build_from_angles_edges(); + } + catch(hr_parse_exception& ex) { + throw hr_parse_exception(ex.s + ep.where()); + } cc.connections.resize(cc.size()); for(int i=0; i tver; - glhr::textured_vertex charvertex(int x1, int y1, ld tx, ld ty) { glhr::textured_vertex res; res.coords[0] = x1; @@ -842,12 +840,12 @@ EX ld realradius() { ld vradius = current_display->radius; if(sphere) { if(sphereflipped()) - vradius /= sqrt(vid.alpha*vid.alpha - 1); + vradius /= sqrt(pconf.alpha*pconf.alpha - 1); else vradius = 1e12; // use the following } if(euclid) - vradius = current_display->radius * get_sightrange() / (1 + vid.alpha) / 2.5; + vradius = current_display->radius * get_sightrange() / (1 + pconf.alpha) / 2.5; vradius = min(vradius, min(vid.xres, vid.yres) / 2); return vradius; } @@ -857,14 +855,14 @@ EX void drawmessage(const string& s, int& y, color_t col) { int space; if(dual::state) space = vid.xres; - else if(y > current_display->ycenter + rrad * vid.stretch) + else if(y > current_display->ycenter + rrad * pconf.stretch) space = vid.xres; else if(y > current_display->ycenter) - space = current_display->xcenter - rhypot(rrad, (y-current_display->ycenter) / vid.stretch); + space = current_display->xcenter - rhypot(rrad, (y-current_display->ycenter) / pconf.stretch); else if(y > current_display->ycenter - vid.fsize) space = current_display->xcenter - rrad; - else if(y > current_display->ycenter - vid.fsize - rrad * vid.stretch) - space = current_display->xcenter - rhypot(rrad, (current_display->ycenter-vid.fsize-y) / vid.stretch); + else if(y > current_display->ycenter - vid.fsize - rrad * pconf.stretch) + space = current_display->xcenter - rhypot(rrad, (current_display->ycenter-vid.fsize-y) / pconf.stretch); else space = vid.xres; @@ -949,7 +947,7 @@ EX void drawCircle(int x, int y, int size, color_t color, color_t fillcolor IS(0 if(ISMOBILE && pts > 72) pts = 72; for(int r=0; rset_all(0); glhr::vertices(glcoords); @@ -969,13 +967,13 @@ EX void drawCircle(int x, int y, int size, color_t color, color_t fillcolor IS(0 #if CAP_XGD gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size); #elif CAP_SDLGFX - if(vid.stretch == 1) { + if(pconf.stretch == 1) { if(fillcolor) filledCircleColor(s, x, y, size, fillcolor); if(color) ((vid.antialias && AA_NOGL)?aacircleColor:circleColor) (s, x, y, size, color); } else { - if(fillcolor) filledEllipseColor(s, x, y, size, size * vid.stretch, fillcolor); - if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * vid.stretch, color); + if(fillcolor) filledEllipseColor(s, x, y, size, size * pconf.stretch, fillcolor); + if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * pconf.stretch, color); } #elif CAP_SDL int pts = size * 4; @@ -1022,7 +1020,7 @@ EX void displayColorButton(int x, int y, const string& name, int key, int align, } ld textscale() { - return vid.fsize / (current_display->radius * cgi.crossf) * (1+vid.alpha) * 2; + return vid.fsize / (current_display->radius * cgi.crossf) * (1+pconf.alpha) * 2; } EX bool setfsize = true; diff --git a/celldrawer.cpp b/celldrawer.cpp index 64509065..3fd2aeeb 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -143,12 +143,22 @@ void celldrawer::setcolors() { case laCrossroads2: case laCrossroads3: case laCrossroads4: case laCrossroads5: case laRose: case laPower: case laWildWest: case laHalloween: case laRedRock: case laDragon: case laStorms: case laTerracotta: case laMercuryRiver: - case laDesert: case laKraken: case laDocks: case laCA: + case laDesert: case laKraken: case laDocks: case laMotion: case laGraveyard: case laWineyard: case laLivefjord: case laRlyeh: case laHell: case laCrossroads: case laJungle: case laAlchemist: case laFrog: fcol = floorcolors[c->land]; break; + case laCA: + fcol = floorcolors[c->land]; + if(geosupport_chessboard()) { + if(chessvalue(c)) fcol += 0x202020; + } + else if(geosupport_threecolor()) { + fcol += 0x202020 * pattern_threecolor(c); + } + break; + case laWet: fcol = 0x40FF40; break; @@ -1580,6 +1590,10 @@ void celldrawer::draw_features_and_walls_3d() { if(c->move(a) && (among(pmodel, mdPerspective, mdGeodesic) || gmatrix0.count(c->move(a)))) b = (patterns::innerwalls && (tC0(V)[2] < tC0(V * currentmap->adj(c, a))[2])) || !isWall3(c->move(a), dummy); if(b) { + #if CAP_WRL + /* always render */ + if(wrl::in && wrl::print) ; else + #endif if(pmodel == mdPerspective && !sphere && !quotient && !kite::in() && !nonisotropic && !hybri && !experimental && !nih) { if(a < 4 && among(geometry, gHoroTris, gBinary3) && celldistAlt(c) >= celldistAlt(centerover)) continue; else if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(centerover)) continue; diff --git a/changelog.txt b/changelog.txt index f2072fb1..eb2b7efa 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3904,3 +3904,14 @@ Geometries: - reverse FPP mode on iOS should be fixed - fixed the missing message when PC could not stay in place - fixed a potential rare crash + +2020-04-17 21:00 Update 11.3j: +- the Hypersian Rug mode now uses HyperRogue's 3D engine. As a result, its controls are consistent with HyperRogue 3D (which has received shift-strafe for this consistency), you can now view it in all models, and embed in Solv and Nil geometries. +- a drawing tool (see creative mode). Contrary to Vector Graphics Editor (now called Shape Editor) and texture editor, this simulates on using a huge non-Euclidean blackboard (unless played in Euclidean geometry or in a quotient space, of course). +- WRL export (export e.g. your Hypersian Rug or a hyperbolic honeycomb in ball model to a 3D modelling tool or a 3D printer) +- fixed some issues with 'vertical stretch' (acted inconsistently, stretched text) +- fixed central inversion in 3D models +- better error information when trying to read an illegal tes file +- CA mode now automatically awards Orb of Aether (so you can step space) and is simulated on every step in animation mode +- more accurate Solv rendering (using RK4 method instead of buggy midpoint) +- when giving values, you can use "..|" (animate with sharp changes); "../" can be also used for spline interpolation diff --git a/complex.cpp b/complex.cpp index f6d768fe..3940d371 100644 --- a/complex.cpp +++ b/complex.cpp @@ -3347,6 +3347,14 @@ EX namespace ca { EX eWall wlive = waFloorA; + EX unordered_set changed; + + EX void list_adj(cell *c) { + changed.insert(c); + for(cell* c1: adj_minefield_cells(c)) + changed.insert(c1); + } + // you can also do -mineadj EX string fillup(string s) { @@ -3372,6 +3380,12 @@ EX namespace ca { shift(); wlive = eWall(argi()); return 0; } + if(argis("-carun")) { + shift(); int iter = argi(); + start_game(); + for(int i=0; iland != laCA) return; - vector& allcells = currentmap->allcells(); + if(items[itOrbAether] < 2) items[itOrbAether] = 2; + vector allcells; + for(cell *c: changed) allcells.push_back(c); + changed.clear(); int dcs = isize(allcells); std::vector willlive(dcs); int old = 0, xold = 0; for(int i=0; iland != laCA) return; + if(c->land != laCA) continue; int nei = 0, live = 0; for(cell *c2: adj_minefield_cells(c)) { nei++; if(c2->wall == wlive) live++; @@ -3412,9 +3429,15 @@ EX namespace ca { } for(int i=0; iwall; c->wall = willlive[i] ? wlive : waNone; + if(c->wall != last) { + dynamicval d(prob, 0); + setdist(c, 7, nullptr); + list_adj(c); + } } - println(hlog, tie(dcs, old, xold)); + println(hlog, make_tuple(dcs, old, xold, isize(changed))); } EX } diff --git a/complex2.cpp b/complex2.cpp index 90e3bea7..e861c827 100644 --- a/complex2.cpp +++ b/complex2.cpp @@ -559,10 +559,14 @@ EX hookset hooks_mark; EX void performMarkCommand(cell *c) { if(!c) return; if(callhandlers(false, hooks_mark, c)) return; - if(c->land == laCA && c->wall == waNone) - c->wall = waFloorA; - else if(c->land == laCA && c->wall == waFloorA) + if(c->land == laCA && c->wall == waNone) { + c->wall = ca::wlive; + ca::list_adj(c); + } + else if(c->land == laCA && c->wall == ca::wlive) { c->wall = waNone; + ca::list_adj(c); + } if(c->land != laMinefield) return; if(c->item) return; if(!mightBeMine(c)) return; diff --git a/config.cpp b/config.cpp index 176ffeb4..d872ae27 100644 --- a/config.cpp +++ b/config.cpp @@ -305,10 +305,10 @@ EX void initConfig() { addsaver(precise_width, "precisewidth", .5); addsaver(linepatterns::width, "pattern-linewidth", 1); addsaver(fat_edges, "fat-edges"); - addsaver(vid.scale, "scale", 1); - addsaver(vid.xposition, "xposition", 0); - addsaver(vid.yposition, "yposition", 0); - addsaver(vid.alpha, "projection", 1); + addsaver(pconf.scale, "scale", 1); + addsaver(pconf.xposition, "xposition", 0); + addsaver(pconf.yposition, "yposition", 0); + addsaver(pconf.alpha, "projection", 1); addsaver(vid.sspeed, "scrollingspeed", 0); addsaver(vid.mspeed, "movement speed", 1); addsaver(vid.full, "fullscreen", false); @@ -329,14 +329,14 @@ EX void initConfig() { // special graphics - addsaver(vid.ballangle, "ball angle", 20); + addsaver(pconf.ballangle, "ball angle", 20); addsaver(vid.yshift, "Y shift", 0); addsaver(vid.use_wall_radar, "wallradar", true); addsaver(vid.fixed_facing, "fixed facing", 0); addsaver(vid.fixed_facing_dir, "fixed facing dir", 90); addsaver(vid.fixed_yz, "fixed YZ", true); - addsaver(vid.camera_angle, "camera angle", 0); - addsaver(vid.ballproj, "ballproj", 1); + addsaver(pconf.camera_angle, "camera angle", 0); + addsaver(pconf.ballproj, "ballproj", 1); addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE); addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE); @@ -362,6 +362,20 @@ EX void initConfig() { addsaver(reserve_limit, "memory_reserve", 128); addsaver(show_memory_warning, "show_memory_warning"); + auto& rconf = vid.rug_config; + addsaverenum(rconf.model, "rug-projection", mdEquidistant); + addsaver(rconf.scale, "rug-projection-scale", 1); + addsaver(rconf.alpha, "rug-projection-alpha", 1); + addsaver(rconf.clip_min, "rug-projection-clip-min", -100); + addsaver(rconf.clip_max, "rug-projection-clip-max", +10); + addsaver(rconf.stretch, "rug-projection-stretch", 1); + addsaver(rconf.halfplane_scale, "rug-projection-halfplane scale", 1); + addsaver(rconf.collignon_parameter, "rug-collignon-parameter", 1); + addsaver(rconf.collignon_reflected, "rug-collignon-reflect", false); + addsaver(rconf.euclid_to_sphere, "rug-euclid to sphere projection", 1.5); + addsaver(rconf.twopoint_param, "rug-twopoint parameter", 1); + addsaver(rconf.fisheye_param, "rug-fisheye parameter", 1); + addsaver(rconf.model_transition, "rug-model transition", 1); addsaver(rug::renderonce, "rug-renderonce"); addsaver(rug::rendernogl, "rug-rendernogl"); addsaver(rug::texturesize, "rug-texturesize"); @@ -389,16 +403,16 @@ EX void initConfig() { addsaver(models::rotation_xz, "conformal rotation_xz"); addsaver(models::rotation_xy2, "conformal rotation_2"); addsaver(models::do_rotate, "conformal rotation mode", 1); - addsaver(models::model_orientation, "model orientation", 0); - addsaver(models::model_orientation_yz, "model orientation-yz", 0); - addsaver(models::top_z, "topz", 5); - addsaver(models::model_transition, "model transition", 1); - addsaver(models::halfplane_scale, "halfplane scale", 1); + addsaver(pconf.model_orientation, "model orientation", 0); + addsaver(pconf.model_orientation_yz, "model orientation-yz", 0); + addsaver(pconf.top_z, "topz", 5); + addsaver(pconf.model_transition, "model transition", 1); + addsaver(pconf.halfplane_scale, "halfplane scale", 1); addsaver(history::autoband, "automatic band"); addsaver(history::autobandhistory, "automatic band history"); addsaver(history::dospiral, "do spiral"); - addsaver(models::clip_min, "clip-min", -1); - addsaver(models::clip_max, "clip-max", +1); + addsaver(pconf.clip_min, "clip-min", -1); + addsaver(pconf.clip_max, "clip-max", +1); addsaver(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true); // control @@ -468,13 +482,13 @@ EX void initConfig() { addsaver(vid.fov, "field-of-vision", 90); addsaver(vid.desaturate, "desaturate", 0); addsaverenum(vid.stereo_mode, "stereo-mode"); - addsaver(vid.euclid_to_sphere, "euclid to sphere projection", 1.5); - addsaver(vid.twopoint_param, "twopoint parameter", 1); - addsaver(vid.fisheye_param, "fisheye parameter", 1); - addsaver(vid.stretch, "stretch", 1); + addsaver(pconf.euclid_to_sphere, "euclid to sphere projection", 1.5); + addsaver(pconf.twopoint_param, "twopoint parameter", 1); + addsaver(pconf.fisheye_param, "fisheye parameter", 1); + addsaver(pconf.stretch, "stretch", 1); addsaver(vid.binary_width, "binary-tiling-width", 1); - addsaver(vid.collignon_parameter, "collignon-parameter", 1); - addsaver(vid.collignon_reflected, "collignon-reflect", false); + addsaver(pconf.collignon_parameter, "collignon-parameter", 1); + addsaver(pconf.collignon_reflected, "collignon-reflect", false); addsaver(vid.plevel_factor, "plevel_factor", 0.7); @@ -521,7 +535,7 @@ EX void initConfig() { #if CAP_SHOT addsaver(shot::shotx, "shotx"); addsaver(shot::shoty, "shoty"); - addsaver(shot::make_svg, "shotsvg"); + addsaverenum(shot::format, "shotsvg"); addsaver(shot::transparent, "shottransparent"); addsaver(shot::gamma, "shotgamma"); addsaver(shot::caption, "shotcaption"); @@ -545,11 +559,11 @@ EX void initConfig() { addsaver(slr::steps, "slr-steps"); addsaver(slr::range_xy, "slr-range-xy"); - addsaver(vid.skiprope, "mobius", 0); + addsaver(pconf.skiprope, "mobius", 0); - addsaver(models::formula, "formula"); - addsaverenum(models::basic_model, "basic model"); - addsaver(models::use_atan, "use_atan"); + addsaver(pconf.formula, "formula"); + addsaverenum(pconf.basic_model, "basic model"); + addsaver(pconf.use_atan, "use_atan"); addsaver(arcm::current.symbol, "arcm-symbol", "4^5"); addsaverenum(hybrid::underlying, "product-underlying"); @@ -670,7 +684,7 @@ EX bool inSpecialMode() { tour::on || #endif yendor::on || tactic::on || randomPatternsMode || - geometry != gNormal || pmodel != mdDisk || vid.alpha != 1 || vid.scale != 1 || + geometry != gNormal || pmodel != mdDisk || pconf.alpha != 1 || pconf.scale != 1 || rug::rugged || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE; } @@ -697,7 +711,7 @@ EX bool have_current_settings() { } EX bool have_current_graph_settings() { - if(vid.xposition || vid.yposition || vid.alpha != 1 || vid.scale != 1) + if(pconf.xposition || pconf.yposition || pconf.alpha != 1 || pconf.scale != 1) return true; if(pmodel != mdDisk || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE) return true; @@ -708,8 +722,8 @@ EX bool have_current_graph_settings() { } EX void reset_graph_settings() { - pmodel = mdDisk; vid.alpha = 1; vid.scale = 1; - vid.xposition = vid.yposition = 0; + pmodel = mdDisk; pconf.alpha = 1; pconf.scale = 1; + pconf.xposition = pconf.yposition = 0; #if CAP_RUG if(rug::rugged) rug::close(); #endif @@ -893,6 +907,7 @@ string solhelp() { } EX void edit_sightrange() { + USING_NATIVE_GEOMETRY_IN_RUG; if(vid.use_smart_range) { ld& det = WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3; dialog::editNumber(det, 1, 50, 1, WDIM == 2 ? 8 : 30, XLAT("minimum visible cell in pixels"), ""); @@ -1431,7 +1446,7 @@ EX void showJoyConfig() { EX void projectionDialog() { vid.tc_alpha = ticks; - dialog::editNumber(vid.alpha, -5, 5, .1, 1, + dialog::editNumber(vpconf.alpha, -5, 5, .1, 1, XLAT("projection"), XLAT("HyperRogue uses the Minkowski hyperboloid model internally. " "Klein and Poincaré models can be obtained by perspective, " @@ -1449,17 +1464,17 @@ EX void projectionDialog() { "tanh(g)/tanh(c) units below the center. This in turn corresponds to " "the Poincaré model for g=c, and Klein-Beltrami model for g=0.")); dialog::addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'P'); - dialog::add_action([] () { *dialog::ne.editwhat = 1; vid.scale = 1; dialog::ne.s = "1"; }); + dialog::add_action([] () { *dialog::ne.editwhat = 1; vpconf.scale = 1; dialog::ne.s = "1"; }); dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K'); - dialog::add_action([] () { *dialog::ne.editwhat = 0; vid.scale = 1; dialog::ne.s = "0"; }); + dialog::add_action([] () { *dialog::ne.editwhat = 0; vpconf.scale = 1; dialog::ne.s = "0"; }); if(hyperbolic) { dialog::addSelItem("inverted Poincaré model", "-1", 'I'); - dialog::add_action([] () { *dialog::ne.editwhat = -1; vid.scale = 1; dialog::ne.s = "-1"; }); + dialog::add_action([] () { *dialog::ne.editwhat = -1; vpconf.scale = 1; dialog::ne.s = "-1"; }); } dialog::addItem(sphere ? "orthographic" : "Gans model", 'O'); - dialog::add_action([] () { vid.alpha = vid.scale = 999; dialog::ne.s = dialog::disp(vid.alpha); }); + dialog::add_action([] () { vpconf.alpha = vpconf.scale = 999; dialog::ne.s = dialog::disp(vpconf.alpha); }); dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T'); - dialog::add_action([] () { double d = 1.1; vid.alpha *= d; vid.scale *= d; dialog::ne.s = dialog::disp(vid.alpha); }); + dialog::add_action([] () { double d = 1.1; vpconf.alpha *= d; vpconf.scale *= d; dialog::ne.s = dialog::disp(vpconf.alpha); }); }; } @@ -1564,7 +1579,7 @@ EX void showStereo() { } EX void config_camera_rotation() { - dialog::editNumber(vid.ballangle, 0, 90, 5, 0, XLAT("camera rotation in 3D models"), + dialog::editNumber(pconf.ballangle, 0, 90, 5, 0, XLAT("camera rotation in 3D models"), "Rotate the camera in 3D models (ball model, hyperboloid, and hemisphere). " "Note that hyperboloid and hemisphere models are also available in the " "Hypersian Rug surfaces menu, but they are rendered differently there -- " @@ -1648,9 +1663,9 @@ EX void show3D() { if(GDIM == 2) - dialog::addSelItem(XLAT("Projection at the ground level"), fts(vid.alpha), 'p'); + dialog::addSelItem(XLAT("Projection at the ground level"), fts(pconf.alpha), 'p'); else if(!in_perspective()) - dialog::addSelItem(XLAT("Projection distance"), fts(vid.alpha), 'p'); + dialog::addSelItem(XLAT("Projection distance"), fts(pconf.alpha), 'p'); dialog::addBreak(50); dialog::addSelItem(XLAT("Height of walls"), fts(vid.wall_height), 'w'); @@ -1676,7 +1691,7 @@ EX void show3D() { dialog::editNumber(mouseaim_sensitivity, -1, 1, 0.002, 0.01, XLAT("mouse aiming sensitivity"), "set to 0 to disable"); }); } - dialog::addSelItem(XLAT("camera rotation"), fts(vid.camera_angle), 's'); + dialog::addSelItem(XLAT("camera rotation"), fts(vpconf.camera_angle), 's'); if(GDIM == 2) { dialog::addSelItem(XLAT("fixed facing"), vid.fixed_facing ? fts(vid.fixed_facing_dir) : XLAT("OFF"), 'f'); dialog::add_action([] () { vid.fixed_facing = !vid.fixed_facing; @@ -1730,7 +1745,7 @@ EX void show3D() { } #endif if(GDIM == 2) { - dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && vid.camera_angle, 'T'); + dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && pconf.camera_angle, 'T'); dialog::add_action(geom3::switch_tpp); } @@ -1902,7 +1917,7 @@ EX void show3D() { }; } else if(uni == 's') - dialog::editNumber(vid.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"), + dialog::editNumber(vpconf.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"), XLAT("Rotate the camera. Can be used to obtain a first person perspective, " "or third person perspective when combined with Y shift.") ); @@ -1959,11 +1974,7 @@ EX void showCustomizeChar() { int firsty = dialog::items[0].position / 2; int scale = firsty - 2 * vid.fsize; - dynamicval pm(pmodel, flat_model()); - glClear(GL_DEPTH_BUFFER_BIT); - dynamicval va(vid.alpha, 1); - dynamicval vs(vid.scale, 1); - dynamicval vc(vid.camera_angle, 0); + flat_model_enabler fme; initquickqueue(); transmatrix V = atscreenpos(vid.xres/2, firsty, scale); @@ -2465,7 +2476,7 @@ EX int read_config_args() { else if(argis("-yca")) { PHASEFROM(2); shift_arg_formula(vid.yshift); - shift_arg_formula(vid.camera_angle); + shift_arg_formula(pconf.camera_angle); } else if(argis("-pside")) { PHASEFROM(2); @@ -2473,8 +2484,8 @@ EX int read_config_args() { } else if(argis("-xy")) { PHASEFROM(2); - shift_arg_formula(vid.xposition); - shift_arg_formula(vid.yposition); + shift_arg_formula(pconf.xposition); + shift_arg_formula(pconf.yposition); } else if(argis("-fixdir")) { PHASEFROM(2); @@ -2534,6 +2545,10 @@ EX int read_config_args() { PHASEFROM(2); shift(); neon_mode = eNeon(argi()); } + else if(argis("-smooths")) { + PHASEFROM(2); + shift(); smooth_scrolling = argi(); + } else if(argis("-neonnf")) { PHASEFROM(2); shift(); neon_nofill = argi(); @@ -2578,15 +2593,15 @@ auto ah_config = addHook(hooks_args, 0, read_config_args) + addHook(hooks_args, EX unordered_map params = { {"linewidth", vid.linewidth}, {"patternlinewidth", linepatterns::width}, - {"scale", vid.scale}, - {"xposition", vid.xposition}, - {"yposition", vid.yposition}, - {"projection", vid.alpha}, + {"scale", pconf.scale}, + {"xposition", pconf.xposition}, + {"yposition", pconf.yposition}, + {"projection", pconf.alpha}, {"sspeed", vid.sspeed}, {"mspeed", vid.mspeed}, - {"ballangle", vid.ballangle}, + {"ballangle", pconf.ballangle}, {"yshift", vid.yshift}, - {"cameraangle", vid.camera_angle}, + {"cameraangle", pconf.camera_angle}, {"eye", vid.eye}, {"depth", vid.depth}, {"camera", vid.camera}, @@ -2603,22 +2618,22 @@ EX unordered_map params = { {"star", polygonal::STAR}, {"lvspeed", history::lvspeed}, {"rotation", models::rotation}, - {"mori", models::model_orientation}, - {"mori_yz", models::model_orientation_yz}, - {"clipmin", models::clip_min}, - {"clipmax", models::clip_max}, - {"topz", models::top_z}, - {"mtrans", models::model_transition}, - {"hp", models::halfplane_scale}, + {"mori", pconf.model_orientation}, + {"mori_yz", pconf.model_orientation_yz}, + {"clipmin", pconf.clip_min}, + {"clipmax", pconf.clip_max}, + {"topz", pconf.top_z}, + {"mtrans", pconf.model_transition}, + {"hp", pconf.halfplane_scale}, {"back", backbrightness}, {"ipd", vid.ipd}, {"lr", vid.lr_eyewidth}, {"anaglyph", vid.anaglyph_eyewidth}, {"fov", vid.fov}, - {"ets", vid.euclid_to_sphere}, - {"stretch", vid.stretch}, - {"twopoint", vid.twopoint_param}, - {"fisheye", vid.fisheye_param}, + {"ets", pconf.euclid_to_sphere}, + {"stretch", pconf.stretch}, + {"twopoint", pconf.twopoint_param}, + {"fisheye", pconf.fisheye_param}, {"bwidth", vid.binary_width}, #if CAP_ANIMATIONS {"aperiod", anims::period}, @@ -2630,10 +2645,10 @@ EX unordered_map params = { {"a", anims::a}, {"b", anims::b}, #endif - {"mobius", vid.skiprope}, - {"sang", models::spiral_angle}, - {"spiralx", models::spiral_x}, - {"spiraly", models::spiral_y}, + {"mobius", pconf.skiprope}, + {"sang", pconf.spiral_angle}, + {"spiralx", pconf.spiral_x}, + {"spiraly", pconf.spiral_y}, #if CAP_CRYSTAL {"cprob", crystal::compass_probability}, #endif @@ -2642,7 +2657,7 @@ EX unordered_map params = { {"fade", shot::fade}, {"mgrid", vid.multiplier_grid}, {"mring", vid.multiplier_ring}, - {"collignon", vid.collignon_parameter}, + {"collignon", pconf.collignon_parameter}, {"levellines", levellines}, #endif }; diff --git a/control.cpp b/control.cpp index add772e7..3504b674 100644 --- a/control.cpp +++ b/control.cpp @@ -76,7 +76,7 @@ EX movedir vectodir(hyperpoint P) { transmatrix U = ggmatrix(cwt.at); if(GDIM == 3 && WDIM == 2) U = radar_transform * U; - P = direct_exp(lp_iapply(P), 100); + P = direct_exp(lp_iapply(P)); hyperpoint H = sphereflip * tC0(U); transmatrix Centered = sphereflip * rgpushxto0(H); @@ -87,8 +87,8 @@ EX movedir vectodir(hyperpoint P) { for(int i=0; itype; i++) { transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin); - ld d1 = geo_dist(U * T * C0, Centered * P, iTable); - ld d2 = geo_dist(U * T * C0, Centered * C0, iTable); + ld d1 = geo_dist(U * T * C0, Centered * P); + ld d2 = geo_dist(U * T * C0, Centered * C0); dirdist[i] = d1 - d2; //xspinpush0(-i * 2 * M_PI /cwt.at->type, .5), P); } @@ -308,9 +308,7 @@ EX void handlePanning(int sym, int uni) { if(sym == PSEUDOKEY_WHEELDOWN) shift_view(ztangent(0.05*shiftmul)), didsomething = true, playermoved = false; } - if(rug::rugged || smooth_scrolling) { - return; - } + rug::using_rugview urv; #if !ISPANDORA if(sym == SDLK_END && GDIM == 3) { @@ -319,60 +317,62 @@ EX void handlePanning(int sym, int uni) { if(sym == SDLK_HOME && GDIM == 3) { shift_view(ztangent(+0.2*shiftmul)), didsomething = true, playermoved = false; } - if(sym == SDLK_RIGHT) { + + auto roll = [&] (int dir, ld val) { + if(GDIM == 3 && anyshiftclick) + shift_view(ctangent(dir, -val)), didsomething = true, playermoved = false; /* -val because shift reverses */ + #if CAP_CRYSTAL + else if(rug::rug_control() && rug::in_crystal()) + crystal::apply_rotation(cspin(dir, 2, val)); + #endif + else if(GDIM == 3) + rotate_view(cspin(dir, 2, val)), didsomething = true; + else + View = cpush(dir, val) * View, playermoved = false, didsomething = true; + }; + + if(sym == SDLK_RIGHT) { if(history::on) history::lvspeed += 0.1 * shiftmul; - else if(GDIM == 3) - rotate_view(cspin(0, 2, -0.2*shiftmul)), didsomething = true; - else - View = xpush(-0.2*shiftmul) * View, playermoved = false, didsomething = true; + else roll(0, -0.2*shiftmul); } if(sym == SDLK_LEFT) { if(history::on) history::lvspeed -= 0.1 * shiftmul; - else if(GDIM == 3) - rotate_view(cspin(0, 2, 0.2*shiftmul)), didsomething = true; - else - View = xpush(+0.2*shiftmul) * View, playermoved = false, didsomething = true; + else roll(0, 0.2*shiftmul); } if(sym == SDLK_UP) { if(history::on) history::lvspeed += 0.1 * shiftmul; - else if(GDIM == 3) - rotate_view(cspin(1, 2, 0.2*shiftmul)), didsomething = true; - else - View = ypush(+0.2*shiftmul) * View, playermoved = false, didsomething = true; + else roll(1, 0.2*shiftmul); } if(sym == SDLK_DOWN) { if(history::on) history::lvspeed -= 0.1 * shiftmul; - else if(GDIM == 3) - rotate_view(cspin(1, 2, -0.2*shiftmul)), didsomething = true; - else - View = ypush(-0.2*shiftmul) * View, playermoved = false, didsomething = true; + else roll(1, -0.2*shiftmul); } #endif if(sym == SDLK_PAGEUP) { - if(history::on) + if(history::on && !rug::rug_control()) models::rotation++; else rotate_view(spin(M_PI/cgi.S21/2*shiftmul)), didsomething = true; } if(sym == SDLK_PAGEDOWN) { - if(history::on) + if(history::on && !rug::rug_control()) models::rotation++; else rotate_view(spin(-M_PI/cgi.S21/2*shiftmul)), didsomething = true; } if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN) - if(isGravityLand(cwt.at->land)) playermoved = false; + if(isGravityLand(cwt.at->land) && !rug::rug_control()) playermoved = false; if(sym == PSEUDOKEY_WHEELUP && GDIM == 2) { ld jx = (mousex - current_display->xcenter - .0) / current_display->radius / 10; ld jy = (mousey - current_display->ycenter - .0) / current_display->radius / 10; playermoved = false; - View = gpushxto0(hpxy(jx, jy)) * View; + rotate_view(gpushxto0(hpxy(jx, jy))); sym = 1; } } @@ -493,8 +493,12 @@ EX void handleKeyNormal(int sym, int uni) { pushScreen(inv::show); #endif - if(((sym == SDLK_HOME && GDIM == 2) || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) - fullcenter(); + if(((sym == SDLK_HOME && GDIM == 2 && !rug::rugged) || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) { + if(rug::rug_control()) + rug::reset_view(); + else + fullcenter(); + } if(sym == 'v' && DEFAULTNOR(sym)) pushScreen(showMainMenu); @@ -696,7 +700,8 @@ EX void mainloopiter() { #endif } - if(smooth_scrolling && !shmup::on && !rug::rugged) { + if(smooth_scrolling && !shmup::on) { + rug::using_rugview urv; static int lastticks; ld t = (ticks - lastticks) * shiftmul / 1000.; lastticks = ticks; @@ -705,14 +710,23 @@ EX void mainloopiter() { shift_view(ctangent(2, -t)), didsomething = true, playermoved = false; if(keystate[SDLK_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME)) shift_view(ctangent(2, t)), didsomething = true, playermoved = false; - if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) - rotate_view(GDIM == 2 ? xpush(-t) : cspin(0, 2, -t)), didsomething = true, playermoved = playermoved && GDIM == 3; - if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) - rotate_view(GDIM == 2 ? xpush(t) : cspin(0, 2, t)), didsomething = true, playermoved = playermoved && GDIM == 3; - if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) - rotate_view(GDIM == 2 ? ypush(t) : cspin(1, 2, t)), didsomething = true, playermoved = playermoved && GDIM == 3; - if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) - rotate_view(GDIM == 2 ? ypush(-t) : cspin(1, 2, -t)), didsomething = true, playermoved = playermoved && GDIM == 3; + + auto roll = [&] (int dir, ld val) { + if(GDIM == 3 && anyshiftclick) + shift_view(ctangent(dir, -val)); /* -val because shift reverses */ + #if CAP_CRYSTAL + else if(rug::rug_control() && rug::in_crystal()) + crystal::apply_rotation(cspin(dir, 2, val)); + #endif + else + rotate_view(GDIM == 2 ? cpush(dir, val) : cspin(dir, 2, val)); + didsomething = true, playermoved = playermoved && GDIM == 3; + }; + + if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) roll(0, -t); + if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) roll(0, t); + if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) roll(1, t); + if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) roll(1, -t); if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) { if(history::on) models::rotation+=t; @@ -865,7 +879,7 @@ EX void handle_event(SDL_Event& ev) { else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick) { sym = 1, didsomething = true; if(anyshift) - vid.xposition = vid.yposition = 0; + pconf.xposition = pconf.yposition = 0; } else if(ev.button.button == SDL_BUTTON_LEFT) { sym = getcstat, uni = getcstat, shiftmul = getcshift; @@ -874,12 +888,12 @@ EX void handle_event(SDL_Event& ev) { else if(ev.button.button==SDL_BUTTON_WHEELDOWN) { if(anyctrl && anyshift && !rug::rugged && GDIM == 2) { mapeditor::scaleall(1/1.2); - vid.alpha /= 1.2; + pconf.alpha /= 1.2; } else if(anyctrl && !rug::rugged && GDIM == 2) mapeditor::scaleall(pow(2, -.25)); else if(anyshift && !rug::rugged && GDIM == 2) - vid.alpha -= 0.25; + pconf.alpha -= 0.25; else if(rollchange) { sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true; } @@ -890,12 +904,12 @@ EX void handle_event(SDL_Event& ev) { if(ev.button.button==SDL_BUTTON_WHEELUP) { if(anyctrl && anyshift && !rug::rugged && GDIM == 2) { mapeditor::scaleall(1.2); - vid.alpha *= 1.2; + pconf.alpha *= 1.2; } else if(anyctrl && !rug::rugged && GDIM == 2) mapeditor::scaleall(pow(2, .25)); else if(anyshift && !rug::rugged && GDIM == 2) - vid.alpha += 0.25; + pconf.alpha += 0.25; else if(rollchange) { sym = getcstat, uni = getcstat, shiftmul = -getcshift, wheelclick = true; } @@ -929,8 +943,8 @@ EX void handle_event(SDL_Event& ev) { if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2()) { fix_mouseh(); if(anyctrl) { - vid.xposition += (mousex - lmousex) * 1. / current_display->scrsize, - vid.yposition += (mousey - lmousey) * 1. / current_display->scrsize; + pconf.xposition += (mousex - lmousex) * 1. / current_display->scrsize, + pconf.yposition += (mousey - lmousey) * 1. / current_display->scrsize; } else if(mouseh[LDIM] < 50 && mouseoh[LDIM] < 50) { panning(mouseoh, mouseh); @@ -1047,7 +1061,7 @@ EX bool gmodekeys(int sym, int uni) { if(GDIM == 2) { if(among(NUMBERKEY, '1', '2', '3') && !rug::rugged && euclid && WDIM == 2) { - vid.xposition = vid.yposition = 0; + pconf.xposition = pconf.yposition = 0; ld maxs = 0; auto& cd = current_display; for(auto& p: gmatrix) for(int i=0; itype; i++) { @@ -1057,15 +1071,15 @@ EX bool gmodekeys(int sym, int uni) { maxs = max(maxs, onscreen[0] / cd->xsize); maxs = max(maxs, onscreen[1] / cd->ysize); } - vid.alpha = 1; - vid.scale = vid.scale / 2 / maxs / cd->radius; - if(NUMBERKEY == '3') vid.scale *= 2; - if(NUMBERKEY == '1') vid.scale /= 2; + pconf.alpha = 1; + pconf.scale = pconf.scale / 2 / maxs / cd->radius; + if(NUMBERKEY == '3') pconf.scale *= 2; + if(NUMBERKEY == '1') pconf.scale /= 2; } - else if(NUMBERKEY == '1' && !rug::rugged) { vid.alpha = 999; vid.scale = 998; vid.xposition = vid.yposition = 0; } - else if(NUMBERKEY == '2' && !rug::rugged) { vid.alpha = 1; vid.scale = 0.4; vid.xposition = vid.yposition = 0; } - else if(NUMBERKEY == '3' && !rug::rugged) { vid.alpha = 1; vid.scale = 1; vid.xposition = vid.yposition = 0; } - else if(NUMBERKEY == '4' && !rug::rugged) { vid.alpha = 0; vid.scale = 1; vid.xposition = vid.yposition = 0; } + else if(NUMBERKEY == '1' && !rug::rugged) { pconf.alpha = 999; pconf.scale = 998; pconf.xposition = pconf.yposition = 0; } + else if(NUMBERKEY == '2' && !rug::rugged) { pconf.alpha = 1; pconf.scale = 0.4; pconf.xposition = pconf.yposition = 0; } + else if(NUMBERKEY == '3' && !rug::rugged) { pconf.alpha = 1; pconf.scale = 1; pconf.xposition = pconf.yposition = 0; } + else if(NUMBERKEY == '4' && !rug::rugged) { pconf.alpha = 0; pconf.scale = 1; pconf.xposition = pconf.yposition = 0; } else if(NUMBERKEY == '5') { vid.wallmode += 60 + (shiftmul > 0 ? 1 : -1); vid.wallmode %= 7; } else if(NUMBERKEY == '8') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 6; } else if(uni == '%') { @@ -1183,10 +1197,10 @@ EX void show() { dialog::addItem(XLAT("experiment with geometry"), 'g'); dialog::add_action([] () { runGeometryExperiments(); }); - dialog::addSelItem(XLAT("projection"), fts(vid.alpha), 'p'); + dialog::addSelItem(XLAT("projection"), fts(vpconf.alpha), 'p'); dialog::add_action([] () { projectionDialog(); }); - dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z'); + dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z'); dialog::add_action([] () { editScale(); }); dialog::addItem(XLAT("spherical VR"), 'v'); @@ -1195,7 +1209,7 @@ EX void show() { mode = 0; fullcenter(); mode = 2; sensitivity = 1; vid.stereo_mode = sLR; vid.ipd = 0.2; - vid.alpha = 0; vid.scale = 1; + vpconf.alpha = 0; vpconf.scale = 1; }); dialog::addBreak(100); diff --git a/crystal.cpp b/crystal.cpp index 5c299558..001f4bd7 100644 --- a/crystal.cpp +++ b/crystal.cpp @@ -1230,8 +1230,8 @@ void cut_triangle2(const hyperpoint pa, const hyperpoint pb, const hyperpoint pc rug::rugpoint *rac = rug::addRugpoint(hac, 0); rug::rugpoint *rbc = rug::addRugpoint(hbc, 0); - rac->flat = pac; - rbc->flat = pbc; + rac->native = pac; + rbc->native = pbc; rac->valid = true; rbc->valid = true; rug::triangles.push_back(rug::triangle(rac, rbc, NULL)); @@ -1270,7 +1270,7 @@ EX void build_rugdata() { if(!draw_cut) { rugpoint *v = addRugpoint(tC0(V), 0); - v->flat = coord_to_flat(co); + v->native = coord_to_flat(co); v->valid = true; rugpoint *p[MAX_EDGE_CRYSTAL]; @@ -1278,7 +1278,7 @@ EX void build_rugdata() { for(int i=0; itype; i++) { p[i] = addRugpoint(V * get_corner_position(c, i), 0); p[i]->valid = true; - p[i]->flat = coord_to_flat(vcoord[i]); + p[i]->native = coord_to_flat(vcoord[i]); } for(int i=0; itype; i++) addTriangle(v, p[i], p[(i+1) % c->type]); diff --git a/devmods/tests.cpp b/devmods/tests.cpp index 4c05b5e3..5269b896 100644 --- a/devmods/tests.cpp +++ b/devmods/tests.cpp @@ -6,6 +6,20 @@ namespace hr { namespace tests { +string test_eq(hyperpoint h1, hyperpoint h2, ld err = 1e-6) { + if(sqhypot_d(MDIM, h1 -h2) < err) + return "OK"; + else + return "ERROR"; + } + +string test_eq(transmatrix T1, transmatrix T2, ld err = 1e-6) { + if(eqmatrix(T1, T2, err)) + return "OK"; + else + return "ERROR"; + } + int readArgs() { using namespace arg; @@ -58,16 +72,30 @@ int readArgs() { int cx = (co + 1) % WDIM; int cy = (co + 2) % WDIM; auto oxy = [&] (ld x, ld y, ld z) { hyperpoint h = Hypc; h[co] = z; h[cx] = x; if(WDIM == 3) h[cy] = y; return tC0(bt::normalized_at(h)); }; - ld shrunk_x = geo_dist(oxy(0,0,-1), oxy(.01,0,-1), iTable); - ld shrunk_y = geo_dist(oxy(0,0,-1), oxy(0,.01,-1), iTable); - ld expand_x = geo_dist(oxy(0,0,+1), oxy(.01,0,+1), iTable); - ld expand_y = geo_dist(oxy(0,0,+1), oxy(0,.01,+1), iTable); + ld shrunk_x = geo_dist(oxy(0,0,-1), oxy(.01,0,-1)); + ld shrunk_y = geo_dist(oxy(0,0,-1), oxy(0,.01,-1)); + ld expand_x = geo_dist(oxy(0,0,+1), oxy(.01,0,+1)); + ld expand_y = geo_dist(oxy(0,0,+1), oxy(0,.01,+1)); if(WDIM == 2) shrunk_y = expand_y = 1; println(hlog, "should be 1: ", lalign(10, (shrunk_x * shrunk_y * bt::area_expansion_rate()) / (expand_x * expand_y)), " : ", tie(shrunk_x, shrunk_y, expand_x, expand_y, aer)); if(geometry == gArnoldCat) println(hlog, "(but not in Arnold's cat)"); } } + else if(argis("-test-push")) { + PHASEFROM(3); + for(eGeometry g: {gSol, gNil, gCubeTiling, gSpace534, gCell120}) { + stop_game(); + set_geometry(g); + println(hlog, "testing geometry: ", geometry_name()); + hyperpoint h = hyperpoint(.1, .2, .3, 1); + h = normalize(h); + println(hlog, "h = ", h); + println(hlog, "test rgpushxto0: ", test_eq(rgpushxto0(h) * C0, h)); + println(hlog, "test gpushxto0: ", test_eq(gpushxto0(h) * h, C0)); + println(hlog, "test inverses: ", test_eq(inverse(rgpushxto0(h)), gpushxto0(h))); + } + } else return 1; return 0; } diff --git a/dialogs.cpp b/dialogs.cpp index 8146aa87..7c914f67 100644 --- a/dialogs.cpp +++ b/dialogs.cpp @@ -1263,6 +1263,10 @@ EX namespace dialog { else act(); } + inline void push_confirm_dialog(const reaction_t& act, const string& s) { + pushScreen([act, s] () { confirm_dialog(s, act); }); + } + inline reaction_t add_confirmation(const reaction_t& act) { return [act] { do_if_confirmed(act); }; } diff --git a/dpgen.cpp b/dpgen.cpp index 2ae31d50..9b4fd183 100644 --- a/dpgen.cpp +++ b/dpgen.cpp @@ -66,7 +66,7 @@ void launch(int seed, int elimit, int hlimit) { dual::switch_to(0); specialland = firstland = laCanvas; canvas_default_wall = waSea; - vid.scale = .5; + pconf.scale = .5; dual::switch_to(1); specialland = firstland = laCanvas; shrand(seed); diff --git a/drawing.cpp b/drawing.cpp index f10209fa..0b674604 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -34,6 +34,9 @@ static const int POLY_ALWAYS_IN = (1<<21); // always draw this static const int POLY_TRIANGLES = (1<<22); // made of TRIANGLES, not TRIANGLE_FAN static const int POLY_INTENSE = (1<<23); // extra intense colors static const int POLY_DEBUG = (1<<24); // debug this shape +static const int POLY_PRINTABLE = (1<<25); // these walls are printable +static const int POLY_FAT = (1<<26); // fatten this model in WRL export (used for Rug) +static const int POLY_SHADE_TEXTURE = (1<<27); // texture has 'z' coordinate for shading /** \brief A graphical element that can be drawn. Objects are not drawn immediately but rather queued. * @@ -181,6 +184,7 @@ vector line_vertices; #endif EX void glflush() { + DEBBI(DF_GRAPH, ("glflush")); #if MINIMIZE_GL_CALLS if(isize(triangle_vertices)) { // printf("%08X %08X | %d shapes, %d/%d vertices\n", triangle_color, line_color, shapes_merged, isize(triangle_vertices), isize(line_vertices)); @@ -262,13 +266,13 @@ void add1(const hyperpoint& H) { } bool is_behind(const hyperpoint& H) { - return pmodel == mdDisk && (hyperbolic ? H[2] >= 0 : true) && (nonisotropic ? false : vid.alpha + H[2] <= BEHIND_LIMIT); + return pmodel == mdDisk && (hyperbolic ? H[2] >= 0 : true) && (nonisotropic ? false : pconf.alpha + H[2] <= BEHIND_LIMIT); } hyperpoint be_just_on_view(const hyperpoint& H1, const hyperpoint &H2) { - // H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - vid.alpha - // H2[2]- BEHIND_LIMIT + vid.alpha = t * (H2[2] - H1[2]) - ld t = (H2[2] - BEHIND_LIMIT + vid.alpha) / (H2[2] - H1[2]); + // H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - pconf.alpha + // H2[2]- BEHIND_LIMIT + pconf.alpha = t * (H2[2] - H1[2]) + ld t = (H2[2] - BEHIND_LIMIT + pconf.alpha) / (H2[2] - H1[2]); return H1 * t + H2 * (1-t); } @@ -289,14 +293,14 @@ EX bool two_sided_model() { if(pmodel == mdDisk) return sphere; if(pmodel == mdHemisphere) return true; if(pmodel == mdRotatedHyperboles) return true; - if(pmodel == mdSpiral && models::spiral_cone < 360) return true; + if(pmodel == mdSpiral && pconf.spiral_cone < 360) return true; return false; } EX int get_side(const hyperpoint& H) { if(pmodel == mdDisk && sphere) { double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; - double horizon = curnorm / vid.alpha; + double horizon = curnorm / pconf.alpha; return (H[2] <= -horizon) ? -1 : 1; } if(pmodel == mdRotatedHyperboles) @@ -310,7 +314,7 @@ EX int get_side(const hyperpoint& H) { applymodel(H, res); return res[2] < 0 ? -1 : 1; } - if(pmodel == mdSpiral && models::spiral_cone < 360) { + if(pmodel == mdSpiral && pconf.spiral_cone < 360) { return cone_side(H); } return 0; @@ -334,13 +338,13 @@ void fixpoint(glvertex& hscr, hyperpoint H) { } hyperpoint Hscr; applymodel(good, Hscr); - hscr = glhr::makevertex(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*current_display->radius); + hscr = glhr::makevertex(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*current_display->radius); } void addpoint(const hyperpoint& H) { if(true) { ld z = current_display->radius; - // if(vid.alpha + H[2] <= BEHIND_LIMIT && pmodel == mdDisk) poly_flags |= POLY_BEHIND; + // if(pconf.alpha + H[2] <= BEHIND_LIMIT && pmodel == mdDisk) poly_flags |= POLY_BEHIND; if(spherespecial) { @@ -350,7 +354,7 @@ void addpoint(const hyperpoint& H) { } else if(sphere && (poly_flags & POLY_ISSIDE)) { double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; - double horizon = curnorm / vid.alpha; + double horizon = curnorm / pconf.alpha; poly_flags |= POLY_NOTINFRONT; if(last_infront && nif_error_in(glcoords.back()[0], glcoords.back()[1], H[0], H[1])) poly_flags |= POLY_NIF_ERROR; @@ -358,8 +362,8 @@ void addpoint(const hyperpoint& H) { last_infront = true; z *= - (sqrt(curnorm - horizon*horizon) / (vid.alpha - horizon)) / - (sqrt(curnorm - H[2]*H[2]) / (vid.alpha+H[2])); + (sqrt(curnorm - horizon*horizon) / (pconf.alpha - horizon)) / + (sqrt(curnorm - H[2]*H[2]) / (pconf.alpha+H[2])); } else { poly_flags |= POLY_NOTINFRONT; @@ -385,12 +389,12 @@ void addpoint(const hyperpoint& H) { } if(GDIM == 2) { for(int i=0; i<3; i++) Hscr[i] *= z; - Hscr[1] *= vid.stretch; + Hscr[1] *= pconf.stretch; } else { Hscr[0] *= z; - Hscr[1] *= z * vid.stretch; - Hscr[2] = 1 - 2 * (-Hscr[2] - models::clip_min) / (models::clip_max - models::clip_min); + Hscr[1] *= z * pconf.stretch; + Hscr[2] = 1 - 2 * (-Hscr[2] - pconf.clip_min) / (pconf.clip_max - pconf.clip_min); } add1(Hscr); } @@ -409,7 +413,7 @@ void coords_to_poly() { bool behind3(hyperpoint h) { if(pmodel == mdGeodesic) - h = lp_apply(inverse_exp(h, iTable)); + h = lp_apply(inverse_exp(h)); return h[2] < 0; } @@ -482,11 +486,11 @@ void addpoly(const transmatrix& V, const vector &tab, int ofs, int cnt /* hyperpoint Hscr; applymodel(goodpoint, Hscr); - glcoords.push_back(make_array(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); - glcoords.push_back(make_array(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch+10, Hscr[2]*vid.radius)); - glcoords.push_back(make_array(Hscr[0]*current_display->radius-10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); - glcoords.push_back(make_array(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch-10, Hscr[2]*vid.radius)); - glcoords.push_back(make_array(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); */ + glcoords.push_back(make_array(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius)); + glcoords.push_back(make_array(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch+10, Hscr[2]*vid.radius)); + glcoords.push_back(make_array(Hscr[0]*current_display->radius-10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius)); + glcoords.push_back(make_array(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch-10, Hscr[2]*vid.radius)); + glcoords.push_back(make_array(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius)); */ } } @@ -594,6 +598,7 @@ void dqi_poly::gldraw() { if(tinf) { glhr::be_textured(); + if(flags & POLY_SHADE_TEXTURE) current_display->next_shader_flags |= GF_TEXTURE_SHADED; glBindTexture(GL_TEXTURE_2D, tinf->texture_id); glhr::vertices_texture(v, tinf->tvertices, offset, offset_texture); ioffset = 0; @@ -715,7 +720,7 @@ EX ld scale_at(const transmatrix& T) { EX ld linewidthat(const hyperpoint& h) { if(!(vid.antialias & AA_LINEWIDTH)) return 1; - else if(hyperbolic && pmodel == mdDisk && vid.alpha == 1 && !ISWEB) { + else if(hyperbolic && pmodel == mdDisk && pconf.alpha == 1 && !ISWEB) { double dz = h[LDIM]; if(dz < 1) return 1; else { @@ -750,7 +755,7 @@ vector periods; ld period_at(ld y) { ld m = current_display->radius; - y /= (m * vid.stretch); + y /= (m * pconf.stretch); switch(pmodel) { case mdBand: @@ -760,8 +765,8 @@ ld period_at(ld y) { case mdMollweide: return m * 2 * sqrt(1 - y*y*4); case mdCollignon: { - if(vid.collignon_reflected && y > 0) y = -y; - y += signed_sqrt(vid.collignon_parameter); + if(pconf.collignon_reflected && y > 0) y = -y; + y += signed_sqrt(pconf.collignon_parameter); return abs(m*y*2/1.2); } default: @@ -785,7 +790,7 @@ void adjust(bool tinf) { ld cmin = -chypot/2, cmax = chypot/2, dmin = -chypot, dmax = chypot; - ld z = vid.stretch * current_display->radius; + ld z = pconf.stretch * current_display->radius; switch(pmodel) { case mdSinusoidal: case mdBandEquidistant: case mdMollweide: @@ -797,9 +802,9 @@ void adjust(bool tinf) { break; case mdCollignon: - dmin = z * (signed_sqrt(vid.collignon_parameter - 1) - signed_sqrt(vid.collignon_parameter)); - if(vid.collignon_reflected) dmax = -dmin; - else dmax = z * (signed_sqrt(vid.collignon_parameter + 1) - signed_sqrt(vid.collignon_parameter)); + dmin = z * (signed_sqrt(pconf.collignon_parameter - 1) - signed_sqrt(pconf.collignon_parameter)); + if(pconf.collignon_reflected) dmax = -dmin; + else dmax = z * (signed_sqrt(pconf.collignon_parameter + 1) - signed_sqrt(pconf.collignon_parameter)); break; default: ; @@ -889,7 +894,7 @@ void compute_side_by_centerin(dqi_poly *p, bool& nofill) { else nofill = true; } - applymodel(h1, hscr); hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * vid.stretch; + applymodel(h1, hscr); hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * pconf.stretch; for(int i=0; iradius); h[1] *= vid.stretch; + glvertex h = glhr::pointtogl(h2 * current_display->radius); h[1] *= pconf.stretch; if(i == 0) phases[j].push_back(h); else { @@ -1458,7 +1463,7 @@ void dqi_poly::draw() { for(int i=0; iradius)); // check if the i-th edge intersects the boundary of the ellipse @@ -1474,7 +1479,7 @@ void dqi_poly::draw() { if(c1 < 0) c1 = -c1, c2 = -c2; hyperpoint h = ah1 * c1 + ah2 * c2; h /= hypot_d(3, h); - if(h[2] < 0 && abs(h[0]) < sin(vid.twopoint_param)) cpha = 1-cpha, pha = 2; + if(h[2] < 0 && abs(h[0]) < sin(pconf.twopoint_param)) cpha = 1-cpha, pha = 2; } if(cpha == 1) pha = 0; } @@ -1530,7 +1535,7 @@ void dqi_poly::draw() { last_infront = false; addpoly(V, *tab, offset, cnt); - if(!(sphere && vid.alpha < .9)) if(pmodel != mdJoukowsky) if(!(flags & POLY_ALWAYS_IN)) for(int i=1; i vid.xres * 2 || dy > vid.yres * 2) return; @@ -1556,7 +1561,7 @@ void dqi_poly::draw() { if(poly_flags & POLY_NIF_ERROR) return; - if(spherespecial == 1 && sphere && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && vid.alpha <= 1) { + if(spherespecial == 1 && sphere && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && pconf.alpha <= 1) { bool around_center = false; for(int i=0; i 0 || equi)) can_have_inverse = true; if(pmodel == mdJoukowsky) can_have_inverse = true; - if(pmodel == mdJoukowskyInverted && vid.skiprope) can_have_inverse = true; - if(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) can_have_inverse = true; - if(pmodel == mdSpiral && vid.skiprope) can_have_inverse = true; + if(pmodel == mdJoukowskyInverted && pconf.skiprope) can_have_inverse = true; + if(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) can_have_inverse = true; + if(pmodel == mdSpiral && pconf.skiprope) can_have_inverse = true; if(pmodel == mdCentralInversion) can_have_inverse = true; if(can_have_inverse && !(poly_flags & POLY_ISSIDE)) { @@ -1591,7 +1596,7 @@ void dqi_poly::draw() { } if(poly_flags & POLY_INVERSE) { - if(curradius < vid.alpha - 1e-6) return; + if(curradius < pconf.alpha - 1e-6) return; if(!sphere) return; } @@ -1620,7 +1625,7 @@ void dqi_poly::draw() { ld h = atan2(glcoords[0][0], glcoords[0][1]); for(int i=0; i<=360; i++) { ld a = i * degree + h; - glcoords.push_back(glhr::makevertex(current_display->radius * sin(a), current_display->radius * vid.stretch * cos(a), 0)); + glcoords.push_back(glhr::makevertex(current_display->radius * sin(a), current_display->radius * pconf.stretch * cos(a), 0)); } poly_flags ^= POLY_INVERSE; } @@ -1853,7 +1858,7 @@ int qp[PMAX], qp0[PMAX]; color_t darken_color(color_t& color, bool outline) { int alpha = color & 255; - if(sphere && pmodel == mdDisk && vid.alpha <= 1) + if(sphere && pmodel == mdDisk && pconf.alpha <= 1) return 0; else { if(outline && alpha < 255) @@ -1875,6 +1880,8 @@ void dqi_line::draw_back() { } EX void sort_drawqueue() { + + DEBBI(DF_GRAPH, ("sort_drawqueue")); for(int a=0; a dv (pmodel, mdHyperboloidFlat); for(auto& ptd: ptds) @@ -1964,6 +1972,7 @@ EX void reverse_transparent_walls() { } EX void draw_main() { + DEBBI(DF_GRAPH, ("draw_main")); if(sphere && GDIM == 3 && pmodel == mdPerspective) { for(int p: {1, 0, 2, 3}) { if(elliptic && p < 2) continue; @@ -2032,7 +2041,14 @@ EX hookset hooks_vr_draw_all; #endif EX void drawqueue() { - callhooks(hooks_drawqueue); + + DEBBI(DF_GRAPH, ("drawqueue")); + + #if CAP_WRL + if(wrl::in) { wrl::render(); return; } + #endif + + callhooks(hook_drawqueue); current_display->next_shader_flags = 0; reset_projection(); // reset_projection() is not sufficient here, because we need to know shaderside_projection @@ -2045,6 +2061,8 @@ EX void drawqueue() { profile_start(3); sort_drawqueue(); + + DEBB(DF_GRAPH, ("sort walls")); if(GDIM == 2) for(PPR p: {PPR::REDWALLs, PPR::REDWALLs2, PPR::REDWALLs3, PPR::WALL3s, @@ -2353,7 +2371,7 @@ EX void getcoord0(const hyperpoint& h, int& xc, int &yc, int &sc) { hyperpoint hscr; applymodel(h, hscr); xc = current_display->xcenter + current_display->radius * hscr[0]; - yc = current_display->ycenter + current_display->radius * vid.stretch * hscr[1]; + yc = current_display->ycenter + current_display->radius * pconf.stretch * hscr[1]; sc = 0; // EYETODO sc = vid.eye * current_display->radius * hscr[2]; } diff --git a/fieldpattern.cpp b/fieldpattern.cpp index 52d57b8d..b9121c6e 100644 --- a/fieldpattern.cpp +++ b/fieldpattern.cpp @@ -1,1361 +1,1361 @@ -// Hyperbolic Rogue -- Field Quotient geometry -// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details - -/** \file fieldpattern.cpp - * \brief Field Quotient geometry - */ - -#include "hyper.h" -#if CAP_FIELD -namespace hr { - -EX namespace fieldpattern { - -int limitsq = 10; -int limitp = 10000; -int limitv = 100000; - -#if HDR -#define currfp fieldpattern::getcurrfp() - -struct primeinfo { - int p; - int cells; - bool squared; - }; - -struct fgeomextra { - eGeometry base; - vector primes; - vector dualval; - int current_prime_id; - fgeomextra(eGeometry b, int i) : base(b), current_prime_id(i) {} - }; -#endif - -EX bool isprime(int n) { - for(int k=2; k, MAXMDIM> { - bool operator == (const matrix& B) const { - for(int i=0; i= Prime && tx % Prime) neasy++; - if(ty >= Prime && ty % Prime) neasy++; - int x[2], y[2], z[3]; - for(int i=0; i<3; i++) z[i] = 0; - for(int i=0; i<2; i++) - x[i] = tx%Prime, tx /= Prime; - for(int i=0; i<2; i++) - y[i] = ty%Prime, ty /= Prime; - for(int i=0; i<2; i++) - for(int j=0; j<2; j++) - z[i+j] = (z[i+j] + x[i] * y[j]) % Prime; - z[0] += z[2] * wsquare; - - return m(z[0]) + Prime * m(z[1]); - #endif - } - - int sqr(int x) { return mul(x,x); } - - matrix mmul(const matrix& A, const matrix& B) { - matrix res; - for(int i=0; i matcode; - vector matrices; - - vector qpaths; - - vector qcoords; - - // S7 in 2D, but e.g. 4 for a 3D cube - int rotations; - - // S7 in 2D, but e.g. 24 for a 3D cube - int local_group; - - // Id: Identity - // R : rotate by 1/rotations of the full circle - // P : make a step and turn backwards - // X : in 3-dim, turn by 90 degrees - - matrix Id, R, P, X; - - matrix strtomatrix(string s) { - matrix res = Id; - matrix m = Id; - for(int i=isize(s)-1; i>=0; i--) - if(s[i] == 'R') res = mmul(R, res); - else if (s[i] == 'P') res = mmul(P, res); - else if (s[i] == 'x') { m[0][0] = -1; res = mmul(m, res); m[0][0] = +1; } - else if (s[i] == 'y') { m[1][1] = -1; res = mmul(m, res); m[1][1] = +1; } - else if (s[i] == 'z') { m[2][2] = -1; res = mmul(m, res); m[2][2] = +1; } - return res; - } - - void addas(const matrix& M, int i) { - if(!matcode.count(M)) { - matcode[M] = i; - for(int j=0; j connections; - - vector inverses; // NYI in 3D - - // 2D only - vector rrf; // rrf[i] equals gmul(i, rotations-1) - vector rpf; // rpf[i] equals gmul(i, rotations) - - matrix mpow(matrix M, int N) { - while((N&1) == 0) N >>= 1, M = mmul(M, M); - matrix res = M; - N >>= 1; - while(N) { - M = mmul(M,M); if(N&1) res = mmul(res, M); - N >>= 1; - } - return res; - } - - int gmul(int a, int b) { return matcode[mmul(matrices[a], matrices[b])]; } - int gpow(int a, int N) { return matcode[mpow(matrices[a], N)]; } - - pair gmul(pair a, int b) { - return make_pair(gmul(a.first,b), a.second); - } - - int order(const matrix& M); - - string decodepath(int i) { - string s; - while(i) { - if(i % S7) i--, s += 'R'; - else i = connections[i], s += 'P'; - } - return s; - } - - int orderstats(); - - int cs, sn, ch, sh; - - int solve(); - - void build(); - - static const int MAXDIST = 120; - - vector disthep; - vector disthex; - - vector distwall, distriver, distwall2, distriverleft, distriverright, distflower; - int distflower0; - - vector markers; - - int getdist(pair a, vector& dists); - int getdist(pair a, pair b); - int dijkstra(vector& dists, vector indist[MAXDIST]); - void analyze(); - - int maxdist, otherpole, circrad, wallid, wallorder, riverid; - - bool easy(int i) { - return i < Prime || !(i % Prime); - } - - // 11 * 25 - // (1+z+z^3) * (1+z^3+z^4) == - // 1+z+z^7 == 1+z+z^2(z^5) == 1+z+z^2(1+z^2) = 1+z+z^2+z^4 - - void init(int p) { - Prime = p; - if(solve()) { - printf("error: could not solve the fieldpattern\n"); - exit(1); - } - build(); - } - - fpattern(int p) { - force_hash = 0; - #if CAP_THREAD - dis = nullptr; - #endif - if(!p) return; - init(p); - } - - void findsubpath(); - - vector generate_isometries(); - - bool check_order(matrix M, int req); - - unsigned compute_hash(); - - void set_field(int p, int sq); - - #if MAXMDIM >= 4 - // general 4D - vector fullv; - - void add1(const matrix& M); - void add1(const matrix& M, const transmatrix& Full); - vector generate_isometries3(); - int solve3(); - bool generate_all3(); - - #if CAP_THREAD - struct discovery *dis; - #endif - #endif - }; - -#if CAP_THREAD -struct discovery { - fpattern experiment; - std::shared_ptr discoverer; - std::mutex lock; - std::condition_variable cv; - bool is_suspended; - bool stop_it; - - map > hashes_found; - discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; experiment.Prime = experiment.Field = experiment.wsquare = 0; } - - void activate(); - void suspend(); - void check_suspend(); - void schedule_destruction(); - void discovered(); - ~discovery(); - }; -#endif - -#endif - -bool fpattern::check_order(matrix M, int req) { - matrix P = M; - for(int i=1; i fpattern::generate_isometries() { - matrix T = Id; - int low = wsquare ? 1-Prime : 0; - vector res; - - auto colprod = [&] (int a, int b) { - return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), mul(T[2][a], T[2][b])); - }; - - for(T[0][0]=low; T[0][0]= 4 -vector fpattern::generate_isometries3() { - - matrix T = Id; - int low = wsquare ? 1-Prime : 0; - vector res; - - auto colprod = [&] (int a, int b) { - return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), sub(mul(T[2][a], T[2][b]), mul(T[3][a], T[3][b]))); - }; - - auto rowcol = [&] (int a, int b) { - return add(add(mul(T[a][0], T[0][b]), mul(T[a][1], T[1][b])), add(mul(T[a][2], T[2][b]), mul(T[a][3], T[3][b]))); - }; - - for(T[0][0]=low; T[0][0]check_suspend(); - if(dis && dis->stop_it) return res; - #endif - - for(T[0][2]=low; T[0][2] limitp) return res; - } - - return res; - } - -void fpattern::add1(const matrix& M) { - if(!matcode.count(M)) { - int i = isize(matrices); - matcode[M] = i, matrices.push_back(M); - } - } - -void fpattern::add1(const matrix& M, const transmatrix& Full) { - if(!matcode.count(M)) { - int i = isize(matrices); - matcode[M] = i, matrices.push_back(M), fullv.push_back(Full); - } - } -#endif - -map hash_found; - -unsigned fpattern::compute_hash() { - unsigned hashv = 0; - int iR = matcode[R]; - int iP = matcode[P]; - int iX = matcode[X]; - for(int i=0; i= 4 -bool fpattern::generate_all3() { - - reg3::generate_fulls(); - - matrices.clear(); - matcode.clear(); - add1(Id); - fullv = {hr::Id}; - for(int i=0; i= limitv) { println(hlog, "limitv exceeded"); return false; } - } - unsigned hashv = compute_hash(); - DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv])); - return true; - } - -int fpattern::solve3() { - reg3::construct_relations(); - - DEBB(DF_FIELD, ("generating isometries for ", Field)); - - auto iso3 = generate_isometries(); - auto iso4 = generate_isometries3(); - - int cmb = 0; - - int N = isize(cgi.rels); - - vector fails(N); - - vector possible_P, possible_X, possible_R; - - for(auto& M: iso3) { - if(check_order(M, 2)) - possible_X.push_back(M); - if(check_order(M, cgi.r_order)) - possible_R.push_back(M); - } - for(auto& M: iso4) - if(check_order(M, 2)) - possible_P.push_back(M); - - DEBB(DF_FIELD, ("field = ", Field, " #P = ", isize(possible_P), " #X = ", isize(possible_X), " #R = ", isize(possible_R), " r_order = ", cgi.r_order, " xp_order = ", cgi.xp_order)); - - for(auto& xX: possible_X) - for(auto& xP: possible_P) if(check_order(mmul(xP, xX), cgi.xp_order)) - for(auto& xR: possible_R) if(check_order(mmul(xR, xX), cgi.rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0) - #if CAP_THREAD - if(dis) dis->check_suspend(); - if(dis && dis->stop_it) return 0; - #endif - auto by = [&] (char ch) -> matrix& { return ch == 'X' ? xX : ch == 'R' ? xR : xP; }; - for(int i=0; idiscovered(); continue; } - #endif - if(force_hash && compute_hash() != force_hash) continue; - cmb++; - goto ok; - bad: ; - } - - ok: - - DEBB(DF_FIELD, ("cmb = ", cmb, " for field = ", Field)); - for(int i=0; i a, vector& dists) { - if(!a.second) return dists[a.first]; - int m = MAXDIST; - int ma = dists[a.first]; - int mb = dists[connections[btspin(a.first, 3)]]; - int mc = dists[connections[btspin(a.first, 4)]]; - m = min(m, 1 + ma); - m = min(m, 1 + mb); - m = min(m, 1 + mc); - if(m <= 2 && ma+mb+mc <= m*3-2) return m-1; // special case - m = min(m, 2 + dists[connections[btspin(a.first, 2)]]); - m = min(m, 2 + dists[connections[btspin(a.first, 5)]]); - m = min(m, 2 + dists[connections[btspin(connections[btspin(a.first, 3)], 5)]]); - return m; - } - -int fpattern::getdist(pair a, pair b) { - if(a.first == b.first) return a.second == b.second ? 0 : 1; - if(b.first) a.first = gmul(a.first, inverses[b.first]), b.first = 0; - return getdist(a, b.second ? disthex : disthep); - } - -int fpattern::dijkstra(vector& dists, vector indist[MAXDIST]) { - int N = isize(matrices); - dists.resize(N); - for(int i=0; i indist[MAXDIST]; - - indist[0].push_back(0); - int md0 = dijkstra(disthep, indist); - - if(MWDIM == 4) return; - - indist[1].push_back(0); - indist[1].push_back(connections[3]); - indist[1].push_back(connections[4]); - indist[2].push_back(connections[btspin(connections[3], 5)]); - indist[2].push_back(connections[2]); - indist[2].push_back(connections[5]); - int md1 = dijkstra(disthex, indist); - - maxdist = max(md0, md1); - - otherpole = 0; - - for(int i=0; i disthep[otherpole]) otherpole = i; - // for(int r=0; r fgeomextras = { - fgeomextra(gNormal, 4), - fgeomextra(gOctagon, 1), - fgeomextra(g45, 1), - fgeomextra(g46, 5), - fgeomextra(g47, 1), - fgeomextra(gSchmutzM3, 0), -/* fgeomextra(gSphere, 0), - fgeomextra(gSmallSphere, 0), -> does not find the prime - fgeomextra(gEuclid, 0), - fgeomextra(gEuclidSquare, 0), - fgeomextra(gTinySphere, 0) */ - }; - -EX int current_extra = 0; - -EX void nextPrime(fgeomextra& ex) { - dynamicval g(geometry, ex.base); - int nextprime; - if(isize(ex.primes)) - nextprime = ex.primes.back().p + 1; - else - nextprime = 2; - while(true) { - fieldpattern::fpattern fp(0); - fp.Prime = nextprime; - if(fp.solve() == 0) { - fp.build(); - int cells = isize(fp.matrices) / S7; - ex.primes.emplace_back(primeinfo{nextprime, cells, (bool) fp.wsquare}); - ex.dualval.emplace_back(fp.dual); - break; - } - nextprime++; - } - } - -EX void nextPrimes(fgeomextra& ex) { - while(isize(ex.primes) < 6) - nextPrime(ex); - } - -EX void enableFieldChange() { - fgeomextra& gxcur = fgeomextras[current_extra]; - fieldpattern::quotient_field_changed = true; - nextPrimes(gxcur); - dynamicval g(geometry, gFieldQuotient); - ginf[geometry].sides = ginf[gxcur.base].sides; - ginf[geometry].vertex = ginf[gxcur.base].vertex; - ginf[geometry].distlimit = ginf[gxcur.base].distlimit; - ginf[geometry].tiling_name = ginf[gxcur.base].tiling_name; - fieldpattern::current_quotient_field.init(gxcur.primes[gxcur.current_prime_id].p); - } - -EX void field_from_current() { - auto& go = ginf[geometry]; - dynamicval g(geometry, gFieldQuotient); - auto& gg = ginf[geometry]; - gg.sides = go.sides; - gg.vertex = go.vertex; - gg.distlimit = go.distlimit; - gg.tiling_name = go.tiling_name; - gg.flags = go.flags | qANYQ | qFIELD | qBOUNDED; - gg.g = go.g; - gg.default_variation = go.default_variation; - fieldpattern::quotient_field_changed = true; - } - -#if CAP_THREAD -EX map discoveries; - -void discovery::activate() { - if(!discoverer) { - discoverer = std::make_shared ( [this] { - for(int p=2; p<100; p++) { - experiment.Prime = p; - experiment.solve(); - if(stop_it) break; - } - }); - } - if(is_suspended) { - if(1) { - std::unique_lock lk(lock); - is_suspended = false; - } - cv.notify_one(); - } - } - -void discovery::discovered() { - std::unique_lock lk(lock); - auto& e = experiment; - hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); - } - -void discovery::suspend() { is_suspended = true; } - -void discovery::check_suspend() { - std::unique_lock lk(lock); - if(is_suspended) cv.wait(lk, [this] { return !is_suspended; }); - } - -void discovery::schedule_destruction() { stop_it = true; } -discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } -#endif - -int hk = -#if CAP_THREAD - + addHook(hooks_on_geometry_change, 100, [] { for(auto& d:discoveries) if(!d.second.is_suspended) d.second.suspend(); }) - + addHook(hooks_final_cleanup, 100, [] { - for(auto& d:discoveries) { d.second.schedule_destruction(); if(d.second.is_suspended) d.second.activate(); } - discoveries.clear(); - }) -#endif -#if CAP_COMMANDLINE - + addHook(hooks_args, 0, [] { - using namespace arg; - if(0) ; - else if(argis("-q3-limitsq")) { shift(); limitsq = argi(); } - else if(argis("-q3-limitp")) { shift(); limitp = argi(); } - else if(argis("-q3-limitv")) { shift(); limitv = argi(); } - else return 1; - return 0; - }) -#endif - + 0; - -EX purehookset hooks_on_geometry_change; - -EX int field_celldistance(cell *c1, cell *c2) { - if(geometry != gFieldQuotient) return DISTANCE_UNKNOWN; - if(GOLDBERG) return DISTANCE_UNKNOWN; - auto v1 =fieldpattern::fieldval(c1); - auto v2 =fieldpattern::fieldval(c2); - int d = currfp.getdist(v1, v2); - return d; - } - -EX } - -#define currfp fieldpattern::getcurrfp() - -EX int currfp_gmul(int a, int b) { return currfp.gmul(a,b); } -EX int currfp_inverses(int i) { return currfp.inverses[i]; } -EX int currfp_distwall(int i) { return currfp.distwall[i]; } -EX int currfp_n() { return isize(currfp.matrices); } -EX int currfp_get_R() { return currfp.matcode[currfp.R]; } -EX int currfp_get_P() { return currfp.matcode[currfp.P]; } -EX int currfp_get_X() { return currfp.matcode[currfp.X]; } - -EX void hread_fpattern(hstream& hs, fieldpattern::fpattern& fp) { - hread(hs, fp.Prime); - hread(hs, fp.wsquare); - hread(hs, fp.P); - hread(hs, fp.R); - hread(hs, fp.X); - fp.set_field(fp.Prime, fp.wsquare); - #if MAXMDIM >= 4 - fp.generate_all3(); - #endif - } - -EX void hwrite_fpattern(hstream& hs, fieldpattern::fpattern& fp) { - hwrite(hs, fp.Prime); - hwrite(hs, fp.wsquare); - hwrite(hs, fp.P); - hwrite(hs, fp.R); - hwrite(hs, fp.X); - } - -} -#endif +// Hyperbolic Rogue -- Field Quotient geometry +// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details + +/** \file fieldpattern.cpp + * \brief Field Quotient geometry + */ + +#include "hyper.h" +#if CAP_FIELD +namespace hr { + +EX namespace fieldpattern { + +int limitsq = 10; +int limitp = 10000; +int limitv = 100000; + +#if HDR +#define currfp fieldpattern::getcurrfp() + +struct primeinfo { + int p; + int cells; + bool squared; + }; + +struct fgeomextra { + eGeometry base; + vector primes; + vector dualval; + int current_prime_id; + fgeomextra(eGeometry b, int i) : base(b), current_prime_id(i) {} + }; +#endif + +EX bool isprime(int n) { + for(int k=2; k, MAXMDIM> { + bool operator == (const matrix& B) const { + for(int i=0; i= Prime && tx % Prime) neasy++; + if(ty >= Prime && ty % Prime) neasy++; + int x[2], y[2], z[3]; + for(int i=0; i<3; i++) z[i] = 0; + for(int i=0; i<2; i++) + x[i] = tx%Prime, tx /= Prime; + for(int i=0; i<2; i++) + y[i] = ty%Prime, ty /= Prime; + for(int i=0; i<2; i++) + for(int j=0; j<2; j++) + z[i+j] = (z[i+j] + x[i] * y[j]) % Prime; + z[0] += z[2] * wsquare; + + return m(z[0]) + Prime * m(z[1]); + #endif + } + + int sqr(int x) { return mul(x,x); } + + matrix mmul(const matrix& A, const matrix& B) { + matrix res; + for(int i=0; i matcode; + vector matrices; + + vector qpaths; + + vector qcoords; + + // S7 in 2D, but e.g. 4 for a 3D cube + int rotations; + + // S7 in 2D, but e.g. 24 for a 3D cube + int local_group; + + // Id: Identity + // R : rotate by 1/rotations of the full circle + // P : make a step and turn backwards + // X : in 3-dim, turn by 90 degrees + + matrix Id, R, P, X; + + matrix strtomatrix(string s) { + matrix res = Id; + matrix m = Id; + for(int i=isize(s)-1; i>=0; i--) + if(s[i] == 'R') res = mmul(R, res); + else if (s[i] == 'P') res = mmul(P, res); + else if (s[i] == 'x') { m[0][0] = -1; res = mmul(m, res); m[0][0] = +1; } + else if (s[i] == 'y') { m[1][1] = -1; res = mmul(m, res); m[1][1] = +1; } + else if (s[i] == 'z') { m[2][2] = -1; res = mmul(m, res); m[2][2] = +1; } + return res; + } + + void addas(const matrix& M, int i) { + if(!matcode.count(M)) { + matcode[M] = i; + for(int j=0; j connections; + + vector inverses; // NYI in 3D + + // 2D only + vector rrf; // rrf[i] equals gmul(i, rotations-1) + vector rpf; // rpf[i] equals gmul(i, rotations) + + matrix mpow(matrix M, int N) { + while((N&1) == 0) N >>= 1, M = mmul(M, M); + matrix res = M; + N >>= 1; + while(N) { + M = mmul(M,M); if(N&1) res = mmul(res, M); + N >>= 1; + } + return res; + } + + int gmul(int a, int b) { return matcode[mmul(matrices[a], matrices[b])]; } + int gpow(int a, int N) { return matcode[mpow(matrices[a], N)]; } + + pair gmul(pair a, int b) { + return make_pair(gmul(a.first,b), a.second); + } + + int order(const matrix& M); + + string decodepath(int i) { + string s; + while(i) { + if(i % S7) i--, s += 'R'; + else i = connections[i], s += 'P'; + } + return s; + } + + int orderstats(); + + int cs, sn, ch, sh; + + int solve(); + + void build(); + + static const int MAXDIST = 120; + + vector disthep; + vector disthex; + + vector distwall, distriver, distwall2, distriverleft, distriverright, distflower; + int distflower0; + + vector markers; + + int getdist(pair a, vector& dists); + int getdist(pair a, pair b); + int dijkstra(vector& dists, vector indist[MAXDIST]); + void analyze(); + + int maxdist, otherpole, circrad, wallid, wallorder, riverid; + + bool easy(int i) { + return i < Prime || !(i % Prime); + } + + // 11 * 25 + // (1+z+z^3) * (1+z^3+z^4) == + // 1+z+z^7 == 1+z+z^2(z^5) == 1+z+z^2(1+z^2) = 1+z+z^2+z^4 + + void init(int p) { + Prime = p; + if(solve()) { + printf("error: could not solve the fieldpattern\n"); + exit(1); + } + build(); + } + + fpattern(int p) { + force_hash = 0; + #if CAP_THREAD && MAXMDIM >= 4 + dis = nullptr; + #endif + if(!p) return; + init(p); + } + + void findsubpath(); + + vector generate_isometries(); + + bool check_order(matrix M, int req); + + unsigned compute_hash(); + + void set_field(int p, int sq); + + #if MAXMDIM >= 4 + // general 4D + vector fullv; + + void add1(const matrix& M); + void add1(const matrix& M, const transmatrix& Full); + vector generate_isometries3(); + int solve3(); + bool generate_all3(); + + #if CAP_THREAD + struct discovery *dis; + #endif + #endif + }; + +#if CAP_THREAD && MAXMDIM >= 4 +struct discovery { + fpattern experiment; + std::shared_ptr discoverer; + std::mutex lock; + std::condition_variable cv; + bool is_suspended; + bool stop_it; + + map > hashes_found; + discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; experiment.Prime = experiment.Field = experiment.wsquare = 0; } + + void activate(); + void suspend(); + void check_suspend(); + void schedule_destruction(); + void discovered(); + ~discovery(); + }; +#endif + +#endif + +bool fpattern::check_order(matrix M, int req) { + matrix P = M; + for(int i=1; i fpattern::generate_isometries() { + matrix T = Id; + int low = wsquare ? 1-Prime : 0; + vector res; + + auto colprod = [&] (int a, int b) { + return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), mul(T[2][a], T[2][b])); + }; + + for(T[0][0]=low; T[0][0]= 4 +vector fpattern::generate_isometries3() { + + matrix T = Id; + int low = wsquare ? 1-Prime : 0; + vector res; + + auto colprod = [&] (int a, int b) { + return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), sub(mul(T[2][a], T[2][b]), mul(T[3][a], T[3][b]))); + }; + + auto rowcol = [&] (int a, int b) { + return add(add(mul(T[a][0], T[0][b]), mul(T[a][1], T[1][b])), add(mul(T[a][2], T[2][b]), mul(T[a][3], T[3][b]))); + }; + + for(T[0][0]=low; T[0][0]= 4 + if(dis) dis->check_suspend(); + if(dis && dis->stop_it) return res; + #endif + + for(T[0][2]=low; T[0][2] limitp) return res; + } + + return res; + } + +void fpattern::add1(const matrix& M) { + if(!matcode.count(M)) { + int i = isize(matrices); + matcode[M] = i, matrices.push_back(M); + } + } + +void fpattern::add1(const matrix& M, const transmatrix& Full) { + if(!matcode.count(M)) { + int i = isize(matrices); + matcode[M] = i, matrices.push_back(M), fullv.push_back(Full); + } + } +#endif + +map hash_found; + +unsigned fpattern::compute_hash() { + unsigned hashv = 0; + int iR = matcode[R]; + int iP = matcode[P]; + int iX = matcode[X]; + for(int i=0; i= 4 +bool fpattern::generate_all3() { + + reg3::generate_fulls(); + + matrices.clear(); + matcode.clear(); + add1(Id); + fullv = {hr::Id}; + for(int i=0; i= limitv) { println(hlog, "limitv exceeded"); return false; } + } + unsigned hashv = compute_hash(); + DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv])); + return true; + } + +int fpattern::solve3() { + reg3::construct_relations(); + + DEBB(DF_FIELD, ("generating isometries for ", Field)); + + auto iso3 = generate_isometries(); + auto iso4 = generate_isometries3(); + + int cmb = 0; + + int N = isize(cgi.rels); + + vector fails(N); + + vector possible_P, possible_X, possible_R; + + for(auto& M: iso3) { + if(check_order(M, 2)) + possible_X.push_back(M); + if(check_order(M, cgi.r_order)) + possible_R.push_back(M); + } + for(auto& M: iso4) + if(check_order(M, 2)) + possible_P.push_back(M); + + DEBB(DF_FIELD, ("field = ", Field, " #P = ", isize(possible_P), " #X = ", isize(possible_X), " #R = ", isize(possible_R), " r_order = ", cgi.r_order, " xp_order = ", cgi.xp_order)); + + for(auto& xX: possible_X) + for(auto& xP: possible_P) if(check_order(mmul(xP, xX), cgi.xp_order)) + for(auto& xR: possible_R) if(check_order(mmul(xR, xX), cgi.rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0) + #if CAP_THREAD && MAXMDIM >+ 4 + if(dis) dis->check_suspend(); + if(dis && dis->stop_it) return 0; + #endif + auto by = [&] (char ch) -> matrix& { return ch == 'X' ? xX : ch == 'R' ? xR : xP; }; + for(int i=0; i= 4 + if(dis) { dis->discovered(); continue; } + #endif + if(force_hash && compute_hash() != force_hash) continue; + cmb++; + goto ok; + bad: ; + } + + ok: + + DEBB(DF_FIELD, ("cmb = ", cmb, " for field = ", Field)); + for(int i=0; i a, vector& dists) { + if(!a.second) return dists[a.first]; + int m = MAXDIST; + int ma = dists[a.first]; + int mb = dists[connections[btspin(a.first, 3)]]; + int mc = dists[connections[btspin(a.first, 4)]]; + m = min(m, 1 + ma); + m = min(m, 1 + mb); + m = min(m, 1 + mc); + if(m <= 2 && ma+mb+mc <= m*3-2) return m-1; // special case + m = min(m, 2 + dists[connections[btspin(a.first, 2)]]); + m = min(m, 2 + dists[connections[btspin(a.first, 5)]]); + m = min(m, 2 + dists[connections[btspin(connections[btspin(a.first, 3)], 5)]]); + return m; + } + +int fpattern::getdist(pair a, pair b) { + if(a.first == b.first) return a.second == b.second ? 0 : 1; + if(b.first) a.first = gmul(a.first, inverses[b.first]), b.first = 0; + return getdist(a, b.second ? disthex : disthep); + } + +int fpattern::dijkstra(vector& dists, vector indist[MAXDIST]) { + int N = isize(matrices); + dists.resize(N); + for(int i=0; i indist[MAXDIST]; + + indist[0].push_back(0); + int md0 = dijkstra(disthep, indist); + + if(MWDIM == 4) return; + + indist[1].push_back(0); + indist[1].push_back(connections[3]); + indist[1].push_back(connections[4]); + indist[2].push_back(connections[btspin(connections[3], 5)]); + indist[2].push_back(connections[2]); + indist[2].push_back(connections[5]); + int md1 = dijkstra(disthex, indist); + + maxdist = max(md0, md1); + + otherpole = 0; + + for(int i=0; i disthep[otherpole]) otherpole = i; + // for(int r=0; r fgeomextras = { + fgeomextra(gNormal, 4), + fgeomextra(gOctagon, 1), + fgeomextra(g45, 1), + fgeomextra(g46, 5), + fgeomextra(g47, 1), + fgeomextra(gSchmutzM3, 0), +/* fgeomextra(gSphere, 0), + fgeomextra(gSmallSphere, 0), -> does not find the prime + fgeomextra(gEuclid, 0), + fgeomextra(gEuclidSquare, 0), + fgeomextra(gTinySphere, 0) */ + }; + +EX int current_extra = 0; + +EX void nextPrime(fgeomextra& ex) { + dynamicval g(geometry, ex.base); + int nextprime; + if(isize(ex.primes)) + nextprime = ex.primes.back().p + 1; + else + nextprime = 2; + while(true) { + fieldpattern::fpattern fp(0); + fp.Prime = nextprime; + if(fp.solve() == 0) { + fp.build(); + int cells = isize(fp.matrices) / S7; + ex.primes.emplace_back(primeinfo{nextprime, cells, (bool) fp.wsquare}); + ex.dualval.emplace_back(fp.dual); + break; + } + nextprime++; + } + } + +EX void nextPrimes(fgeomextra& ex) { + while(isize(ex.primes) < 6) + nextPrime(ex); + } + +EX void enableFieldChange() { + fgeomextra& gxcur = fgeomextras[current_extra]; + fieldpattern::quotient_field_changed = true; + nextPrimes(gxcur); + dynamicval g(geometry, gFieldQuotient); + ginf[geometry].sides = ginf[gxcur.base].sides; + ginf[geometry].vertex = ginf[gxcur.base].vertex; + ginf[geometry].distlimit = ginf[gxcur.base].distlimit; + ginf[geometry].tiling_name = ginf[gxcur.base].tiling_name; + fieldpattern::current_quotient_field.init(gxcur.primes[gxcur.current_prime_id].p); + } + +EX void field_from_current() { + auto& go = ginf[geometry]; + dynamicval g(geometry, gFieldQuotient); + auto& gg = ginf[geometry]; + gg.sides = go.sides; + gg.vertex = go.vertex; + gg.distlimit = go.distlimit; + gg.tiling_name = go.tiling_name; + gg.flags = go.flags | qANYQ | qFIELD | qBOUNDED; + gg.g = go.g; + gg.default_variation = go.default_variation; + fieldpattern::quotient_field_changed = true; + } + +#if CAP_THREAD && MAXMDIM >= 4 +EX map discoveries; + +void discovery::activate() { + if(!discoverer) { + discoverer = std::make_shared ( [this] { + for(int p=2; p<100; p++) { + experiment.Prime = p; + experiment.solve(); + if(stop_it) break; + } + }); + } + if(is_suspended) { + if(1) { + std::unique_lock lk(lock); + is_suspended = false; + } + cv.notify_one(); + } + } + +void discovery::discovered() { + std::unique_lock lk(lock); + auto& e = experiment; + hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); + } + +void discovery::suspend() { is_suspended = true; } + +void discovery::check_suspend() { + std::unique_lock lk(lock); + if(is_suspended) cv.wait(lk, [this] { return !is_suspended; }); + } + +void discovery::schedule_destruction() { stop_it = true; } +discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } +#endif + +int hk = +#if CAP_THREAD + + addHook(hooks_on_geometry_change, 100, [] { for(auto& d:discoveries) if(!d.second.is_suspended) d.second.suspend(); }) + + addHook(hooks_final_cleanup, 100, [] { + for(auto& d:discoveries) { d.second.schedule_destruction(); if(d.second.is_suspended) d.second.activate(); } + discoveries.clear(); + }) +#endif +#if CAP_COMMANDLINE + + addHook(hooks_args, 0, [] { + using namespace arg; + if(0) ; + else if(argis("-q3-limitsq")) { shift(); limitsq = argi(); } + else if(argis("-q3-limitp")) { shift(); limitp = argi(); } + else if(argis("-q3-limitv")) { shift(); limitv = argi(); } + else return 1; + return 0; + }) +#endif + + 0; + +EX purehookset hooks_on_geometry_change; + +EX int field_celldistance(cell *c1, cell *c2) { + if(geometry != gFieldQuotient) return DISTANCE_UNKNOWN; + if(GOLDBERG) return DISTANCE_UNKNOWN; + auto v1 =fieldpattern::fieldval(c1); + auto v2 =fieldpattern::fieldval(c2); + int d = currfp.getdist(v1, v2); + return d; + } + +EX } + +#define currfp fieldpattern::getcurrfp() + +EX int currfp_gmul(int a, int b) { return currfp.gmul(a,b); } +EX int currfp_inverses(int i) { return currfp.inverses[i]; } +EX int currfp_distwall(int i) { return currfp.distwall[i]; } +EX int currfp_n() { return isize(currfp.matrices); } +EX int currfp_get_R() { return currfp.matcode[currfp.R]; } +EX int currfp_get_P() { return currfp.matcode[currfp.P]; } +EX int currfp_get_X() { return currfp.matcode[currfp.X]; } + +EX void hread_fpattern(hstream& hs, fieldpattern::fpattern& fp) { + hread(hs, fp.Prime); + hread(hs, fp.wsquare); + hread(hs, fp.P); + hread(hs, fp.R); + hread(hs, fp.X); + fp.set_field(fp.Prime, fp.wsquare); + #if MAXMDIM >= 4 + fp.generate_all3(); + #endif + } + +EX void hwrite_fpattern(hstream& hs, fieldpattern::fpattern& fp) { + hwrite(hs, fp.Prime); + hwrite(hs, fp.wsquare); + hwrite(hs, fp.P); + hwrite(hs, fp.R); + hwrite(hs, fp.X); + } + +} +#endif diff --git a/floorshapes.cpp b/floorshapes.cpp index 1dabc4f7..9a6d6b41 100644 --- a/floorshapes.cpp +++ b/floorshapes.cpp @@ -1011,6 +1011,8 @@ auto floor_hook = #if MAXMDIM >= 4 +EX ld floor_texture_square_size; + void draw_shape_for_texture(floorshape* sh) { int id = sh->id; @@ -1064,23 +1066,30 @@ void draw_shape_for_texture(floorshape* sh) { hyperpoint inmodel; applymodel(center, inmodel); glvertex tmap; - tmap[0] = (1 + inmodel[0] * vid.scale) / 2; - tmap[1] = (1 - inmodel[1] * vid.scale) / 2; + tmap[0] = (1 + inmodel[0] * pconf.scale) / 2; + tmap[1] = (1 - inmodel[1] * pconf.scale) / 2; applymodel(center + v1, inmodel); - tmap[2] = (1 + inmodel[0] * vid.scale) / 2 - tmap[0]; + tmap[2] = (1 + inmodel[0] * pconf.scale) / 2 - tmap[0]; floor_texture_map[sh->id] = tmap; } - // SL2 needs 6 times more - texture_order([&] (ld x, ld y) { + auto tvec_at = [&] (ld x, ld y) { hyperpoint h = center + v1 * x + v2 * y; hyperpoint inmodel; applymodel(h, inmodel); glvec2 v; - v[0] = (1 + inmodel[0] * vid.scale) / 2; - v[1] = (1 - inmodel[1] * vid.scale) / 2; + v[0] = (1 + inmodel[0] * pconf.scale) / 2; + v[1] = (1 - inmodel[1] * pconf.scale) / 2; + return v; + }; + + // SL2 needs 6 times more + texture_order([&] (ld x, ld y) { + auto v = tvec_at(x, y); ftv.tvertices.push_back(glhr::makevertex(v[0], v[1], 0)); }); + + floor_texture_square_size = 2 * (tvec_at(1, 0)[0] - tvec_at(0, 0)[0]); } /** copy the texture vertices so that there are at least qty of them */ @@ -1102,7 +1111,9 @@ EX void bind_floor_texture(hpcshape& li, int id) { ensure_vertex_number(li); } +#if HDR const int FLOORTEXTURESIZE = 4096; +#endif void geometry_information::make_floor_textures_here() { require_shapes(); @@ -1110,9 +1121,9 @@ void geometry_information::make_floor_textures_here() { dynamicval vi(vid, vid); vid.xres = FLOORTEXTURESIZE; vid.yres = FLOORTEXTURESIZE; - vid.scale = 0.125; - vid.camera_angle = 0; - vid.alpha = 1; + pconf.scale = 0.125; + pconf.camera_angle = 0; + pconf.alpha = 1; dynamicval lw(vid.linewidth, 2); floor_textures = new renderbuffer(vid.xres, vid.yres, vid.usingGL); @@ -1127,7 +1138,7 @@ void geometry_information::make_floor_textures_here() { cd->xsize = cd->ysize = FLOORTEXTURESIZE; cd->xcenter = cd->ycenter = cd->scrsize = FLOORTEXTURESIZE/2; - cd->radius = cd->scrsize * vid.scale; + cd->radius = cd->scrsize * pconf.scale; floor_textures->enable(); floor_textures->clear(0); // 0xE8E8E8 = 1 diff --git a/geom-exp.cpp b/geom-exp.cpp index 2f05bd7d..01ed6dea 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -381,21 +381,21 @@ void ge_select_tiling() { EX string current_proj_name() { bool h = hyperbolic || sn::in(); - if(pmodel != mdDisk) - return models::get_model_name(pmodel); - else if(h && vid.alpha == 1) + if(vpconf.model != mdDisk) + return models::get_model_name(vpconf.model); + else if(h && vpconf.alpha == 1) return XLAT("Poincaré model"); - else if(h && vid.alpha == 0) + else if(h && vpconf.alpha == 0) return XLAT("Klein-Beltrami model"); - else if(h && vid.alpha == -1) + else if(h && vpconf.alpha == -1) return XLAT("inverted Poincaré model"); - else if(sphere && vid.alpha == 1) + else if(sphere && vpconf.alpha == 1) return XLAT("stereographic projection"); - else if(sphere && vid.alpha == 0) + else if(sphere && vpconf.alpha == 0) return XLAT("gnomonic projection"); - else if(sphere && vid.alpha >= 999) + else if(sphere && vpconf.alpha >= 999) return XLAT("orthographic projection"); - else if(h && vid.alpha >= 999) + else if(h && vpconf.alpha >= 999) return XLAT("Gans model"); else return XLAT("general perspective"); @@ -405,7 +405,7 @@ EX string dim_name() { return " (" + its(WDIM) + "D)"; } -#if CAP_THREAD +#if CAP_THREAD && MAXMDIM >= 4 EX void showQuotientConfig3() { using namespace fieldpattern; @@ -528,7 +528,7 @@ EX void select_quotient_screen() { println(hlog, "set prime = ", currfp.Prime); start_game(); } - #if CAP_THREAD + #if CAP_THREAD && MAXMDIM >= 4 pushScreen(showQuotientConfig3); #endif } @@ -940,7 +940,7 @@ EX void runGeometryExperiments() { #if CAP_COMMANDLINE -eGeometry readGeo(const string& ss) { +EX eGeometry readGeo(const string& ss) { for(int i=0; i '9') numeric = false; diff --git a/geometry.cpp b/geometry.cpp index 3fe5e183..81262f12 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -710,24 +710,24 @@ EX namespace geom3 { void geometry_information::prepare_compute3() { using namespace geom3; DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute")); - // tanh(depth) / tanh(camera) == vid.alpha + // tanh(depth) / tanh(camera) == pconf.alpha invalid = ""; if(GDIM == 3) ; else if(vid.tc_alpha < vid.tc_depth && vid.tc_alpha < vid.tc_camera) - vid.alpha = tan_auto(vid.depth) / tan_auto(vid.camera); + pconf.alpha = tan_auto(vid.depth) / tan_auto(vid.camera); else if(vid.tc_depth < vid.tc_alpha && vid.tc_depth < vid.tc_camera) { - ld v = vid.alpha * tan_auto(vid.camera); + ld v = pconf.alpha * tan_auto(vid.camera); if(hyperbolic && (v<1e-6-12 || v>1-1e-12)) invalid = "cannot adjust depth", vid.depth = vid.camera; else vid.depth = atan_auto(v); } else { - ld v = tan_auto(vid.depth) / vid.alpha; + ld v = tan_auto(vid.depth) / pconf.alpha; if(hyperbolic && (v<1e-12-1 || v>1-1e-12)) invalid = "cannot adjust camera", vid.camera = vid.depth; else vid.camera = atan_auto(v); } - if(fabs(vid.alpha) < 1e-6) invalid = "does not work with perfect Klein"; + if(fabs(pconf.alpha) < 1e-6) invalid = "does not work with perfect Klein"; if(invalid != "") { INFDEEP = .7; @@ -851,20 +851,20 @@ EX void switch_always3() { EX void switch_tpp() { if(dual::split(switch_fpp)) return; - if(pmodel == mdDisk && vid.camera_angle) { + if(pmodel == mdDisk && pconf.camera_angle) { vid.yshift = 0; - vid.camera_angle = 0; - vid.xposition = 0; - vid.yposition = 0; - vid.scale = 1; + pconf.camera_angle = 0; + pconf.xposition = 0; + pconf.yposition = 0; + pconf.scale = 1; vid.fixed_facing = false; } else { vid.yshift = -0.3; - vid.camera_angle = -45; - vid.scale = 18/16. * vid.xres / vid.yres / multi::players; - vid.xposition = 0; - vid.yposition = -0.9; + pconf.camera_angle = -45; + pconf.scale = 18/16. * vid.xres / vid.yres / multi::players; + pconf.xposition = 0; + pconf.yposition = -0.9; vid.fixed_facing = true; vid.fixed_facing_dir = 90; } diff --git a/graph.cpp b/graph.cpp index f61b9877..20c10d2a 100644 --- a/graph.cpp +++ b/graph.cpp @@ -1390,7 +1390,7 @@ EX bool drawMonsterType(eMonster m, cell *where, const transmatrix& V1, color_t } else { queuepoly(VHEAD1, cgi.shPHead, 0xF0A0D0FF); - queuepoly(VBS, cgi.shFlowerHand, 0xC00000FF); + queuepoly(VBODY * VBS, cgi.shFlowerHand, 0xC00000FF); queuepoly(VBODY2 * VBS, cgi.shSuspenders, 0xC00000FF); } } @@ -2858,10 +2858,10 @@ int haveaura_cached; EX int haveaura() { if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return 0; if(sphere && mdAzimuthalEqui()) return 0; - if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && models::model_transition < 1) + if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && pconf.model_transition < 1) return 2; if(pmodel == mdFisheye) return 1; - return pmodel == mdDisk && (!sphere || vid.alpha > 10) && !euclid; + return pmodel == mdDisk && (!sphere || pconf.alpha > 10) && !euclid; } vector > auraspecials; @@ -2884,7 +2884,7 @@ void apply_joukowsky_aura(hyperpoint& h) { h = ret; } if(nonisotropic) { - h = lp_apply(inverse_exp(h, iTable, true)); + h = lp_apply(inverse_exp(h, pfNO_DISTANCE)); } } @@ -2927,13 +2927,14 @@ void sumaura(int v) { vector auravertices; void drawaura() { + DEBBI(DF_GRAPH, ("draw aura")); if(!haveaura()) return; if(vid.stereo_mode) return; double rad = current_display->radius; - if(sphere && !mdAzimuthalEqui()) rad /= sqrt(vid.alpha*vid.alpha - 1); + if(sphere && !mdAzimuthalEqui()) rad /= sqrt(pconf.alpha*pconf.alpha - 1); if(hyperbolic && pmodel == mdFisheye) { ld h = 1; - h /= vid.fisheye_param; + h /= pconf.fisheye_param; ld nrad = h / sqrt(2 + h*h); rad *= nrad; } @@ -2959,9 +2960,9 @@ void drawaura() { for(int x=0; xxcenter) / rad; - ld hy = (y * 1. - current_display->ycenter) / rad / vid.stretch; + ld hy = (y * 1. - current_display->ycenter) / rad / pconf.stretch; - if(vid.camera_angle) camrotate(hx, hy); + if(pconf.camera_angle) camrotate(hx, hy); ld fac = sqrt(hx*hx+hy*hy); if(fac < 1) continue; @@ -3007,8 +3008,8 @@ void drawaura() { facs[10] = 10; cmul[1] = cmul[0]; - bool inversion = vid.alpha <= -1 || pmodel == mdJoukowsky; - bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && models::model_transition < 1; + bool inversion = pconf.alpha <= -1 || pmodel == mdJoukowsky; + bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && pconf.model_transition < 1; for(int r=0; r<=AURA; r++) for(int z=0; z<11; z++) { float rr = (M_PI * 2 * r) / AURA; @@ -3024,7 +3025,7 @@ void drawaura() { else models::apply_orientation(c1, s1); - ld& mt = models::model_transition; + ld& mt = pconf.model_transition; ld mt2 = 1 - mt; ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2); @@ -3034,7 +3035,7 @@ void drawaura() { } cx[r][z][0] = rad0 * c; - cx[r][z][1] = rad0 * s * vid.stretch; + cx[r][z][1] = rad0 * s * pconf.stretch; for(int u=0; u<3; u++) cx[r][z][u+2] = bak[u] + (aurac[rm][u] / (aurac[rm][3]+.1) - bak[u]) * cmul[z]; @@ -3362,7 +3363,7 @@ bool openorsafe(cell *c) { EX color_t stdgridcolor = 0x202020FF; EX int gridcolor(cell *c1, cell *c2) { - if(cmode & sm::DRAW) return Dark(forecolor); + if(cmode & sm::DRAW && !mapeditor::drawing_tool) return Dark(forecolor); if(!c2) return 0x202020 >> darken; int rd1 = rosedist(c1), rd2 = rosedist(c2); @@ -3667,7 +3668,7 @@ bool celldrawer::cell_clipped() { hyperpoint H = tC0(V); if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ; else { - hyperpoint H2 = inverse_exp(H, iLazy); + hyperpoint H2 = inverse_exp(H, pQUICK); for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -.4) return true; } noclipped++; @@ -3676,7 +3677,7 @@ bool celldrawer::cell_clipped() { hyperpoint H = tC0(V); if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ; else { - hyperpoint H2 = inverse_exp(H, iLazy); + hyperpoint H2 = inverse_exp(H, pQUICK); for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -2) return true; } noclipped++; @@ -3697,7 +3698,8 @@ EX void gridline(const transmatrix& V1, const hyperpoint h1, const transmatrix& if(WDIM == 3 && fat_edges) { transmatrix T = V1 * rgpushxto0(h1); transmatrix S = rspintox(inverse(T) * V2 * h2); - queuepoly(T * S, cgi.generate_pipe(d, vid.linewidth), col); + auto& p = queuepoly(T * S, cgi.generate_pipe(d, vid.linewidth), col); + p.intester = xpush0(d/2); return; } #endif @@ -4470,12 +4472,12 @@ EX void precise_mouseover() { if(WDIM == 3) { mouseover2 = mouseover = centerover; ld best = HUGE_VAL; - hyperpoint h = direct_exp(lp_iapply(ztangent(0.01)), 100); + hyperpoint h = direct_exp(lp_iapply(ztangent(0.01))); transmatrix cov = ggmatrix(mouseover2); forCellIdEx(c1, i, mouseover2) { hyperpoint h1 = tC0(cov * currentmap->adj(mouseover2, i)); - ld dist = geo_dist(h, h1, iTable) - geo_dist(C0, h1, iTable); + ld dist = geo_dist(h, h1) - geo_dist(C0, h1); if(dist < best) mouseover = c1, best = dist; } return; @@ -4559,7 +4561,7 @@ EX void drawthemap() { #endif if(non_spatial_model()) spatial_graphics = false; - if(pmodel == mdDisk && abs(vid.alpha) < 1e-6) spatial_graphics = false; + if(pmodel == mdDisk && abs(pconf.alpha) < 1e-6) spatial_graphics = false; if(!spatial_graphics) wmspatial = mmspatial = false; if(GDIM == 3) wmspatial = mmspatial = true; @@ -4628,6 +4630,8 @@ EX void drawthemap() { profile_stop(4); drawFlashes(); + mapeditor::draw_dtshapes(); + if(multi::players > 1 && !shmup::on) { if(multi::centerplayer != -1) cwtV = multi::whereis[multi::centerplayer]; @@ -4762,7 +4766,7 @@ EX void calcparam() { cd->xcenter = cd->xtop + cd->xsize / 2; cd->ycenter = cd->ytop + cd->ysize / 2; - if(vid.scale > -1e-2 && vid.scale < 1e-2) vid.scale = 1; + if(pconf.scale > -1e-2 && pconf.scale < 1e-2) pconf.scale = 1; ld realradius = min(cd->xsize / 2, cd->ysize / 2); @@ -4784,11 +4788,11 @@ EX void calcparam() { if(current_display->sidescreen) cd->xcenter = vid.yres/2; } - cd->radius = vid.scale * cd->scrsize; + cd->radius = pconf.scale * cd->scrsize; if(GDIM == 3 && in_perspective()) cd->radius = cd->scrsize; realradius = min(realradius, cd->radius); - ld aradius = sphere ? cd->radius / (vid.alpha - 1) : cd->radius; + ld aradius = sphere ? cd->radius / (pconf.alpha - 1) : cd->radius; if(dronemode) { cd->ycenter -= cd->radius; cd->ycenter += vid.fsize/2; cd->ycenter += vid.fsize/2; cd->radius *= 2; } @@ -4800,8 +4804,8 @@ EX void calcparam() { cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius; } - cd->xcenter += cd->scrsize * vid.xposition; - cd->ycenter += cd->scrsize * vid.yposition; + cd->xcenter += cd->scrsize * pconf.xposition; + cd->ycenter += cd->scrsize * pconf.yposition; cd->tanfov = tan(vid.fov * degree / 2); diff --git a/history.cpp b/history.cpp index 1b09e302..998135cf 100644 --- a/history.cpp +++ b/history.cpp @@ -362,7 +362,7 @@ EX namespace history { } ld measureLength() { - ld r = bandhalf * vid.scale; + ld r = bandhalf * pconf.scale; ld tpixels = 0; int siz = isize(v); @@ -526,7 +526,7 @@ EX namespace history { dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i'); // bool notconformal0 = (pmodel >= 5 && pmodel <= 6) && !euclid; - // bool notconformal = notconformal0 || abs(vid.alpha-1) > 1e-3; + // bool notconformal = notconformal0 || abs(pconf.alpha-1) > 1e-3; dialog::addSelItem(XLAT("projection"), current_proj_name(), 'm'); diff --git a/hud.cpp b/hud.cpp index 3f973788..ccc35010 100644 --- a/hud.cpp +++ b/hud.cpp @@ -401,20 +401,12 @@ EX void drawStats() { if(geometry == gRotSpace || geometry == gProduct) rots::draw_underlying(!cornermode); { - dynamicval pm(pmodel, flat_model()); - glClear(GL_DEPTH_BUFFER_BIT); - // dynamicval v(vid, vid); - // vid.alpha = vid.scale = 1; - dynamicval va(vid.alpha, 1); - dynamicval vs(vid.scale, 1); - dynamicval vc(vid.camera_angle, 0); - if(prod) vid.alpha = 30, vid.scale = 30; auto& cd = current_display; auto xc = cd->xcenter; auto yc = cd->ycenter; - calcparam(); + flat_model_enabler fme; if(crosshair_color && crosshair_size > 0) { initquickqueue(); @@ -469,7 +461,7 @@ EX void drawStats() { int spots = 0; for(int u=vid.fsize; u rad) { + if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / pconf.stretch) > rad) { spots++; } if(spots >= bycorner[cor] && spots >= 3) { @@ -482,7 +474,7 @@ EX void drawStats() { } for(int u=vid.fsize; u rad) { + if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / pconf.stretch) > rad) { if(next >= isize(glyphstoshow)) break; int cx = u; diff --git a/hyper.h b/hyper.h index a4ccc666..09ba788f 100644 --- a/hyper.h +++ b/hyper.h @@ -13,8 +13,8 @@ #define _HYPER_H_ // version numbers -#define VER "11.3i" -#define VERNUM_HEX 0xA829 +#define VER "11.3j" +#define VERNUM_HEX 0xA82A #include "sysconfig.h" @@ -228,9 +228,46 @@ enum eStereo { sOFF, sAnaglyph, sLR, sODS }; enum eModel : int; +/** configuration of the projection */ +struct projection_configuration { + eModel model; /**< which projection, see classes.cpp */ + ld xposition, yposition; /**< move the center to another position */ + ld scale, alpha, camera_angle, fisheye_param, twopoint_param, stretch, ballangle, ballproj, euclid_to_sphere; + ld clip_min, clip_max; + ld model_orientation, halfplane_scale, model_orientation_yz; + ld collignon_parameter; + bool collignon_reflected; + string formula; + eModel basic_model; + ld top_z; + ld model_transition; + ld spiral_angle; + ld spiral_x; + ld spiral_y; + bool use_atan; + ld right_spiral_multiplier; + ld any_spiral_multiplier; + ld sphere_spiral_multiplier; + ld spiral_cone; + ld skiprope; + ld product_z_scale; + + projection_configuration() { + formula = "z^2"; top_z = 5; model_transition = 1; spiral_angle = 70; spiral_x = 10; spiral_y = 7; + right_spiral_multiplier = 1; + any_spiral_multiplier = 1; + sphere_spiral_multiplier = 2; + spiral_cone = 360; + use_atan = false; + product_z_scale = 1; + } + }; + struct videopar { - ld scale, alpha, sspeed, mspeed, yshift, camera_angle; - ld ballangle, ballproj, euclid_to_sphere, twopoint_param, fisheye_param, stretch, binary_width, fixed_facing_dir; + projection_configuration projection_config, rug_config; + ld yshift; + ld sspeed, mspeed; + ld binary_width, fixed_facing_dir; int mobilecompasssize; int radarsize; // radar for 3D geometries ld radarrange; @@ -251,8 +288,6 @@ struct videopar { int xscr, yscr; - ld xposition, yposition; - bool grid; bool particles; @@ -304,8 +339,6 @@ struct videopar { int cells_drawn_limit; int cells_generated_limit; // limit on cells generated per frame - ld skiprope; - eStereo stereo_mode; ld ipd; ld lr_eyewidth, anaglyph_eyewidth; @@ -319,7 +352,6 @@ struct videopar { ld depth; // world level below the plane ld camera; // camera level above the plane ld wall_height, creature_scale, height_width; - eModel vpmodel; ld lake_top, lake_bottom; ld rock_wall_ratio; ld human_wall_ratio; @@ -331,7 +363,6 @@ struct videopar { ld eye; bool auto_eye; - ld collignon_parameter; bool collignon_reflected; ld plevel_factor; bool bubbles_special, bubbles_threshold, bubbles_all; int joysmooth; @@ -341,7 +372,7 @@ extern videopar vid; #define WDIM cginf.g.gameplay_dimension #define GDIM cginf.g.graphical_dimension -#define MDIM cginf.g.homogeneous_dimension +#define MDIM (MAXMDIM == 3 ? 3 : cginf.g.homogeneous_dimension) #define LDIM (MDIM-1) #define cclass g.kind @@ -660,7 +691,9 @@ enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMul #define MODELCOUNT ((int) mdGUARD) -#define pmodel (vid.vpmodel) +#define pconf vid.projection_config +#define vpconf (rug::rugged ? vid.rug_config : vid.projection_config) +#define pmodel (pconf.model) color_t darkena(color_t c, int lev, int a); diff --git a/hyperpoint.cpp b/hyperpoint.cpp index b4c98c41..de0c4dcf 100644 --- a/hyperpoint.cpp +++ b/hyperpoint.cpp @@ -702,6 +702,8 @@ EX transmatrix rpushxto0(const hyperpoint& H) { EX transmatrix ggpushxto0(const hyperpoint& H, ld co) { if(translatable) { + if(nonisotropic) + return co > 0 ? eupush(H) : inverse(eupush(H)); return eupush(co * H); } if(prod) { @@ -1030,13 +1032,13 @@ EX bool asign(ld y1, ld y2) { return signum(y1) != signum(y2); } EX ld xcross(ld x1, ld y1, ld x2, ld y2) { return x1 + (x2 - x1) * y1 / (y1 - y2); } -EX transmatrix parallel_transport(const transmatrix Position, const transmatrix& ori, const hyperpoint direction, int precision IS(100)) { +EX transmatrix parallel_transport(const transmatrix Position, const transmatrix& ori, const hyperpoint direction) { if(nonisotropic) return nisot::parallel_transport(Position, direction); else if(prod) { hyperpoint h = product::direct_exp(ori * direction); return Position * rgpushxto0(h); } - else return Position * rgpushxto0(direct_exp(direction, precision)); + else return Position * rgpushxto0(direct_exp(direction)); } EX void apply_parallel_transport(transmatrix& Position, const transmatrix orientation, const hyperpoint direction) { @@ -1151,8 +1153,8 @@ EX hyperpoint tangent_length(hyperpoint dir, ld length) { } /** exponential function: follow the geodesic given by v */ -EX hyperpoint direct_exp(hyperpoint v, int steps) { - if(sn::in()) return nisot::numerical_exp(v, steps); +EX hyperpoint direct_exp(hyperpoint v) { + if(sn::in()) return nisot::numerical_exp(v); if(nil) return nilv::formula_exp(v); if(sl2) return slr::formula_exp(v); if(prod) return product::direct_exp(v); @@ -1163,20 +1165,26 @@ EX hyperpoint direct_exp(hyperpoint v, int steps) { } #if HDR -enum iePrecision { iLazy, iTable }; +constexpr flagtype pfNO_INTERPOLATION = 1; /**< in tables (sol/nih geometries), do not use interpolations */ +constexpr flagtype pfNO_DISTANCE = 2; /**< we just need the directions -- this makes it a bit faster in sol/nih geometries */ +constexpr flagtype pfLOW_BS_ITER = 4; /**< low iterations in binary search (nil geometry, sl2 not affected currently) */ + +constexpr flagtype pQUICK = pfNO_INTERPOLATION | pfLOW_BS_ITER; + +constexpr flagtype pNORMAL = 0; #endif /** inverse exponential function \see hr::direct_exp */ -EX hyperpoint inverse_exp(const hyperpoint h, iePrecision p, bool just_direction IS(true)) { +EX hyperpoint inverse_exp(const hyperpoint h, flagtype prec IS(pNORMAL)) { #if CAP_SOLV if(sn::in()) { if(nih) - return sn::get_inverse_exp_nsym(h, p == iLazy, just_direction); + return sn::get_inverse_exp_nsym(h, prec); else - return sn::get_inverse_exp_symsol(h, p == iLazy, just_direction); + return sn::get_inverse_exp_symsol(h, prec); } #endif - if(nil) return nilv::get_inverse_exp(h, p == iLazy ? 5 : 20); + if(nil) return nilv::get_inverse_exp(h, prec); if(sl2) return slr::get_inverse_exp(h); if(prod) return product::inverse_exp(h); ld d = acos_auto_clamp(h[GDIM]); @@ -1186,9 +1194,15 @@ EX hyperpoint inverse_exp(const hyperpoint h, iePrecision p, bool just_direction return v; } -EX ld geo_dist(const hyperpoint h1, const hyperpoint h2, iePrecision p) { +EX ld geo_dist(const hyperpoint h1, const hyperpoint h2, flagtype prec IS(pNORMAL)) { if(!nonisotropic) return hdist(h1, h2); - return hypot_d(3, inverse_exp(inverse(nisot::translate(h1)) * h2, p, false)); + return hypot_d(3, inverse_exp(inverse(nisot::translate(h1)) * h2, prec)); + } + +EX ld geo_dist_q(const hyperpoint h1, const hyperpoint h2, flagtype prec IS(pNORMAL)) { + auto d = geo_dist(h1, h2, prec); + if(elliptic && d > M_PI/2) return M_PI - d; + return d; } EX hyperpoint lp_iapply(const hyperpoint h) { diff --git a/hypgraph.cpp b/hypgraph.cpp index 0a5e9c25..f747630e 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -12,7 +12,7 @@ ld ghx, ghy, ghgx, ghgy; hyperpoint ghpm = C0; #if HDR -inline bool sphereflipped() { return sphere && vid.alpha > 1.1 && GDIM == 3; } +inline bool sphereflipped() { return sphere && pconf.alpha > 1.1 && GDIM == 3; } #endif void ghcheck(hyperpoint &ret, const hyperpoint &H) { @@ -22,7 +22,7 @@ void ghcheck(hyperpoint &ret, const hyperpoint &H) { } EX void camrotate(ld& hx, ld& hy) { - ld cam = vid.camera_angle * degree; + ld cam = pconf.camera_angle * degree; GLfloat cc = cos(cam); GLfloat ss = sin(cam); ld ux = hx, uy = hy * cc + ss, uz = cc - ss * hy; @@ -37,7 +37,7 @@ EX bool non_spatial_model() { return pmodel && vid.consider_shader_projection && (get_shader_flags() & SF_DIRECT); } -EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeometryClass gc IS(ginf[geometry].cclass)) { +EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(pconf.alpha), eGeometryClass gc IS(ginf[geometry].cclass)) { ld hx = h[0], hy = h[1]; if(gc == gcEuclid) @@ -59,7 +59,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet B /= A; C /= A; ld rootsign = 1; - // if(gc == gcSphere && vid.alpha > 1) rootsign = -1; + // if(gc == gcSphere && pconf.alpha > 1) rootsign = -1; ld hz = B / 2 + rootsign * sqrt(C + B*B/4); @@ -72,7 +72,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet return H; } -EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(vid.alpha)) { +EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(pconf.alpha)) { ld s = 1 / (alpha + z[LDIM]); z[0] *= s; z[1] *= s; @@ -88,21 +88,21 @@ EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(vid.alpha)) { EX hyperpoint gethyper(ld x, ld y) { ld hx = (x - current_display->xcenter) / current_display->radius; - ld hy = (y - current_display->ycenter) / current_display->radius / vid.stretch; + ld hy = (y - current_display->ycenter) / current_display->radius / pconf.stretch; if(pmodel) { ghx = hx, ghy = hy; return ghpm; } - if(vid.camera_angle) camrotate(hx, hy); + if(pconf.camera_angle) camrotate(hx, hy); return perspective_to_space(hpxyz(hx, hy, 0)); } void ballmodel(hyperpoint& ret, double alpha, double d, double zl) { hyperpoint H = ypush(vid.camera) * xpush(d) * ypush(zl) * C0; - ld tzh = vid.ballproj + H[LDIM]; + ld tzh = pconf.ballproj + H[LDIM]; ld ax = H[0] / tzh; ld ay = H[1] / tzh; @@ -165,7 +165,7 @@ ld find_zlev(hyperpoint& H) { } ld get_tz(hyperpoint H) { - ld tz = vid.alpha+H[LDIM]; + ld tz = pconf.alpha+H[LDIM]; if(tz < BEHIND_LIMIT && tz > -BEHIND_LIMIT) tz = BEHIND_LIMIT; return tz; } @@ -237,7 +237,7 @@ void band_conformal(ld& x, ld& y) { } void make_twopoint(ld& x, ld& y) { - auto p = vid.twopoint_param; + auto p = pconf.twopoint_param; ld dleft = hypot_auto(x-p, y); ld dright = hypot_auto(x+p, y); if(sphere) { @@ -264,7 +264,7 @@ hyperpoint mobius(hyperpoint h, ld angle, ld scale = 1) { } hyperpoint compute_hybrid(hyperpoint H, int rootid) { - auto& t = vid.twopoint_param; + auto& t = pconf.twopoint_param; hyperpoint Hl = xpush(+t) * H; hyperpoint Hr = xpush(-t) * H; ld g = (Hl[0] + 1e-7) / (Hl[1] + 1e-8); @@ -315,11 +315,11 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { hyperpoint H_orig = H; - if(models::product_model()) { + if(models::product_model(pmodel)) { ld zlev = zlevel(H); H /= exp(zlev); hybrid::in_underlying_geometry([&] { applymodel(H, ret); }); - ret[2] = zlev * models::product_z_scale; + ret[2] = zlev * pconf.product_z_scale; ret = NLP * ret; return; } @@ -337,7 +337,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { } case mdGeodesic: { - auto S = lp_apply(inverse_exp(H, iTable)); + auto S = lp_apply(inverse_exp(H, pNORMAL | pfNO_DISTANCE)); ld ratio = vid.xres / current_display->tanfov / current_display->radius / 2; ret[0] = S[0]/S[2] * ratio; ret[1] = S[1]/S[2] * ratio; @@ -360,23 +360,23 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { case mdDisk: { if(nonisotropic) { - ret = lp_apply(inverse_exp(H, iTable, true)); + ret = lp_apply(inverse_exp(H, pNORMAL | pfNO_DISTANCE)); ld w; if(sn::in()) { // w = 1 / sqrt(1 - sqhypot_d(3, ret)); - // w = w / (vid.alpha + w); - w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * vid.alpha + 1); + // w = w / (pconf.alpha + w); + w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * pconf.alpha + 1); } else { w = hypot_d(3, ret); - w = sinh(w) / ((vid.alpha + cosh(w)) * w); + w = sinh(w) / ((pconf.alpha + cosh(w)) * w); } for(int i=0; i<3; i++) ret[i] *= w; ret[3] = 1; break; } ld tz = get_tz(H); - if(!vid.camera_angle) { + if(!pconf.camera_angle) { ret[0] = H[0] / tz; ret[1] = H[1] / tz; if(GDIM == 3) ret[2] = H[2] / tz; @@ -386,7 +386,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { else { ld tx = H[0]; ld ty = H[1]; - ld cam = vid.camera_angle * degree; + ld cam = pconf.camera_angle * degree; GLfloat cc = cos(cam); GLfloat ss = sin(cam); ld ux = tx, uy = ty * cc - ss * tz, uz = tz * cc + ss * ty; @@ -400,6 +400,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { case mdCentralInversion: { ld tz = get_tz(H); for(int d=0; d topz) { ld scale = sqrt(topz*topz-1) / hypot_d(2, H); H *= scale; @@ -531,8 +532,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { } } else { - H = space_to_perspective(H, vid.alpha); - H[2] = 1 - vid.alpha; + H = space_to_perspective(H, pconf.alpha); + H[2] = 1 - pconf.alpha; } ret[0] = H[0] / 3; @@ -546,14 +547,14 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { case mdFisheye: { ld zlev; if(nonisotropic) { - H = lp_apply(inverse_exp(H, iTable, false)); + H = lp_apply(inverse_exp(H)); zlev = 1; } else { zlev = find_zlev(H); H = space_to_perspective(H); } - H /= vid.fisheye_param; + H /= pconf.fisheye_param; H[LDIM] = zlev; ret = H / sqrt(1 + sqhypot_d(GDIM+1, H)); if(GDIM == 3) ret[LDIM] = zlev; @@ -564,8 +565,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { models::apply_orientation_yz(H[1], H[2]); models::apply_orientation(H[0], H[1]); auto yz = move_z_to_y(H); - hyperpoint Hl = xpush(-vid.twopoint_param) * H; - hyperpoint Hr = xpush(+vid.twopoint_param) * H; + hyperpoint Hl = xpush(-pconf.twopoint_param) * H; + hyperpoint Hr = xpush(+pconf.twopoint_param) * H; ld lyx = (Hl[1] + 1e-7) / (Hl[0] + 1e-8); ld ryx = (Hr[1] + 1e-7) / (Hr[0] + 1e-8); // (r.x + t) * lyx = (r.x - t) * ryx = r.y @@ -574,8 +575,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { // r.x = -t * (ryx+lyx) / (lyx-ryx) // r.x = - 2 * t * lyx * ryx / lyx / ryx - ret[0] = -vid.twopoint_param * (ryx + lyx) / (lyx - ryx); - ret[1] = (ret[0] + vid.twopoint_param) * lyx; + ret[0] = -pconf.twopoint_param * (ryx + lyx) / (lyx - ryx); + ret[1] = (ret[0] + pconf.twopoint_param) * lyx; ret[2] = 0; move_y_to_z(ret, yz); @@ -603,11 +604,11 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { models::apply_orientation(H[0], H[1]); // with equal speed skiprope: models::apply_orientation(H[1], H[0]); - if(vid.skiprope) { + if(pconf.skiprope) { static ld last_skiprope = 0; static transmatrix lastmatrix; - if(vid.skiprope != last_skiprope) { - ret = mobius(C0, -vid.skiprope, 2); + if(pconf.skiprope != last_skiprope) { + ret = mobius(C0, -pconf.skiprope, 2); const cld c1(1, 0); const cld c2(2, 0); const cld c4(4, 0); @@ -617,7 +618,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { hyperpoint zr = hpxyz(real(z), imag(z), 0); hyperpoint inhyp = perspective_to_space(zr, 1, gcHyperbolic); - last_skiprope = vid.skiprope; + last_skiprope = pconf.skiprope; lastmatrix = rgpushxto0(inhyp); } H = lastmatrix * H; @@ -628,7 +629,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { ld r = hypot_d(2, H); ld c = H[0] / r; ld s = H[1] / r; - ld& mt = models::model_transition; + ld& mt = pconf.model_transition; ld a = 1 - .5 * mt, b = .5 * mt; swap(a, b); @@ -636,8 +637,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { ret[1] = (a * r - b/r) * s / 2; ret[2] = 0; - if(vid.skiprope) - ret = mobius(ret, vid.skiprope, 2); + if(pconf.skiprope) + ret = mobius(ret, pconf.skiprope, 2); if(pmodel == mdJoukowskyInverted) { ld r2 = sqhypot_d(2, ret); @@ -681,8 +682,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { } case mdBand: - if(models::model_transition != 1) { - ld& mt = models::model_transition; + if(pconf.model_transition != 1) { + ld& mt = pconf.model_transition; H = space_to_perspective(H); @@ -745,10 +746,10 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { find_zlev(H); makeband(H, ret, [] (ld& x, ld& y) { ld sgn = 1; - if(vid.collignon_reflected && y > 0) y = -y, sgn = -1; - y = signed_sqrt(sin_auto(y) + vid.collignon_parameter); + if(pconf.collignon_reflected && y > 0) y = -y, sgn = -1; + y = signed_sqrt(sin_auto(y) + pconf.collignon_parameter); x *= y / 1.2; - y -= signed_sqrt(vid.collignon_parameter); + y -= signed_sqrt(pconf.collignon_parameter); y *= sgn; y *= M_PI; }); @@ -768,7 +769,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { case mdEquidistant: case mdEquiarea: case mdEquivolume: { if(nonisotropic || prod) { - ret = lp_apply(inverse_exp(H, iTable, false)); + ret = lp_apply(inverse_exp(H)); ret[3] = 1; break; } @@ -813,7 +814,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { ret[1] = cosh(x) * factor; ret[2] = 0; - if(models::use_atan) { + if(pconf.use_atan) { ret[0] = atan(ret[0]); ret[1] = atan(ret[1]); } @@ -822,7 +823,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { } case mdFormula: { - dynamicval m(pmodel, models::basic_model); + dynamicval m(pmodel, pconf.basic_model); applymodel(H, ret); exp_parser ep; ep.extra_params["z"] = cld(ret[0], ret[1]); @@ -832,7 +833,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { ep.extra_params["ux"] = H[0]; ep.extra_params["uy"] = H[1]; ep.extra_params["uz"] = H[2]; - ep.s = models::formula; + ep.s = pconf.formula; cld res; try { res = ep.parse(); @@ -852,15 +853,15 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { else ret = H; z = cld(ret[0], ret[1]) * models::spiral_multiplier; - if(models::spiral_cone < 360) { - ld alpha = imag(z) * 360 / models::spiral_cone; + if(pconf.spiral_cone < 360) { + ld alpha = imag(z) * 360 / pconf.spiral_cone; ld r = real(z); r = exp(r); ret[0] = -sin(alpha) * r; ret[1] = cos(alpha) * r; if(euclid) ret = models::euclidean_spin * ret; - ret[2] = (r-1) * sqrt( pow(360/models::spiral_cone, 2) - 1); + ret[2] = (r-1) * sqrt( pow(360/pconf.spiral_cone, 2) - 1); models::apply_ball(ret[2], ret[1]); } @@ -870,8 +871,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) { ret[1] = imag(z); if(euclid) ret = models::euclidean_spin * ret; - if(vid.skiprope) - ret = mobius(ret, vid.skiprope, 1); + if(pconf.skiprope) + ret = mobius(ret, pconf.skiprope, 1); } } @@ -929,12 +930,12 @@ EX bool behindsphere(const hyperpoint& h) { if(mdBandAny()) return false; - if(vid.alpha > 1) { - if(h[LDIM] > -1/vid.alpha) return true; + if(pconf.alpha > 1) { + if(h[LDIM] > -1/pconf.alpha) return true; } - if(vid.alpha <= 1) { - if(h[LDIM] < .2-vid.alpha) return true; + if(pconf.alpha <= 1) { + if(h[LDIM] < .2-pconf.alpha) return true; } return false; @@ -949,11 +950,11 @@ ld to01(ld a0, ld a1, ld x) { EX ld spherity(const hyperpoint& h) { if(!sphere) return 1; - if(vid.alpha > 1) { - return to01(1/vid.alpha, 1, abs(h[2])); + if(pconf.alpha > 1) { + return to01(1/pconf.alpha, 1, abs(h[2])); } - if(vid.alpha <= 1) { + if(pconf.alpha <= 1) { return to01(-1.5, 1, h[2]); } @@ -1002,7 +1003,7 @@ EX transmatrix actualV(const heptspin& hs, const transmatrix& V) { EX bool point_behind(hyperpoint h) { if(sphere) return false; if(!in_perspective()) return false; - if(pmodel == mdGeodesic) h = inverse_exp(h, iLazy); + if(pmodel == mdGeodesic) h = inverse_exp(h, pQUICK); if(pmodel == mdPerspective && prod) h = product::inverse_exp(h); h = lp_apply(h); return h[2] < 1e-8; @@ -1039,15 +1040,15 @@ EX bool in_smart_range(const transmatrix& T) { applymodel(h, h1); if(invalid_point(h1)) return false; ld x = current_display->xcenter + current_display->radius * h1[0]; - ld y = current_display->ycenter + current_display->radius * h1[1] * vid.stretch; + ld y = current_display->ycenter + current_display->radius * h1[1] * pconf.stretch; if(x > current_display->xtop + current_display->xsize * 2) return false; if(x < current_display->xtop - current_display->xsize * 1) return false; if(y > current_display->ytop + current_display->ysize * 2) return false; if(y < current_display->ytop - current_display->ysize * 1) return false; if(GDIM == 3) { - if(-h1[2] < models::clip_min * 2 - models::clip_max) return false; - if(-h1[2] > models::clip_max * 2 - models::clip_min) return false; + if(-h1[2] < pconf.clip_min * 2 - pconf.clip_max) return false; + if(-h1[2] > pconf.clip_max * 2 - pconf.clip_min) return false; } ld epsilon = 0.01; @@ -1057,14 +1058,14 @@ EX bool in_smart_range(const transmatrix& T) { hyperpoint h2; applymodel(T * cpush0(i, epsilon), h2); ld x1 = current_display->radius * abs(h2[0] - h1[0]) / epsilon; - ld y1 = current_display->radius * abs(h2[1] - h1[1]) * vid.stretch / epsilon; + ld y1 = current_display->radius * abs(h2[1] - h1[1]) * pconf.stretch / epsilon; dx = max(dx, x1); dy = max(dy, y1); if(GDIM == 3) dz = max(dz, abs(h2[2] - h1[2])); dh[i] = hypot(x1, y1); } if(GDIM == 3) { - if(-h1[2] + 2 * dz < models::clip_min || -h1[2] - 2 * dz > models::clip_max) return false; + if(-h1[2] + 2 * dz < pconf.clip_min || -h1[2] - 2 * dz > pconf.clip_max) return false; sort(dh, dh+GDIM); ld scale = sqrt(dh[1] * dh[2]) * cgi.scalefactor * hcrossf7; if(scale <= (WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3)) return false; @@ -1191,7 +1192,7 @@ void hrmap_standard::draw() { if(sphere && pmodel == mdSpiral && !in_multi) { in_multi = true; if(models::ring_not_spiral) { - int qty = ceil(1. / models::sphere_spiral_multiplier); + int qty = ceil(1. / pconf.sphere_spiral_multiplier); if(qty > 100) qty = 100; for(int i=-qty; i < qty; i++) { band_shift = 2 * M_PI * i; @@ -1505,8 +1506,8 @@ EX void fullcenter() { transmatrix screenpos(ld x, ld y) { transmatrix V = Id; - V[0][2] += (x - current_display->xcenter) / current_display->radius * (1+vid.alpha); - V[1][2] += (y - current_display->ycenter) / current_display->radius * (1+vid.alpha); + V[0][2] += (x - current_display->xcenter) / current_display->radius * (1+pconf.alpha); + V[1][2] += (y - current_display->ycenter) / current_display->radius * (1+pconf.alpha); return V; } @@ -1517,6 +1518,27 @@ transmatrix screenpos(ld x, ld y) { EX eModel flat_model() { return MDIM == 4 ? mdPixel : mdDisk; } +/** \brief enable the 'flat' model for drawing HUD. See hr::flat_model_enabler */ +EX void enable_flat_model() { + glClear(GL_DEPTH_BUFFER_BIT); + pmodel = flat_model(); + pconf.alpha = 1; + pconf.scale = 1; + pconf.camera_angle = 0; + pconf.stretch = 1; + if(prod) pconf.alpha = 30, pconf.scale = 30; + calcparam(); + } + +#if HDR +/** \brief enable the 'flat' model for drawing HUD. Use RAII so it will be switched back later */ +struct flat_model_enabler { + projection_configuration bak; + flat_model_enabler() { bak = pconf; enable_flat_model(); } + ~flat_model_enabler() { pconf = bak; calcparam(); } + }; +#endif + EX transmatrix atscreenpos(ld x, ld y, ld size) { transmatrix V = Id; @@ -1541,7 +1563,7 @@ EX transmatrix atscreenpos(ld x, ld y, ld size) { void circle_around_center(ld radius, color_t linecol, color_t fillcol, PPR prio) { #if CAP_QUEUE - if(among(pmodel, mdDisk, mdEquiarea, mdEquidistant, mdFisheye) && !(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) && vid.camera_angle == 0) { + if(among(pmodel, mdDisk, mdEquiarea, mdEquidistant, mdFisheye) && !(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) && pconf.camera_angle == 0) { hyperpoint ret; applymodel(xpush0(radius), ret); ld r = hypot_d(2, ret); @@ -1552,7 +1574,7 @@ void circle_around_center(ld radius, color_t linecol, color_t fillcol, PPR prio) #if CAP_QUEUE for(int i=0; i<=360; i++) curvepoint(xspinpush0(i * degree, 10)); auto& c = queuecurve(linecol, fillcol, prio); - if(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) + if(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) c.flags |= POLY_FORCE_INVERTED; if(pmodel == mdJoukowsky) c.flags |= POLY_FORCE_INVERTED; @@ -1571,7 +1593,7 @@ EX void draw_model_elements() { switch(pmodel) { case mdRotatedHyperboles: { - queuestr(current_display->xcenter, current_display->ycenter + current_display->radius * vid.alpha, 0, vid.fsize, "X", ringcolor, 1, 8); + queuestr(current_display->xcenter, current_display->ycenter + current_display->radius * pconf.alpha, 0, vid.fsize, "X", ringcolor, 1, 8); return; } @@ -1581,11 +1603,11 @@ EX void draw_model_elements() { for(int mode=0; mode<4; mode++) { for(int s=-200; s<=200; s ++) { ld p = tanh(s / 40.); - ld a = vid.twopoint_param * (1+p); - ld b = vid.twopoint_param * (1-p); + ld a = pconf.twopoint_param * (1+p); + ld b = pconf.twopoint_param * (1-p); ld h = ((mode & 2) ? -1 : 1) * sqrt(asin_auto(tan_auto(a) * tan_auto(b))); - hyperpoint H = xpush(p * vid.twopoint_param) * ypush0(h); + hyperpoint H = xpush(p * pconf.twopoint_param) * ypush0(h); hyperpoint res = compute_hybrid(H, 2 | mode); models::apply_orientation(res[0], res[1]); @@ -1600,9 +1622,9 @@ EX void draw_model_elements() { } case mdTwoPoint: case mdSimulatedPerspective: { - ld a = -models::model_orientation * degree; - queuestr(xspinpush0(a, +vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); - queuestr(xspinpush0(a, -vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); + ld a = -pconf.model_orientation * degree; + queuestr(xspinpush0(a, +pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); + queuestr(xspinpush0(a, -pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); return; } @@ -1616,10 +1638,10 @@ EX void draw_model_elements() { if(hyperbolic) { #if CAP_QUEUE curvepoint(point3(0,0,1)); - curvepoint(point3(0,0,-vid.alpha)); + curvepoint(point3(0,0,-pconf.alpha)); queuecurve(ringcolor, 0, PPR::CIRCLE); - ld& tz = models::top_z; + ld& tz = pconf.top_z; ld z = acosh(tz); hyperpoint a = xpush0(z); @@ -1629,12 +1651,12 @@ EX void draw_model_elements() { a[1] = sb * a[2] / -cb; a[0] = sqrt(-1 + a[2] * a[2] - a[1] * a[1]); - curvepoint(point3(0,0,-vid.alpha)); + curvepoint(point3(0,0,-pconf.alpha)); curvepoint(a); curvepoint(point3(0,0,0)); a[0] = -a[0]; curvepoint(a); - curvepoint(point3(0,0,-vid.alpha)); + curvepoint(point3(0,0,-pconf.alpha)); queuecurve(ringcolor, 0, PPR::CIRCLE); curvepoint(point3(-1,0,0)); @@ -1643,10 +1665,10 @@ EX void draw_model_elements() { a[1] = sb * tz / -cb; a[0] = sqrt(tz * tz - a[1] * a[1]); - a[2] = tz - vid.alpha; + a[2] = tz - pconf.alpha; curvepoint(a); - curvepoint(point3(0,0,-vid.alpha)); + curvepoint(point3(0,0,-pconf.alpha)); a[0] = -a[0]; curvepoint(a); queuecurve(ringcolor, 0, PPR::CIRCLE); @@ -1727,7 +1749,7 @@ EX void draw_boundary(int w) { for(int b=-1; b<=1; b+=2) for(ld a=-90; a<=90+1e-6; a+=pow(.5, vid.linequality)) { - ld x = sin(a * vid.twopoint_param * b / 90); + ld x = sin(a * pconf.twopoint_param * b / 90); ld y = 0; ld z = -sqrt(1 - x*x); models::apply_orientation(y, x); @@ -1747,9 +1769,9 @@ EX void draw_boundary(int w) { case mdBand: case mdBandEquidistant: case mdBandEquiarea: case mdSinusoidal: case mdMollweide: case mdCentralCyl: case mdCollignon: { if(GDIM == 3) return; - if(pmodel == mdBand && models::model_transition != 1) return; + if(pmodel == mdBand && pconf.model_transition != 1) return; bool bndband = (among(pmodel, mdBand, mdCentralCyl) ? hyperbolic : sphere); - transmatrix T = spin(-models::model_orientation * degree); + transmatrix T = spin(-pconf.model_orientation * degree); ld right = M_PI/2 - 1e-5; if(bndband) queuestraight(T * ypush0(hyperbolic ? 10 : right), 2, lc, fc, p); @@ -1773,7 +1795,7 @@ EX void draw_boundary(int w) { case mdHalfplane: if(hyperbolic && GDIM == 2) { - queuestraight(xspinpush0(-models::model_orientation * degree - M_PI/2, fakeinf), 1, lc, fc, p); + queuestraight(xspinpush0(-pconf.model_orientation * degree - M_PI/2, fakeinf), 1, lc, fc, p); return; } break; @@ -1810,7 +1832,7 @@ EX void draw_boundary(int w) { case mdHyperboloid: { if(hyperbolic) { - ld& tz = models::top_z; + ld& tz = pconf.top_z; ld mz = acosh(tz); ld cb = models::cos_ball; ld sb = models::sin_ball; @@ -1871,7 +1893,7 @@ EX void draw_boundary(int w) { for(ld a=-10; a<=10; a+=0.01 / (1 << vid.linequality) / u) { cld z = exp(cld(a, a * imag(sm) / real(sm) + M_PI)); hyperpoint ret = point2(real(z), imag(z)); - ret = mobius(ret, vid.skiprope, 1); + ret = mobius(ret, pconf.skiprope, 1); ret *= current_display->radius; curvepoint(ret); } @@ -1884,8 +1906,8 @@ EX void draw_boundary(int w) { default: break; } - if(sphere && pmodel == mdDisk && vid.alpha > 1) { - double rad = current_display->radius / sqrt(vid.alpha*vid.alpha - 1); + if(sphere && pmodel == mdDisk && pconf.alpha > 1) { + double rad = current_display->radius / sqrt(pconf.alpha*pconf.alpha - 1); queuecircle(current_display->xcenter, current_display->ycenter, rad, lc, p, fc); return; } @@ -1898,7 +1920,7 @@ EX void draw_boundary(int w) { EX ld band_shift = 0; EX void fix_the_band(transmatrix& T) { if(((mdinf[pmodel].flags & mf::uses_bandshift) && T[LDIM][LDIM] > 1e6) || (sphere && pmodel == mdSpiral)) { - T = spin(models::model_orientation * degree) * T; + T = spin(pconf.model_orientation * degree) * T; hyperpoint H = tC0(T); find_zlev(H); @@ -1911,7 +1933,7 @@ EX void fix_the_band(transmatrix& T) { band_shift += x; T = xpush(-x) * T; fixmatrix(T); - T = spin(-models::model_orientation * degree) * T; + T = spin(-pconf.model_orientation * degree) * T; } } @@ -1995,7 +2017,7 @@ EX bool do_draw(cell *c, const transmatrix& T) { if(cells_drawn > vid.cells_drawn_limit) return false; if(cells_drawn < 50) { limited_generation(c); return true; } if(nil && pmodel == mdGeodesic) { - ld dist = hypot_d(3, inverse_exp(tC0(T), iLazy)); + ld dist = hypot_d(3, inverse_exp(tC0(T), pQUICK)); if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : 0.9)) return false; if(dist <= extra_generation_distance && !limited_generation(c)) return false; } @@ -2004,7 +2026,7 @@ EX bool do_draw(cell *c, const transmatrix& T) { if(!limited_generation(c)) return false; } else if(pmodel == mdGeodesic && nih) { - hyperpoint h = inverse_exp(tC0(T), iLazy, false); + hyperpoint h = inverse_exp(tC0(T), pQUICK); ld dist = hypot_d(3, h); if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : cgi.corner_bonus)) return false; if(dist <= extra_generation_distance && !limited_generation(c)) return false; @@ -2056,7 +2078,7 @@ EX int cone_side(const hyperpoint H) { cld z = cld(ret[0], ret[1]) * models::spiral_multiplier; auto zth = [&] (cld z) { - ld alpha = imag(z) * 360 / models::spiral_cone; + ld alpha = imag(z) * 360 / pconf.spiral_cone; ld r = real(z); r = exp(r); @@ -2064,7 +2086,7 @@ EX int cone_side(const hyperpoint H) { ret[0] = -sin(alpha) * r; ret[1] = cos(alpha) * r; - ret[2] = (r-1) * sqrt( pow(360/models::spiral_cone, 2) - 1); + ret[2] = (r-1) * sqrt( pow(360/pconf.spiral_cone, 2) - 1); models::apply_ball(ret[2], ret[1]); return ret; @@ -2096,7 +2118,7 @@ EX void rotate_view(transmatrix T) { /** shift the view according to the given tangent vector */ EX transmatrix get_shift_view_of(const hyperpoint H, const transmatrix V) { if(!nonisotropic) { - return rgpushxto0(direct_exp(lp_iapply(H), 100)) * V; + return rgpushxto0(direct_exp(lp_iapply(H))) * V; } else if(!nisot::geodesic_movement) { transmatrix IV = inverse(V); @@ -2132,7 +2154,7 @@ void multiply_view(transmatrix T) { EX void shift_view_to(hyperpoint H) { if(!nonisotropic) multiply_view(gpushxto0(H)); - else shift_view(-inverse_exp(H, iTable, false)); + else shift_view(-inverse_exp(H)); } EX void shift_view_towards(hyperpoint H, ld l) { @@ -2141,7 +2163,7 @@ EX void shift_view_towards(hyperpoint H, ld l) { else if(nonisotropic && !nisot::geodesic_movement) shift_view(tangent_length(H-C0, -l)); else { - hyperpoint ie = inverse_exp(H, iTable, true); + hyperpoint ie = inverse_exp(H, pNORMAL | pfNO_DISTANCE); if(prod) ie = lp_apply(ie); shift_view(tangent_length(ie, -l)); } diff --git a/inventory.cpp b/inventory.cpp index 94de7f7a..436e6e5e 100644 --- a/inventory.cpp +++ b/inventory.cpp @@ -513,14 +513,7 @@ EX namespace inv { int j = 0, oc = 6; if(1) { - dynamicval pm(pmodel, flat_model()); - glClear(GL_DEPTH_BUFFER_BIT); - // dynamicval v(vid, vid); - // vid.alpha = vid.scale = 1; - dynamicval va(vid.alpha, 1); - dynamicval vs(vid.scale, 1); - dynamicval vc(vid.camera_angle, 0); - calcparam(); + flat_model_enabler fme; for(int i=0; iwall = (hrand(1000000) < ca::prob * 1000000) ? ca::wlive : waNone; + if(c->wall == ca::wlive) + ca::list_adj(c); + } break; case laLivefjord: diff --git a/legacy.cpp b/legacy.cpp index cc82a6e6..f9aa5efa 100644 --- a/legacy.cpp +++ b/legacy.cpp @@ -49,7 +49,7 @@ void loadOldConfig(FILE *f) { float a, b, c, d; err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d); if(err == 4) { - vid.scale = a; vid.alpha = c; vid.sspeed = d; + vid.scale = a; pconf.alpha = c; vid.sspeed = d; } err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &vid.antialias); vid.usingGL = gl; diff --git a/mapeditor.cpp b/mapeditor.cpp index 9cb85b24..60d3d818 100644 --- a/mapeditor.cpp +++ b/mapeditor.cpp @@ -10,12 +10,241 @@ namespace hr { EX namespace mapeditor { + EX bool drawing_tool; + #if HDR enum eShapegroup { sgPlayer, sgMonster, sgItem, sgFloor, sgWall }; static const int USERSHAPEGROUPS = 5; #endif + + EX color_t dtfill = 0; + EX color_t dtcolor = 0x000000FF; + EX ld dtwidth = .02; - hyperpoint lstart; + /* drawing_tool shapes */ + struct dtshape { + cell *where; + color_t col, fill; + ld lw; + + virtual void rotate(const transmatrix& T) = 0; + virtual void save(hstream& hs) = 0; + virtual dtshape* load(hstream& hs) = 0; + virtual void draw(const transmatrix& V) = 0; + virtual ld distance(hyperpoint h) = 0; + virtual ~dtshape() {} + }; + + struct dtline : dtshape { + + hyperpoint s, e; + + void rotate(const transmatrix& T) override { + s = T * s; + e = T * e; + } + void save(hstream& hs) override { + hs.write(1); + hs.write(s); + hs.write(e); + } + dtshape *load(hstream& hs) override { + hs.read(s); + hs.read(e); + return this; + } + void draw(const transmatrix& V) override { + queueline(V*s, V*e, col, 2 + vid.linequality); + } + ld distance(hyperpoint h) override { + return hdist(s, h) + hdist(e, h) - hdist(s, e); + } + }; + + struct dtcircle : dtshape { + + hyperpoint s; ld radius; + + void rotate(const transmatrix& T) override { + s = T * s; + } + void save(hstream& hs) override { + hs.write(2); + hs.write(s); + hs.write(radius); + } + dtshape *load(hstream& hs) override { + hs.read(s); + hs.read(radius); + return this; + } + void draw(const transmatrix& V) override { + ld len = sin_auto(radius); + int ll = ceil(360 * len); + transmatrix W = V * rgpushxto0(s); + for(int i=0; i<=ll; i++) + curvepoint(W * xspinpush0(360*degree*i/ll, radius)); + queuecurve(col, fill, PPR::LINE); + } + + ld distance(hyperpoint h) override { + return abs(hdist(s, h) - radius); + } + }; + + struct dtfree : dtshape { + + vector lh; + + void rotate(const transmatrix& T) override { + for(auto& h: lh) h = T * h; + } + void save(hstream& hs) override { + hs.write(3); + hs.write(lh); + } + dtshape *load(hstream& hs) override { + hs.read(lh); + return this; + } + void draw(const transmatrix& V) override { + for(auto& p: lh) curvepoint(V*p); + queuecurve(col, fill, PPR::LINE); + } + + ld distance(hyperpoint h) override { + ld mind = 99; + for(auto h1: lh) mind = min(mind, hdist(h, h1)); + return mind; + } + }; + + vector> dtshapes; + + EX void draw_dtshapes() { + for(auto& shp: dtshapes) { + if(shp == nullptr) continue; + auto& sh = *shp; + cell*& c = sh.where; + for(const transmatrix& V: current_display->all_drawn_copies[c]) { + dynamicval lw(vid.linewidth, vid.linewidth * sh.lw); + sh.draw(V); + } + } + + if(drawing_tool && (cmode & sm::DRAW)) { + dynamicval lw(vid.linewidth, vid.linewidth * dtwidth * 100); + if(holdmouse && mousekey == 'c') + queue_hcircle(rgpushxto0(lstart), hdist(lstart, mouseh)); + else if(holdmouse && mousekey == 'l') + queueline(lstart, mouseh, dtcolor, 4 + vid.linequality, PPR::LINE); + else if(!holdmouse) { + transmatrix T = rgpushxto0(mouseh); + queueline(T * xpush0(-.1), T * xpush0(.1), dtcolor); + queueline(T * ypush0(-.1), T * ypush0(.1), dtcolor); + } + } + } + + /** dtshapes takes ownership of sh */ + void dt_add(cell *where, dtshape *sh) { + sh->where = where; + sh->col = dtcolor; + sh->fill = dtfill; + sh->lw = dtwidth * 100; + + dtshapes.push_back(unique_ptr(sh)); + } + + EX void dt_add_line(hyperpoint h1, hyperpoint h2, int maxl) { + if(hdist(h1, h2) > 1 && maxl > 0) { + hyperpoint h3 = mid(h1, h2); + dt_add_line(h1, h3, maxl-1); + dt_add_line(h3, h2, maxl-1); + return; + } + cell *b = centerover; + transmatrix T = rgpushxto0(h1); + + auto T1 = inverse(ggmatrix(b)) * T; + virtualRebase(b, T1); + + auto l = new dtline; + l->s = tC0(T1); + l->e = T1 * gpushxto0(h1) * h2; + dt_add(b, l); + } + + EX void dt_add_circle(hyperpoint h1, hyperpoint h2) { + cell *b = centerover; + + auto d = hdist(h1, h2); + + h1 = inverse(ggmatrix(b)) * h1; + virtualRebase(b, h1); + + auto l = new dtcircle; + l->s = h1; + l->radius = d; + dt_add(b, l); + } + + dtshape *load_shape(hstream& hs) { + char type = hs.get(); + switch(type) { + case 1: + return (new dtline)->load(hs); + case 2: + return (new dtcircle)->load(hs); + case 3: + return (new dtfree)->load(hs); + default: + return nullptr; + } + } + + dtfree *cfree; + cell *cfree_at; + transmatrix cfree_T; + + EX void dt_add_free(hyperpoint h) { + + cell *b = centerover; + transmatrix T = rgpushxto0(h); + auto T1 = inverse(ggmatrix(b)) * T; + virtualRebase(b, T1); + + if(cfree) + cfree->lh.push_back(cfree_T * h); + + if(b != cfree_at && !(dtfill && cfree_at)) { + cfree = new dtfree; + dt_add(b, cfree); + cfree_T = T1 * gpushxto0(h); + cfree->lh.push_back(cfree_T * h); + cfree_at = b; + } + } + + EX void dt_erase(hyperpoint h) { + ld nearest = 1; + int nearest_id = -1; + int id = -1; + for(auto& shp: dtshapes) { + id++; + if(shp == nullptr) continue; + auto& sh = *shp; + cell*& c = sh.where; + for(const transmatrix& V: current_display->all_drawn_copies[c]) { + ld dist = sh.distance(inverse(V) * h); + if(dist < nearest) nearest = dist, nearest_id = id; + } + } + if(nearest_id >= 0) + dtshapes.erase(dtshapes.begin() + nearest_id); + } + + EX hyperpoint lstart; cell *lstartcell; ld front_edit = 0.5; enum class eFront { sphere_camera, sphere_center, equidistants, const_x, const_y }; @@ -43,16 +272,16 @@ EX namespace mapeditor { // mx = xb + ssiz*xpos + mrx * scale // mx = xb + ssiz*xpos' + mrx * scale' - ld mrx = (.0 + mousex - current_display->xcenter) / vid.scale; - ld mry = (.0 + mousey - current_display->ycenter) / vid.scale; + ld mrx = (.0 + mousex - current_display->xcenter) / vpconf.scale; + ld mry = (.0 + mousey - current_display->ycenter) / vpconf.scale; if(vid.xres > vid.yres) { - vid.xposition += (vid.scale - vid.scale*z) * mrx / current_display->scrsize; - vid.yposition += (vid.scale - vid.scale*z) * mry / current_display->scrsize; + vpconf.xposition += (vpconf.scale - vpconf.scale*z) * mrx / current_display->scrsize; + vpconf.yposition += (vpconf.scale - vpconf.scale*z) * mry / current_display->scrsize; } - vid.scale *= z; - // printf("scale = " LDF "\n", vid.scale); + vpconf.scale *= z; + // printf("scale = " LDF "\n", vpconf.scale); #if CAP_TEXTURE // display(texture::itt); texture::config.itt = xyscale(texture::config.itt, 1/z); @@ -101,6 +330,39 @@ namespace mapstream { vector cellbyid; vector relspin; + void load_drawing_tool(fhstream& hs) { + using namespace mapeditor; + if(hs.vernum < 0xA82A) return; + int i = hs.get(); + while(i--) { + auto sh = load_shape(hs); + if(!sh) continue; + hs.read(sh->col); + hs.read(sh->fill); + hs.read(sh->lw); + int id = hs.get(); + sh->where = cellbyid[id]; + sh->rotate(spin(currentmap->spin_angle(sh->where, relspin[id]) - currentmap->spin_angle(sh->where, 0))); + dtshapes.push_back(unique_ptr(sh)); + } + } + + void save_drawing_tool(hstream& hs) { + using namespace mapeditor; + hs.write(isize(dtshapes)); + for(auto& shp: dtshapes) { + if(shp == nullptr) { hs.write(0); } + else { + auto& sh = *shp; + sh.save(hs); + hs.write(sh.col); + hs.write(sh.fill); + hs.write(sh.lw); + hs.write(cellids[sh.where]); + } + } + } + void addToQueue(cell* c) { if(cellids.count(c)) return; @@ -325,6 +587,8 @@ namespace mapstream { int32_t n = -1; f.write(n); int32_t id = cellids.count(cwt.at) ? cellids[cwt.at] : -1; f.write(id); + + save_drawing_tool(f); f.write(vid.always3); f.write(mutantphase); @@ -507,6 +771,8 @@ namespace mapstream { savecount = 0; savetime = 0; cheater = 1; + load_drawing_tool(f); + dynamicval a3(vid.always3, vid.always3); if(f.vernum >= 0xA616) { f.read(vid.always3); geom3::apply_always3(); } @@ -730,7 +996,7 @@ namespace mapeditor { displayButton(8, vid.yres-8-fs*11, XLAT("F1 = help"), SDLK_F1, 0); displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0); displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0); - displayButton(8, vid.yres-8-fs*7, XLAT("F5 = restart"), SDLK_F5, 0); + displayButton(8, vid.yres-8-fs*7, drawing_tool ? XLAT("F5 = clear") : XLAT("F5 = restart"), SDLK_F5, 0); #if CAP_SHOT displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0); #endif @@ -983,6 +1249,32 @@ namespace mapeditor { if(d == -1 && fix) d = hrand(mouseover->type); return cellwalker(mouseover, d); } + + void save_level() { + dialog::openFileDialog(levelfile, XLAT("level to save:"), ".lev", [] () { + if(mapstream::saveMap(levelfile.c_str())) { + addMessage(XLAT("Map saved to %1", levelfile)); + return true; + } + else { + addMessage(XLAT("Failed to save map to %1", levelfile)); + return false; + } + }); + } + + void load_level() { + dialog::openFileDialog(levelfile, XLAT("level to load:"), ".lev", [] () { + if(mapstream::loadMap(levelfile.c_str())) { + addMessage(XLAT("Map loaded from %1", levelfile)); + return true; + } + else { + addMessage(XLAT("Failed to load map from %1", levelfile)); + return false; + } + }); + } void showList() { dialog::v.clear(); @@ -1083,31 +1375,10 @@ namespace mapeditor { else if(uni == 'G') push_debug_screen(); else if(sym == SDLK_F5) { - restart_game(); + dialog::push_confirm_dialog([] { restart_game(); }, XLAT("Are you sure you want to clear the map?")); } - else if(sym == SDLK_F2) { - dialog::openFileDialog(levelfile, XLAT("level to save:"), ".lev", [] () { - if(mapstream::saveMap(levelfile.c_str())) { - addMessage(XLAT("Map saved to %1", levelfile)); - return true; - } - else { - addMessage(XLAT("Failed to save map to %1", levelfile)); - return false; - } - }); - } - else if(sym == SDLK_F3) - dialog::openFileDialog(levelfile, XLAT("level to load:"), ".lev", [] () { - if(mapstream::loadMap(levelfile.c_str())) { - addMessage(XLAT("Map loaded from %1", levelfile)); - return true; - } - else { - addMessage(XLAT("Failed to load map from %1", levelfile)); - return false; - } - }); + else if(sym == SDLK_F2) save_level(); + else if(sym == SDLK_F3) load_level(); #if CAP_SHOT else if(sym == SDLK_F6) { pushScreen(shot::menu); @@ -1188,7 +1459,7 @@ namespace mapeditor { unsigned gridcolor = 0xC0C0C040; hyperpoint in_front_dist(ld d) { - return direct_exp(lp_iapply(ztangent(d)), 100); + return direct_exp(lp_iapply(ztangent(d))); } hyperpoint find_mouseh3() { @@ -1203,7 +1474,7 @@ namespace mapeditor { ld d1 = front_edit; hyperpoint h1 = in_front_dist(d); if(front_config == eFront::sphere_center) - d1 = geo_dist(drawtrans * C0, h1, iTable); + d1 = geo_dist(drawtrans * C0, h1); if(front_config == eFront::equidistants) { hyperpoint h = idt * in_front_dist(d); d1 = asin_auto(h[2]); @@ -1232,6 +1503,7 @@ namespace mapeditor { ld equi_range = 1; EX void drawGrid() { + if(!drawcell) drawcell = cwt.at; color_t lightgrid = gridcolor; lightgrid -= (lightgrid & 0xFF) / 2; transmatrix d2 = drawtrans * rgpushxto0(ccenter) * rspintox(gpushxto0(ccenter) * coldcenter); @@ -1246,7 +1518,7 @@ namespace mapeditor { } if(front_config == eFront::sphere_center) for(int i=0; i<4; i+=2) { auto pt = [&] (ld a, ld b) { - return d2 * direct_exp(spin(a*degree) * cspin(0, 2, b*degree) * xtangent(front_edit), 100); + return d2 * direct_exp(spin(a*degree) * cspin(0, 2, b*degree) * xtangent(front_edit)); }; for(int ai=0; ai> 8) | ((texture::config.paint_color & 0xFF) << 24); + + bool freedraw = drawing_tool; + #if CAP_TEXTURE + bool intexture = texture::config.tstate == texture::tsActive; + freedraw |= intexture; + #else + always_false intexture; + #endif + + if(freedraw) { + + int tcolor = (dtcolor >> 8) | ((dtcolor & 0xFF) << 24); if(uni == '-' && !clickused) { - if(mousekey == 'l' || mousekey == 'c') { + if(mousekey == 'e') { + dt_erase(mouseh); + clickused = true; + } + else if(mousekey == 'l' || mousekey == 'c') { if(!holdmouse) lstart = mouseh, lstartcell = mouseover, holdmouse = true; } - else { + else if(intexture) { if(!holdmouse) texture::config.data.undoLock(); texture::drawPixel(mouseover, mouseh, tcolor); holdmouse = true; lstartcell = NULL; } + else { + dt_add_free(mouseh); + holdmouse = true; + } } if(sym == PSEUDOKEY_RELEASE) { printf("release\n"); - if(mousekey == 'l') { + if(mousekey == 'l' && intexture) { texture::config.data.undoLock(); texture::where = mouseover; texture::drawPixel(mouseover, mouseh, tcolor); texture::drawLine(mouseh, lstart, tcolor); lstartcell = NULL; } - if(mousekey == 'c') { + else if(mousekey == 'l') { + dt_add_line(mouseh, lstart, 10); + lstartcell = NULL; + } + else if(mousekey == 'c' && intexture) { texture::config.data.undoLock(); ld rad = hdist(lstart, mouseh); - int circp = int(1 + 3 * (circlelength(rad) / texture::penwidth)); + int circp = int(1 + 3 * (circlelength(rad) / dtwidth)); if(circp > 1000) circp = 1000; transmatrix T = rgpushxto0(lstart); texture::where = lstartcell; @@ -2037,13 +2361,21 @@ namespace mapeditor { texture::drawPixel(T * xspinpush0(2 * M_PI * i / circp, rad), tcolor); lstartcell = NULL; } + else if(mousekey == 'c') { + dt_add_circle(lstart, mouseh); + lstartcell = NULL; + } + else { + cfree = nullptr; + cfree_at = nullptr; + } } if(uni >= 1000 && uni < 1010) - texture::config.paint_color = texture_colors[uni - 1000 + 1]; + dtcolor = texture_colors[uni - 1000 + 1]; if(uni >= 2000 && uni < 2010) - texture::penwidth = brush_sizes[uni - 2000]; + dtwidth = brush_sizes[uni - 2000]; if(uni == '0') texture::texturesym = !texture::texturesym; @@ -2054,16 +2386,19 @@ namespace mapeditor { if(uni == 'p') { if(!clickused) - dialog::openColorDialog(texture::config.paint_color, texture_colors); + dialog::openColorDialog(dtcolor, texture_colors); + } + + if(uni == 'f') { + if(dtfill == dtcolor) + dtfill = 0; + else + dtfill = dtcolor; } if(uni == 'b') - dialog::editNumber(texture::penwidth, 0, 0.1, 0.005, 0.02, XLAT("brush size"), XLAT("brush size")); + dialog::editNumber(dtwidth, 0, 0.1, 0.005, 0.02, XLAT("brush size"), XLAT("brush size")); } -#else - (void)clickused; - if(0); -#endif else { dslayer %= USERLAYERS; @@ -2137,6 +2472,10 @@ namespace mapeditor { if(!cheater) patterns::displaycodes = false; if(!cheater) patterns::whichShape = 0; modelcell.clear(); + mapeditor::dtshapes.clear(); + mapeditor::cfree = nullptr; + mapeditor::cfree_at = nullptr; + drawcell = nullptr; }) + addHook(hooks_removecells, 0, [] () { modelcell.clear(); @@ -2159,8 +2498,7 @@ namespace mapeditor { transmatrix textrans; -#if CAP_TEXTURE - void queue_hcircle(transmatrix Ctr, ld radius) { + EX void queue_hcircle(transmatrix Ctr, ld radius) { vector pts; int circp = int(6 * pow(2, vid.linequality)); if(radius > 0.04) circp *= 2; @@ -2170,9 +2508,8 @@ namespace mapeditor { pts.push_back(Ctr * xspinpush0(M_PI*j*2/circp, radius)); for(int j=0; j= 500, '1'); - dialog::addBoolItem(XLAT(sphere ? "stereographic projection" : euclid ? "zoomed out" : "small Poincaré model"), vid.alpha == 1 && vid.scale < 1, '2'); - dialog::addBoolItem(XLAT(sphere ? "zoomed stereographic projection" : euclid ? "zoomed in" : "big Poincaré model"), vid.alpha == 1 && vid.scale >= 1, '3'); - dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vid.alpha == 0, '4'); + dialog::addBoolItem(XLAT("orthogonal projection"), vpconf.alpha >= 500, '1'); + dialog::addBoolItem(XLAT(sphere ? "stereographic projection" : euclid ? "zoomed out" : "small Poincaré model"), vpconf.alpha == 1 && vpconf.scale < 1, '2'); + dialog::addBoolItem(XLAT(sphere ? "zoomed stereographic projection" : euclid ? "zoomed in" : "big Poincaré model"), vpconf.alpha == 1 && vpconf.scale >= 1, '3'); + dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vpconf.alpha == 0, '4'); } else { dialog::addBoolItem(XLAT("first person perspective"), vid.yshift == 0 && vid.sspeed > -5, '1'); @@ -433,13 +433,25 @@ EX void showCreative() { #endif #if CAP_EDIT - dialog::addItem(XLAT("vector graphics editor"), 'g'); + dialog::addItem(XLAT("shape editor"), 'g'); dialog::add_action([] { + mapeditor::drawing_tool = false; pushScreen(mapeditor::showDrawEditor); mapeditor::initdraw(cwt.at); }); #endif +#if CAP_EDIT + dialog::addItem(XLAT("drawing tool"), 'd'); + dialog::add_action([] { + dialog::cheat_if_confirmed([] { + mapeditor::drawing_tool = true; + pushScreen(mapeditor::showDrawEditor); + mapeditor::initdraw(cwt.at); + }); + }); +#endif + // display modes #if CAP_MODEL if(GDIM == 2) { @@ -875,11 +887,11 @@ EX void showStartMenu() { specialland = laHalloween; set_geometry(gSphere); start_game(); - vid.alpha = 999; - vid.scale = 998; + pconf.alpha = 999; + pconf.scale = 998; } } -#if CAP_RACING +#if CAP_RACING && MAXMDIM >= 4 else if(uni == 'r' - 96) { popScreenAll(); resetModes(); @@ -889,13 +901,13 @@ EX void showStartMenu() { specialland = racing::race_lands[rand() % isize(racing::race_lands)]; start_game(); pmodel = mdBand; - models::model_orientation = racing::race_angle; + pconf.model_orientation = racing::race_angle; racing::race_advance = 1; vid.yshift = 0; - vid.camera_angle = 0; - vid.xposition = 0; - vid.yposition = 0; - vid.scale = 1; + pconf.camera_angle = 0; + pconf.xposition = 0; + pconf.yposition = 0; + pconf.scale = 1; vid.use_smart_range = 1; vid.smart_range_detail = 3; } diff --git a/models.cpp b/models.cpp index 733b75a1..3974d4f2 100644 --- a/models.cpp +++ b/models.cpp @@ -115,20 +115,13 @@ inline bool mdPseudocylindrical() { return mdBandAny() && !(mdinf[pmodel].flags EX namespace models { - EX string formula = "z^2"; - EX eModel basic_model; - EX ld rotation = 0; EX ld rotation_xz = 90; EX ld rotation_xy2 = 90; EX int do_rotate = 1; - EX ld model_orientation, halfplane_scale, model_orientation_yz; - EX ld clip_min, clip_max; EX ld ocos, osin, ocos_yz, osin_yz; EX ld cos_ball, sin_ball; EX bool model_straight, model_straight_yz; - EX ld top_z = 5; - EX ld model_transition = 1; #if HDR // screen coordinates to logical coordinates: apply_orientation(x,y) @@ -146,53 +139,43 @@ EX namespace models { return spin(rotation_xy2 * degree) * cspin(0, 2, -rotation_xz * degree) * spin(rotation * degree); } - EX ld spiral_angle = 70; - EX ld spiral_x = 10; - EX ld spiral_y = 7; int spiral_id = 7; - EX bool use_atan = false; EX cld spiral_multiplier; - EX ld right_spiral_multiplier = 1; - EX ld any_spiral_multiplier = 1; - EX ld sphere_spiral_multiplier = 2; - EX ld spiral_cone = 360; EX ld spiral_cone_rad; EX bool ring_not_spiral; /** the matrix to rotate the Euclidean view from the standard coordinates to the screen coordinates */ EX transmatrix euclidean_spin; - EX ld product_z_scale = 1; - EX void configure() { - ld ball = -vid.ballangle * degree; + ld ball = -pconf.ballangle * degree; cos_ball = cos(ball), sin_ball = sin(ball); - ocos = cos(model_orientation * degree); - osin = sin(model_orientation * degree); - ocos_yz = cos(model_orientation_yz * degree); - osin_yz = sin(model_orientation_yz * degree); + ocos = cos(pconf.model_orientation * degree); + osin = sin(pconf.model_orientation * degree); + ocos_yz = cos(pconf.model_orientation_yz * degree); + osin_yz = sin(pconf.model_orientation_yz * degree); model_straight = (ocos > 1 - 1e-9); model_straight_yz = GDIM == 2 || (ocos_yz > 1-1e-9); if(history::on) history::apply(); if(!euclid) { - ld b = spiral_angle * degree; + ld b = pconf.spiral_angle * degree; ld cos_spiral = cos(b); ld sin_spiral = sin(b); - spiral_cone_rad = spiral_cone * degree; + spiral_cone_rad = pconf.spiral_cone * degree; ring_not_spiral = abs(cos_spiral) < 1e-3; ld mul = 1; - if(sphere) mul = .5 * sphere_spiral_multiplier; - else if(ring_not_spiral) mul = right_spiral_multiplier; - else mul = any_spiral_multiplier * cos_spiral; + if(sphere) mul = .5 * pconf.sphere_spiral_multiplier; + else if(ring_not_spiral) mul = pconf.right_spiral_multiplier; + else mul = pconf.any_spiral_multiplier * cos_spiral; spiral_multiplier = cld(cos_spiral, sin_spiral) * cld(spiral_cone_rad * mul / 2., 0); } if(euclid) { euclidean_spin = pispin * inverse(cview() * master_relative(centerover, true)); euclidean_spin = gpushxto0(euclidean_spin * C0) * euclidean_spin; - hyperpoint h = inverse(euclidean_spin) * (C0 + (euc::eumove(gp::loc{1,0})*C0 - C0) * spiral_x + (euc::eumove(gp::loc{0,1})*C0 - C0) * spiral_y); + hyperpoint h = inverse(euclidean_spin) * (C0 + (euc::eumove(gp::loc{1,0})*C0 - C0) * vpconf.spiral_x + (euc::eumove(gp::loc{0,1})*C0 - C0) * vpconf.spiral_y); spiral_multiplier = cld(0, 2 * M_PI) / cld(h[0], h[1]); } @@ -222,18 +205,27 @@ EX namespace models { return true; } - EX bool model_has_orientation() { + EX bool has_orientation(eModel m) { return - among(pmodel, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic) || mdBandAny(); + among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic) || mdBandAny(); } - EX bool model_has_transition() { - return among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdBand) && GDIM == 2; + EX bool is_perspective(eModel m) { + return among(m, mdPerspective, mdGeodesic); + } + + EX bool is_3d(const projection_configuration& p) { + if(GDIM == 3) return true; + return among(p.model, mdBall, mdHyperboloid, mdHemisphere) || (p.model == mdSpiral && p.spiral_cone != 360); } - EX bool product_model() { + EX bool has_transition(eModel m) { + return among(m, mdJoukowsky, mdJoukowskyInverted, mdBand) && GDIM == 2; + } + + EX bool product_model(eModel m) { if(!prod) return false; - if(among(pmodel, mdPerspective, mdHyperboloid, mdEquidistant)) return false; + if(among(m, mdPerspective, mdHyperboloid, mdEquidistant)) return false; return true; } @@ -286,16 +278,16 @@ EX namespace models { dialog::editNumber(spiral_id, 0, isize(torus_zeros)-1, 1, 10, XLAT("match the period of the torus"), ""); dialog::reaction = [] () { auto& co = torus_zeros[spiral_id]; - spiral_x = co.first; - spiral_y = co.second; + vpconf.spiral_x = co.first; + vpconf.spiral_y = co.second; }; dialog::bound_low(0); dialog::bound_up(isize(torus_zeros)-1); } EX void edit_formula() { - if(pmodel != mdFormula) basic_model = pmodel; - dialog::edit_string(formula, "formula", + if(vpconf.model != mdFormula) vpconf.basic_model = vpconf.model; + dialog::edit_string(vpconf.formula, "formula", XLAT( "This lets you specify the projection as a formula f. " "The formula has access to the value 'z', which is a complex number corresponding to the (x,y) coordinates in the currently selected model; " @@ -314,12 +306,12 @@ EX namespace models { curvepoint(point2(a*current_display->radius, +M_PI/2*current_display->radius)); queuecurve(forecolor, 0, PPR::LINE); } - queuereset(pmodel, PPR::LINE); + queuereset(vpconf.model, PPR::LINE); quickqueue(); }; #endif dialog::reaction_final = [] () { - pmodel = mdFormula; + vpconf.model = mdFormula; }; } @@ -351,24 +343,25 @@ EX namespace models { cmode = sm::SIDE | sm::MAYDARK | sm::CENTER; gamescreen(0); dialog::init(XLAT("models & projections")); + USING_NATIVE_GEOMETRY_IN_RUG; for(int i=0; i= 1) { - ld phi = acos(sqrt(1/vid.stretch)); + if(sphere && vpconf.stretch >= 1) { + ld phi = acos(sqrt(1/vpconf.stretch)); dialog::addInfo(XLAT("The current value makes the map conformal at the latitude of %1 (%2°).", fts(phi), fts(phi / degree))); } - else if(hyperbolic && abs(vid.stretch) <= 1 && abs(vid.stretch) >= 1e-9) { - ld phi = acosh(abs(sqrt(1/vid.stretch))); + else if(hyperbolic && abs(vpconf.stretch) <= 1 && abs(vpconf.stretch) >= 1e-9) { + ld phi = acosh(abs(sqrt(1/vpconf.stretch))); dialog::addInfo(XLAT("The current value makes the map conformal %1 units from the main line.", fts(phi))); } else dialog::addInfo(""); @@ -406,9 +399,12 @@ EX namespace models { EX void model_menu() { cmode = sm::SIDE | sm::MAYDARK | sm::CENTER; gamescreen(0); + USING_NATIVE_GEOMETRY_IN_RUG; dialog::init(XLAT("models & projections")); - dialog::addSelItem(XLAT("projection type"), get_model_name(pmodel), 'm'); + auto vpmodel = vpconf.model; + + dialog::addSelItem(XLAT("projection type"), get_model_name(vpmodel), 'm'); dialog::add_action_push(model_list); if(nonisotropic && !sl2) @@ -420,61 +416,61 @@ EX namespace models { dialog::lastItem().value += " " + its(rotation) + "°"; else dialog::lastItem().value += " " + its(rotation) + "°" + its(rotation_xz) + "°" + its(rotation_xy2) + "°"; - dialog::add_action([] { edit_rotation(models::rotation); }); + dialog::add_action([] { edit_rotation(rotation); }); - // if(pmodel == mdBand && sphere) + // if(vpmodel == mdBand && sphere) if(!in_perspective()) { - dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z'); + dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z'); dialog::add_action(editScale); } - if(abs(vid.alpha-1) > 1e-3 && pmodel != mdBall && pmodel != mdHyperboloid && pmodel != mdHemisphere && pmodel != mdDisk) { + if(abs(pconf.alpha-1) > 1e-3 && vpmodel != mdBall && vpmodel != mdHyperboloid && vpmodel != mdHemisphere && vpmodel != mdDisk) { dialog::addBreak(50); dialog::addInfo("NOTE: this works 'correctly' only if the Poincaré model/stereographic projection is used."); dialog::addBreak(50); } - if(among(pmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) { - dialog::addSelItem(XLAT("projection distance"), fts(vid.alpha) + " (" + current_proj_name() + ")", 'p'); + if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) { + dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", 'p'); dialog::add_action(projectionDialog); } - if(model_has_orientation()) { - dialog::addSelItem(XLAT("model orientation"), fts(model_orientation) + "°", 'l'); + if(has_orientation(vpmodel)) { + dialog::addSelItem(XLAT("model orientation"), fts(vpconf.model_orientation) + "°", 'l'); dialog::add_action([] () { - dialog::editNumber(model_orientation, 0, 360, 90, 0, XLAT("model orientation"), ""); + dialog::editNumber(vpconf.model_orientation, 0, 360, 90, 0, XLAT("model orientation"), ""); }); if(GDIM == 3) { - dialog::addSelItem(XLAT("model orientation (y/z plane)"), fts(model_orientation_yz) + "°", 'L'); + dialog::addSelItem(XLAT("model orientation (y/z plane)"), fts(vpconf.model_orientation_yz) + "°", 'L'); dialog::add_action([] () { - dialog::editNumber(model_orientation_yz, 0, 360, 90, 0, XLAT("model orientation (y/z plane)"), ""); + dialog::editNumber(vpconf.model_orientation_yz, 0, 360, 90, 0, XLAT("model orientation (y/z plane)"), ""); }); } } - if(GDIM == 3 && pmodel != mdPerspective) { + if(GDIM == 3 && vpmodel != mdPerspective) { const string cliphelp = XLAT( "Your view of the 3D model is naturally bounded from four directions by your window. " "Here, you can also set up similar bounds in the Z direction. Radius of the ball/band " "models, and the distance from the center to the plane in the halfspace model, are 1.\n\n"); - dialog::addSelItem(XLAT("near clipping plane"), fts(clip_max), 'c'); + dialog::addSelItem(XLAT("near clipping plane"), fts(vpconf.clip_max), 'c'); dialog::add_action([cliphelp] () { - dialog::editNumber(clip_max, -10, 10, 0.2, 1, XLAT("near clipping plane"), + dialog::editNumber(vpconf.clip_max, -10, 10, 0.2, 1, XLAT("near clipping plane"), cliphelp + XLAT("Objects with Z coordinate " "bigger than this parameter are not shown. This is useful with the models which " "extend infinitely in the Z direction, or if you want things close to your character " "to be not obscured by things closer to the camera.")); }); - dialog::addSelItem(XLAT("far clipping plane"), fts(clip_min), 'C'); + dialog::addSelItem(XLAT("far clipping plane"), fts(vpconf.clip_min), 'C'); dialog::add_action([cliphelp] () { - dialog::editNumber(clip_min, -10, 10, 0.2, -1, XLAT("far clipping plane"), + dialog::editNumber(vpconf.clip_min, -10, 10, 0.2, -1, XLAT("far clipping plane"), cliphelp + XLAT("Objects with Z coordinate " "smaller than this parameter are not shown; it also affects the fog effect" " (near clipping plane = 0% fog, far clipping plane = 100% fog).")); }); } - if(pmodel == mdPolynomial) { + if(vpmodel == mdPolynomial) { dialog::addSelItem(XLAT("coefficient"), fts(polygonal::coefr[polygonal::coefid]), 'x'); dialog::add_action([] () { @@ -496,26 +492,26 @@ EX namespace models { }); } - if(pmodel == mdHalfplane) { - dialog::addSelItem(XLAT("half-plane scale"), fts(halfplane_scale), 'b'); + if(vpmodel == mdHalfplane) { + dialog::addSelItem(XLAT("half-plane scale"), fts(vpconf.halfplane_scale), 'b'); dialog::add_action([] () { - dialog::editNumber(halfplane_scale, 0, 2, 0.25, 1, XLAT("half-plane scale"), ""); + dialog::editNumber(vpconf.halfplane_scale, 0, 2, 0.25, 1, XLAT("half-plane scale"), ""); }); } - if(pmodel == mdRotatedHyperboles) { - dialog::addBoolItem_action(XLAT("use atan to make it finite"), use_atan, 'x'); + if(vpmodel == mdRotatedHyperboles) { + dialog::addBoolItem_action(XLAT("use atan to make it finite"), vpconf.use_atan, 'x'); } - if(pmodel == mdBall) { - dialog::addSelItem(XLAT("projection in ball model"), fts(vid.ballproj), 'x'); + if(vpmodel == mdBall) { + dialog::addSelItem(XLAT("projection in ball model"), fts(vpconf.ballproj), 'x'); dialog::add_action([] () { - dialog::editNumber(vid.ballproj, 0, 100, .1, 0, XLAT("projection in ball model"), + dialog::editNumber(vpconf.ballproj, 0, 100, .1, 0, XLAT("projection in ball model"), "This parameter affects the ball model the same way as the projection parameter affects the disk model."); }); } - if(pmodel == mdPolygonal) { + if(vpmodel == mdPolygonal) { dialog::addSelItem(XLAT("polygon sides"), its(polygonal::SI), 'x'); dialog::add_action([] () { dialog::editNumber(polygonal::SI, 3, 10, 1, 4, XLAT("polygon sides"), ""); @@ -534,90 +530,91 @@ EX namespace models { }); } - if(pmodel == mdBall || pmodel == mdHyperboloid || pmodel == mdHemisphere || (pmodel == mdSpiral && spiral_cone != 360)) { - dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vid.ballangle) + "°", 'b'); + if(is_3d(vpconf) && GDIM == 2) { + dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vpconf.ballangle) + "°", 'b'); dialog::add_action(config_camera_rotation); } - if(pmodel == mdHyperboloid) { - dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(top_z), 'l'); + if(vpmodel == mdHyperboloid) { + dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(vpconf.top_z), 'l'); dialog::add_action([](){ - dialog::editNumber(top_z, 1, 20, 0.25, 4, XLAT("maximum z coordinate to show"), ""); + dialog::editNumber(vpconf.top_z, 1, 20, 0.25, 4, XLAT("maximum z coordinate to show"), ""); }); } - if(model_has_transition()) { - dialog::addSelItem(XLAT("model transition"), fts(model_transition), 't'); + if(has_transition(vpmodel)) { + dialog::addSelItem(XLAT("model transition"), fts(vpconf.model_transition), 't'); dialog::add_action([]() { - dialog::editNumber(model_transition, 0, 1, 0.1, 1, XLAT("model transition"), + dialog::editNumber(vpconf.model_transition, 0, 1, 0.1, 1, XLAT("model transition"), "You can change this parameter for a transition from another model to this one." ); }); } - if(among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) { - dialog::addSelItem(XLAT("Möbius transformations"), fts(vid.skiprope) + "°", 'S'); + if(among(vpmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) { + dialog::addSelItem(XLAT("Möbius transformations"), fts(vpconf.skiprope) + "°", 'S'); dialog::add_action([](){ - dialog::editNumber(vid.skiprope, 0, 360, 15, 0, XLAT("Möbius transformations"), ""); + dialog::editNumber(vpconf.skiprope, 0, 360, 15, 0, XLAT("Möbius transformations"), ""); }); } - if(pmodel == mdHemisphere && euclid) { - dialog::addSelItem(XLAT("parameter"), fts(vid.euclid_to_sphere), 'l'); + if(vpmodel == mdHemisphere && euclid) { + dialog::addSelItem(XLAT("parameter"), fts(vpconf.euclid_to_sphere), 'l'); dialog::add_action([] () { - dialog::editNumber(vid.euclid_to_sphere, 0, 10, .1, 1, XLAT("parameter"), + dialog::editNumber(vpconf.euclid_to_sphere, 0, 10, .1, 1, XLAT("parameter"), "Stereographic projection to a sphere. Choose the radius of the sphere." ); dialog::scaleLog(); }); } - if(among(pmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) { - dialog::addSelItem(XLAT("parameter"), fts(vid.twopoint_param), 'b'); - dialog::add_action([](){ - dialog::editNumber(vid.twopoint_param, 1e-3, 10, .1, 1, XLAT("parameter"), + if(among(vpmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) { + dialog::addSelItem(XLAT("parameter"), fts(vpconf.twopoint_param), 'b'); + dialog::add_action([vpmodel](){ + dialog::editNumber(vpconf.twopoint_param, 1e-3, 10, .1, 1, XLAT("parameter"), + s0 + (vpmodel == mdTwoPoint ? "This model maps the world so that the distances from two points " - "are kept. This parameter gives the distance from the two points to " + "are kept. " : "") + "This parameter gives the distance from the two points to " "the center." ); dialog::scaleLog(); }); } - if(pmodel == mdFisheye) { - dialog::addSelItem(XLAT("parameter"), fts(vid.fisheye_param), 'b'); + if(vpmodel == mdFisheye) { + dialog::addSelItem(XLAT("parameter"), fts(vpconf.fisheye_param), 'b'); dialog::add_action([](){ - dialog::editNumber(vid.fisheye_param, 1e-3, 10, .1, 1, XLAT("parameter"), + dialog::editNumber(vpconf.fisheye_param, 1e-3, 10, .1, 1, XLAT("parameter"), "Size of the fish eye." ); dialog::scaleLog(); }); } - if(pmodel == mdCollignon) { - dialog::addSelItem(XLAT("parameter"), fts(vid.collignon_parameter) + (vid.collignon_reflected ? " (r)" : ""), 'b'); + if(vpmodel == mdCollignon) { + dialog::addSelItem(XLAT("parameter"), fts(vpconf.collignon_parameter) + (vpconf.collignon_reflected ? " (r)" : ""), 'b'); dialog::add_action([](){ - dialog::editNumber(vid.collignon_parameter, -1, 1, .1, 1, XLAT("parameter"), + dialog::editNumber(vpconf.collignon_parameter, -1, 1, .1, 1, XLAT("parameter"), "" ); dialog::extra_options = [] { - dialog::addBoolItem_action(XLAT("reflect"), vid.collignon_reflected, 'R'); + dialog::addBoolItem_action(XLAT("reflect"), vpconf.collignon_reflected, 'R'); }; }); } - if(pmodel == mdSpiral && !euclid) { - dialog::addSelItem(XLAT("spiral angle"), fts(spiral_angle) + "°", 'x'); + if(vpmodel == mdSpiral && !euclid) { + dialog::addSelItem(XLAT("spiral angle"), fts(vpconf.spiral_angle) + "°", 'x'); dialog::add_action([](){ - dialog::editNumber(spiral_angle, 0, 360, 15, 0, XLAT("spiral angle"), + dialog::editNumber(vpconf.spiral_angle, 0, 360, 15, 0, XLAT("spiral angle"), XLAT("set to 90° for the ring projection") ); }); ld& which = - sphere ? sphere_spiral_multiplier : - ring_not_spiral ? right_spiral_multiplier : - any_spiral_multiplier; + sphere ? vpconf.sphere_spiral_multiplier : + ring_not_spiral ? vpconf.right_spiral_multiplier : + vpconf.any_spiral_multiplier; dialog::addSelItem(XLAT("spiral multiplier"), fts(which) + "°", 'M'); dialog::add_action([&which](){ @@ -631,20 +628,20 @@ EX namespace models { ); }); - dialog::addSelItem(XLAT("spiral cone"), fts(spiral_cone) + "°", 'C'); + dialog::addSelItem(XLAT("spiral cone"), fts(vpconf.spiral_cone) + "°", 'C'); dialog::add_action([](){ - dialog::editNumber(spiral_cone, 0, 360, -45, 360, XLAT("spiral cone"), ""); + dialog::editNumber(vpconf.spiral_cone, 0, 360, -45, 360, XLAT("spiral cone"), ""); }); } - if(pmodel == mdSpiral && euclid) { - dialog::addSelItem(XLAT("spiral period: x"), fts(spiral_x), 'x'); + if(vpmodel == mdSpiral && euclid) { + dialog::addSelItem(XLAT("spiral period: x"), fts(vpconf.spiral_x), 'x'); dialog::add_action([](){ - dialog::editNumber(spiral_x, -20, 20, 1, 10, XLAT("spiral period: x"), ""); + dialog::editNumber(vpconf.spiral_x, -20, 20, 1, 10, XLAT("spiral period: x"), ""); }); - dialog::addSelItem(XLAT("spiral period: y"), fts(spiral_y), 'y'); + dialog::addSelItem(XLAT("spiral period: y"), fts(vpconf.spiral_y), 'y'); dialog::add_action([](){ - dialog::editNumber(spiral_y, -20, 20, 1, 10, XLAT("spiral period: y"), ""); + dialog::editNumber(vpconf.spiral_y, -20, 20, 1, 10, XLAT("spiral period: y"), ""); }); if(euclid && quotient) { dialog::addSelItem(XLAT("match the period"), its(spiral_id), 'n'); @@ -652,13 +649,13 @@ EX namespace models { } } - dialog::addSelItem(XLAT("vertical stretch"), fts(vid.stretch), 's'); + dialog::addSelItem(XLAT("vertical stretch"), fts(vpconf.stretch), 's'); dialog::add_action(edit_stretch); - if(product_model()) { - dialog::addSelItem(XLAT("product Z stretch"), fts(product_z_scale), 'Z'); + if(product_model(vpmodel)) { + dialog::addSelItem(XLAT("product Z stretch"), fts(vpconf.product_z_scale), 'Z'); dialog::add_action([] { - dialog::editNumber(product_z_scale, 0.1, 10, 0.1, 1, XLAT("product Z stretch"), ""); + dialog::editNumber(vpconf.product_z_scale, 0.1, 10, 0.1, 1, XLAT("product Z stretch"), ""); dialog::scaleLog(); }); } @@ -667,7 +664,7 @@ EX namespace models { bool shaderside_projection = get_shader_flags() & SF_DIRECT; if(vid.consider_shader_projection && !shaderside_projection) dialog::lastItem().value = XLAT("N/A"); - if(vid.consider_shader_projection && shaderside_projection && pmodel) + if(vid.consider_shader_projection && shaderside_projection && vpmodel) dialog::lastItem().value += XLAT(" (2D only)"); dialog::add_action([] { vid.consider_shader_projection = !vid.consider_shader_projection; }); @@ -677,7 +674,7 @@ EX namespace models { dialog::addItem(XLAT("history mode"), 'a'); dialog::add_action_push(history::history_menu); #if CAP_RUG - if(GDIM == 2) { + if(GDIM == 2 || rug::rugged) { dialog::addItem(XLAT("hypersian rug mode"), 'u'); dialog::add_action_push(rug::show); } @@ -707,39 +704,39 @@ EX namespace models { shift_arg_formula(history::extra_line_steps); } else if(argis("-stretch")) { - PHASEFROM(2); shift_arg_formula(vid.stretch); + PHASEFROM(2); shift_arg_formula(vpconf.stretch); } else if(argis("-PM")) { - PHASEFROM(2); shift(); pmodel = read_model(args()); - if(pmodel == mdFormula) { - shift(); basic_model = eModel(argi()); - shift(); formula = args(); + PHASEFROM(2); shift(); vpconf.model = read_model(args()); + if(vpconf.model == mdFormula) { + shift(); vpconf.basic_model = eModel(argi()); + shift(); vpconf.formula = args(); } } else if(argis("-ballangle")) { PHASEFROM(2); - shift_arg_formula(vid.ballangle); + shift_arg_formula(vpconf.ballangle); } else if(argis("-topz")) { PHASEFROM(2); - shift_arg_formula(models::top_z); + shift_arg_formula(vpconf.top_z); } else if(argis("-twopoint")) { PHASEFROM(2); - shift_arg_formula(vid.twopoint_param); + shift_arg_formula(vpconf.twopoint_param); } else if(argis("-hp")) { PHASEFROM(2); - shift_arg_formula(models::halfplane_scale); + shift_arg_formula(vpconf.halfplane_scale); } else if(argis("-mori")) { PHASEFROM(2); - shift_arg_formula(models::model_orientation); + shift_arg_formula(vpconf.model_orientation); } else if(argis("-mori2")) { PHASEFROM(2); - shift_arg_formula(models::model_orientation); - shift_arg_formula(models::model_orientation_yz); + shift_arg_formula(vpconf.model_orientation); + shift_arg_formula(vpconf.model_orientation_yz); } else if(argis("-crot")) { PHASEFROM(2); @@ -749,43 +746,43 @@ EX namespace models { } else if(argis("-clip")) { PHASEFROM(2); - shift_arg_formula(models::clip_min); - shift_arg_formula(models::clip_max); + shift_arg_formula(vpconf.clip_min); + shift_arg_formula(vpconf.clip_max); } else if(argis("-mtrans")) { PHASEFROM(2); - shift_arg_formula(models::model_transition); + shift_arg_formula(vpconf.model_transition); } else if(argis("-sang")) { PHASEFROM(2); - shift_arg_formula(models::spiral_angle); + shift_arg_formula(vpconf.spiral_angle); if(sphere) - shift_arg_formula(models::sphere_spiral_multiplier); - else if(models::spiral_angle == 90) - shift_arg_formula(models::right_spiral_multiplier); + shift_arg_formula(vpconf.sphere_spiral_multiplier); + else if(vpconf.spiral_angle == 90) + shift_arg_formula(vpconf.right_spiral_multiplier); } else if(argis("-ssm")) { PHASEFROM(2); - shift_arg_formula(models::any_spiral_multiplier); + shift_arg_formula(vpconf.any_spiral_multiplier); } else if(argis("-scone")) { PHASEFROM(2); - shift_arg_formula(models::spiral_cone); + shift_arg_formula(vpconf.spiral_cone); } else if(argis("-sxy")) { PHASEFROM(2); - shift_arg_formula(models::spiral_x); - shift_arg_formula(models::spiral_y); + shift_arg_formula(vpconf.spiral_x); + shift_arg_formula(vpconf.spiral_y); } else if(argis("-mob")) { PHASEFROM(2); - shift_arg_formula(vid.skiprope); + shift_arg_formula(vpconf.skiprope); } else if(argis("-zoom")) { - PHASEFROM(2); shift_arg_formula(vid.scale); + PHASEFROM(2); shift_arg_formula(vpconf.scale); } else if(argis("-alpha")) { - PHASEFROM(2); shift_arg_formula(vid.alpha); + PHASEFROM(2); shift_arg_formula(vpconf.alpha); } else if(argis("-d:model")) launch_dialog(model_menu); diff --git a/netgen.cpp b/netgen.cpp index ccc748f6..f36c412b 100644 --- a/netgen.cpp +++ b/netgen.cpp @@ -307,7 +307,7 @@ EX namespace netgen { renderbuffer rbuf(2000, 2000, vid.usingGL); dynamicval dv(vid, vid); - vid.xres = vid.yres = 2000; vid.scale = 0.99; + vid.xres = vid.yres = 2000; pconf.scale = 0.99; if(1) { resetbuffer rb; diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 3894fd16..8757b840 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -104,7 +104,7 @@ EX namespace sn { hyperpoint res; if(lazy) { - return decompress(get_int(int(ix), int(iy), int(iz))); + return decompress(get_int(int(ix+.5), int(iy+.5), int(iz+.5))); } else { @@ -552,7 +552,7 @@ EX namespace sn { } } - EX hyperpoint get_inverse_exp_symsol(hyperpoint h, bool lazy, bool just_direction) { + EX hyperpoint get_inverse_exp_symsol(hyperpoint h, flagtype flags) { auto& s = get_tabled(); s.load(); @@ -562,18 +562,17 @@ EX namespace sn { if(h[2] < 0.) { iz = -iz; swap(ix, iy); } - hyperpoint res = s.get(ix, iy, iz, lazy); + hyperpoint res = s.get(ix, iy, iz, flags & pfNO_INTERPOLATION); if(h[2] < 0.) { swap(res[0], res[1]); res[2] = -res[2]; } if(h[0] < 0.) res[0] = -res[0]; if(h[1] < 0.) res[1] = -res[1]; - if(!just_direction) return table_to_azeq(res); - - return res; + if(flags & pfNO_DISTANCE) return res; + return table_to_azeq(res); } - EX hyperpoint get_inverse_exp_nsym(hyperpoint h, bool lazy, bool just_direction) { + EX hyperpoint get_inverse_exp_nsym(hyperpoint h, flagtype flags) { auto& s = get_tabled(); s.load(); @@ -581,14 +580,13 @@ EX namespace sn { ld iy = h[1] >= 0. ? sn::x_to_ix(h[1]) : sn::x_to_ix(-h[1]); ld iz = sn::z_to_iz(h[2]); - hyperpoint res = s.get(ix, iy, iz, lazy); + hyperpoint res = s.get(ix, iy, iz, flags & pfNO_INTERPOLATION); if(h[0] < 0.) res[0] = -res[0]; if(h[1] < 0.) res[1] = -res[1]; - if(!just_direction) return table_to_azeq(res); - - return res; + if(flags & pfNO_DISTANCE) return res; + return table_to_azeq(res); } EX string shader_symsol = sn::common + @@ -745,7 +743,7 @@ EX namespace nilv { ); } - EX hyperpoint get_inverse_exp(hyperpoint h, int iterations) { + EX hyperpoint get_inverse_exp(hyperpoint h, flagtype prec IS(pNORMAL)) { ld wmin, wmax; ld side = h[2] - h[0] * h[1] / 2; @@ -769,11 +767,13 @@ EX namespace nilv { ld s = sin(2 * alpha_total); + int max_iter = (prec & pfLOW_BS_ITER) ? 5 : 20; + for(int it=0;; it++) { ld w = (wmin + wmax) / 2; ld z = b * b * (s + (sin(w) - w)/(cos(w) - 1)) + w; - if(it == iterations) { + if(it == max_iter) { ld alpha = alpha_total - w/2; ld c = b / sin(w/2); return point3(c * w * cos(alpha), c * w * sin(alpha), w); @@ -967,7 +967,7 @@ EX namespace nilv { EX hyperpoint on_geodesic(hyperpoint s0, hyperpoint s1, ld x) { hyperpoint local = inverse(nisot::translate(s0)) * s1; - hyperpoint h = get_inverse_exp(local, 100); + hyperpoint h = get_inverse_exp(local); return nisot::translate(s0) * formula_exp(h * x); } @@ -1822,7 +1822,7 @@ EX namespace rots { hybrid::in_underlying_geometry([&] { hyperpoint h = tC0(T); Spin = inverse(gpushxto0(h) * T); - d = hr::inverse_exp(h, iTable); + d = hr::inverse_exp(h); alpha = atan2(Spin[0][1], Spin[0][0]); distance = hdist0(h); beta = atan2(h[1], h[0]); @@ -1933,8 +1933,8 @@ EX namespace rots { dynamicval m3(playerV, Id); dynamicval m4(actual_view_transform, Id); dynamicval pm(pmodel, mdDisk); - dynamicval pss(vid.scale, (sphere ? 10 : 1) * underlying_scale); - dynamicval psa(vid.alpha, sphere ? 10 : 1); + dynamicval pss(pconf.scale, (sphere ? 10 : 1) * underlying_scale); + dynamicval psa(pconf.alpha, sphere ? 10 : 1); dynamicval p(hybrid::pmap, NULL); dynamicval psr(sightrange_bonus, 0); calcparam(); @@ -1970,25 +1970,29 @@ EX namespace nisot { #endif return true; } - - EX void geodesic_step(hyperpoint& at, hyperpoint& velocity) { - auto acc = christoffel(at, velocity, velocity); - - auto at2 = at + velocity / 2; - auto velocity2 = velocity + acc / 2; - - auto acc2 = christoffel(at2, velocity2, velocity2); - - at = at + velocity + acc2 / 2; - - velocity = velocity + acc; + + EX hyperpoint get_acceleration(const hyperpoint& at, const hyperpoint& vel) { + return christoffel(at, vel, vel); } - EX hyperpoint numerical_exp(hyperpoint v, int steps) { + EX void geodesic_step(hyperpoint& at, hyperpoint& vel) { + /* RK4 method */ + auto acc1 = get_acceleration(at, vel); + auto acc2 = get_acceleration(at + vel/2, vel + acc1/2); + auto acc3 = get_acceleration(at + vel/2 + acc1/4, vel + acc2/2); + auto acc4 = get_acceleration(at + vel + acc2/2, vel + acc3); + + at += vel + (acc1+acc2+acc3)/6; + vel += (acc1+2*acc2+2*acc3+acc4)/6; + } + + EX int rk_steps = 20; + + EX hyperpoint numerical_exp(hyperpoint v) { hyperpoint at = point31(0, 0, 0); - v /= steps; + v /= rk_steps; v[3] = 0; - for(int i=0; imaster->c7 != c; } -int chessvalue(cell *c) { +EX int chessvalue(cell *c) { #if CAP_ARCM if(arcm::in()) return arcm::chessvalue(c); @@ -2938,6 +2938,10 @@ int read_pattern_args() { lp->color = arghex(); } + else if(argis("-fat-edges")) { + PHASEFROM(2); shift(); fat_edges = argi(); + } + else if(argis("-palw")) { PHASEFROM(2); shift(); string ss = args(); diff --git a/polygons.cpp b/polygons.cpp index d782e07d..41986e45 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -756,7 +756,7 @@ void geometry_information::make_wall(int id, vector vertices, vector ld yy = log(2) / 2; bshape(shWall3D[id], PPR::WALL); - last->flags |= POLY_TRIANGLES; + last->flags |= POLY_TRIANGLES | POLY_PRINTABLE; hyperpoint center = Hypc; int n = isize(vertices); @@ -842,8 +842,13 @@ void geometry_information::make_wall(int id, vector vertices, vector hpcpush(mid(C0, hpc[a])); if(shWall3D[id].flags & POLY_TRIANGLES) last->flags |= POLY_TRIANGLES; + if(shWall3D[id].flags & POLY_PRINTABLE) + last->flags |= POLY_PRINTABLE; finishshape(); + + shWall3D[id].intester = C0; + shMiniWall3D[id].intester = C0; shPlainWall3D[id] = shWall3D[id]; // force_triangles ? shWall3D[id] : shWireframe3D[id]; } diff --git a/quit.cpp b/quit.cpp index 1d62fa16..e398e7e8 100644 --- a/quit.cpp +++ b/quit.cpp @@ -274,13 +274,13 @@ EX hint hints[] = { specialland = laHalloween; set_geometry(gSphere); start_game(); - vid.alpha = 999; - vid.scale = 998; + pconf.alpha = 999; + pconf.scale = 998; } else { resetModes(); - vid.alpha = 1; - vid.scale = 1; + pconf.alpha = 1; + pconf.scale = 1; } } }, @@ -415,7 +415,7 @@ EX void showMission() { if(canmove) { if(sphere) { dialog::addItem(XLAT("return to your game"), '1'); - dialog::addItem(XLAT(vid.alpha < 2 ? "orthogonal projection" : "stereographic projection"), '3'); + dialog::addItem(XLAT(pconf.alpha < 2 ? "orthogonal projection" : "stereographic projection"), '3'); } else if(euclid) { dialog::addItem(XLAT("return to your game"), '2'); diff --git a/racing.cpp b/racing.cpp index 20b427d2..78e08e52 100644 --- a/racing.cpp +++ b/racing.cpp @@ -939,15 +939,15 @@ void race_projection() { dialog::init(XLAT("racing projections")); - dialog::addBoolItem(XLAT("Poincaré disk model"), pmodel == mdDisk && !vid.camera_angle, '1'); + dialog::addBoolItem(XLAT("Poincaré disk model"), pmodel == mdDisk && !pconf.camera_angle, '1'); dialog::add_action([] () { pmodel = mdDisk; race_advance = 0; vid.yshift = 0; - vid.camera_angle = 0; - vid.xposition = 0; - vid.yposition = 0; - vid.scale = 1; + pconf.camera_angle = 0; + pconf.xposition = 0; + pconf.yposition = 0; + pconf.scale = 1; vid.use_smart_range = 0; vid.smart_range_detail = 3; }); @@ -955,13 +955,13 @@ void race_projection() { dialog::addBoolItem(XLAT("band"), pmodel == mdBand, '2'); dialog::add_action([] () { pmodel = mdBand; - models::model_orientation = race_angle; + pconf.model_orientation = race_angle; race_advance = 1; vid.yshift = 0; - vid.camera_angle = 0; - vid.xposition = 0; - vid.yposition = 0; - vid.scale = 1; + pconf.camera_angle = 0; + pconf.xposition = 0; + pconf.yposition = 0; + pconf.scale = 1; vid.use_smart_range = 1; vid.smart_range_detail = 3; }); @@ -969,26 +969,26 @@ void race_projection() { dialog::addBoolItem(XLAT("half-plane"), pmodel == mdHalfplane, '3'); dialog::add_action([] () { pmodel = mdHalfplane; - models::model_orientation = race_angle + 90; + pconf.model_orientation = race_angle + 90; race_advance = 0.5; vid.yshift = 0; - vid.camera_angle = 0; - vid.xposition = 0; - vid.yposition = 0; - vid.scale = 1; + pconf.camera_angle = 0; + pconf.xposition = 0; + pconf.yposition = 0; + pconf.scale = 1; vid.use_smart_range = 1; vid.smart_range_detail = 3; }); - dialog::addBoolItem(XLAT("third-person perspective"), pmodel == mdDisk && vid.camera_angle, '4'); + dialog::addBoolItem(XLAT("third-person perspective"), pmodel == mdDisk && pconf.camera_angle, '4'); dialog::add_action([] () { pmodel = mdDisk; race_advance = 0; vid.yshift = -0.3; - vid.camera_angle = -45; - vid.scale = 18/16. * vid.xres / vid.yres / multi::players; - vid.xposition = 0; - vid.yposition = -0.9; + pconf.camera_angle = -45; + pconf.scale = 18/16. * vid.xres / vid.yres / multi::players; + pconf.xposition = 0; + pconf.yposition = -0.9; vid.use_smart_range = 1; vid.smart_range_detail = 3; }); @@ -1011,8 +1011,8 @@ void race_projection() { dialog::addSelItem(XLAT("race angle"), fts(race_angle), 'a'); dialog::add_action([] () { dialog::editNumber(race_angle, 0, 360, 15, 90, XLAT("race angle"), ""); - int q = models::model_orientation - race_angle; - dialog::reaction = [q] () { models::model_orientation = race_angle + q; }; + int q = pconf.model_orientation - race_angle; + dialog::reaction = [q] () { pconf.model_orientation = race_angle + q; }; }); } @@ -1060,6 +1060,7 @@ void race_projection() { bool alternate = false; + #if MAXMDIM >= 4 EX void thurston_racing() { gamescreen(1); dialog::init(XLAT("racing in Thurston geometries")); @@ -1085,16 +1086,20 @@ void race_projection() { add_thurston_race(XLAT("Euclidean"), [] { stop_game(); euc::clear_torus3(); set_geometry(gBitrunc3); }); add_thurston_race(XLAT("hyperbolic"), [] { set_geometry(gBinary3); vid.texture_step = 4; }); add_thurston_race(XLAT("spherical"), [] { set_geometry(gCell120); }); + #if CAP_SOLV add_thurston_race(XLAT("Solv geometry"), [] { sn::solrange_xy = 10; sn::solrange_z = 3; set_geometry(gSol); }); + #endif add_thurston_race(XLAT("S2xE"), [] { set_geometry(gSphere); set_variation(eVariation::bitruncated); set_geometry(gProduct); }); add_thurston_race(XLAT("H2xE"), [] { set_geometry(gNormal); set_variation(eVariation::bitruncated); set_geometry(gProduct); }); add_thurston_race(XLAT("Nil"), [] { stop_game(); nilv::nilperiod[0] = 0; set_geometry(gNil); }); add_thurston_race(XLAT("PSL(2,R)"), [] { set_geometry(gNormal); set_variation(eVariation::pure); set_geometry(gRotSpace); }); } else { + #if CAP_SOLV add_thurston_race(XLAT("stretched hyperbolic"), [] { set_geometry(gNIH); vid.texture_step = 4; }); add_thurston_race(XLAT("stretched Solv"), [] { set_geometry(gSolN); sn::solrange_xy = 10; sn::solrange_z = 3; vid.texture_step = 4; }); add_thurston_race(XLAT("periodic Solv"), [] { stop_game(); sn::solrange_xy = 5; sn::solrange_z = 2; asonov::period_xy = 8; asonov::period_z = 0; asonov::set_flags(); set_geometry(gArnoldCat); }); + #endif add_thurston_race(XLAT("hyperbolic crystal"), [] { set_geometry(gCrystal344); vid.texture_step = 4; }); add_thurston_race(XLAT("torus x E"), [] { stop_game(); euc::eu_input = euc::torus3(4, 4, 0); set_geometry(gCubeTiling); }); add_thurston_race(XLAT("hyperbolic regular"), [] { set_geometry(gSpace534); }); @@ -1108,6 +1113,7 @@ void race_projection() { dialog::addBack(); dialog::display(); } + #endif void raceconfigurer() { @@ -1195,8 +1201,10 @@ void race_projection() { }); } + #if MAXMDIM >= 4 dialog::addItem(XLAT("racing in Thurston geometries"), 'T'); dialog::add_action_push(thurston_racing); + #endif dialog::addBack(); dialog::display(); @@ -1373,8 +1381,7 @@ EX void drawStats() { if(!racing::on) return; - dynamicval pm(pmodel, flat_model()); - glClear(GL_DEPTH_BUFFER_BIT); + flat_model_enabler fme; initquickqueue(); int bsize = vid.fsize * 2; diff --git a/radar.cpp b/radar.cpp index 1b72f0d8..c30435a0 100644 --- a/radar.cpp +++ b/radar.cpp @@ -26,7 +26,7 @@ pair makeradar(hyperpoint h) { ld d = hdist0(h); if(sol && nisot::geodesic_movement) { - h = inverse_exp(h, iLazy); + h = inverse_exp(h, pQUICK); ld r = hypot_d(3, h); if(r < 1) h = h * (atanh(r) / r); else return {false, h}; diff --git a/raycaster.cpp b/raycaster.cpp index c9205a0f..25ff76ce 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -25,7 +25,7 @@ EX int want_use = 1; EX ld exp_start = 1, exp_decay_exp = 4, exp_decay_poly = 10; -EX ld maxstep_sol = .02; +EX ld maxstep_sol = .05; EX ld maxstep_nil = .1; EX ld minstep = .001; @@ -479,6 +479,10 @@ void enable_raycaster() { " return vec4(x*vel.y*tra.y - 0.5*dot(vel.yz,tra.zy), -.5*x*dot(vel.yx,tra.xy) + .5 * dot(vel.zx,tra.xz), -.5*(x*x-1.)*dot(vel.yx,tra.xy)+.5*x*dot(vel.zx,tra.xz), 0.);\n" // " return vec4(0.,0.,0.,0.);\n" " }\n"; + + fsh += "mediump vec4 get_acc(mediump vec4 pos, mediump vec4 vel) {\n" + " return christoffel(pos, vel, vel);\n" + " }\n"; if(sn::in() && !asonov) fsh += "uniform mediump float uBinaryWidth;\n"; @@ -502,12 +506,13 @@ void enable_raycaster() { if(nil) fmain += "tangent = translate(position, itranslate(position, tangent));\n"; if(sn::in()) fmain += - "mediump vec4 acc = christoffel(position, tangent, tangent);\n" - "mediump vec4 pos2 = position + tangent * dist / 2.;\n" - "mediump vec4 tan2 = tangent + acc * dist / 2.;\n" - "mediump vec4 acc2 = christoffel(pos2, tan2, tan2);\n" - "mediump vec4 nposition = position + tangent * dist + acc2 / 2. * dist * dist;\n"; - + "mediump vec4 vel = tangent * dist;\n" + "mediump vec4 acc1 = get_acc(position, vel);\n" + "mediump vec4 acc2 = get_acc(position + vel / 2., vel + acc1/2.);\n" + "mediump vec4 acc3 = get_acc(position + vel / 2. + acc1/4., vel + acc2/2.);\n" + "mediump vec4 acc4 = get_acc(position + vel + acc2/2., vel + acc3/2.);\n" + "mediump vec4 nposition = position + vel + (acc1+acc2+acc3)/6.;\n"; + if(nil) { fmain += "mediump vec4 xp, xt;\n" @@ -629,7 +634,7 @@ void enable_raycaster() { "position = nposition;\n"; if(!nil) fmain += - "tangent = tangent + acc * dist;\n"; + "tangent = tangent + (acc1+2.*acc2+2.*acc3+acc4)/(6.*dist);\n"; } else fmain += "position = position + tangent * dist;\n"; @@ -1138,7 +1143,7 @@ EX void configure() { if(nonisotropic) { dialog::addSelItem(XLAT("max step"), fts(maxstep_current()), 'x'); dialog::add_action([] { - dialog::editNumber(maxstep_current(), 1e-6, 1, 0.1, sol ? 0.03 : 0.1, XLAT("max step"), "affects the precision of solving the geodesic equation in Solv"); + dialog::editNumber(maxstep_current(), 1e-6, 1, 0.1, sol ? 0.05 : 0.1, XLAT("max step"), "affects the precision of solving the geodesic equation in Solv"); dialog::scaleLog(); dialog::bound_low(1e-9); dialog::reaction = reset_raycaster; diff --git a/reg3.cpp b/reg3.cpp index 2c689701..da4360fc 100644 --- a/reg3.cpp +++ b/reg3.cpp @@ -122,8 +122,8 @@ EX namespace reg3 { vertex_distance = binsearch(0, M_PI, [&] (ld d) { // sometimes breaks in elliptic dynamicval g(geometry, elliptic ? gCell120 : geometry); - hyperpoint v2 = direct_exp(dir_v2 * d, iTable); - hyperpoint v3 = direct_exp(dir_v3 * d, iTable); + hyperpoint v2 = direct_exp(dir_v2 * d); + hyperpoint v3 = direct_exp(dir_v3 * d); return hdist(v2, v3) >= edge_length; }); } @@ -131,7 +131,7 @@ EX namespace reg3 { DEBB(DF_GEOM, ("vertex_distance = ", vertex_distance)); /* actual vertex */ - hyperpoint v2 = direct_exp(dir_v2 * vertex_distance, iTable); + hyperpoint v2 = direct_exp(dir_v2 * vertex_distance); hyperpoint mid = Hypc; for(int i=0; i>>> xptds; for(int i=0; i<4; i++) for(auto& p: subscr[i]) xptds[int(p->prio)][i].push_back(move(p)); - + for(auto& sm: xptds) for(auto& sm2: sm.second) { int i = sm2.first; ptds.clear(); for(auto& p: sm2.second) ptds.push_back(move(p)); - vid.scale = .5; - vid.xposition = (!(i&2)) ? xdst : -xdst; - vid.yposition = (!(i&1)) ? ydst : -ydst; + pconf.scale = .5; + pconf.xposition = (!(i&2)) ? xdst : -xdst; + pconf.yposition = (!(i&1)) ? ydst : -ydst; calcparam(); drawqueue(); } @@ -436,8 +436,8 @@ void bantar_anim() { breakanim = true; } mapeditor::drawplayer = true; - vid.xposition = vid.yposition = 0; - vid.scale = 1; + pconf.xposition = pconf.yposition = 0; + pconf.scale = 1; } bool bmap; diff --git a/rogueviz/collatz.cpp b/rogueviz/collatz.cpp index 17c7d421..0a0dd396 100644 --- a/rogueviz/collatz.cpp +++ b/rogueviz/collatz.cpp @@ -145,7 +145,7 @@ void collatz_video(const string &fname) { if(vizid == &collatz_id) { sightrange_bonus = 3; genrange_bonus = 3; - dronemode = true; vid.camera_angle = -45; rog3 = true; patterns::whichShape = '8'; + dronemode = true; pconf.camera_angle = -45; rog3 = true; patterns::whichShape = '8'; vid.aurastr = 512; collatz::lookup(763, 60); diff --git a/rogueviz/flocking.cpp b/rogueviz/flocking.cpp index cf1aa322..4dd625d9 100644 --- a/rogueviz/flocking.cpp +++ b/rogueviz/flocking.cpp @@ -103,7 +103,7 @@ namespace flocking { for(int i=0; iat but relative to m->at // m2's position relative to m (tC0 means *(0,0,1)) - hyperpoint ac = inverse_exp(tC0(at2), iTable, false); + hyperpoint ac = inverse_exp(tC0(at2)); if(use_rot) ac = Rot * ac; // distance and azimuth to m2 diff --git a/rogueviz/janko.cpp b/rogueviz/janko.cpp index b2a57032..6188f06b 100644 --- a/rogueviz/janko.cpp +++ b/rogueviz/janko.cpp @@ -2,6 +2,8 @@ #include "../hyper.h" +#if !ISWEB + namespace hr { eGeometry gJanko1(eGeometry(-1)); @@ -27,7 +29,7 @@ struct jmatrix : array, 7> { }; vector jms; -unordered_map ids; +std::unordered_map ids; jmatrix J, Z, id; @@ -142,3 +144,4 @@ auto shot_hooks = addHook(hooks_args, 100, [] { } +#endif diff --git a/rogueviz/newconf.cpp b/rogueviz/newconf.cpp index b666d3ba..68c25941 100644 --- a/rogueviz/newconf.cpp +++ b/rogueviz/newconf.cpp @@ -638,11 +638,11 @@ void draw_ncee() { nctinf2.tvertices.clear(); ld map_ypos = vid.yres * (mapping_split + 1) / 2 - cd->ycenter; - ld sca2 = (vid.yres * (1-mapping_split) / 2 - 10) / vid.scale; + ld sca2 = (vid.yres * (1-mapping_split) / 2 - 10) / pconf.scale; if(show_mapping) { for(int iter=-10; iter<=10; iter++) { - ld maxx = period * vid.scale / 4; + ld maxx = period * pconf.scale / 4; ld scax = sca2 * maxx / 0.5; ld xpos = scax * 2 * iter; curvepoint(hpxy(xpos-scax, map_ypos-sca2)); @@ -714,7 +714,7 @@ void draw_ncee() { z = !z; for(int s=0; s<3; s++) { curvepoint(hc(c[s].x, c[s].y)); - nctinf.tvertices.push_back(glhr::makevertex((vx[c[s].y][c[s].x]/cscale-delta)*vid.scale/2+.5, vy[c[s].y][c[s].x]*vid.scale/2+.5, 0)); + nctinf.tvertices.push_back(glhr::makevertex((vx[c[s].y][c[s].x]/cscale-delta)*pconf.scale/2+.5, vy[c[s].y][c[s].x]*pconf.scale/2+.5, 0)); } }; @@ -800,7 +800,7 @@ void prepare_ncee_map() { dynamicval cgl(vid.cells_generated_limit, 9999999); dynamicval r(rug::display_warning, false); // vid.consider_shader_projection = false; - vid.scale = 0.5; + pconf.scale = 0.5; rug::init(); rug::prepareTexture(); rug::rugged = false; diff --git a/rogueviz/pentagonal.cpp b/rogueviz/pentagonal.cpp index 48ab2b5e..619ee90f 100644 --- a/rogueviz/pentagonal.cpp +++ b/rogueviz/pentagonal.cpp @@ -34,7 +34,7 @@ array mts; rug::rugpoint *pt(hyperpoint h, hyperpoint c, int id) { auto r = rug::addRugpoint(C0, -1); - r->flat = h; + r->native = h; r->x1 = (1 + c[0]) / 16 + (id/8) / 8.; r->y1 = (1 + c[1]) / 16 + (id%8) / 8.; r->valid = true; @@ -190,7 +190,7 @@ void run_snub(int v, int w) { create_model(); printf("points = %d tris = %d side = %d\n", isize(rug::points), isize(rug::triangles), isize(sideangles)); rug::model_distance = euclid ? 4 : 2; - rug::rug_perspective = hyperbolic; + vid.rug_config.model = hyperbolic ? mdPerspective : mdEquidistant; showstartmenu = false; snubon = true; rug::invert_depth = hyperbolic; diff --git a/rogueviz/staircase.cpp b/rogueviz/staircase.cpp index a572a6c8..4b328998 100644 --- a/rogueviz/staircase.cpp +++ b/rogueviz/staircase.cpp @@ -30,7 +30,7 @@ hyperpoint spcoord(hyperpoint h) { rug::rugpoint *pt(hyperpoint h, hyperpoint c) { auto r = rug::addRugpoint(C0, -1); - r->flat = spcoord(h); + r->native = spcoord(h); r->x1 = c[0]; r->y1 = c[1]; r->valid = true; @@ -120,17 +120,17 @@ void make_staircase() { println(hlog, "scurvature = ", scurvature, " progress = ", progress, " strafe=", strafex, ",", strafey); rug::renderonce = true; - rug::rug_perspective = true; + vid.rug_config.model = mdPerspective; if(scurvature > -1e-6 && scurvature < 1e-6) { - rug::gwhere = gEuclid; + rug::gwhere = rug::rgEuclid; acurvature = 1; } else if(scurvature < 0) { - rug::gwhere = gNormal; + rug::gwhere = rug::rgHyperbolic; acurvature = -scurvature; } else { - rug::gwhere = gSphere; + rug::gwhere = rug::rgSphere; acurvature = scurvature; } rug::ruggospeed = acurvature; diff --git a/rogueviz/sunflower.cpp b/rogueviz/sunflower.cpp index 910d099f..11852abd 100644 --- a/rogueviz/sunflower.cpp +++ b/rogueviz/sunflower.cpp @@ -87,13 +87,9 @@ bool sunflower_cell(cell *c, transmatrix V) { if(adjust_rug) { using namespace rug; - if(rug_perspective) - push_all_points(2, +model_distance); model_distance = sqrt(zdensity) * distance_per_rug; - if(rug_perspective) - push_all_points(2, -model_distance); } iqty = qty; diff --git a/rogueviz/tree.cpp b/rogueviz/tree.cpp index 2f215e9b..074cdb07 100644 --- a/rogueviz/tree.cpp +++ b/rogueviz/tree.cpp @@ -113,7 +113,7 @@ int readArgs() { if(0) ; - else if(argis("-tol")) { + else if(argis("-tree")) { PHASE(3); shift(); tree::read(args()); } diff --git a/rogueviz/triangle.cpp b/rogueviz/triangle.cpp index 7b11ca2d..59b8836d 100644 --- a/rogueviz/triangle.cpp +++ b/rogueviz/triangle.cpp @@ -111,9 +111,9 @@ struct trianglemaker { // println(hlog, "uds = ", uds); - for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(start + ds[a] * ca, iTable, false))); + for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(start + ds[a] * ca))); - for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(uds[a], iTable, false))); + for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(uds[a]))); // compute cube vertices diff --git a/rug.cpp b/rug.cpp index cee16bd7..a0dbe265 100644 --- a/rug.cpp +++ b/rug.cpp @@ -19,6 +19,8 @@ EX bool rug_failure = false; EX namespace rug { +EX transmatrix rugView; + EX ld lwidth = 2; EX bool in_crystal() { return surface::sh == surface::dsCrystal; } @@ -42,8 +44,8 @@ bool computed = false; bool valid; bool inqueue; double dist; - hyperpoint h; // point in the represented space - hyperpoint flat; // point in the native space, in azeq + hyperpoint h; // point in the represented space + hyperpoint native; // point in the native space hyperpoint precompute; vector edges; vector anticusp_edges; @@ -52,8 +54,8 @@ bool computed = false; rugpoint *getglue() { return glue ? (glue = glue->getglue()) : this; } - hyperpoint& glueflat() { - return glue->flat; + hyperpoint& gluenative() { + return glue->native; } rugpoint() { glue = NULL; } void glueto(rugpoint *x) { @@ -67,6 +69,7 @@ bool computed = false; struct triangle { rugpoint *m[3]; + triangle() { m[0] = m[1] = m[2] = nullptr; } triangle(rugpoint *m1, rugpoint *m2, rugpoint *m3) { m[0] = m1; m[1] = m2; m[2] = m3; } @@ -91,9 +94,19 @@ EX void subdivide(); EX ld modelscale = 1; EX ld model_distance = 4; -EX eGeometry gwhere = gEuclid; +#if HDR +constexpr eGeometry rgHyperbolic = gSpace534; +constexpr eGeometry rgEuclid = gCubeTiling; +constexpr eGeometry rgSphere = gCell120; +constexpr eGeometry rgElliptic = gECell120; +#endif -#define USING_NATIVE_GEOMETRY dynamicval gw(geometry, gwhere == gElliptic ? gSphere : gwhere) +EX eGeometry gwhere = rgEuclid; + +#if HDR +#define USING_NATIVE_GEOMETRY_IN_RUG dynamicval gw(geometry, rug::rugged ? hr::rug::gwhere : geometry) +#define USING_NATIVE_GEOMETRY dynamicval gw(geometry, hr::rug::gwhere) +#endif // hypersian rug datatypes and globals //------------------------------------- @@ -121,114 +134,24 @@ EX rugpoint *finger_center; EX ld finger_range = .1; EX ld finger_force = 1; -EX int rugdim; +#define rconf (vid.rug_config) -EX bool rug_perspective = ISANDROID; - -// extra geometry functions -//-------------------------- - -// returns a matrix M -// such that inverse(M) * h1 = ( |h1|, 0, 0) and inverse(M) * h2 = ( .., .., 0) - -transmatrix orthonormalize(hyperpoint h1, hyperpoint h2) { - - hyperpoint vec[3] = {h1, h2, h1 ^ h2}; - - for(int i=0; i<3; i++) { - for(int j=0; j1e-4) { - println(hlog, "Error: h[2] = ", h[2]); - rug_failure = true; - } - if(euclid) { - h[2] = 1; - return h; - } - ld d = hypot(h[0], h[1]); - if(d == 0) { - h[2] = 1; - return h; - } - if(sphere) { - ld d0 = d ? d : 1; - h[0] = sin(d) * h[0]/d0; - h[1] = sin(d) * h[1]/d0; - h[2] = cos(d); - } - else { - ld d0 = d ? d : 1; - h[0] = sinh(d) * h[0]/d0; - h[1] = sinh(d) * h[1]/d0; - h[2] = cosh(d); - } - return h; - } - -hyperpoint hyperboloid_to_azeq(hyperpoint h) { - if(euclid) { - h[2] = 0; - return h; - } - else { - ld d = hdist0(h); - if(d == 0) { h[2] = 0; return h; } - ld d2 = hypot_d(2, h); - if(d2 == 0) { h[2] = 0; return h; } - h[0] = d * h[0] / d2; - h[1] = d * h[1] / d2; - h[2] = 0; - return h; - } - } - -struct normalizer { - - transmatrix M, Mi; - - dynamicval gw; - - normalizer (hyperpoint h1, hyperpoint h2) : gw(geometry, gwhere == gElliptic ? gSphere : gwhere) { - M = orthonormalize(h1, h2); - Mi = inverse(M); - } - - hyperpoint operator() (hyperpoint h) { return azeq_to_hyperboloid(Mi*h); } - hyperpoint operator[] (hyperpoint h) { return M*hyperboloid_to_azeq(h); } - }; +EX bool perspective() { return models::is_perspective(rconf.model); } void push_point(hyperpoint& h, int coord, ld val) { - if(fast_euclidean && gwhere == gEuclid) + USING_NATIVE_GEOMETRY; + if(fast_euclidean && euclid) h[coord] += val; else if(!val) return; else { - // if(zero_d(3, h)) { h[0] = 1e-9; h[1] = 1e-10; h[2] = 1e-11; } - normalizer n(hpxyz(coord==0,coord==1,coord==2), h); - hyperpoint f = n(h); - h = n[xpush(val) * f]; + h = cpush(coord, val) * h; } } EX void push_all_points(int coord, ld val) { if(!val) return; else for(int i=0; iflat, coord, val); + push_point(points[i]->native, coord, val); } // construct the graph @@ -236,20 +159,25 @@ EX void push_all_points(int coord, ld val) { int hyprand; +bool rug_euclid() { USING_NATIVE_GEOMETRY; return euclid; } +bool rug_hyperbolic() { USING_NATIVE_GEOMETRY; return hyperbolic; } +bool rug_sphere() { USING_NATIVE_GEOMETRY; return sphere; } +bool rug_elliptic() { USING_NATIVE_GEOMETRY; return elliptic; } + EX rugpoint *addRugpoint(hyperpoint h, double dist) { rugpoint *m = new rugpoint; m->h = h; /* - ld tz = vid.alpha+h[2]; + ld tz = pconf.alpha+h[2]; m->x1 = (1 + h[0] / tz) / 2; m->y1 = (1 + h[1] / tz) / 2; */ hyperpoint onscreen; applymodel(m->h, onscreen); - m->x1 = (1 + onscreen[0] * vid.scale) / 2; - m->y1 = (1 - onscreen[1] * vid.scale) / 2; + m->x1 = (1 + onscreen[0] * pconf.scale) / 2; + m->y1 = (1 - onscreen[1] * pconf.scale) / 2; m->valid = false; if(euclid && quotient && !bounded) { @@ -269,73 +197,72 @@ EX rugpoint *addRugpoint(hyperpoint h, double dist) { if(z==0) z = 1; hpoint = hpoint * hpdist / z; - m->flat = hpxyz(hpoint[0], hpoint[1] * sin(a*2*M_PI), hpoint[1]*cos(a*2*M_PI)); + m->native = point31(hpoint[0], hpoint[1] * sin(a*2*M_PI), hpoint[1]*cos(a*2*M_PI)); } else if(sphere) { m->valid = good_shape = true; ld scale; - if(gwhere == gEuclid) { + USING_NATIVE_GEOMETRY; + if(euclid) { scale = modelscale; } - else if(gwhere == gNormal) { + else if(hyperbolic) { // sinh(scale) = modelscale scale = asinh(modelscale); } - else /* sphere/elliptic*/ { + else if(sphere) { if(modelscale >= 1) // do as good as we can... scale = M_PI / 2 - 1e-3, good_shape = false, m->valid = false; else scale = asin(modelscale); } - m->flat = h * scale; + else + scale = 1; + m->native = h * scale; + m->native = hpxy3(m->native[0], m->native[1], m->native[2]); } - else if(euclid && gwhere == gEuclid) { - m->flat = h * modelscale; + else if(euclid && rug_euclid()) { + m->native = h * modelscale; + m->native[2] = 0; + #if MAXMDIM >= 4 + m->native[3] = 1; + #endif m->valid = good_shape = true; } - else if(gwhere == gNormal && (euclid || (hyperbolic && modelscale >= 1))) { + else if(rug_hyperbolic() && euclid) { + m->valid = good_shape = true; + USING_NATIVE_GEOMETRY; + m->native = tC0(parabolic13(h[0]*modelscale, h[1]*modelscale)); + } + + else if(rug_hyperbolic() && hyperbolic && modelscale >= 1) { m->valid = good_shape = true; - ld d = hdist0(h); - ld d0 = hypot_d(2, h); if(!d0) d0 = 1; + // radius of the equidistant + ld r = acosh(modelscale); + + h[3] = h[2]; h[2] = 0; - hyperpoint hpoint; - bool orig_euclid = euclid; USING_NATIVE_GEOMETRY; - if(orig_euclid) { - d *= modelscale; - // point on a horocycle going through C0, in distance d along the horocycle - hpoint = hpxy(d*d/2, d); - } - else { - // radius of the equidistant - ld r = acosh(modelscale); - // point on an equdistant going through C0 in distance d along the guiding line - // hpoint = hpxy(cosh(r) * sinh(r) * (cosh(d) - 1), sinh(d) * cosh(r)); - hpoint = xpush(r) * ypush(d) * xpush0(-r); - hpoint[0] = -hpoint[0]; - } - - ld hpdist = hdist0(hpoint); - ld z = hypot_d(2, hpoint); - if(z==0) z = 1; - m->flat = hpxyz(hpdist * h[0]/d0 * hpoint[1] / z, hpdist * h[1]/d0 * hpoint[1] / z, -hpdist * hpoint[0] / z); + m->native = rgpushxto0(h) * cpush0(2, r); } else { - m->flat = h; + m->native = h; ld hd = h[LDIM]; - for(int d=GDIM; dflat[d] = (hd - .99) * (rand() % 1000 - rand() % 1000) / 1000; + for(int d=GDIM; dnative[d] = (hd - .99) * (rand() % 1000 - rand() % 1000) / 1000; + } + USING_NATIVE_GEOMETRY; + #if MAXMDIM >= 4 + m->native[3] = 1; + m->native = normalize(m->native); + #endif } - if(rug_perspective) - push_point(m->flat, 2, -model_distance); - - // if(rug_perspective && gwhere == gEuclid) m->flat[2] -= 3; m->inqueue = false; m->dist = dist; points.push_back(m); @@ -343,8 +270,9 @@ EX rugpoint *addRugpoint(hyperpoint h, double dist) { } EX rugpoint *findRugpoint(hyperpoint h) { + USING_NATIVE_GEOMETRY; for(int i=0; ih - h) < 1e-5) return points[i]; + if(geo_dist_q(points[i]->h, h) < 1e-5) return points[i]; return NULL; } @@ -414,7 +342,7 @@ EX void sort_rug_points() { void calcLengths() { for(auto p: points) for(auto& edge: p->edges) - edge.len = hdist(p->h, edge.target->h) * modelscale; + edge.len = geo_dist_q(p->h, edge.target->h) * modelscale; } EX void calcparam_rug() { @@ -424,7 +352,7 @@ EX void calcparam_rug() { cd->xsize = cd->ysize = TEXTURESIZE; cd->xcenter = cd->ycenter = cd->scrsize = HTEXTURESIZE; - cd->radius = cd->scrsize * vid.scale; + cd->radius = cd->scrsize * pconf.scale; } EX void buildTorusRug() { @@ -480,23 +408,29 @@ EX void buildTorusRug() { ld ax = alpha + 1.124651, bx = beta + 1.214893; ld x = xfactor * sin(ax), y = xfactor * cos(ax), z = yfactor * sin(bx), t = yfactor * cos(bx); - - ld d; - ld hxyz = sqrt(x*x+y*y+z*z); - - if(gwhere == gNormal) { - d = hxyz / (1-t) / mx; - d *= .95; - hyperpoint test = hpxy(d, 0); - test = perspective_to_space(test, 1, gcHyperbolic); - d = acosh(test[2]) / hxyz; - } - else { - d = (gwhere == gSphere) ? acos(t) / hxyz : modelscale * 3 / (1-t) / mx; - } - r->flat = r->h = hpxyz(x*d, y*d, z*d); + if(1) { + hyperpoint hp = hyperpoint(x, y, z, t); + USING_NATIVE_GEOMETRY; + + /* spherical coordinates are already good, otherwise... */ + + if(!sphere) { + /* stereographic projection to get Euclidean conformal torus */ + hp /= (t+1); + hp /= mx; + #if MAXMDIM >= 4 + hp[3] = 1; + #endif + } + + /* ... in H^3, use inverse Poincare to get hyperbolic conformal torus */ + + if(hyperbolic) + hp = perspective_to_space(hp, 1, gcHyperbolic); + } + r->valid = true; static const int X = 100003; // a prime @@ -539,19 +473,16 @@ EX void buildTorusRug() { for(auto p: points) maxz = max(maxz, max(abs(p->x1), abs(p->y1))); - if(1) vid.scale = 1 / maxz; + if(1) pconf.scale = 1 / maxz; if(1) for(auto p: points) - p->x1 = (1 + vid.scale * p->x1)/2, - p->y1 = (1 - vid.scale * p->y1)/2; + p->x1 = (1 + pconf.scale * p->x1)/2, + p->y1 = (1 - pconf.scale * p->y1)/2; qvalid = 0; for(auto p: points) if(!p->glue) qvalid++; println(hlog, "qvalid = ", qvalid); - if(rug_perspective) - push_all_points(2, -model_distance); - return; } @@ -562,14 +493,8 @@ EX void verify() { auto m2 = e.target; ld l = e.len; - ld l0; - if(fast_euclidean) l0 = sqhypot_d(rugdim, m->flat - m2->flat); - else { - normalizer n(m->flat, m2->flat); - hyperpoint h1 = n(m->flat); - hyperpoint h2 = n(m2->flat); - l0 = hdist(h1, h2); - } + USING_NATIVE_GEOMETRY; + ld l0 = geo_dist_q(m->native, m2->native); ratios.push_back(l0 / l); } @@ -668,17 +593,18 @@ EX void enqueue(rugpoint *m) { bool force_euclidean(rugpoint& m1, rugpoint& m2, double rd, bool is_anticusp = false, double d1=1, double d2=1) { if(!m1.valid || !m2.valid) return false; - // double rd = hdist(m1.h, m2.h) * xd; - double t = sqhypot_d(rugdim, m1.flat - m2.flat); + USING_NATIVE_GEOMETRY; + // double rd = geo_dist_q(m1.h, m2.h) * xd; + double t = sqhypot_d(3, m1.native - m2.native); if(is_anticusp && t > rd*rd) return false; t = sqrt(t); current_total_error += (t-rd) * (t-rd); bool nonzero = abs(t-rd) > err_zero_current; double force = (t - rd) / t / 2; // 20.0; - for(int i=0; i0) enqueue(&m2); } return nonzero; @@ -686,35 +612,31 @@ bool force_euclidean(rugpoint& m1, rugpoint& m2, double rd, bool is_anticusp = f bool force(rugpoint& m1, rugpoint& m2, double rd, bool is_anticusp=false, double d1=1, double d2=1) { if(!m1.valid || !m2.valid) return false; - if(gwhere == gEuclid && fast_euclidean) { + if(rug_euclid() && fast_euclidean) { return force_euclidean(m1, m2, rd, is_anticusp, d1, d2); } - normalizer n(m1.flat, m2.flat); - hyperpoint f1 = n(m1.flat); - hyperpoint f2 = n(m2.flat); + USING_NATIVE_GEOMETRY; - ld t = hdist(f1, f2); + ld t = geo_dist_q(m1.native, m2.native); if(is_anticusp && t > rd) return false; current_total_error += (t-rd) * (t-rd); bool nonzero = abs(t-rd) > err_zero_current; double forcev = (t - rd) / 2; // 20.0; - transmatrix T = gpushxto0(f1); - transmatrix T1 = spintox(T * f2) * T; + transmatrix T = inverse(rgpushxto0(m1.native)); + hyperpoint ie = inverse_exp(T * m2.native); - transmatrix iT1 = inverse(T1); + transmatrix iT = rgpushxto0(m1.native); - for(int i=0; i<3; i++) if(std::isnan(m1.flat[i])) { + for(int i=0; i %s\n", q, display(m->flat)); - if(std::isnan(m->flat[0]) || std::isnan(m->flat[1]) || std::isnan(m->flat[2])) + #if MAXMDIM >= 4 + if(q>0) m->native = normalize(h); + #else + if(q>0) m->native = h / q; + #endif + + if(std::isnan(m->native[0]) || std::isnan(m->native[1]) || std::isnan(m->native[2])) throw rug_exception(); } @@ -779,15 +705,8 @@ ld sse(const hyperpoint& h) { ld sse = 0; for(auto& p: preset_points) { ld l = p.first; - ld l0; - if(fast_euclidean) - l0 = hypot_d(rugdim, h - p.second->flat); - else { - normalizer n(h, p.second->flat); - hyperpoint h1 = n(h); - hyperpoint h2 = n(p.second->flat); - l0 = hdist(h1, h2); - } + USING_NATIVE_GEOMETRY; + ld l0 = geo_dist_q(h, p.second->native); sse += (l0-l) * (l0-l); } @@ -801,16 +720,17 @@ EX void optimize(rugpoint *m, bool do_preset) { // int ed0 = isize(preset_points); for(auto& e: m->edges) if(e.target->valid) preset_points.emplace_back(e.len, e.target); - if(gwhere >= gSphere || GDIM == 3) { - ld cur = sse(m->flat); + if(!rug_euclid()) { + ld cur = sse(m->native); for(int it=0; it<500; it++) { ld ex = exp(-it/60); again: - hyperpoint last = m->flat; - m->flat[(it/2)%rugdim] += ((it&1)?1:-1) * ex; - ld now = sse(m->flat); + hyperpoint last = m->native; + USING_NATIVE_GEOMETRY; + m->native = rgpushxto0(m->native) * cpush0((it/2)%3, (it&1)?ex:-ex); + ld now = sse(m->native); if(now < cur) { cur = now; ex *= 1.2; goto again; } - else m->flat = last; + else m->native = last; } } } @@ -860,10 +780,12 @@ EX void subdivide() { rugpoint *mm = addRugpoint(mid(m->h, m2->h), (m->dist+m2->dist)/2); halves[make_pair(m, m2)] = mm; if(!good_shape) { - normalizer n(m->flat, m2->flat); - hyperpoint h1 = n(m->flat); - hyperpoint h2 = n(m2->flat); - mm->flat = n[mid(h1, h2)]; + #if MAXMDIM >= 4 + USING_NATIVE_GEOMETRY; + mm->native = mid(m->native, m2->native); + #else + mm->native = (m->native + m2->native) / 2; + #endif } mm->valid = m->valid && m2->valid; if(mm->valid) qvalid++; @@ -880,33 +802,9 @@ EX void subdivide() { println(hlog, "result ", make_tuple(isize(points), isize(triangles))); } -EX ld slow_modeldist(const hyperpoint& h1, const hyperpoint& h2) { - normalizer n(h1, h2); - hyperpoint f1 = n(h1); - hyperpoint f2 = n(h2); - return hdist(f1, f2); - } - -typedef array hyperpoint4; - -hyperpoint4 azeq_to_4(const hyperpoint& h) { - array res; - ld rad = hypot_d(3, h); - res[3] = cos(rad); - ld sr = sin(rad) / rad; - for(int j=0; j<3; j++) res[j] = h[j] * sr; - return res; - } - EX ld modeldist(const hyperpoint& h1, const hyperpoint& h2) { - if(gwhere == gSphere) { - hyperpoint4 coord[2] = { azeq_to_4(h1), azeq_to_4(h2) }; - ld edist = 0; - for(int j=0; j<4; j++) edist += sqr(coord[0][j] - coord[1][j]); - return 2 * asin(sqrt(edist) / 2); - } - - return slow_modeldist(h1, h2); + USING_NATIVE_GEOMETRY; + return geo_dist_q(h1, h2); } typedef long long bincode; @@ -925,8 +823,7 @@ bincode get_bincode(hyperpoint h) { case gcHyperbolic: return acd_bin(hypot_d(3, h)); case gcSphere: { - auto p = azeq_to_4(h); - return acd_bin(p[0]) + acd_bin(p[1]) * sY + acd_bin(p[2]) * sZ + acd_bin(p[3]) * sT; + return acd_bin(h[0]) + acd_bin(h[1]) * sY + acd_bin(h[2]) * sZ + acd_bin(h[3]) * sT; } } return 0; @@ -946,7 +843,7 @@ void generate_deltas(vector& target, int dim, bincode offset) { int detect_cusp_at(rugpoint *p, rugpoint *q) { if(hdist(p->h, q->h) * modelscale <= anticusp_dist) return 0; - else if(modeldist(p->flat, q->flat) > anticusp_dist - err_zero_current) + else if(modeldist(p->native, q->native) > anticusp_dist - err_zero_current) return 1; else { add_anticusp_edge(p, q); @@ -967,7 +864,7 @@ int detect_cusps() { map > code_to_point; for(auto p: points) if(p->valid) - code_to_point[get_bincode(p->flat)].push_back(p); + code_to_point[get_bincode(p->native)].push_back(p); vector deltas; generate_deltas(deltas, gwhere == gEuclid ? 3 : gwhere == gNormal ? 1 : 4, 0); @@ -1068,35 +965,16 @@ EX void physics() { bool use_precompute; -void getco(rugpoint *m, hyperpoint& h, int &spherepoints) { - h = use_precompute ? m->getglue()->precompute : m->getglue()->flat; - if(rug_perspective && gwhere >= gSphere) { - if(h[2] > 0) { - ld rad = hypot_d(3, h); - // turn M_PI to -M_PI - // the only difference between sphere and elliptic is here: - // in elliptic, we subtract PI from the distance - ld rad_to = (gwhere == gSphere ? M_PI + M_PI : M_PI) - rad; - ld r = -rad_to / rad; - h *= r; - spherepoints++; - } - } - } - extern int besti; #if CAP_ODS /* these functions are for the ODS projection, used in VR videos */ -bool project_ods(hyperpoint azeq, hyperpoint& h1, hyperpoint& h2, bool eye) { +bool project_ods(hyperpoint h, hyperpoint& h1, hyperpoint& h2, bool eye) { USING_NATIVE_GEOMETRY; - ld d = hypot_d(3, azeq); - ld sindbd = sin_auto(d)/d, cosd = cos_auto(d); - - return ods::project(hyperpoint(azeq[0] * sindbd, azeq[1] * sindbd, azeq[2] * sindbd, cosd), h1, h2, eye); - + return ods::project(h, h1, h2, eye); + // printf("%10.5lf %10.5lf %10.5lf ", azeq[0], azeq[1], azeq[2]); // printf(" => %10.5lf %10.5lf %10.5lf %10.5lf", x, y, z, t); @@ -1110,99 +988,26 @@ vector ct_array; vector cp_array; +EX basic_textureinfo tinf; + void drawTriangle(triangle& t) { - int num = t.m[2] ? 3 : 2; - for(int i=0; ivalid) return; - // if(t.m[i]->dist >= get_sightrange()+.51) return; } - dt++; - -#if CAP_ODS - if(vid.stereo_mode == sODS) { - hyperpoint pts[3]; - - // not implemented - if(num == 2) return; - - for(int i=0; igetglue()->flat; - - hyperpoint hc = (pts[1] - pts[0]) ^ (pts[2] - pts[0]); - double hch = hypot_d(3, hc); - - ld col = (2 + hc[0]/hch) / 3; - - bool natsph = among(gwhere, gSphere, gElliptic); - - bool ok = true; - array h; - for(int eye=0; eye<2; eye++) { - if(true) { - for(int i=0; i<3; i++) - ok = ok && project_ods(pts[i], h[i], h[i+3], eye); - if(!ok) return; - for(int i=0; i<6; i++) { - // let Delta be from 0 to 2PI - if(h[i][2]<0) h[i][2] += 2 * M_PI; - // Theta is from -PI/2 to PI/2. Let it be from 0 to PI - h[i][1] += (eye?-1:1) * M_PI/2; - } - } - else { - for(int i=0; i<6; i++) - h[i][0] = -h[i][0], - h[i][1] = -h[i][1], - h[i][2] = 2*M_PI-h[i][2]; - } - if(natsph) { - if(raddif(h[4][0], h[0][0]) < raddif(h[1][0], h[0][0])) - swap(h[1], h[4]); - if(raddif(h[5][0], h[0][0]) < raddif(h[2][0], h[0][0])) - swap(h[5], h[2]); - } - else { - if(h[0][2] < 0) swap(h[0], h[3]); - if(h[1][2] < 0) swap(h[1], h[4]); - if(h[2][2] < 0) swap(h[2], h[5]); - } - if(abs(h[1][1] - h[0][1]) > M_PI/2) return; - if(abs(h[2][1] - h[0][1]) > M_PI/2) return; - cyclefix(h[1][0], h[0][0]); - cyclefix(h[2][0], h[0][0]); - cyclefix(h[4][0], h[3][0]); - cyclefix(h[5][0], h[3][0]); - for(int s: {0, 3}) { - int fst = 0, lst = 0; - if(h[s+1][0] < -M_PI || h[s+2][0] < -M_PI) lst++; - if(h[s+1][0] > +M_PI || h[s+2][0] > +M_PI) fst--; - for(int x=fst; x<=lst; x++) for(int i=0; i<3; i++) { - ct_array.emplace_back( - hpxyz(h[s+i][0] + 2*M_PI*x, h[s+i][1], h[s+i][2]), - t.m[i]->x1, t.m[i]->y1, - col); - } - if(!natsph) break; - } - } - return; - } -#endif - - int spherepoints = 0; - array h; - for(int i=0; inative - t.m[0]->native) ^ (t.m[2]->native - t.m[0]->native); double hch = hypot_d(3, hc); col = (2 + hc[0]/hch) / 3; + if(nonisotropic) col = (9+col) / 10; + } + + for(int i=0; i<3; i++) { + curvepoint(t.m[i]->native); + tinf.tvertices.push_back(glhr::pointtogl(point3(t.m[i]->x1, t.m[i]->y1, col))); } - - for(int i=0; ix1, t.m[i]->y1, col); } EX struct renderbuffer *glbuf; @@ -1245,8 +1050,6 @@ EX void prepareTexture() { rb.reset(); } -double xview, yview; - EX bool no_fog; EX ld lowrug = 1e-2; @@ -1256,114 +1059,46 @@ EX GLuint alternate_texture; EX bool invert_depth; -EX void drawRugScene() { - glbuf->use_as_texture(); - if(alternate_texture) - glBindTexture( GL_TEXTURE_2D, alternate_texture); +EX bool rug_control() { return rug::rugged; } + +#if HDR + +struct using_rugview { + using_rugview() { if(rug_control()) swap(View, rugView), swap(geometry, gwhere); } + ~using_rugview() { if(rug_control()) swap(View, rugView), swap(geometry, gwhere); } + }; - if(backcolor == 0) - glClearColor(0.05f,0.05f,0.05f,1.0f); - else - glhr::colorClear(backcolor << 8 | 0xFF); -#ifdef GLES_ONLY - glClearDepthf(invert_depth ? -1.0f : 1.0f); -#else - glClearDepth(invert_depth ? -1.0f : 1.0f); #endif - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + +EX void drawRugScene() { + USING_NATIVE_GEOMETRY; + tinf.texture_id = alternate_texture ? alternate_texture : glbuf->renderedTexture; + tinf.tvertices.clear(); - glDisable(GL_BLEND); - current_display->next_shader_flags = GF_VARCOLOR | GF_TEXTURE; - glhr::set_depthtest(true); - glhr::set_depthwrite(true); - glDepthFunc(invert_depth ? GL_GREATER : GL_LESS); + ptds.clear(); - for(int ed=current_display->stereo_active() && vid.stereo_mode != sODS ? -1 : 0; ed < 2; ed += 2) { - use_precompute = false; - ct_array.clear(); - cp_array.clear(); - if(ed == 1 && vid.stereo_mode == sAnaglyph) - glClear(GL_DEPTH_BUFFER_BIT); - - dynamicval p(pmodel, mdManual); - current_display->set_all(ed); - eyewidth_translate(ed); + for(auto t: triangles) drawTriangle(t); + + auto& rug = queuecurve(0, 0xFFFFFFFF, PPR::LINE); - if(glhr::current_glprogram->uLevelLines != -1) - glUniform1f(glhr::current_glprogram->uLevelLines, levellines); - - if(vid.stereo_mode == sODS) { - glhr::projection_multiply(glhr::ortho(M_PI, M_PI, 100)); // 2*M_PI)); - } - else if(rug_perspective || current_display->stereo_active()) { - - xview = current_display->tanfov; - yview = current_display->tanfov * vid.yres / vid.xres; - - glhr::projection_multiply(glhr::frustum(xview, yview, lowrug, hirug)); - xview = -xview; yview = -yview; - - if(!rug_perspective) - glhr::projection_multiply(glhr::translate(0, 0, -model_distance)); - if(ed) { - if(gwhere == gEuclid) - glhr::projection_multiply(glhr::translate(vid.ipd*ed/2, 0, 0)); - else { - use_precompute = true; - for(auto p: points) { - p->precompute = p->flat; - push_point(p->precompute, 0, vid.ipd*ed/2); - } - } - } - } - else { - xview = current_display->tanfov * model_distance; - yview = current_display->tanfov * model_distance * vid.yres / vid.xres; - // glOrtho(-xview, xview, yview, -yview, -1000, 1000); - - glhr::projection_multiply(glhr::ortho(xview, yview, -1000)); - } - glhr::color2(0xFFFFFFFF); - - glhr::fog_max( - no_fog ? 1000 : - gwhere == gSphere && rug_perspective ? 10 : - gwhere == gElliptic && rug_perspective ? 4 : - 100, - darkena(backcolor, 0, 0xFF) - ); - GLERR("fog_max"); - - for(int t=0; t 0) { - glhr::prepare(ct_array); - glDrawArrays(GL_TRIANGLES, 0, isize(ct_array)); - } - - GLERR("rugz"); - if(isize(cp_array) > 0) { - glhr::prepare(cp_array); - glLineWidth(lwidth); - glDrawArrays(GL_LINES, 0, isize(cp_array)); - } - GLERR("rugt"); - - current_display->set_mask(0); - GLERR("afterrug"); + if(nonisotropic) { + transmatrix T2 = eupush( tC0(inverse(rugView)) ); + NLP = rugView * T2; + rug.V = inverse(NLP) * rugView; } - - glEnable(GL_BLEND); - - if(rug_failure) { - rug::close(); - rug::clear_model(); - rug::init(); + else { + rug.V = rugView; } + + rug.offset_texture = 0; + rug.tinf = &tinf; + rug.flags = POLY_TRIANGLES | POLY_FAT | POLY_PRINTABLE | POLY_ALWAYS_IN | POLY_ISSIDE | POLY_SHADE_TEXTURE; + + dynamicval p(pconf, rconf); + calcparam(); + + drawqueue(); } // organization @@ -1388,7 +1123,6 @@ EX void ensure_glbuf() { EX void reopen() { if(rugged) return; - rugdim = 2 * GDIM - 1; when_enabled = 0; GLERR("before init"); ensure_glbuf(); @@ -1429,7 +1163,7 @@ EX void init_model() { if(r->x1<0 || r->x1>1 || r->y1<0 || r->y1 > 1) valid = false; - if(sphere && pmodel == mdDisk && vid.alpha > 1) + if(sphere && pmodel == mdDisk && pconf.alpha > 1) valid = false; if(display_warning && !valid) @@ -1446,10 +1180,19 @@ EX void init_model() { } } +EX void reset_view() { + rugView = Id; + if(perspective()) { + using_rugview urv; + shift_view(ztangent(model_distance)); + } + } + EX void init() { if(dual::state) return; reopen(); if(rugged) init_model(); + reset_view(); } EX void clear_model() { @@ -1470,23 +1213,10 @@ int lastticks; ld protractor = 0; -EX void apply_rotation(const transmatrix& t) { - if(!rug_perspective) currentrot = t * currentrot; - #if CAP_CRYSTAL - if(in_crystal()) crystal::apply_rotation(t); - else - #endif - for(auto p: points) p->flat = t * p->flat; - } - -EX void move_forward(ld distance) { - if(rug_perspective) push_all_points(2, distance); - else model_distance /= exp(distance); - } - #define CAP_HOLDKEYS (CAP_SDL && !ISWEB) EX bool handlekeys(int sym, int uni) { + USING_NATIVE_GEOMETRY; if(NUMBERKEY == '1') { ld bdist = 1e12; if(finger_center) @@ -1507,7 +1237,7 @@ EX bool handlekeys(int sym, int uni) { crystal::switch_z_coordinate(); else #endif - apply_rotation(cspin(0, 2, M_PI)); + rotate_view(cspin(0, 2, M_PI)); return true; } else if(NUMBERKEY == '3') { @@ -1516,7 +1246,7 @@ EX bool handlekeys(int sym, int uni) { crystal::flip_z(); else #endif - apply_rotation(cspin(0, 2, M_PI/2)); + rotate_view(cspin(0, 2, M_PI/2)); return true; } #if CAP_CRYSTAL @@ -1525,29 +1255,13 @@ EX bool handlekeys(int sym, int uni) { return true; } #endif -#if !CAP_HOLDKEYS - else if(sym == SDLK_PAGEUP || uni == '[') { - move_forward(.1); - return true; - } - else if(sym == SDLK_PAGEDOWN || uni == ']') { - move_forward(-.1); - return true; - } - else if(sym == SDLK_HOME) { apply_rotation(cspin(0, 1, .1)); return true; } - else if(sym == SDLK_END) { apply_rotation(cspin(1, 0, .1)); return true; } - else if(sym == SDLK_DOWN) { apply_rotation(cspin(2, 1, .1)); return true; } - else if(sym == SDLK_UP) { apply_rotation(cspin(1, 2, .1)); return true; } - else if(sym == SDLK_LEFT) { apply_rotation(cspin(2, 0, .1)); return true; } - else if(sym == SDLK_RIGHT) { apply_rotation(cspin(0, 2, .1)); return true; } -#endif else return false; } EX void finger_on(int coord, ld val) { for(auto p: points) { ld d = hdist(finger_center->h, p->getglue()->h); - push_point(p->flat, coord, val * finger_force * exp( - sqr(d / finger_range))); + push_point(p->native, coord, val * finger_force * exp( - sqr(d / finger_range))); } enqueue(finger_center), good_shape = false; } @@ -1570,29 +1284,17 @@ EX void actDraw() { physics(); drawRugScene(); - #if CAP_ORIENTATION - if(!when_enabled) ticks = when_enabled; - if(ticks < when_enabled + 500) - last_orientation = getOrientation(); - else { - transmatrix next_orientation = getOrientation(); - apply_rotation(inverse(next_orientation) * last_orientation); - last_orientation = next_orientation; - } - #endif - - int qm = 0; double alpha = (ticks - lastticks) / 1000.0; lastticks = ticks; - if(ruggo) move_forward(ruggo * alpha); + // if(ruggo) move_forward(ruggo * alpha); #if CAP_HOLDKEYS Uint8 *keystate = SDL_GetKeyState(NULL); if(keystate[SDLK_LALT]) alpha /= 10; + #endif - transmatrix t = Id; - + #if CAP_HOLDKEYS auto perform_finger = [=] () { if(keystate[SDLK_HOME]) finger_range /= exp(alpha); if(keystate[SDLK_END]) finger_range *= exp(alpha); @@ -1604,69 +1306,8 @@ EX void actDraw() { if(keystate[SDLK_PAGEUP]) finger_on(2, +alpha); }; - if(cmode & sm::NUMBER) { - } - else if(rug_perspective) { - - ld strafex = 0, strafey = 0, push = 0; - - if(finger_center) - perform_finger(); - else { - if(keystate[SDLK_HOME]) qm++, t = t * cspin(0, 1, alpha), protractor += alpha; - if(keystate[SDLK_END]) qm++, t = t * cspin(1, 0, alpha), protractor -= alpha; - if(!keystate[SDLK_LSHIFT]) { - if(keystate[SDLK_DOWN]) qm++, t = t * cspin(2, 1, alpha), protractor += alpha; - if(keystate[SDLK_UP]) qm++, t = t * cspin(1, 2, alpha), protractor -= alpha; - if(keystate[SDLK_LEFT]) qm++, t = t * cspin(2, 0, alpha), protractor += alpha; - if(keystate[SDLK_RIGHT]) qm++, t = t * cspin(0, 2, alpha), protractor -= alpha; - } - if(keystate[SDLK_PAGEDOWN]) push -= alpha; - if(keystate[SDLK_PAGEUP]) push += alpha; - - if(keystate[SDLK_LSHIFT]) { - if(keystate[SDLK_LEFT]) strafex += alpha; - if(keystate[SDLK_RIGHT]) strafex -= alpha; - if(keystate[SDLK_UP]) strafey -= alpha; - if(keystate[SDLK_DOWN]) strafey += alpha; - } - } - - if(qm) { - if(keystate[SDLK_LCTRL]) - push_all_points(2, +model_distance); - apply_rotation(t); - if(keystate[SDLK_LCTRL]) - push_all_points(2, -model_distance); - } - - model_distance -= push; - push_all_points(2, push * ruggospeed); - push_all_points(0, strafex * ruggospeed); - push_all_points(1, strafey * ruggospeed); - } - else { - if(finger_center) - perform_finger(); - else { - if(keystate[SDLK_HOME] && !in_crystal()) qm++, t = inverse(currentrot); - if(keystate[SDLK_END]) { - qm++; - if(in_crystal()) t = t * cspin(0, 1, alpha); - else t = currentrot * cspin(0, 1, alpha) * inverse(currentrot); - } - if(keystate[SDLK_DOWN]) qm++, t = t * cspin(1, 2, alpha); - if(keystate[SDLK_UP]) qm++, t = t * cspin(2, 1, alpha); - if(keystate[SDLK_LEFT]) qm++, t = t * cspin(0, 2, alpha); - if(keystate[SDLK_RIGHT]) qm++, t = t * cspin(2, 0, alpha); - if(keystate[SDLK_PAGEUP]) model_distance /= exp(alpha * ruggospeed); - if(keystate[SDLK_PAGEDOWN]) model_distance *= exp(alpha * ruggospeed); - } - - if(qm) { - apply_rotation(t); - } - } + if(finger_center) + perform_finger(); #endif } catch(rug_exception) { @@ -1676,24 +1317,20 @@ EX void actDraw() { int besti; -void getco_pers(rugpoint *r, hyperpoint& p, int& spherepoints, bool& error) { - getco(r, p, spherepoints); - if(rug_perspective) { - if(p[2] >= 0) - error = true; - else { - p[0] /= p[2]; - p[1] /= p[2]; - } - } - } - static const ld RADAR_INF = 1e12; ld radar_distance = RADAR_INF; EX hyperpoint gethyper(ld x, ld y) { - double mx = (x - current_display->xcenter)/vid.xres * 2 * xview; - double my = (current_display->ycenter - y)/vid.yres * 2 * yview; + + projection_configuration bak = pconf; + pconf = rconf; + calcparam(); + + double mx = (x - current_display->xcenter)/current_display->radius; + double my = (y - current_display->ycenter)/current_display->radius/pconf.stretch; + + calcparam(); + radar_distance = RADAR_INF; double rx1=0, ry1=0; @@ -1709,9 +1346,22 @@ EX hyperpoint gethyper(ld x, ld y) { hyperpoint p0, p1, p2; bool error = false; int spherepoints = 0; - getco_pers(r0, p0, spherepoints, error); - getco_pers(r1, p1, spherepoints, error); - getco_pers(r2, p2, spherepoints, error); + if(1) { + USING_NATIVE_GEOMETRY; + // USING_RUG_PMODEL; + // dynamicval m(pmodel, mdEquidistant); + + if(elliptic && pmodel == mdDisk) { + int sp = + (r0->native[3] < 0) + (r1->native[3] < 0) + (r2->native[3] < 0); + if(sp == 1 || sp == 2) continue; + } + + applymodel(r0->native, p0); + applymodel(r1->native, p1); + applymodel(r2->native, p2); + } + if(error || spherepoints == 1 || spherepoints == 2) continue; double dx1 = p1[0] - p0[0]; double dy1 = p1[1] - p0[1]; @@ -1727,7 +1377,7 @@ EX hyperpoint gethyper(ld x, ld y) { tx /= det; ty /= det; if(tx >= 0 && ty >= 0 && tx+ty <= 1) { double rz1 = p0[2] * (1-tx-ty) + p1[2] * tx + p2[2] * ty; - rz1 = -rz1; if(!rug_perspective) rz1 += model_distance; + rz1 = -rz1; if(rz1 < radar_distance) { radar_distance = rz1; rx1 = r0->x1 + (r1->x1 - r0->x1) * tx + (r2->x1 - r0->x1) * ty; @@ -1737,6 +1387,7 @@ EX hyperpoint gethyper(ld x, ld y) { } } + pconf = bak; if(!found) return Hypc; double px = rx1 * TEXTURESIZE, py = (1-ry1) * TEXTURESIZE; @@ -1786,6 +1437,35 @@ void change_texturesize() { ld old_distance; +EX void rug_geometry_choice() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(0); + dialog::init(XLAT("hypersian rug mode"), iinf[itPalace].color, 150, 100); + + USING_NATIVE_GEOMETRY; + + dialog::addBoolItem("Euclidean", euclid, 'a'); + dialog::add_action([] { gwhere = rgEuclid; popScreen(); }); + + dialog::addBoolItem("hyperbolic", hyperbolic, 'b'); + dialog::add_action([] { gwhere = rgHyperbolic; popScreen(); }); + + dialog::addBoolItem("spherical", sphere && !elliptic, 'c'); + dialog::add_action([] { gwhere = rgSphere; popScreen(); }); + + dialog::addBoolItem("elliptic", elliptic, 'd'); + dialog::add_action([] { gwhere = rgElliptic; popScreen(); }); + + dialog::addBoolItem("Nil", nil, 'e'); + dialog::add_action([] { gwhere = gNil; popScreen(); }); + + dialog::addBoolItem("Solv", sol, 'e'); + dialog::add_action([] { gwhere = gSol; popScreen(); }); + + dialog::addBack(); + dialog::display(); + } + EX void show() { cmode = sm::SIDE | sm::MAYDARK; gamescreen(0); @@ -1806,9 +1486,10 @@ EX void show() { dialog::lastItem().value += " (" + its(qvalid) + ")"; dialog::addSelItem(XLAT("model distance"), fts(model_distance), 'd'); - dialog::addBoolItem(XLAT("projection"), rug_perspective, 'p'); - dialog::lastItem().value = XLAT(rug_perspective ? "perspective" : - gwhere == gEuclid ? "orthogonal" : "azimuthal equidistant"); + if(rug::rugged) { + dialog::addSelItem(XLAT("projection"), models::get_model_name(rconf.model), 'p'); + } + else dialog::addBreak(100); if(!rug::rugged) dialog::addSelItem(XLAT("native geometry"), geometry_name(gwhere), 'n'); else @@ -1830,11 +1511,10 @@ EX void show() { edit_levellines('L'); #if CAP_SURFACE - if(hyperbolic) { - if(gwhere == gEuclid) - dialog::addItem(XLAT("smooth surfaces"), 'c'); - else dialog::addBreak(100); - } + if(hyperbolic) + dialog::addItem(XLAT("smooth surfaces"), 'c'); + else + dialog::addBreak(100); #endif dialog::addBreak(50); @@ -1914,7 +1594,7 @@ EX void show() { if(!camera_center) push_all_points(2, model_distance); for(auto p:points) { if(adjust_edges) for(auto& e: p->edges) e.len *= modelscale / last; - if(adjust_points) p->flat *= modelscale / last; + if(adjust_points) p->native *= modelscale / last; enqueue(p); } if(adjust_distance) model_distance = model_distance * modelscale / last; @@ -1925,13 +1605,7 @@ EX void show() { } } else if(uni == 'p') { - rug_perspective = !rug_perspective; - if(rugged) { - if(rug_perspective) - push_all_points(2, -model_distance); - else - push_all_points(2, +model_distance); - } + pushScreen(models::model_menu); } else if(uni == 'd') { dialog::editNumber(model_distance, -10, 10, .1, 1, XLAT("model distance"), @@ -1940,8 +1614,9 @@ EX void show() { ); old_distance = model_distance; dialog::reaction = [] () { - if(rug::rugged && rug_perspective) { - push_all_points(2, old_distance - model_distance); + if(rug::rugged && perspective()) { + using_rugview rv; + shift_view(ztangent(old_distance - model_distance)); } old_distance = model_distance; }; @@ -1955,8 +1630,10 @@ EX void show() { } else if(uni == 'f') pushScreen(showStereo); - else if(uni == 'n' && !rug::rugged) - gwhere = eGeometry((gwhere+1) % 4); + #if MAXMDIM >= 4 + else if(uni == 'n' && !rug::rugged) + pushScreen(rug_geometry_choice); + #endif else if(uni == 'g' && !rug::rugged && CAP_SDL) rendernogl = !rendernogl; else if(uni == 's') { @@ -1978,6 +1655,88 @@ EX void select() { pushScreen(rug::show); } +EX void rug_save(string fname) { + fhstream f(fname, "wb"); + if(!f.f) { + addMessage(XLAT("Failed to save rug to %1", fname)); + return; + } + f.write(f.vernum); + f.write(gwhere); + USING_NATIVE_GEOMETRY; + int N = isize(points); + f.write(N); + map ids; + for(int i=0; ivalid); + f.write(p->x1); + f.write(p->y1); + f.write(p->native); + f.write(get_id(p->glue)); + } + int M = isize(triangles); + f.write(M); + for(auto t: triangles) { + f.write(get_id(t.m[0])); + f.write(get_id(t.m[1])); + f.write(get_id(t.m[2])); + } + + int cp = isize(surface::coverage); + f.write(cp); + for(auto p: surface::coverage) f.write(p.first), f.write(p.second); + } + +EX void rug_load(string fname) { + clear_model(); + fhstream f(fname, "rb"); + if(!f.f) { + addMessage(XLAT("Failed to load rug from %1", fname)); + return; + } + f.read(f.vernum); + f.read(gwhere); + USING_NATIVE_GEOMETRY; + int N = f.get(); + println(hlog, "N = ", N); + points.resize(N); + for(int i=0; i(); + if(i == -1) p = nullptr; + else p = points[i]; + }; + for(auto p: points) { + f.read(p->valid); + f.read(p->x1); + f.read(p->y1); + f.read(p->native); + by_id(p->glue); + } + triangles.resize(f.get()); + for(auto& t: triangles) { + by_id(t.m[0]); + by_id(t.m[1]); + by_id(t.m[2]); + } + + surface::coverage.resize(f.get()); + for(auto p: surface::coverage) f.read(p.first), f.read(p.second); + good_shape = true; + } + #if CAP_COMMANDLINE int rugArgs() { using namespace arg; @@ -1988,17 +1747,34 @@ int rugArgs() { } else if(argis("-ruggeo")) { - shift(); gwhere = (eGeometry) argi(); + shift(); gwhere = readGeo(args()); + if(gwhere == gEuclid) gwhere = rgEuclid; + if(gwhere == gSphere) gwhere = rgSphere; + if(gwhere == gNormal) gwhere = rgHyperbolic; + if(gwhere == gElliptic) gwhere = rgElliptic; } else if(argis("-rugpers")) { - rug_perspective = true; + USING_NATIVE_GEOMETRY; + rconf.model = nonisotropic ? mdGeodesic : mdPerspective; } else if(argis("-rugonce")) { renderonce = true; } + else if(argis("-rugsave")) { + shift(); rug_save(args()); + } + + else if(argis("-rugload")) { + PHASE(3); + start_game(); + calcparam(); + rug::init(); + shift(); rug_load(args()); + } + else if(argis("-rugdist")) { shift_arg_formula(model_distance); } @@ -2021,7 +1797,7 @@ int rugArgs() { } else if(argis("-rugorth")) { - rug_perspective = false; + rconf.model = mdEquidistant; } else if(argis("-rugerr")) { @@ -2039,8 +1815,8 @@ int rugArgs() { } else if(argis("-rugon")) { - start_game(); PHASE(3); + start_game(); calcparam(); rug::init(); } diff --git a/screenshot.cpp b/screenshot.cpp index 18ca0245..fc5957d5 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -86,10 +86,10 @@ EX always_false in; EX void circle(int x, int y, int size, color_t col, color_t fillcol, double linewidth) { if(!invisible(col) || !invisible(fillcol)) { - if(vid.stretch == 1) + if(pconf.stretch == 1) println(f, ""); else - println(f, ""); + println(f, ""); } } @@ -215,7 +215,7 @@ int read_args() { else if(argis("-svgshot")) { PHASE(3); shift(); start_game(); printf("saving SVG screenshot to %s\n", argcs()); - shot::make_svg = true; + shot::format = shot::screenshot_format::svg; shot::take(argcs()); } else if(argis("-svgtwm")) { @@ -233,6 +233,374 @@ auto ah = addHook(hooks_args, 0, read_args); #endif EX } +/** wrl renderer */ +EX namespace wrl { +#if !CAP_WRL +EX always_false in; +#endif + +#if CAP_WRL + EX bool in; + + EX bool print; + EX bool textures = true; + + EX ld rug_width = .01; + + fhstream f; + string filename; + + string coord(ld val) { + char buf[100]; + snprintf(buf, 100, "%f", val); + return buf; + } + + string coord(const hyperpoint& v, int q) { + char buf[100]; + if(q == 3) snprintf(buf, 100, "%f, %f, %f", v[0], v[1], v[2]); + if(q == 2) snprintf(buf, 100, "%f, %f", v[0], v[1]); + return buf; + } + + string color(color_t col, ld v) { + char buf[100]; + ld cols[4]; + for(int i=0; i<4; i++) { + + cols[i] = part(col, i); + cols[i] /= 255; + cols[i] = pow(cols[i], shot::gamma) * shot::fade * v; + } + + snprintf(buf, 100, "%.3f %.3f %.3f", cols[3], cols[2], cols[1]); + return buf; + } + + typedef unsigned long long hashtype; + hashtype hash(ld x) { return hashtype(x * 1000000 + .5); } + + hashtype hash(hyperpoint h) { + return hash(h[0]) + 7 * hash(h[1]) + 13 * hash(h[2]); + } + + EX void fatten(vector& data, vector& tdata) { + map normals; + for(int i=0; i data2; + vector tdata2; + for(int i=0; i 0) na = -na; + if((normal | nb) > 0) nb = -nb; + if((normal | nc) > 0) nc = -nc; + bool bad = false; + for(int i=0; i<3; i++) { + if(isnan(na[i]) || isnan(nb[i]) || isnan(nc[i])) bad = true; + } + if(bad) { + println(hlog, "bad vertex"); + continue; + } + data2.push_back(a+na); data2.push_back(b+nb); data2.push_back(c+nc); + data2.push_back(b+nb); data2.push_back(a+na); data2.push_back(a-na); + data2.push_back(b+nb); data2.push_back(a-na); data2.push_back(b-nb); + data2.push_back(c+nc); data2.push_back(b+nb); data2.push_back(b-nb); + data2.push_back(c+nc); data2.push_back(b-nb); data2.push_back(c-nc); + data2.push_back(a+na); data2.push_back(c+nc); data2.push_back(c-nc); + data2.push_back(a+na); data2.push_back(c-nc); data2.push_back(a-na); + data2.push_back(b-nb); data2.push_back(a-na); data2.push_back(c-nc); + if(isize(tdata)) { + auto ta = tdata[i], tb = tdata[i+1], tc = tdata[i+2]; + for(auto p: {ta, tb, tc, tb, ta, ta, tb, ta, tb, tc, tb, tb, tc, tb, tc, ta, tc, tc, ta, tc, ta, tb, ta, tc}) + tdata2.push_back(p); + } + } + data = data2; + tdata = tdata2; + } + + bool used_rug; + + map, int> texture_position; + map gradient_position; + + pair texid(dqi_poly& p) { + return make_pair(p.color, p.tinf->tvertices[0]); + } + + /** 0 = no/unknown/disabled texture, 1 = rug, 2 = gradient, 3 = floor texture */ + EX int texture_type(dqi_poly& p) { + if(!p.tinf) return 0; + if(!CAP_PNG) return 0; + if(!textures) return 0; + if(p.tinf == &rug::tinf) return 1; + #if MAXMDIM >= 4 + if(p.tinf->texture_id == (int) floor_textures->renderedTexture) + return (p.tinf->tvertices[0][0] == 0) ? 2 : 3; + #endif + return 0; + } + + EX void prepare(dqi_poly& p) { + if(print && !(p.flags & POLY_PRINTABLE)) return; + if(!(p.flags & POLY_TRIANGLES)) return; + int tt = texture_type(p); + if(tt == 2) gradient_position[p.color] = 0; + if(tt == 3) texture_position[texid(p)] = 0; + } + + #if MAXMDIM >= 4 + int fts_int, fts, fts_row; + #endif + + map, vector>> all_data; + + EX void polygon(dqi_poly& p) { + if(print && !(p.flags & POLY_PRINTABLE)) return; + if(!(p.flags & POLY_TRIANGLES)) return; + int tt = texture_type(p); + + vector data; + vector tdata; + for(int i=0; itvertices[p.offset_texture+i]); + } + for(auto& d: data) { + hyperpoint h; + h = p.V * d; + applymodel(h, d); + } + if(print && (p.flags & POLY_FAT)) { + fatten(data, tdata); + p.cnt = isize(data); + } + else if(print) { + hyperpoint ctr1; + applymodel(p.V * p.intester, ctr1); + println(hlog, "intester = ", p.intester); + ld sdet = 0; + if(1) { + dynamicval g(geometry, gEuclid); + for(int i=0; i= 4 + if(tt == 2) { + ld x = (fts - .5 - gradient_position[p.color]) / fts; + for(auto& d: tdata) d[0] = x; + } + + if(tt == 3) { + int tp = texture_position[texid(p)]; + auto xy = make_array(tp % fts_row, tp / fts_row); + auto zero = p.tinf->tvertices[0]; + ld sca = FLOORTEXTURESIZE*1./fts_int; + for(auto& d: tdata) + for(int c: {0, 1}) + d[c] = ((d[c] - zero[c])*sca + xy[c] + .5) * fts_int / fts; + } + #endif + + for(auto& d: tdata) ad.second.push_back(d); + } + + EX void render() { + #if MAXMDIM >= 4 + for(auto& p: ptds) { + auto p2 = dynamic_cast(&*p); + if(p2) + prepare(*p2); + } + + int tps = 0; + for(auto& p: texture_position) p.second = tps++; + int gps = 0; + for(auto& p: gradient_position) p.second = gps++; + + fts_int = floor_texture_square_size * FLOORTEXTURESIZE + 4; + fts = 64; + + while(fts < gps || (fts-gps)/fts_int * fts/fts_int < tps) + fts *= 2; + + fts_row = (fts-gps)/fts_int; + #endif + + for(auto& p: ptds) { + auto p2 = dynamic_cast(&*p); + if(p2) + polygon(*p2); + } + } + + EX void take(const string& fname, const function& what IS(shot::default_screenshot_content)) { + dynamicval v2(in, true); + dynamicval v3(noshadow, true); + filename = fname; + + ptds.clear(); + all_data.clear(); + what(); + + f.f = fopen(fname.c_str(), "wt"); + + println(f, "#VRML V2.0 utf8"); + println(f, "WorldInfo { title \"3D model exported from HyperRogue\" info [ \"3D models exported from HyperRogue are public domain\" ] }"); + + for(auto& p: all_data) { + const string& app = p.first; + auto& data = p.second.first; + auto& tdata = p.second.second; + + println(f, "Shape {"); + println(f, " appearance Appearance {"); + println(f, app); + println(f, " }"); + // println(f, "# V = ", p.V); + println(f, " geometry IndexedFaceSet {"); + println(f, " coord Coordinate {"); + + println(f, " point ["); + for(auto& d: data) println(f, " ", coord(d, 3), ","); + println(f, " ]"); + println(f, " }"); + + if(!tdata.empty()) { + println(f, " texCoord TextureCoordinate {"); + println(f, " point ["); + + for(auto& d: tdata) + println(f, " ", coord(glhr::gltopoint(d), 2), ","); + println(f, " ]"); + println(f, " }"); + } + + println(f, " coordIndex ["); + for(int i=0; ienable(); + SDL_Surface *s = rug::glbuf->render(); + dynamicval dx(shot::shotx, rug::texturesize); + dynamicval dy(shot::shoty, rug::texturesize); + shot::postprocess(filename + "-rug.png", s, s); + } + + #if MAXMDIM >= 4 + if(isize(texture_position) || isize(gradient_position)) { + SDL_Surface *s = shot::empty_surface(fts, fts, false); + for(auto& p: gradient_position) { + int x = fts - p.second - 1; + for(int y=0; y> 8; + part(qpixel(s, x, y), 3) = 0xFF; + } + } + + SDL_Surface *floor = floor_textures->render(); + for(auto& p: texture_position) { + int nx = p.second % fts_row; + int ny = p.second / fts_row; + color_t col = p.first.first; + int xs = p.first.second[0] * FLOORTEXTURESIZE - fts_int/2; + int ys = p.first.second[1] * FLOORTEXTURESIZE - fts_int/2; + swap(xs, ys); // I do not understand why + for(int y=0; y& what) { + resetbuffer rb; + + renderbuffer glbuf(vid.xres, vid.yres, vid.usingGL); + glbuf.enable(); + current_display->set_viewport(0); + + dynamicval v8(backcolor, transparent ? 0xFF000000 : backcolor); + #if CAP_RUG + if(rug::rugged && !rug::renderonce) rug::prepareTexture(); + #endif + glbuf.clear(backcolor); + what(); + + SDL_Surface *sdark = glbuf.render(); + + if(transparent) { + renderbuffer glbuf1(vid.xres, vid.yres, vid.usingGL); + backcolor = 0xFFFFFFFF; + #if CAP_RUG + if(rug::rugged && !rug::renderonce) rug::prepareTexture(); + #endif + glbuf1.enable(); + glbuf1.clear(backcolor); + current_display->set_viewport(0); + what(); + + postprocess(fname, sdark, glbuf1.render()); + } + else postprocess(fname, sdark, sdark); + } +#endif + EX void take(string fname, const function& what IS(default_screenshot_content)) { if(cheater) doOvergenerate(); - + #if CAP_SVG - int multiplier = make_svg ? svg::divby : shot_aa; + int multiplier = (format == screenshot_format::svg) ? svg::divby : shot_aa; #else int multiplier = shot_aa; #endif @@ -352,45 +762,25 @@ EX void take(string fname, const function& what IS(default_screenshot_co models::configure(); callhooks(hooks_take); - if(make_svg) { - #if CAP_SVG - svg::render(fname, what); - #endif - } - - else { - #if CAP_PNG - resetbuffer rb; - - renderbuffer glbuf(vid.xres, vid.yres, vid.usingGL); - glbuf.enable(); - current_display->set_viewport(0); - - dynamicval v8(backcolor, transparent ? 0xFF000000 : backcolor); - #if CAP_RUG - if(rug::rugged && !rug::renderonce) rug::prepareTexture(); - #endif - glbuf.clear(backcolor); - what(); - - SDL_Surface *sdark = glbuf.render(); - - if(transparent) { - renderbuffer glbuf1(vid.xres, vid.yres, vid.usingGL); - backcolor = 0xFFFFFFFF; - #if CAP_RUG - if(rug::rugged && !rug::renderonce) rug::prepareTexture(); + switch(format) { + case screenshot_format::wrl: + #if CAP_WRL + wrl::take(fname); #endif - glbuf1.enable(); - glbuf1.clear(backcolor); - current_display->set_viewport(0); - what(); - - postprocess(fname, sdark, glbuf1.render()); - } - else postprocess(fname, sdark, sdark); - #endif - } + return; + + case screenshot_format::svg: + #if CAP_SVG + svg::render(fname, what); + #endif + return; + + case screenshot_format::png: + #if CAP_PNG + render_png(fname, what); + #endif + return; + } } #if CAP_COMMANDLINE @@ -399,7 +789,7 @@ int png_read_args() { if(argis("-pngshot")) { PHASE(3); shift(); start_game(); printf("saving PNG screenshot to %s\n", argcs()); - make_svg = false; + format = screenshot_format::png; shot::take(argcs()); } else if(argis("-pngsize")) { @@ -417,6 +807,20 @@ int png_read_args() { else if(argis("-shotaa")) { shift(); shot_aa = argi(); } + #if CAP_WRL + else if(argis("-modelshot")) { + PHASE(3); shift(); start_game(); + printf("saving WRL model to %s\n", argcs()); + shot::format = screenshot_format::wrl; wrl::print = false; + shot::take(argcs()); + } + else if(argis("-printshot")) { + PHASE(3); shift(); start_game(); + printf("saving 3D printable model to %s\n", argcs()); + shot::format = screenshot_format::wrl; wrl::print = true; + shot::take(argcs()); + } + #endif else return 1; return 0; } @@ -424,30 +828,118 @@ int png_read_args() { auto ah_png = addHook(hooks_args, 0, png_read_args); #endif +EX string format_name() { + if(format == screenshot_format::svg) return "SVG"; + if(format == screenshot_format::wrl) return "WRL"; + if(format == screenshot_format::png) return "PNG"; + return "?"; + } + +EX string format_extension() { + if(format == screenshot_format::svg) return ".svg"; + if(format == screenshot_format::wrl) return ".wrl"; + if(format == screenshot_format::png) return ".png"; + return "?"; + } + + +EX void choose_screenshot_format() { + cmode = sm::SIDE; + gamescreen(0); + dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100); + #if CAP_PNG + dialog::addItem(XLAT("PNG"), 'p'); + dialog::add_action([] { format = screenshot_format::png; popScreen(); }); + #endif + #if CAP_SVG + dialog::addItem(XLAT("SVG"), 's'); + dialog::add_action([] { format = screenshot_format::svg; popScreen(); }); + #endif + #if CAP_WRL + dialog::addItem(XLAT("WRL"), 'w'); + dialog::add_action([] { format = screenshot_format::wrl; popScreen(); }); + #endif + dialog::addBack(); + dialog::display(); + } + EX void menu() { cmode = sm::SIDE; gamescreen(0); - if(!CAP_SVG) make_svg = false; - if(!CAP_PNG) make_svg = true; + if(format == screenshot_format::svg && !CAP_SVG) + format = screenshot_format::png; + if(format == screenshot_format::png && !CAP_PNG) + format = screenshot_format::svg; dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100); - dialog::addSelItem(XLAT("format"), make_svg ? "SVG" : "PNG", 'f'); - dialog::add_action([] { make_svg = !make_svg; }); - dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x'); - dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); }); - dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y'); - dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); }); - if(make_svg) { - #if CAP_SVG - using namespace svg; - dialog::addSelItem(XLAT("precision"), "1/"+its(divby), 'p'); - dialog::add_action([] { divby *= 10; if(divby > 1000000) divby = 1; }); - #endif + dialog::addSelItem(XLAT("format"), format_name(), 'f'); + dialog::add_action_push(choose_screenshot_format); + bool dowrl = format == screenshot_format::wrl; + if(!dowrl) { + dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x'); + dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); }); + dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y'); + dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); }); } - else { - dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's'); - dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; }); + + switch(format) { + case screenshot_format::svg: { + #if CAP_SVG + using namespace svg; + dialog::addSelItem(XLAT("precision"), "1/"+its(divby), 'p'); + dialog::add_action([] { divby *= 10; if(divby > 1000000) divby = 1; }); + #endif + + if(models::is_3d(vpconf) || rug::rugged) { + dialog::addInfo("SVG screenshots do not work in this 3D mode", 0xFF0000); + if(GDIM == 2 && !rug::rugged) { + dialog::addSelItem(XLAT("projection"), current_proj_name(), '1'); + dialog::add_action_push(models::model_menu); + } + #if CAP_WRL + else { + dialog::addItem(XLAT("WRL"), 'w'); + dialog::add_action([] { format = screenshot_format::wrl; }); + } + #endif + } + + #if CAP_TEXTURE + if(texture::config.tstate == texture::tsActive) + dialog::addInfo("SVG screenshots do not work with textures", 0xFF0000); + #endif + break; + } + + case screenshot_format::png: { + #if CAP_PNG + dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's'); + dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; }); + #endif + break; + } + + case screenshot_format::wrl: { + #if CAP_WRL + if(!models::is_3d(vpconf) && !rug::rugged) { + dialog::addInfo("this format is for 3D projections", 0xFF0000); + if(GDIM == 2) { + dialog::addItem(XLAT("hypersian rug mode"), 'u'); + dialog::add_action_push(rug::show); + } + } + else if(rug::rugged ? rug::perspective() : models::is_perspective(vpconf.model)) { + dialog::addInfo("this does not work well in perspective projections", 0xFF8000); + dialog::addSelItem(XLAT("projection"), current_proj_name(), '1'); + dialog::add_action_push(models::model_menu); + } + dialog::addBoolItem_action("generate a model for 3D printing", wrl::print, 'p'); + #if CAP_PNG + dialog::addBoolItem_action("use textures", wrl::textures, 'u'); + #endif + #endif + } } - dialog::addBoolItem_action(XLAT("transparent"), transparent, 't'); + if(!dowrl) dialog::addBoolItem_action(XLAT("transparent"), transparent, 't'); dialog::addSelItem(XLAT("gamma"), fts(gamma), 'g'); dialog::add_action([] { dialog::editNumber(gamma, 0, 2, .1, .5, XLAT("gamma"), "higher value = darker"); }); @@ -455,10 +947,13 @@ EX void menu() { dialog::addSelItem(XLAT("brightness"), fts(fade), 'b'); dialog::add_action([] { dialog::editNumber(fade, 0, 2, .1, 1, XLAT("brightness"), "higher value = lighter"); }); - dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, 'h'); - - dialog::addBoolItem_action_neg(XLAT("hide the player"), mapeditor::drawplayer, 'H'); + if(!dowrl) dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, 'h'); + dialog::addBoolItem_action_neg(XLAT("hide the player"), mapeditor::drawplayer, 'H'); + #if CAP_WRL + if(dowrl && wrl::print) dialog::lastItem().value = XLAT("N/A"); + #endif + if(WDIM == 2) { dialog::addItem(XLAT("centering"), 'x'); dialog::add_action([] { @@ -483,18 +978,6 @@ EX void menu() { dialog::addBreak(100); -#if CAP_RUG - if(make_svg && rug::rugged) - dialog::addInfo("SVG screenshots do not work in this 3D mode", 0xFF0000); - else -#endif -#if CAP_TEXTURE - if(make_svg && texture::config.tstate == texture::tsActive) - dialog::addInfo("SVG screenshots do not work with textures", 0xFF0000); - else -#endif - dialog::addBreak(100); - dialog::addItem(XLAT("take screenshot"), 'z'); dialog::add_action([] () { #if ISWEB @@ -502,8 +985,13 @@ EX void menu() { #else static string pngfile = "hqshot.png"; static string svgfile = "svgshot.svg"; - string& file = make_svg ? svgfile : pngfile; - dialog::openFileDialog(file, XLAT("screenshot"), make_svg ? ".svg" : ".png", [&file] () { + static string wrlfile = "model.wrl"; + string& file = + format == screenshot_format::png ? pngfile : + format == screenshot_format::svg ? svgfile : + wrlfile; + + dialog::openFileDialog(file, XLAT("screenshot"), format_extension(), [&file] () { dynamicval cgl(vid.cells_generated_limit, 9999999); shot::take(file); return true; @@ -727,32 +1215,30 @@ EX void apply() { #if CAP_RUG if(rug::rugged) { if(rug_rotation1) { - rug::apply_rotation(cspin(1, 2, rug_angle * degree)); - rug::apply_rotation(cspin(0, 2, rug_rotation1 * 2 * M_PI * t / period)); - rug::apply_rotation(cspin(1, 2, -rug_angle * degree)); + rug::rugView = cspin(1, 2, -rug_angle * degree) * cspin(0, 2, rug_rotation1 * 2 * M_PI * t / period) * cspin(1, 2, rug_angle * degree) * rug::rugView; } if(rug_rotation2) { - rug::apply_rotation(rug::currentrot * cspin(0, 1, rug_rotation2 * 2 * M_PI * t / period) * inverse(rug::currentrot)); + rug::rugView = rug::rugView * cspin(0, 1, rug_rotation2 * 2 * M_PI * t / period); } } #endif - vid.skiprope += skiprope_rotation * t * 2 * M_PI / period; + pconf.skiprope += skiprope_rotation * t * 2 * M_PI / period; if(ballangle_rotation) { - if(models::model_has_orientation()) - models::model_orientation += ballangle_rotation * 360 * t / period; + if(models::has_orientation(vpconf.model)) + vpconf.model_orientation += ballangle_rotation * 360 * t / period; else - vid.ballangle += ballangle_rotation * 360 * t / period; + vpconf.ballangle += ballangle_rotation * 360 * t / period; } if(joukowsky_anim) { ld t = ticks / period; t = t - floor(t); if(pmodel == mdBand) { - models::model_transition = t * 4 - 1; + vpconf.model_transition = t * 4 - 1; } else { - models::model_transition = t / 1.1; - vid.scale = (1 - models::model_transition) / 2.; + vpconf.model_transition = t / 1.1; + vpconf.scale = (1 - vpconf.model_transition) / 2.; } } apply_animated_parameters(); @@ -779,6 +1265,7 @@ bool record_animation() { int newticks = i * period / noframes; cmode = (env_shmup ? sm::NORMAL : 0); while(ticks < newticks) shmup::turn(1), ticks++; + ca::simulate(); if(playermoved) centerpc(INF), optimizeview(); dynamicval v2(inHighQual, true); apply(); @@ -1010,7 +1497,7 @@ EX void show() { }); } #endif - if(models::model_has_orientation()) + if(models::has_orientation(vpconf.model)) animator(XLAT("model rotation"), ballangle_rotation, 'I'); else if(among(pmodel, mdHyperboloid, mdHemisphere, mdBall)) animator(XLAT("3D rotation"), ballangle_rotation, '3'); @@ -1042,7 +1529,8 @@ EX void show() { dialog::add_action([] () { dialog::editNumber(noframes, 0, 300, 30, 5, XLAT("frames to record"), ""); }); dialog::addSelItem(XLAT("record to a file"), animfile, 'R'); dialog::add_action([] () { - dialog::openFileDialog(animfile, XLAT("record to a file"), shot::make_svg ? ".svg" : ".png", record_animation); + dialog::openFileDialog(animfile, XLAT("record to a file"), + shot::format_extension(), record_animation); }); #endif dialog::addBack(); @@ -1188,9 +1676,9 @@ startanim null_animation { "", no_init, [] { gamescreen(2); }}; startanim joukowsky { "Joukowsky transform", no_init, [] { dynamicval dm(pmodel, mdJoukowskyInverted); - dynamicval dt(models::model_orientation, ticks / 25.); + dynamicval dt(pconf.model_orientation, ticks / 25.); dynamicval dv(vid.use_smart_range, 2); - dynamicval ds(vid.scale, 1/4.); + dynamicval ds(pconf.scale, 1/4.); models::configure(); dynamicval dc(ringcolor, 0); gamescreen(2); @@ -1199,7 +1687,7 @@ startanim joukowsky { "Joukowsky transform", no_init, [] { startanim bandspin { "spinning in the band model", no_init, [] { dynamicval dm(pmodel, mdBand); - dynamicval dt(models::model_orientation, ticks / 25.); + dynamicval dt(pconf.model_orientation, ticks / 25.); dynamicval dv(vid.use_smart_range, 2); models::configure(); gamescreen(2); @@ -1212,8 +1700,8 @@ startanim perspective { "projection distance", no_init, [] { x /= 2; x *= 1.5; x = tan(x); - dynamicval da(vid.alpha, x); - dynamicval ds(vid.scale, (1+x)/2); + dynamicval da(pconf.alpha, x); + dynamicval ds(pconf.scale, (1+x)/2); calcparam(); gamescreen(2); explorable(projectionDialog); @@ -1224,16 +1712,15 @@ startanim rug { "Hypersian Rug", [] { rug::init(), rug::rugged = false; }, [] { dynamicval b(rug::rugged, true); rug::physics(); - rug::apply_rotation(cspin(1, 2, ticks / 3000.)); + dynamicval t(rug::rugView, cspin(1, 2, ticks / 3000.) * rug::rugView); gamescreen(2); - rug::apply_rotation(cspin(1, 2, -ticks / 3000.)); if(!rug::rugged) current = &null_animation; explorable([] { rug::rugged = true; pushScreen(rug::show); }); }}; startanim spin_around { "spinning around", no_init, [] { - dynamicval da(vid.alpha, 999); - dynamicval ds(vid.scale, 500); + dynamicval da(pconf.alpha, 999); + dynamicval ds(pconf.scale, 500); ld alpha = 2 * M_PI * ticks / 10000.; ld circle_radius = acosh(2.); dynamicval dv(View, spin(-cos_auto(circle_radius)*alpha) * xpush(circle_radius) * spin(alpha) * View); diff --git a/shaders.cpp b/shaders.cpp index 8760e090..1a64e84d 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -16,8 +16,9 @@ constexpr flagtype GF_TEXTURE = 1; constexpr flagtype GF_VARCOLOR = 2; constexpr flagtype GF_LIGHTFOG = 4; constexpr flagtype GF_LEVELS = 8; +constexpr flagtype GF_TEXTURE_SHADED = 16; -constexpr flagtype GF_which = 15; +constexpr flagtype GF_which = 31; constexpr flagtype SF_PERS3 = 256; constexpr flagtype SF_BAND = 512; @@ -62,7 +63,15 @@ shared_ptr write_shader(flagtype shader_flags) { varying += "varying mediump vec4 vColor;\n"; fmain += "gl_FragColor = vColor;\n"; - if(shader_flags & GF_TEXTURE) { + if(shader_flags & GF_TEXTURE_SHADED) { + vsh += "attribute mediump vec3 aTexture;\n"; + varying += "varying mediump vec3 vTexCoord;\n"; + fsh += "uniform mediump sampler2D tTexture;\n"; + vmain += "vTexCoord = aTexture;\n"; + fmain += "gl_FragColor *= texture2D(tTexture, vTexCoord.xy);\n"; + fmain += "gl_FragColor.rgb *= vTexCoord.z;\n"; + } + else if(shader_flags & GF_TEXTURE) { vsh += "attribute mediump vec2 aTexture;\n"; varying += "varying mediump vec2 vTexCoord;\n"; fsh += "uniform mediump sampler2D tTexture;\n"; @@ -359,7 +368,7 @@ void display_data::set_projection(int ed) { if(pmodel == mdManual) return; - if(vid.stretch != 1 && (shader_flags & SF_DIRECT)) glhr::projection_multiply(glhr::scale(vid.stretch, 1, 1)); + if(pconf.stretch != 1 && (shader_flags & SF_DIRECT) && pmodel != mdPixel) glhr::projection_multiply(glhr::scale(1, pconf.stretch, 1)); if(vid.stereo_mode != sODS) eyewidth_translate(ed); @@ -367,10 +376,8 @@ void display_data::set_projection(int ed) { auto ortho = [&] (ld x, ld y) { glhr::glmatrix M = glhr::ortho(x, y, 1); if(shader_flags & SF_ZFOG) { - using models::clip_max; - using models::clip_min; - M[2][2] = 2 / (clip_max - clip_min); - M[3][2] = (clip_min + clip_max) / (clip_max - clip_min); + M[2][2] = 2 / (pconf.clip_max - pconf.clip_min); + M[3][2] = (pconf.clip_min + pconf.clip_max) / (pconf.clip_max - pconf.clip_min); auto cols = glhr::acolor(darkena(backcolor, 0, 0xFF)); glUniform4f(selected->uFogColor, cols[0], cols[1], cols[2], cols[3]); } @@ -409,7 +416,7 @@ void display_data::set_projection(int ed) { glhr::fog_max(1/sightranges[geometry], darkena(backcolor, 0, 0xFF)); } else { - if(vid.alpha > -1) { + if(pconf.alpha > -1) { // Because of the transformation from H3 to the Minkowski hyperboloid, // points with negative Z can be generated in some 3D settings. // This happens for points below the camera, but above the plane. @@ -420,14 +427,14 @@ void display_data::set_projection(int ed) { GLfloat sc = current_display->radius / (cd->ysize/2.); glhr::projection_multiply(glhr::frustum(cd->xsize / cd->ysize, 1)); glhr::projection_multiply(glhr::scale(sc, -sc, -1)); - glhr::projection_multiply(glhr::translate(0, 0, vid.alpha)); + glhr::projection_multiply(glhr::translate(0, 0, pconf.alpha)); if(ed) glhr::projection_multiply(glhr::translate(vid.ipd * ed/2, 0, 0)); } if(selected->uPP != -1) { glhr::glmatrix pp = glhr::id; if(get_shader_flags() & SF_USE_ALPHA) - pp[3][2] = GLfloat(vid.alpha); + pp[3][2] = GLfloat(pconf.alpha); if(get_shader_flags() & SF_ORIENT) { if(GDIM == 3) for(int a=0; a<4; a++) @@ -440,7 +447,7 @@ void display_data::set_projection(int ed) { } if(selected->uAlpha != -1) - glhr::set_ualpha(vid.alpha); + glhr::set_ualpha(pconf.alpha); if(selected->uLevelLines != -1) { glUniform1f(selected->uLevelLines, levellines); @@ -458,12 +465,12 @@ void display_data::set_projection(int ed) { if(selected->shader_flags & SF_HALFPLANE) { glhr::projection_multiply(glhr::translate(0, 1, 0)); glhr::projection_multiply(glhr::scale(-1, 1, 1)); - glhr::projection_multiply(glhr::scale(models::halfplane_scale, models::halfplane_scale, GDIM == 3 ? models::halfplane_scale : 1)); + glhr::projection_multiply(glhr::scale(pconf.halfplane_scale, pconf.halfplane_scale, GDIM == 3 ? pconf.halfplane_scale : 1)); glhr::projection_multiply(glhr::translate(0, 0.5, 0)); } - if(vid.camera_angle && pmodel != mdPixel) { - ld cam = vid.camera_angle * degree; + if(pconf.camera_angle && pmodel != mdPixel) { + ld cam = pconf.camera_angle * degree; GLfloat cc = cos(cam); GLfloat ss = sin(cam); diff --git a/shmup.cpp b/shmup.cpp index 512b01a5..0d5bc122 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -815,7 +815,7 @@ void movePlayer(monster *m, int delta) { hyperpoint jh = hpxy(mdx/100.0, mdy/100.0); hyperpoint ctr = m->pat * C0; - if(sphere && vid.alpha > 1.001) for(int i=0; i<3; i++) ctr[i] = -ctr[i]; + if(sphere && pconf.alpha > 1.001) for(int i=0; i<3; i++) ctr[i] = -ctr[i]; hyperpoint h = inverse(m->pat) * rgpushxto0(ctr) * jh; @@ -1104,7 +1104,7 @@ void movePlayer(monster *m, int delta) { int i0 = i; for(int a=0; a<3; a++) v[a] = (i0 % 3) - 1, i0 /= 3; v = v * .1 / hypot_d(3, v); - transmatrix T1 = (i == 13) ? nat : parallel_transport(nat, m->ori, v, 2); + transmatrix T1 = (i == 13) ? nat : parallel_transport(nat, m->ori, v); cell *c3 = c2; while(true) { cell *c4 = findbaseAround(tC0(T1), c3, 1); @@ -1619,10 +1619,10 @@ void moveBullet(monster *m, int delta) { m->dead = true; if(inertia_based) { - nat = parallel_transport(nat, m->ori, m->inertia * delta, 10); + nat = parallel_transport(nat, m->ori, m->inertia * delta); } else - nat = parallel_transport(nat, m->ori, fronttangent(delta * SCALE * m->vel / speedfactor()), 10); + nat = parallel_transport(nat, m->ori, fronttangent(delta * SCALE * m->vel / speedfactor())); cell *c2 = m->findbase(nat, 1); if(m->parent && isPlayer(m->parent) && markOrb(itOrbLava) && c2 != m->base && !isPlayerOn(m->base)) @@ -2106,14 +2106,14 @@ void moveMonster(monster *m, int delta) { if(inertia_based) { if(igo) return; - nat = parallel_transport(nat, m->ori, m->inertia * delta, 10); + nat = parallel_transport(nat, m->ori, m->inertia * delta); } else if(WDIM == 3 && igo) { ld fspin = rand() % 1000; - nat = parallel_transport(nat0, m->ori, cspin(1,2,fspin) * spin(igospan[igo]) * xtangent(step), 10); + nat = parallel_transport(nat0, m->ori, cspin(1,2,fspin) * spin(igospan[igo]) * xtangent(step)); } else { - nat = parallel_transport(nat0, m->ori, spin(igospan[igo]) * xtangent(step), 10); + nat = parallel_transport(nat0, m->ori, spin(igospan[igo]) * xtangent(step)); } if(m->type != moRagingBull && !peace::on) diff --git a/shyp-geodesics.dat b/shyp-geodesics.dat index 87d9c754..794376a5 100644 Binary files a/shyp-geodesics.dat and b/shyp-geodesics.dat differ diff --git a/solv-geodesics.dat b/solv-geodesics.dat index 36338712..935164f8 100644 Binary files a/solv-geodesics.dat and b/solv-geodesics.dat differ diff --git a/ssol-geodesics.dat b/ssol-geodesics.dat index c07b005e..c45bfcd5 100644 Binary files a/ssol-geodesics.dat and b/ssol-geodesics.dat differ diff --git a/surface.cpp b/surface.cpp index ad2de140..81d0708a 100644 --- a/surface.cpp +++ b/surface.cpp @@ -24,7 +24,7 @@ string shape_name[] = { "hypersian rug", "tractricoid", "Dini's surface", "Kuen EX eShape sh; -hyperpoint unit_vector[3] = {hpxyz(1,0,0), hpxyz(0,1,0), hpxyz(0,0,1)}; +hyperpoint unit_vector[3] = {point3(1,0,0), point3(0,1,0), point3(0,0,1)}; ld last_int_of = 0, last_int = 0; @@ -67,7 +67,7 @@ hyperpoint coord(hyperpoint h) { ld r = 1 / cosh(t); ld x = t - tanh(t); - return hpxyz( r * sin(v), r * cos(v), x ); + return point31( r * sin(v), r * cos(v), x ); break; } @@ -77,7 +77,7 @@ hyperpoint coord(hyperpoint h) { ld a = sqrt(1-dini_b*dini_b); - return hpxyz( a * sin(v) * sin(t), a * cos(v) * sin(t), a * (cos(t) + log(tan(t/2))) + dini_b * v ); + return point31( a * sin(v) * sin(t), a * cos(v) * sin(t), a * (cos(t) + log(tan(t/2))) + dini_b * v ); break; } @@ -87,7 +87,7 @@ hyperpoint coord(hyperpoint h) { ld deno = 1 / (1 + u * u * sin(v) * sin(v)); - return hpxyz( + return point31( 2 * (cos(u) + u * sin(u)) * sin(v) * deno, 2 * (sin(u) - u * cos(u)) * sin(v) * deno, log(tan(v/2)) + 2 * cos(v) * deno @@ -101,7 +101,7 @@ hyperpoint coord(hyperpoint h) { ld phi = hyper_b * cosh(v); ld psi = integral(v); - return hpxyz( phi * cos(u), phi * sin(u), psi ); + return point31( phi * cos(u), phi * sin(u), psi ); } default: @@ -122,10 +122,10 @@ hyperpoint coord_derivative(hyperpoint h, int cc) { ld v = h[1]; if(cc == 0) { ld phi = hyper_b * cosh(v); - return hpxyz( phi * -sin(u), phi * cos(u), 0 ); + return point3( phi * -sin(u), phi * cos(u), 0 ); } else { - return hpxyz( hyper_b * sinh(v) * cos(u), hyper_b * sinh(v) * sin(u), f(v) ); + return point3( hyper_b * sinh(v) * cos(u), hyper_b * sinh(v) * sin(u), f(v) ); } } case dsKuen: { @@ -134,12 +134,12 @@ hyperpoint coord_derivative(hyperpoint h, int cc) { ld denom = pow(sin(v),2)*(u*u)+1; ld denom2 = denom * denom; if(cc == 1) - return hpxyz ( + return point3( 2*sin(v)/denom*u*cos(u)+-4*(sin(u)*u+cos(u))*pow(sin(v),3)/denom2*u, -4*pow(sin(v),3)*(sin(u)-u*cos(u))/denom2*u+2*sin(u)*sin(v)/denom*u, -4*pow(sin(v),2)/denom2*u*cos(v) ); - else return hpxyz ( + else return point3( 2*(sin(u)*u+cos(u))/denom*cos(v)+-4*(sin(u)*u+cos(u))*pow(sin(v),2)/denom2*(u*u)*cos(v), 2*(sin(u)-u*cos(u))/denom*cos(v)+-4*pow(sin(v),2)*(sin(u)-u*cos(u))/denom2*(u*u)*cos(v), -4*sin(v)/denom2*(u*u)*pow(cos(v),2)+1/tan(v/2)*(pow(tan(v/2),2)+1)/2+-2*sin(v)/denom @@ -166,13 +166,13 @@ ld compute_curvature(hyperpoint at) { hyperpoint shape_origin() { switch(sh) { case dsDini: - return hpxyz(M_PI * .82, 0, 0); + return point31(M_PI * .82, 0, 0); case dsTractricoid: - return hpxyz(1, 0, 0); + return point31(1, 0, 0); case dsKuen: - return hpxyz(M_PI * .500001, M_PI * 1, 0); + return point31(M_PI * .500001, M_PI * 1, 0); case dsHyperlike: - return hpxyz(0,0,0); + return point31(0,0,0); default: return Hypc; } @@ -284,7 +284,7 @@ transmatrix create_M_matrix(hyperpoint zero, hyperpoint v1) { transmatrix T = build_matrix(Te0, Te1, Hypc, C03); v1 = v1 / hypot_d(3, T*v1); - hyperpoint v2 = hpxyz(1e-3, 1e-4, 0); + hyperpoint v2 = point3(1e-3, 1e-4, 0); v2 = v2 - v1 * ((T*v1) | (T*v2)) / hypot_d(3, T*v1); v2 = v2 / hypot_d(3, T*v2); @@ -307,9 +307,9 @@ dexp_origin at_zero(hyperpoint zero, transmatrix start) { println(hlog, "zero = ", zero); println(hlog, "curvature at zero = ", compute_curvature(zero)); - println(hlog, "curvature at X1 = ", compute_curvature(zero + hpxyz(.3, 0, 0))); - println(hlog, "curvature at X2 = ", compute_curvature(zero + hpxyz(0, .3, 0))); - println(hlog, "curvature at X3 = ", compute_curvature(zero + hpxyz(.4, .3, 0))); + println(hlog, "curvature at X1 = ", compute_curvature(zero + point3(.3, 0, 0))); + println(hlog, "curvature at X2 = ", compute_curvature(zero + point3(0, .3, 0))); + println(hlog, "curvature at X3 = ", compute_curvature(zero + point3(.4, .3, 0))); return {start, create_M_matrix(zero, unit_vector[0]), zero}; } @@ -338,14 +338,14 @@ void addTriangleV(rug::rugpoint *t1, rug::rugpoint *t2, rug::rugpoint *t3, ld le } hyperpoint kuen_cross(ld v, ld u) { - auto du = coord_derivative(hpxyz(v,u,0), 0); - auto dv = coord_derivative(hpxyz(v,u,0), 1); + auto du = coord_derivative(point3(v,u,0), 0); + auto dv = coord_derivative(point3(v,u,0), 1); return du^dv; } ld kuen_hypot(ld v, ld u) { - auto du = coord_derivative(hpxyz(v,u,0), 0); - auto dv = coord_derivative(hpxyz(v,u,0), 1); + auto du = coord_derivative(point3(v,u,0), 0); + auto dv = coord_derivative(point3(v,u,0), 1); auto n = hypot_d(3, du^dv); return n; } @@ -368,7 +368,7 @@ int dexp_comb_colors[16] = { }; int coverage_style; -vector > coverage; +EX vector > coverage; #ifndef CAP_KUEN_MAP #define CAP_KUEN_MAP 0 @@ -385,8 +385,8 @@ void draw_kuen_map() { for(int h=0; h<512; h++) { ld v = M_PI * (r+.5) / 512; ld u = 2 * M_PI * (h+.5) / 512; - auto du = coord_derivative(hpxyz(v,u,0), 0); - auto dv = coord_derivative(hpxyz(v,u,0), 1); + auto du = coord_derivative(point3(v,u,0), 0); + auto dv = coord_derivative(point3(v,u,0), 1); auto n = hypot_d(3, du^dv); if(n > nmax) nmax = n; @@ -459,7 +459,8 @@ void run_hyperlike() { int sgn = y > 0 ? 1 : -1; ld phi = hyper_b * cosh(y); int pt = y * precision * sgn / hyperlike_bound(); - p->flat = hpxyz(phi * cos(x), phi * sin(x), sgn * integral_table[pt]); + USING_NATIVE_GEOMETRY; + p->native = point31(phi * cos(x), phi * sin(x), sgn * integral_table[pt]); p->valid = true; } } @@ -520,7 +521,8 @@ void run_kuen() { np->inqueue = false; np->dist = 0; np->h = p->h; - np->flat = coord(px.params); + USING_NATIVE_GEOMETRY; + np->native = coord(px.params); np->surface_point = px; np->dexp_id = p->dexp_id; coverages[p->dexp_id] |= pid[part]; @@ -534,9 +536,11 @@ void run_kuen() { for(int i=0; i<3; i++) if(!r[i]) looks_good = false; if(!looks_good) continue; - for(int i=0; i<3; i++) - if(hypot_d(3, r[i]->flat - r[(i+1)%3]->flat) > .2) + for(int i=0; i<3; i++) { + USING_NATIVE_GEOMETRY; + if(hypot_d(3, r[i]->native - r[(i+1)%3]->native) > .2) looks_good = false; + } if(looks_good) addTriangleV(r[0], r[1], r[2]); } @@ -557,9 +561,11 @@ void run_kuen() { template void run_function(T f) { full_mesh(); - for(auto p: rug::points) - p->flat = f(p->h), + for(auto p: rug::points) { + USING_NATIVE_GEOMETRY; + p->native = f(p->h), p->valid = true; + } } void run_other() { @@ -572,7 +578,10 @@ void run_other() { auto h = p->h; p->surface_point = map_to_surface(h, dp); - p->flat = coord(p->surface_point.params); + if(1) { + USING_NATIVE_GEOMETRY; + p->native = coord(p->surface_point.params); + } history::progress(XLAT("solving the geodesics on: %1, %2/%3", XLAT(shape_name[sh]), its(it), its(isize(rug::points)))); if(p->surface_point.remaining_distance == 0) coverage.emplace_back(h, rchar(it) + 256 * 7); @@ -594,10 +603,9 @@ EX void run_shape(eShape s) { coverage.clear(); need_mouseh = true; sh = s; - transmatrix crot = rug::rugged ? rug::currentrot : Id; - rug::apply_rotation(inverse(crot)); if(rug::rugged) rug::close(); + rug::gwhere = rug::rgEuclid; rug::init(); // if(!rug::rugged) rug::reopen(); @@ -641,8 +649,8 @@ EX void run_shape(eShape s) { ld minx = 1e9, maxx = -1e9; for(auto p: rug::points) if(p->valid) { - minx = min(p->flat[2], minx); - maxx = max(p->flat[2], maxx); + minx = min(p->native[2], minx); + maxx = max(p->native[2], maxx); rug::qvalid++; } @@ -650,12 +658,8 @@ EX void run_shape(eShape s) { ld shift = -(minx + maxx) / 2; for(auto p: rug::points) if(p->valid) - p->flat[2] += shift; + p->native[2] += shift; } - - rug::apply_rotation(crot); - if(rug::rug_perspective) - rug::push_all_points(2, -rug::model_distance); } void cancel_shape() { @@ -703,6 +707,13 @@ EX void show_surfaces() { if((rug::rugged && sh && sh != dsHyperboloid && sh != dsHemisphere) || coverage_style) dialog::addSelItem(XLAT("display coverage"), cstyles[coverage_style], 'c'); else dialog::addBreak(100); + + #if CAP_FILES + if(rug::rugged) + dialog::addItem(XLAT("save the current embedding"), 's'); + else + dialog::addItem(XLAT("load a saved embedding"), 's'); + #endif dialog::addHelp(); dialog::addBack(); @@ -722,6 +733,15 @@ EX void show_surfaces() { "For convenience, you can also choose other 3D models from this menu." )); + #if CAP_FILES + else if(uni == 's') { + static string rugname = "saved.rug"; + if(rug::rugged) + dialog::openFileDialog(rugname, XLAT("save embedding to:"), ".rug", [] () { rug::rug_save(rugname); return true; }); + else + dialog::openFileDialog(rugname, XLAT("load embedding from:"), ".rug", [] () { rug::init(); rug::rug_load(rugname); return true; }); + } + #endif else if(uni == '1') run_shape(dsTractricoid); else if(uni == '2') @@ -747,7 +767,7 @@ EX void show_surfaces() { } else if(uni == 'x') for(auto p: rug::points) - p->flat = p->surface_point.params; + p->native = p->surface_point.params; else if(uni == '#') dialog::editNumber(dini_b, -1, 1, .05, .15, XLAT("parameter"), XLAT("The larger the number, the more twisted it is.") diff --git a/sysconfig.h b/sysconfig.h index c5b71623..2275d8d5 100644 --- a/sysconfig.h +++ b/sysconfig.h @@ -227,6 +227,10 @@ #define CAP_SVG (CAP_FILES && !ISMOBILE && !ISMINI) #endif +#ifndef CAP_WRL +#define CAP_WRL (CAP_FILES && !ISMOBILE && !ISMINI && !ISWEB) +#endif + #ifndef CAP_POLY #define CAP_POLY (CAP_SDLGFX || CAP_GL || CAP_SVG) #endif diff --git a/system.cpp b/system.cpp index 10954e42..fa620319 100644 --- a/system.cpp +++ b/system.cpp @@ -225,6 +225,9 @@ EX void initgame() { items[itOrbWinter] = 30; items[itOrbFlash] = 30; } + + if(firstland == laCA) + items[itOrbAether] = 2; if(tactic::on && firstland == laCaribbean) { if(hiitemsMax(itRedGem) >= 25) items[itRedGem] = min(hiitemsMax(itRedGem), 50); @@ -436,19 +439,19 @@ void applyBox(int& t) { } /** \brief the next box should contain tb */ -void applyBoxBignum(bignum& tb) { +void applyBoxBignum(bignum& tb, string name) { float tf; int ti; if(saving) tf = tb.approx_ld(); if(saving) memcpy(&ti, &tf, 4); - applyBox(ti); + applyBoxNum(ti, name); if(loading) memcpy(&tf, &ti, 4); if(loading) tb = bignum(tf); } /** \brief the next box should contain i, and possibly be named name */ EX void applyBoxNum(int& i, string name IS("")) { - fakebox[boxid] = (name == ""); + fakebox[boxid] = (name == "" || name[0] == '@'); boxname[boxid] = name; monsbox[boxid] = false; applyBox(i); @@ -572,8 +575,8 @@ EX void applyBoxes() { applyBoxNum(cheater, "number of cheats"); fakebox[boxid] = true; - if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.at->land, ""); - else if(loading) firstland = safetyland = eLand(applyBoxLoad()); + if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.at->land, "@safetyland"); + else if(loading) firstland = safetyland = eLand(applyBoxLoad("@safetyland")); else lostin = eLand(save.box[boxid++]); for(int i=itOrbLightning; i<25; i++) applyBoxOrb(eItem(i)); @@ -620,9 +623,9 @@ EX void applyBoxes() { applyBoxOrb(itOrbSpace); int geo = geometry; - applyBoxNum(geo, ""); geometry = eGeometry(geo); + applyBoxNum(geo, "@geometry"); geometry = eGeometry(geo); applyBoxBool(hardcore, "hardcore"); - applyBoxNum(hardcoreAt, ""); + applyBoxNum(hardcoreAt, "@hardcoreAt"); applyBoxBool(shmup::on, "shmup"); if(saving) applyBoxSave(specialland, "euclid land"); else if(loading) specialland = eLand(applyBoxLoad("euclid land")); @@ -656,8 +659,8 @@ EX void applyBoxes() { applyBoxM(moPrincessMoved, false); // live Princess for Safety applyBoxM(moPrincessArmedMoved, false); // live Princess for Safety applyBoxM(moMouse); - applyBoxNum(princess::saveArmedHP, ""); - applyBoxNum(princess::saveHP, ""); + applyBoxNum(princess::saveArmedHP, "@saveArmedHP"); + applyBoxNum(princess::saveHP, "@saveHP"); applyBoxI(itIvory); applyBoxI(itElemental); @@ -687,8 +690,8 @@ EX void applyBoxes() { applyBoxOrb(itOrbLuck); applyBoxOrb(itOrbStunning); - applyBoxBool(tactic::on, ""); - applyBoxNum(elec::lightningfast, ""); + applyBoxBool(tactic::on, "@tactic"); + applyBoxNum(elec::lightningfast, "@lightningfast"); // if(save.box[boxid]) printf("lotus = %d (lost = %d)\n", save.box[boxid], isHaunted(lostin)); if(loadingHi && isHaunted(lostin)) boxid++; @@ -700,7 +703,7 @@ EX void applyBoxes() { applyBoxOrb(itMutant2); applyBoxOrb(itOrbFreedom); applyBoxM(moRedFox); - applyBoxBool(survivalist); + applyBoxBool(survivalist, "@survivalist"); if(loadingHi) applyBoxI(itLotus); else applyBoxNum(truelotus, "lotus/escape"); @@ -730,17 +733,17 @@ EX void applyBoxes() { applyBoxM(moDragonHead); applyBoxOrb(itOrbDomination); applyBoxI(itBabyTortoise); - applyBoxNum(tortoise::seekbits, ""); + applyBoxNum(tortoise::seekbits, "@seekbits"); applyBoxM(moTortoise); applyBoxOrb(itOrbShell); - applyBoxNum(safetyseed); + applyBoxNum(safetyseed, "@safetyseed"); // (+18) for(int i=0; i<6; i++) { - applyBoxNum(multi::treasures[i]); - applyBoxNum(multi::kills[i]); - applyBoxNum(multi::deaths[i]); + applyBoxNum(multi::treasures[i], "@multi-treasures" + its(i)); + applyBoxNum(multi::kills[i], "@multi-kills" + its(i)); + applyBoxNum(multi::deaths[i], "@multi-deaths" + its(i)); } // (+8) applyBoxM(moDragonTail); @@ -757,7 +760,7 @@ EX void applyBoxes() { bool sph; sph = false; applyBoxBool(sph, "sphere"); if(sph) geometry = gSphere; sph = false; applyBoxBool(sph, "elliptic"); if(sph) geometry = gElliptic; - applyBox(princess::reviveAt); + applyBoxNum(princess::reviveAt, "@reviveAt"); applyBoxI(itDodeca); applyBoxI(itAmethyst); @@ -780,13 +783,13 @@ EX void applyBoxes() { applyBoxM(moGadfly); // 10.0: - applyBoxNum(hinttoshow); // 258 + applyBoxNum(hinttoshow, "@hinttoshow"); // 258 addinv(itOrbMirror); addinv(itGreenStone); list_invorb(); applyBoxBool(inv::on, "inventory"); // 306 #if CAP_INV - applyBoxNum(inv::rseed); + applyBoxNum(inv::rseed, "@inv-rseed"); #else { int u; applyBoxNum(u); } #endif @@ -828,16 +831,16 @@ EX void applyBoxes() { applyBoxM(moMonk); bool v2 = false; - applyBoxBool(v2); if(loading && v2) variation = eVariation::goldberg; - applyBox(gp::param.first); - applyBox(gp::param.second); + applyBoxBool(v2, "@variation"); if(loading && v2) variation = eVariation::goldberg; + applyBoxNum(gp::param.first, "@gp-first"); + applyBoxNum(gp::param.second, "@gp-second"); v2 = false; applyBoxBool(v2); if(loading && v2) variation = eVariation::irregular; - applyBox(irr::cellcount); + applyBoxNum(irr::cellcount, "@irr-cellcount"); list_invorb(); - applyBox(irr::bitruncations_performed); + applyBoxNum(irr::bitruncations_performed, "@irr-bitruncations"); applyBoxI(itVarTreasure); applyBoxI(itBrownian); @@ -855,8 +858,8 @@ EX void applyBoxes() { applyBoxM(moNarciss); applyBoxM(moMirrorSpirit); - applyBox(clearing::direct); - applyBoxBignum(clearing::imputed); + applyBoxNum(clearing::direct, "@clearing-direct"); + applyBoxBignum(clearing::imputed, "@clearing-imputed"); applyBoxOrb(itOrbImpact); applyBoxOrb(itOrbChaos); diff --git a/textures.cpp b/textures.cpp index ea0a07b4..2fe5e2e1 100644 --- a/textures.cpp +++ b/textures.cpp @@ -65,7 +65,6 @@ struct texture_data { struct texture_config { string texturename; string configname; - color_t paint_color; eTextureState tstate; eTextureState tstate_max; @@ -121,7 +120,6 @@ struct texture_config { texturename = "textures/hyperrogue-texture.png"; configname = "textures/hyperrogue.txc"; itt = Id; - paint_color = 0x000000FF; grid_color = 0; mesh_color = 0; master_color = 0xFFFFFF30; @@ -355,7 +353,7 @@ hyperpoint texture_config::texture_coordinates(hyperpoint h) { hyperpoint inmodel; applymodel(h, inmodel); inmodel[0] *= current_display->radius * 1. / current_display->scrsize; - inmodel[1] *= current_display->radius * vid.stretch / current_display->scrsize; + inmodel[1] *= current_display->radius * pconf.stretch / current_display->scrsize; inmodel[2] = 1; inmodel = itt * inmodel; inmodel[0] = (inmodel[0] + 1) / 2; @@ -531,7 +529,7 @@ void texture_config::mark_triangles() { } } -static const auto current_texture_parameters = tie(geometry, variation, patterns::whichPattern, patterns::subpattern_flags, pmodel, vid.scale, vid.alpha); +static const auto current_texture_parameters = tie(geometry, variation, patterns::whichPattern, patterns::subpattern_flags, pmodel, pconf.scale, pconf.alpha); void texture_config::clear_texture_map() { texture_map.clear(); @@ -624,9 +622,9 @@ void texture_config::saveFullTexture(string tn) { addMessage(XLAT("Saving full texture to %1...", tn)); dynamicval dd(grid_color, 0); dynamicval dm(mesh_color, 0); - dynamicval dx(vid.xposition, 0); - dynamicval dy(vid.yposition, 0); - dynamicval dvs(vid.scale, (pmodel == mdDisk && !euclid) ? 1 : vid.scale); + dynamicval dx(pconf.xposition, 0); + dynamicval dy(pconf.yposition, 0); + dynamicval dvs(pconf.scale, (pmodel == mdDisk && !euclid) ? 1 : pconf.scale); dynamicval dro(rug::rugged, false); dynamicval dnh(nohud, true); texture::saving = true; @@ -741,9 +739,9 @@ struct magic_param { void apply(ld delta) { if(have_mp(mpProjection)) - vid.alpha *= exp(delta * proj); + pconf.alpha *= exp(delta * proj); if(have_mp(mpScale)) - vid.scale *= exp(delta * scale); + pconf.scale *= exp(delta * scale); if(do_spin) { if(have_mp(mpRotate)) @@ -853,7 +851,7 @@ void mousemovement() { // do not zoom in portrait! if(nonzero && !newmove) { View = inverse(spintox(mouseeu)) * spintox(lastmouse) * View; - vid.scale = vid.scale * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse); + pconf.scale = pconf.scale * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse); config.perform_mapping(); } if(nonzero) lastmouse = mouseeu; @@ -863,7 +861,7 @@ void mousemovement() { case tpsProjection: { if(nonzero && !newmove) { - vid.alpha = vid.alpha * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse); + pconf.alpha = pconf.alpha * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse); } if(nonzero) lastmouse = mouseeu; newmove = false; @@ -958,9 +956,9 @@ void init_textureconfig() { addsaverenum(targetgeometry, "geometry", gNormal); addsaverenum(pmodel, "used model", mdDisk); addsaver(vid.yshift, "Y shift", 0); - addsaver(vid.yposition, "Y position", 0); - addsaver(vid.xposition, "X position", 0); - addsaver(vid.camera_angle, "camera angle", 0); + addsaver(pconf.yposition, "Y position", 0); + addsaver(pconf.xposition, "X position", 0); + addsaver(pconf.camera_angle, "camera angle", 0); addsaverenum(targetvariation, "bitruncated", eVariation::bitruncated); // ... geometry parameters @@ -977,9 +975,9 @@ void init_textureconfig() { addsaver(config.color_alpha, "alpha color", 0); addsaver(config.mesh_color, "mesh color", 0); - addsaver(vid.alpha, "projection", 1); - addsaver(vid.scale, "scale", 1); - addsaver(vid.stretch, "stretch", 1); + addsaver(pconf.alpha, "projection", 1); + addsaver(pconf.scale, "scale", 1); + addsaver(pconf.stretch, "stretch", 1); addsaver(vid.binary_width, "binary-tiling-width", 1); addsaver(config.texturename, "texture filename", ""); @@ -1449,8 +1447,6 @@ array ptc(const array& h) { return make_array(ptc(h[0]), ptc(h[1]), ptc(h[2])); } -EX ld penwidth = .02; - int texture_distance(pair p1, pair p2) { return max(abs(p1.first-p2.first), abs(p1.second - p2.second)); } @@ -1521,7 +1517,7 @@ void filltriangle(const array& v, const array& p, color void splitseg(const transmatrix& A, const array& angles, const array& h, const array& p, color_t col, int lev) { ld newangle = (angles[0] + angles[1]) / 2; - hyperpoint nh = A * xspinpush0(newangle, penwidth); + hyperpoint nh = A * xspinpush0(newangle, mapeditor::dtwidth); auto np = ptc(nh); filltriangle(make_array(h[0],h[1],nh), make_array(p[0],p[1],np), col, lev); @@ -1538,7 +1534,7 @@ void fillcircle(hyperpoint h, color_t col) { ld step = M_PI * 2/3; - array mh = make_array(A * xpush0(penwidth), A * xspinpush0(step, penwidth), A * xspinpush0(-step, penwidth)); + array mh = make_array(A * xpush0(mapeditor::dtwidth), A * xspinpush0(step, mapeditor::dtwidth), A * xspinpush0(-step, mapeditor::dtwidth)); auto mp = ptc(mh); filltriangle(mh, mp, col, 0); @@ -1596,7 +1592,7 @@ EX void drawPixel(hyperpoint h, color_t col) { } EX void drawLine(hyperpoint h1, hyperpoint h2, color_t col, int steps IS(10)) { - if(steps > 0 && hdist(h1, h2) > penwidth / 3) { + if(steps > 0 && hdist(h1, h2) > mapeditor::dtwidth / 3) { hyperpoint h3 = mid(h1, h2); drawLine(h1, h3, col, steps-1); drawLine(h3, h2, col, steps-1); diff --git a/tour.cpp b/tour.cpp index cb21b2d7..87fb1859 100644 --- a/tour.cpp +++ b/tour.cpp @@ -175,7 +175,7 @@ EX void slidehelp() { /** \brief return from a subgame launched while in presentation */ void return_geometry() { gamestack::pop(); - vid.scale = 1; vid.alpha = 1; + pconf.scale = 1; pconf.alpha = 1; presentation(pmGeometryReset); addMessage(XLAT("Returned to your game.")); } @@ -262,11 +262,11 @@ bool handleKeyTour(int sym, int uni) { break; case '1': set_geometry(gSphere); - vid.alpha = 1, vid.scale = .5; + pconf.alpha = 1, pconf.scale = .5; break; case '2': set_geometry(gEuclid); - vid.alpha = 1, vid.scale = .5; + pconf.alpha = 1, pconf.scale = .5; break; } start_game(); @@ -288,7 +288,7 @@ bool handleKeyTour(int sym, int uni) { return true; } if(NUMBERKEY == '3' && sphere) { - if(vid.alpha < 2) vid.scale = 400, vid.alpha = 400; else vid.scale = .5, vid.alpha = 1; + if(pconf.alpha < 2) pconf.scale = 400, pconf.alpha = 400; else pconf.scale = .5, pconf.alpha = 1; addMessage(XLAT("Changed the projection.")); return true; } @@ -450,8 +450,8 @@ EX namespace ss { EX void start() { currentslide = 0; - vid.scale = 1; - vid.alpha = 1; + pconf.scale = 1; + pconf.alpha = 1; pmodel = mdDisk; if(!tour::on) presentation(pmStartAll); else { @@ -839,8 +839,8 @@ EX slide default_slides[] = { {models+"Beltrami-Klein model", 43, LEGAL::ANY | USE_SLIDE_NAME, "This model renders straight lines as straight, but it distorts angles.", [] (presmode mode) { - if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) vid.alpha = 0; - if(mode == 3) vid.alpha = 1; + if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) pconf.alpha = 0; + if(mode == 3) pconf.alpha = 1; } }, {models+"Gans model", 44, LEGAL::ANY | USE_SLIDE_NAME, @@ -849,8 +849,8 @@ EX slide default_slides[] = { "model are all obtained by looking at either the hyperboloid model or an " "equidistant surface from various distances.", [] (presmode mode) { - if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) vid.alpha = 400, vid.scale = 150; - if(mode == 3) vid.alpha = vid.scale = 1; + if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) pconf.alpha = 400, pconf.scale = 150; + if(mode == 3) pconf.alpha = pconf.scale = 1; } }, {models+"Band model", 45, LEGAL::NONEUC | USE_SLIDE_NAME, diff --git a/util.cpp b/util.cpp index 3941c4e1..04928814 100644 --- a/util.cpp +++ b/util.cpp @@ -134,7 +134,13 @@ struct hr_parse_exception : hr_exception { struct exp_parser { string s; int at; - exp_parser() { at = 0; } + int line_number, last_line; + exp_parser() { at = 0; line_number = 1; last_line = 0; } + + string where() { + if(s.find('\n')) return "(line " + its(line_number) + ", pos " + its(at-last_line) + ")"; + else return "(pos " + its(at) + ")"; + } map extra_params; @@ -168,14 +174,21 @@ struct exp_parser { void force_eat(const char *c) { skip_white(); - if(!eat(c)) throw hr_parse_exception("expected: " + string(c)); + if(!eat(c)) throw hr_parse_exception("expected: " + string(c) + " at " + where()); } }; #endif void exp_parser::skip_white() { - while(next() == ' ' || next() == '\n' || next() == '\r' || next() == '\t') at++; + while(next() == ' ' || next() == '\n' || next() == '\r' || next() == '\t') { + if(next() == '\r') last_line++; + if(next() == '\n') { + println(hlog, "new line at ", at); + line_number++, last_line = at; + } + at++; + } } string exp_parser::next_token() {