new tiling: Arnold's cat

This commit is contained in:
Zeno Rogue 2019-11-08 15:01:03 +01:00
parent 2cf540cfbc
commit d199411248
13 changed files with 327 additions and 13 deletions

239
asonov.cpp Normal file
View File

@ -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 <cstdio>
//#include <cmath>
namespace hr {
EX namespace asonov {
EX hyperpoint tx, ty, tz;
EX int period_xy = 8;
EX int period_z = 8;
struct coord: public array<int,3> {
coord() {}
coord(int x, int y, int z) : array<int,3>(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<coord, heptagon*> at;
unordered_map<heptagon*, coord> 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<heptagon> (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; a<S7; a++) if(h2 == h1->move(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; i<S7; i++)
dq::enqueue_by_matrix(h->cmove(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();
}
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -594,6 +594,7 @@ vector<geometryinfo> 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

View File

@ -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 };

View File

@ -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(),

View File

@ -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];

View File

@ -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"

View File

@ -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;
});

View File

@ -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;

View File

@ -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)

View File

@ -170,7 +170,7 @@ shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
switch(cgclass) {
#if CAP_SOLV
case gcSolNIH:
switch(geometry) {
switch(solnihv::geom()) {
case gSol:
vsh += solnihv::shader_symsol;
break;