From d199411248b56f386320e5d16fb9d97e539c1c34 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Fri, 8 Nov 2019 15:01:03 +0100 Subject: [PATCH] new tiling: Arnold's cat --- asonov.cpp | 239 ++++++++++++++++++++++++++++++++++++++++++++++ binary-tiling.cpp | 2 +- cell.cpp | 1 + celldrawer.cpp | 4 +- classes.cpp | 1 + classes.h | 1 + geom-exp.cpp | 5 +- graph.cpp | 2 + hyper.cpp | 1 + nonisotropic.cpp | 21 +++- polygons.cpp | 17 ++++ raycaster.cpp | 44 +++++++-- shaders.cpp | 2 +- 13 files changed, 327 insertions(+), 13 deletions(-) create mode 100644 asonov.cpp diff --git a/asonov.cpp b/asonov.cpp new file mode 100644 index 00000000..bfb56521 --- /dev/null +++ b/asonov.cpp @@ -0,0 +1,239 @@ +// Hyperbolic Rogue -- Arnold's cat map +// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details + +/** \file asonov.cpp + * \brief Arnold's cat map + */ + + +#include "hyper.h" + +//#include +//#include + +namespace hr { + +EX namespace asonov { + +EX hyperpoint tx, ty, tz; + +EX int period_xy = 8; +EX int period_z = 8; + +struct coord: public array { + coord() {} + coord(int x, int y, int z) : array(make_array(zgmod(x, period_xy), zgmod(y, period_xy), zgmod(z, period_z))) {} + coord shift(int x, int y, int z=0) { return coord(self[0]+x, self[1]+y, self[2]+z); } + coord up() { return coord(self[0]*2-self[1], self[1]-self[0], self[2]+1); } + coord down() { return coord(self[0]+self[1], self[0]+self[1]*2, self[2]-1); } + + coord addmove(int d) { + switch(d) { + case 0: return up().shift(0, 0); + case 1: return up().shift(1, -1); + case 2: return up().shift(-1, 0); + case 3: return up().shift(0, -1); + case 4: return shift(1, 0); + case 5: return shift(0, 1); + case 6: return down().shift(0, 0); + case 7: return down().shift(0, 1); + case 8: return down().shift(1, 1); + case 9: return down().shift(1, 2); + case 10: return shift(-1, 0); + case 11: return shift(0, -1); + default: throw "error"; + } + } + }; + +EX void prepare() { + using namespace hr; + transmatrix A = Id; + A[0][0] = 1; + A[0][1] = 1; + A[1][0] = 1; + A[1][1] = 2; + static bool first = true; + if(first) println(hlog, "amain"); + + // if(true) { + double det = hr::det(A); + if(det != 1) { printf("wrong det\n"); return; } + + // (a00-x)(a11-x) - a01*a10 = 0 + + // x^2 - (a00+a11) x + 1 = 0 + + double b = (A[0][0] + A[1][1]) / 2; + + // x^2 - 2b x + b^2 = b^2-1 + + // if(b*b <= 1) { printf("imaginary eigenvalues\n"); return 0; } + + // x = b + sqrt(b^2-1) + + hyperpoint lambda; + lambda[0] = b + sqrt(b*b-1); + lambda[1] = b - sqrt(b*b-1); + + if(first) println(hlog, "lambda = ", lambda); + + transmatrix eigen = Id; + + for(int i: {0,1}) { + eigen[0][i] = 1; + eigen[1][i] = (lambda[i] - A[0][0]) / A[0][1]; + } + + if(first) println(hlog, "eigen = ", eigen); + if(first) println(hlog, "A*eigen = ", A*eigen); + + transmatrix ieigen = inverse(eigen); + if(first) println(hlog, "ieigen = ", ieigen); + if(first) println(hlog, "ieigen*A = ", ieigen * A); + + tx = point3(ieigen[0][0], ieigen[1][0], 0); + ty = point3(ieigen[0][1], ieigen[1][1], 0); + tz = -point3(0, 0, log(lambda[0])); + + println(hlog, "tx = ", tx); + println(hlog, "ty = ", ty); + println(hlog, "tz = ", tz); + + for(int a=0; a<12; a++) { + int b = (a+6) % 12; + coord test(1, 10, 100); + auto test1 = test.addmove(a).addmove(b); + println(hlog, test == test1 ? "OK" : "BAD", " : ", test, " vs ", test1, " ## ", test.addmove(a)); + } + } + +struct hrmap_asonov : hrmap { + unordered_map at; + unordered_map coords; + + heptagon *getOrigin() override { return get_at(coord(0,0,0)); } + + hrmap_asonov() { prepare(); } + + ~hrmap_asonov() { + for(auto& p: at) clear_heptagon(p.second); + } + + heptagon *get_at(coord c) { + auto& h = at[c]; + if(h) return h; + h = tailored_alloc (S7); + h->c7 = newCell(S7, h); + coords[h] = c; + h->dm4 = 0; + h->distance = c[2]; + h->zebraval = c[0]; + h->emeraldval = c[1]; + h->cdata = NULL; + h->alt = NULL; + return h; + } + + heptagon *create_step(heptagon *parent, int d) override { + auto p = coords[parent]; + auto q = p.addmove(d); + auto child = get_at(q); + parent->c.connect(d, child, (d + 6) % 12, false); + return child; + } + + transmatrix adjmatrix(int i) { + coord c = coord(0,0,0).addmove(i); + if(c[0] > period_xy/2) c[0] -= period_xy; + if(c[1] > period_xy/2) c[1] -= period_xy; + if(c[2] > period_z/2) c[2] -= period_z; + transmatrix T = eupush(tz * c[2]) * eupush(tx * c[0] + ty * c[1]);; + if(i < 4) return T * eupush(ty/2); + if(i >= 6 && i < 10) return eupush(-ty/2) * T; + return T; + } + + virtual transmatrix relative_matrix(heptagon *h2, heptagon *h1) override { + for(int a=0; amove(a)) return adjmatrix(a); + return Id; + } + + void draw() override { + dq::visited_by_matrix.clear(); + + dq::enqueue_by_matrix(viewctr.at, cview()); + + while(!dq::drawqueue.empty()) { + auto& p = dq::drawqueue.front(); + heptagon *h = get<0>(p); + transmatrix V = get<1>(p); + dq::drawqueue.pop(); + + cell *c = h->c7; + if(!do_draw(c, V)) continue; + drawcell(c, V); + if(wallopt && isWall3(c) && isize(dq::drawqueue) > 1000) continue; + + for(int i=0; icmove(i), V * adjmatrix(i)); + } + } + }; + +EX hrmap *new_map() { return new hrmap_asonov; } + +EX int period_xy_edit, period_z_edit; + +EX void set_flags() { + auto& flag = ginf[gArnoldCat].flags; + set_flag(flag, qANYQ, period_xy || period_z); + set_flag(flag, qBOUNDED, period_xy && period_z); + set_flag(flag, qSMALL, period_xy && period_z && (period_xy * period_xy * period_z <= 4096)); + } + +EX void prepare_config() { + period_xy_edit = period_xy; + period_z_edit = period_z; + } + +EX void show_config() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(1); + dialog::init(XLAT("Solv quotient spaces")); + + dialog::addSelItem(XLAT("%1 period", "X/Y"), its(period_xy_edit), 'x'); + dialog::add_action([=] { + dialog::editNumber(period_xy_edit, 0, 64, 1, 0, XLAT("%1 period", "X/Y"), + XLAT("Note: the value 0 functions effectively as the size of int (2^32).") + ); + dialog::bound_low(0); + }); + + dialog::addSelItem(XLAT("%1 period", "Z"), its(period_z_edit), 'z'); + dialog::add_action([=] { + dialog::editNumber(period_z_edit, 0, 64, 1, 0, XLAT("%1 period", "Z"), + XLAT("Set to 0 to make it non-periodic.") + ); + dialog::bound_low(0); + }); + + dialog::addBreak(50); + + dialog::addItem(XLAT("activate"), 'a'); + dialog::add_action([] { + stop_game(); + period_xy = period_xy_edit; + period_z = period_z_edit; + set_flags(); + geometry = gArnoldCat; + start_game(); + }); + + dialog::addBreak(50); + dialog::addBack(); + dialog::display(); + } + +} +} diff --git a/binary-tiling.cpp b/binary-tiling.cpp index f0a55007..b5e464de 100644 --- a/binary-tiling.cpp +++ b/binary-tiling.cpp @@ -574,7 +574,7 @@ EX namespace binary { } EX void build_tmatrix() { - if(among(geometry, gBinaryTiling, gSol)) return; // unused + if(among(geometry, gBinaryTiling, gSol, gArnoldCat)) return; // unused use_direct = (1 << (S7-1)) - 1; if(geometry == gBinary4) { use_direct = 3; diff --git a/cell.cpp b/cell.cpp index d88272b6..427bfe95 100644 --- a/cell.cpp +++ b/cell.cpp @@ -234,6 +234,7 @@ EX void initcells() { hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap); if(res) currentmap = res; + else if(geometry == gArnoldCat) currentmap = asonov::new_map(); else if(nonisotropic || hybri) currentmap = nisot::new_map(); #if CAP_CRYSTAL else if(cryst) currentmap = crystal::new_map(); diff --git a/celldrawer.cpp b/celldrawer.cpp index aa0e872c..5a1a6f6c 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -1529,7 +1529,7 @@ void celldrawer::draw_features_and_walls_3d() { else if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(viewcenter())) continue; else if(c->move(a)->master->distance > c->master->distance && c->master->distance > viewctr.at->distance && !quotient) continue; } - else if(sol && in_perspective() && !nih) { + else if(sol && in_perspective() && !nih && geometry != gArnoldCat) { ld b = vid.binary_width * log(2) / 2; const ld l = log(2) / 2; switch(a) { @@ -1681,7 +1681,7 @@ void celldrawer::bookkeeping() { orig = gm[LDIM][LDIM] == 0 ? true : euwrap ? hdist0(tC0(gm)) >= hdist0(tC0(V)) : - nil ? sqhypot_d(3, tC0(gm)) >= sqhypot_d(3, tC0(V)) : + (nil||sol) ? sqhypot_d(3, tC0(gm)) >= sqhypot_d(3, tC0(V)) : sphereflipped() ? fabs(gm[LDIM][LDIM]-1) <= fabs(V[LDIM][LDIM]-1) : fabs(gm[LDIM][LDIM]-1) >= fabs(V[LDIM][LDIM]-1) - 1e-8; diff --git a/classes.cpp b/classes.cpp index 5867ef0c..f2615bed 100644 --- a/classes.cpp +++ b/classes.cpp @@ -594,6 +594,7 @@ vector ginf = { {"{3,3,6}","none", "{3,3,6} hyperbolic honeycomb", "336", 4, 6, qIDEAL, giHyperb3, 0x49600, {{7, 2}}, eVariation::pure}, {"{3,4,4}","none", "{3,4,4} hyperbolic honeycomb", "344", 8, 4, qIDEAL, giHyperb3, 0x50000, {{7, 2}}, eVariation::pure}, {"{3,4,4}","Crystal", "4D crystal in H3", "Cryst3" , 8, 4, qIDEAL | qANYQ | qCRYSTAL, giHyperb3, 0x52000, {{7, 3}}, eVariation::pure}, + {"cat", "cat", "Arnold's cat map", "cat", 12, 3, qBINARY | qSOL | qsBQ, giSolNIH, 0x52200, {{6, 4}}, eVariation::pure}, }; // bits: 9, 10, 15, 16, (reserved for later) 17, 18 diff --git a/classes.h b/classes.h index 76d02b86..42a5d1fe 100644 --- a/classes.h +++ b/classes.h @@ -216,6 +216,7 @@ enum eGeometry { gBinary4, gSol, gKiteDart2, gKiteDart3, gNil, gProduct, gRotSpace, gTernary, gNIH, gSolN, gInfOrder, gSpace336, gSpace344, gCrystal344, + gArnoldCat, gGUARD}; enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSolNIH, gcNil, gcProduct, gcSL2 }; diff --git a/geom-exp.cpp b/geom-exp.cpp index de04660b..2457cf46 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -779,7 +779,7 @@ EX void showEuclideanMenu() { }); } - else if(euwrap || geometry == gFieldQuotient || cryst || archimedean || (euclid && WDIM == 3) || nil) { + else if(euwrap || geometry == gFieldQuotient || cryst || archimedean || (euclid && WDIM == 3) || nil || geometry == gArnoldCat) { dialog::addItem(XLAT("advanced parameters"), '4'); dialog::add_action([] { if(0); @@ -798,6 +798,9 @@ EX void showEuclideanMenu() { else if(nil) nilv::prepare_niltorus3(), pushScreen(nilv::show_niltorus3); + else if(geometry == gArnoldCat) + asonov::prepare_config(), + pushScreen(asonov::show_config); #endif else if(euwrap) prepare_torusconfig(), diff --git a/graph.cpp b/graph.cpp index 165eb781..9f039480 100644 --- a/graph.cpp +++ b/graph.cpp @@ -3562,6 +3562,7 @@ EX int get_darkval(cell *c, int d) { const int darkval_hh[14] = {0,0,0,1,1,1,2,2,2,3,3,3,1,0}; const int darkval_hrec[7] = {0,0,2,4,2,4,0}; const int darkval_sol[8] = {0,2,4,4,0,2,4,4}; + const int darkval_asonov[12] = {0,2,0,2,4,5,0,2,0,2,4,5}; const int darkval_penrose[12] = {0, 2, 0, 2, 4, 4, 6, 6, 6, 6, 6, 6}; const int darkval_nil[8] = {6,6,0,3,6,6,0,3}; const int darkval_nih[11] = {0,2,0,2,4,6,6,6,6,6,6}; @@ -3572,6 +3573,7 @@ EX int get_darkval(cell *c, int d) { if(geometry == gHoroHex) return darkval_hh[d]; if(geometry == gHoroRec) return darkval_hrec[d]; if(penrose) return darkval_penrose[d]; + if(geometry == gArnoldCat) return darkval_asonov[d]; if(sol) return darkval_sol[d]; if(nih) return darkval_nih[d]; if(binarytiling) return darkval_hbt[d]; diff --git a/hyper.cpp b/hyper.cpp index f6bc65d1..6b9190fa 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -34,6 +34,7 @@ #include "heptagon.cpp" #include "binary-tiling.cpp" #include "nonisotropic.cpp" +#include "asonov.cpp" #include "penrose.cpp" #include "archimedean.cpp" #include "euclid.cpp" diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 5d0ab54b..d5da9643 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -47,6 +47,15 @@ EX namespace nisot { #if CAP_SOLV EX namespace solnihv { + EX eGeometry geom() { + switch(geometry) { + case gSol: case gArnoldCat: + return gSol; + default: + return geometry; + } + } + #if HDR struct tabled_inverses { int PRECX, PRECY, PRECZ; @@ -432,7 +441,7 @@ EX namespace solnihv { hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) { const ld l2 = log(2); const ld l3 = log(3); - switch(geometry) { + switch(geom()) { case gSolN: return hpxyz3( -(velocity[2] * transported[0] + velocity[0] * transported[2]) * l2, @@ -609,7 +618,7 @@ EX namespace solnihv { EX tabled_inverses sont = solnihv::tabled_inverses("ssol-geodesics.dat"); EX tabled_inverses& get_tabled() { - switch(geometry) { + switch(geom()) { case gSol: return solt; case gNIH: return niht; case gSolN: return sont; @@ -1861,6 +1870,14 @@ EX namespace nisot { nilv::set_flags(); return 0; } + else if(argis("-catperiod")) { + PHASEFROM(2); + if(sol) stop_game(); + shift(); asonov::period_xy = argi(); + shift(); asonov::period_z = argi(); + asonov::set_flags(); + return 0; + } return 1; }); diff --git a/polygons.cpp b/polygons.cpp index d969e332..9548a392 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -1027,6 +1027,23 @@ void geometry_information::create_wall3d() { make_wall(7, {pt(-1,00,+1), pt(+1,00,+1), pt(+1,-1,+1), pt(-1,-1,+1)}); } + if(geometry == gArnoldCat) { + asonov::prepare(); + auto pt = [&] (int x, int y, int z) { return asonov::tx*x/2 + asonov::ty*y/2 + asonov::tz*z/2 + C0; }; + make_wall(0, {pt(-1,-1,+1), pt(00,+1,+1), pt(+1,+1,+1)}); + make_wall(1, {pt(00,-1,+1), pt(+1,+1,+1), pt(+1,-1,+1)}); + make_wall(2, {pt(-1,+1,+1), pt(00,+1,+1), pt(-1,-1,+1)}); + make_wall(3, {pt(-1,-1,+1), pt(+1,+1,+1), pt(00,-1,+1)}); + make_wall(4, {pt(+1,-1,-1), pt(+1,00,-1), pt(+1,+1,-1), pt(+1,+1,+1), pt(+1,-1,+1)}); + make_wall(5, {pt(-1,+1,-1), pt(-1,+1,+1), pt(00,+1,+1), pt(+1,+1,+1), pt(+1,+1,-1)}); + make_wall(6, {pt(-1,-1,-1), pt(-1,00,-1), pt(+1,-1,-1)}); + make_wall(7, {pt(-1,00,-1), pt(-1,+1,-1), pt(+1,-1,-1)}); + make_wall(8, {pt(-1,+1,-1), pt(+1,00,-1), pt(+1,-1,-1)}); + make_wall(9, {pt(-1,+1,-1), pt(+1,+1,-1), pt(+1,00,-1)}); + make_wall(10, {pt(-1,+1,-1), pt(-1,00,-1), pt(-1,-1,-1), pt(-1,-1,+1), pt(-1,+1,+1)}); + make_wall(11, {pt(+1,-1,-1), pt(+1,-1,+1), pt(00,-1,+1), pt(-1,-1,+1), pt(-1,-1,-1)}); + } + if(geometry == gNIH) { ld zstep = .5; ld bwh = vid.binary_width / 6; diff --git a/raycaster.cpp b/raycaster.cpp index f20a1f99..7b630e13 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -86,7 +86,7 @@ struct raycaster : glhr::GLprogram { GLint uStart, uStartid, uM, uLength, uFovX, uFovY, uIPD; GLint uWallstart, uWallX, uWallY; GLint tConnections, tWallcolor, tTextureMap; - GLint uBinaryWidth, uPLevel, uLP; + GLint uBinaryWidth, uPLevel, uLP, uStraighten; GLint uLinearSightRange, uExpStart, uExpDecay; GLint uBLevel; @@ -105,6 +105,7 @@ struct raycaster : glhr::GLprogram { uWallY = glGetUniformLocation(_program, "uWallY"); uBinaryWidth = glGetUniformLocation(_program, "uBinaryWidth"); + uStraighten = glGetUniformLocation(_program, "uStraighten"); uPLevel = glGetUniformLocation(_program, "uPLevel"); uLP = glGetUniformLocation(_program, "uLP"); @@ -131,7 +132,8 @@ void enable_raycaster() { last_geometry = geometry; deg = S7; if(prod) deg += 2; if(!our_raycaster) { - bool use_reflect = reflect_val && !nil && !levellines; + bool asonov = geometry == gArnoldCat; + bool use_reflect = reflect_val && !nil && !levellines && !asonov; string vsh = "attribute vec4 aPosition;\n" @@ -408,7 +410,7 @@ void enable_raycaster() { // " return vec4(0.,0.,0.,0.);\n" " }\n"; - if(solnih) fsh += "uniform float uBinaryWidth;\n"; + if(solnih && !asonov) fsh += "uniform float uBinaryWidth;\n"; fmain += " dist = next < minstep ? 2.*next : next;\n"; @@ -473,11 +475,18 @@ void enable_raycaster() { if(nil) fmain += "float rz = (abs(nposition.x) > abs(nposition.y) ? -nposition.x*nposition.y : 0.) + nposition.z;\n"; + + if(asonov) { + fsh += "uniform mat4 uStraighten;\n"; + fmain += "vec4 sp = uStraighten * nposition;\n"; + } fmain += "if(next >= minstep) {\n"; - if(nih) fmain += + if(asonov) fmain += + "if(abs(sp.x) > 1. || abs(sp.y) > 1. || abs(sp.z) > 1.) {\n"; + else if(nih) fmain += "if(abs(nposition.x) > uBinaryWidth || abs(nposition.y) > uBinaryWidth || abs(nposition.z) > .5) {\n"; else if(sol) fmain += "if(abs(nposition.x) > uBinaryWidth || abs(nposition.y) > uBinaryWidth || abs(nposition.z) > log(2.)/2.) {\n"; @@ -492,7 +501,26 @@ void enable_raycaster() { "else {\n"; if(solnih) { - if(sol && !nih) fmain += + if(asonov) fmain += + "if(sp.x > 1.) which = 4;\n" + "if(sp.y > 1.) which = 5;\n" + "if(sp.x <-1.) which = 10;\n" + "if(sp.y <-1.) which = 11;\n" + "if(sp.z > 1.) {\n" + "float best = 999.;\n" + "for(int i=0; i<4; i++) {\n" + "float cand = len(uStraighten * uM[i] * position);\n" + "if(cand < best) { best = cand; which = i;}\n" + "}\n" + "}\n" + "if(sp.z < -1.) {\n" + "float best = 999.;\n" + "for(int i=6; i<10; i++) {\n" + "float cand = len(uStraighten * uM[i] * position);\n" + "if(cand < best) { best = cand; which = i;}\n" + "}\n" + "}\n"; + else if(sol && !nih) fmain += "if(nposition.x > uBinaryWidth) which = 0;\n" "if(nposition.x <-uBinaryWidth) which = 4;\n" "if(nposition.y > uBinaryWidth) which = 1;\n" @@ -508,7 +536,7 @@ void enable_raycaster() { if(nih && !sol) fmain += "if(nposition.z > .5) which = 4;\n" "if(nposition.z < -.5) which = (nposition.y > uBinaryWidth/3. ? 9 : nposition.y < -uBinaryWidth/3. ? 5 : 7) + (nposition.x>0.?1:0);\n"; - if(sol && !nih) fmain += + if(sol && !nih && !asonov) fmain += "if(nposition.z > log(2.)/2.) which = nposition.x > 0. ? 3 : 2;\n" "if(nposition.z <-log(2.)/2.) which = nposition.y > 0. ? 7 : 6;\n"; } @@ -901,6 +929,10 @@ EX void cast() { glUniform1f(o->uLevelLines, levellines); if(o->uBinaryWidth != -1) glUniform1f(o->uBinaryWidth, vid.binary_width/2 * (nih?1:log(2))); + if(o->uStraighten != -1) { + transmatrix T = build_matrix(asonov::tx/2, asonov::ty/2, asonov::tz/2, C0); + glUniformMatrix4fv(o->uStraighten, 1, 0, glhr::tmtogl_transpose(inverse(T)).as_array()); + } if(o->uPLevel != -1) glUniform1f(o->uPLevel, cgi.plevel / 2); if(o->uBLevel != -1) diff --git a/shaders.cpp b/shaders.cpp index e3198b48..a7cfda4a 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -170,7 +170,7 @@ shared_ptr write_shader(flagtype shader_flags) { switch(cgclass) { #if CAP_SOLV case gcSolNIH: - switch(geometry) { + switch(solnihv::geom()) { case gSol: vsh += solnihv::shader_symsol; break;