diff --git a/cell.cpp b/cell.cpp index 4c2bb099..71cb8ed6 100644 --- a/cell.cpp +++ b/cell.cpp @@ -202,6 +202,9 @@ EX cell *createMov(cell *c, int d) { else if(kite::in()) kite::find_cell_connection(c, d); #endif + else if(fake::in()) { + return FPIU(createMov(c, d)); + } #if CAP_IRR else if(IRREGULAR) { irr::link_cell(c, d); @@ -287,6 +290,7 @@ EX void initcells() { hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap); if(res) currentmap = res; + else if(fake::in()) currentmap = fake::new_map(); else if(asonov::in()) currentmap = asonov::new_map(); else if(nonisotropic || hybri) currentmap = nisot::new_map(); #if CAP_CRYSTAL diff --git a/celldrawer.cpp b/celldrawer.cpp index b87b5bd4..441484fa 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -1816,7 +1816,7 @@ void celldrawer::bookkeeping() { else { playerV = V * ddspin(c, cwt.spin, 0); if(cwt.mirrored) playerV = playerV * Mirror; - if((!confusingGeometry() && !inmirrorcount) || eqmatrix(V, current_display->which_copy, 1e-2)) + if((!confusingGeometry() && !fake::in() && !inmirrorcount) || eqmatrix(V, current_display->which_copy, 1e-2)) current_display->which_copy = V; if(orig) cwtV = playerV; } diff --git a/classes.cpp b/classes.cpp index 09701155..6ba0f7dc 100644 --- a/classes.cpp +++ b/classes.cpp @@ -730,7 +730,7 @@ enum eGeometry { gTernary, gNIH, gSolN, gInfOrder, gSpace336, gSpace344, gCrystal344, gArnoldCat, gArbitrary, gInfOrder4, gCrystal534, gSpace535, gSpace536, gSeifertCover, gSeifertWeber, gHomologySphere, - gInfOrderMixed, gSpace436, + gInfOrderMixed, gSpace436, gFake, gGUARD}; enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSolNIH, gcNil, gcProduct, gcSL2 }; @@ -913,6 +913,7 @@ EX vector ginf = { {"{5,3,3}","SW", "Poincaré homology sphere", "533s", 12, 3, qsSINGLE, giSphere3, 0x31400, {{7, 2}}, eVariation::pure}, {"{?,oo}", "none", "{3/4,∞} (infinite triangles and squares)", "ooxm", 3, OINF, qIDEAL | qINFMIXED, giHyperb2, 0x49400, {{6, 6}}, eVariation::pure}, {"{4,3,6}","none", "{4,3,6} hyperbolic honeycomb", "436", 6, 6, qIDEAL, giHyperb3, 0x31400, {{7, 2}}, eVariation::pure}, + {"?", "none", "fake", "", 0, 0, qRAYONLY, giHyperb3, 0x31400, {{7, 2}}, eVariation::pure} }; // bits: 9, 10, 15, 16, (reserved for later) 17, 18 diff --git a/crystal.cpp b/crystal.cpp index 9d9b59d0..5d5b6f37 100644 --- a/crystal.cpp +++ b/crystal.cpp @@ -745,7 +745,7 @@ EX color_t colorize(cell *c, char whichCanvas) { for(int a=0; a<3; a++) co[a] = i%5, i /= 5; } #endif - else if(euclid) { + else if(euc::in()) { auto tab = euc::get_ispacemap()[c->master]; for(int a=0; a<3; a++) co[a] = tab[a]; if(PURE) for(int a=0; a<3; a++) co[a] *= 2; diff --git a/euclid.cpp b/euclid.cpp index de0e380f..ed6053a3 100644 --- a/euclid.cpp +++ b/euclid.cpp @@ -306,10 +306,13 @@ EX namespace euc { }; hrmap_euclidean* cubemap() { + if(fake::in()) return FPIU(cubemap()); return ((hrmap_euclidean*) currentmap); } - hrmap_euclidean* eucmap() { return cubemap(); } + hrmap_euclidean* eucmap() { + return cubemap(); + } EX vector& get_current_shifttable() { return cubemap()->shifttable; } EX map& get_spacemap() { return cubemap()->spacemap; } @@ -1171,6 +1174,11 @@ EX int cyldist(gp::loc a, gp::loc b) { EX void generate() { + if(fake::in()) { + fake::generate(); + return; + } + auto v = euc::get_shifttable(); auto& cs = cgi.cellshape; @@ -1238,6 +1246,7 @@ EX void generate() { * (For example, Archimedean, kite, or fake with underlying non-Euclidean geometry returns false) */ EX bool in() { + if(fake::in()) return FPIU(in()); return euclid && standard_tiling(); } diff --git a/fake.cpp b/fake.cpp new file mode 100644 index 00000000..834ab002 --- /dev/null +++ b/fake.cpp @@ -0,0 +1,385 @@ +#include "hyper.h" + +// Fake non-Euclidean + +namespace hr { + +EX namespace fake { + + EX ld scale; + + EX eGeometry underlying; + EX geometry_information *underlying_cgip; + EX hrmap *pmap; + EX geometry_information *pcgip; + EX eGeometry actual_geometry; + + EX bool in() { return geometry == gFake; } + + // a dummy map that does nothing + struct hrmap_fake : hrmap { + hrmap *underlying_map; + + template auto in_underlying(const T& t) -> decltype(t()) { + pcgip = cgip; + dynamicval gpm(pmap, this); + dynamicval gag(actual_geometry, geometry); + dynamicval g(geometry, underlying); + dynamicval gc(cgip, underlying_cgip); + dynamicval gu(currentmap, underlying_map); + return t(); + } + + heptagon *getOrigin() override { return in_underlying([this] { return underlying_map->getOrigin(); }); } + + cell* gamestart() override { return in_underlying([this] { return underlying_map->gamestart(); }); } + + hrmap_fake() { + in_underlying([this] { initcells(); underlying_map = currentmap; }); + for(hrmap*& m: allmaps) if(m == underlying_map) m = NULL; + } + + heptagon *create_step(heptagon *parent, int d) override { + parent->c.connect(d, parent, d, false); + return parent; + } + + transmatrix adj(cell *c, int d) override { + transmatrix S1, S2; + ld dist; + in_underlying([c, d, &S1, &S2, &dist] { + transmatrix T = currentmap->adj(c, d); + S1 = rspintox(tC0(T)); + transmatrix T1 = spintox(tC0(T)) * T; + dist = hdist0(tC0(T1)); + S2 = xpush(-dist) * T1; + }); + + if(WDIM == 2) { + + hyperpoint a1, a2, b1, b2; + + in_underlying([c, d, &a1, &a2, &b1, &b2] { + a1 = get_corner_position(c, d); + a2 = get_corner_position(c, (d+1) % c->type); + + auto c1 = c->move(d); + auto d1 = c->c.spin(d); + b1 = get_corner_position(c1, d1); + b2 = get_corner_position(c1, (d1+1) % c1->type); + }); + + cgi.adjcheck = hdist0(mid(befake(a1), befake(a2))) + hdist0(mid(befake(b1), befake(b2))); + } + + return S1 * xpush(cgi.adjcheck) * S2; + } + + void draw_recursive(cell *c, const transmatrix& V, ld a0, ld a1, cell *parent, int depth) { + band_shift = 0; + if(!do_draw(c, V)) return; + drawcell(c, V); + + if(depth >= 15) return; + + // queuestr(V, .2, fts(a0)+":"+fts(a1), 0xFFFFFFFF, 1); + + ld d = hdist0(tC0(V)); + + if(false) { + curvepoint(spin(-a0) * xpush0(d)); + curvepoint(spin(-a0) * xpush0(d+.2)); + curvepoint(spin(-a1) * xpush0(d+.2)); + curvepoint(spin(-a1) * xpush0(d)); + curvepoint(spin(-a0) * xpush0(d)); + queuecurve(0xFF0000FF, 0, PPR::LINE); + } + + + indenter id(2); + for(int i=0; itype; i++) if(c->move(i) && c->move(i) != parent) { + auto h0 = V * befake(FPIU(get_corner_position(c, i))); + auto h1 = V * befake(FPIU(get_corner_position(c, (i+1) % c->type))); + ld b0 = atan2(h0); + ld b1 = atan2(h1); + while(b1 < b0) b1 += 2 * M_PI; + if(a0 == -1) { + draw_recursive(c->move(i), V * adj(c, i), b0, b1, c, depth+1); + } + else { + if(b1 - b0 > M_PI) continue; + + if(b0 < a0 - M_PI) b0 += 2 * M_PI; + if(b0 > a0 + M_PI) b0 -= 2 * M_PI; + if(b0 < a0) b0 = a0; + + if(b1 > a1 + M_PI) b1 -= 2 * M_PI; + if(b1 < a1 - M_PI) b1 += 2 * M_PI; + if(b1 > a1) b1 = a1; + + if(b0 > b1) continue; + + draw_recursive(c->move(i), V * adj(c, i), b0, b1, c, depth+1); + } + } + } + + transmatrix relative_matrix(cell *h2, cell *h1, const hyperpoint& hint) override { + if(h1 == h2) return Id; + + for(int a=0; atype; a++) if(h1->move(a) == h2) + return adj(h1, a); + + return Id; + } + + transmatrix relative_matrix(heptagon *h2, heptagon *h1, const hyperpoint& hint) override { + return relative_matrix(h2->c7, h1->c7, hint); + } + + void draw() override { + sphereflip = Id; + + // for(int i=0; i(p); + transmatrix V = get<1>(p); + dynamicval b(band_shift, get<2>(p)); + bandfixer bf(V); + dq::drawqueue_c.pop(); + + if(!do_draw(c, V)) continue; + drawcell(c, V); + if(in_wallopt() && isWall3(c) && isize(dq::drawqueue_c) > 1000) continue; + + for(int i=0; imove(i)) { + enqueue(c->move(i), V * adj(c, i)); + } + } + } + }; + + EX hrmap* new_map() { return new hrmap_fake; }; + + EX hrmap* get_umap() { if(!dynamic_cast(currentmap)) return nullptr; else return ((hrmap_fake*)currentmap)->underlying_map; } + + #if HDR + template auto in_underlying_geometry(const T& f) -> decltype(f()) { + if(!fake::in()) return f(); + dynamicval g(geometry, underlying); + dynamicval gag(actual_geometry, geometry); + dynamicval gc(cgip, underlying_cgip); + dynamicval gpm(pmap, currentmap); + dynamicval gm(currentmap, get_umap()); + return f(); + } + + #define FPIU(x) hr::fake::in_underlying_geometry([&] { return (x); }) + #endif + +EX hyperpoint befake(hyperpoint h) { + auto h1 = h / h[WDIM] * scale; + h1[WDIM] = 1; + if(material(h1) > 1e-3) + h1 = normalize(h1); + return h1; + } + +EX vector befake(const vector& v) { + vector res; + for(auto& h: v) res.push_back(befake(h)); + return res; + } + +EX ld compute_around(bool setup) { + auto &ucgi = *underlying_cgip; + + auto fcs = befake(ucgi.cellshape); + + if(setup) { + cgi.cellshape = fcs; + cgi.vertices_only = befake(ucgi.vertices_only); + } + + hyperpoint h = Hypc; + for(int i=0; i 0) + h = normalize(h); + + if(setup) + cgi.adjcheck = 2 * hdist0(h); + + hyperpoint u = Hypc; + u += fcs[0]; + u += fcs[1]; + + if(material(u) <= 0) + return HUGE_VAL; + + u = normalize(u); + hyperpoint h2 = rspintox(h) * xpush0(2 * hdist0(h)); + + h2 = spintox(u) * h2; + u = spintox(u) * u; + + h2 = gpushxto0(u) * h2; + u = gpushxto0(u) * u; + + println(hlog, "h = ", hdist0(h), " ucgi = ", format("%p", &ucgi), " @ ", hyperbolic, " / ", sphere, " h2 = ", h2); + + ld x = hypot(h2[1], h2[2]); + ld y = h2[0]; + return 360 / (90 + atan(y/x) / degree); + } + +EX void generate() { + println(hlog, "Generating fake"); + FPIU( cgi.require_basics() ); + auto &ucgi = *underlying_cgip; + + cgi.loop = ucgi.loop; + cgi.face = ucgi.face; + + for(int a=0; a<16; a++) + for(int b=0; b<16; b++) { + cgi.dirs_adjacent[a][b] = ucgi.dirs_adjacent[a][b]; + cgi.next_dir[a][b] = ucgi.next_dir[a][b]; + } + + for(int b=0; b<12; b++) + cgi.spins[b] = ucgi.spins[b]; + + compute_around(true); + } + +int get_middle() { + if(S7 == 20) return 5; + if(S7 == 8) return 4; + return 3; + } + +EX ld around; + +EX void compute_scale() { + + int middle = get_middle(); + + // the value of 'around' which makes the tiling Euclidean + ld good = M_PI / asin(cos(M_PI/middle) / sin(M_PI/underlying_cgip->face)); + + println(hlog, "good = ", good); + + if(abs(good - around) < 1e-6) good = around; + + if(around == good) { + ginf[gFake].g = WDIM == 3 ? giEuclid3 : giEuclid2; + } + + if(around > good) { + ginf[gFake].g = WDIM == 3 ? giHyperb3 : giHyperb2; + } + + if(around < good) { + ginf[gFake].g = WDIM == 3 ? giSphere3 : giSphere2; + } + + ld around_ideal = 1/(1/2. - 1./get_middle()); + println(hlog, "around_ideal = ", around_ideal); + + if(euclid) scale = 1; + else if(abs(around_ideal - around) < 1e-6) { + hyperpoint h0 = underlying_cgip->cellshape[0]; + auto s = kleinize(h0); + ld d = hypot_d(LDIM, s); + scale = 1/d; + + hyperpoint h = h0; + auto h1 = h / h[WDIM] * scale; + h1[WDIM] = 1; + + println(hlog, "material = ", material(h1)); + } + else { + ld minscale = 0, maxscale = 10; + for(int it=0; it<100; it++) { + scale = (minscale + maxscale) / 2; + ld ar = compute_around(false); + println(hlog, "scale = ", scale, " ar = ", ar); + if(sphere) { + if(ar < around) maxscale = scale; + else minscale = scale; + } + else { + if(ar > around) maxscale = scale; + else minscale = scale; + } + } + } + sightranges[gFake] = sightranges[underlying] * scale; + } + +void set_gfake(int c, ld _around) { + stop_game(); + cgi.require_basics(); + fake::scale = scale; + underlying = geometry; + underlying_cgip = cgip; + ginf[gFake] = ginf[underlying]; + + set_geometry(gFake); + + around = _around; + + compute_scale(); + check_cgi(); + + compute_scale(); + check_cgi(); + } + +EX void change_around() { + if(around > 2) { + ld t = sightranges[gFake] / (sightranges[underlying] * scale); + compute_scale(); + ray::reset_raycaster(); + sightranges[gFake] *= t; + } + }; + +int readArgs() { + using namespace arg; + + if(0) ; + else if(argis("-gfake")) { + if(fake::in()) shift_arg_formula(around, change_around); + else { + shift(); int c = argi(); + ld around; + shift_arg_formula(around); + set_gfake(c, around); + } + } + else return 1; + return 0; + } + +auto fundamentalhook = addHook(hooks_args, 100, readArgs); + +EX } + +} + diff --git a/fieldpattern.cpp b/fieldpattern.cpp index b9121c6e..185d5179 100644 --- a/fieldpattern.cpp +++ b/fieldpattern.cpp @@ -1085,6 +1085,7 @@ EX bool quotient_field_changed; #define STR(x) string(x, sizeof(x)) EX struct fpattern& getcurrfp() { + if(fake::in()) return *FPIU(&getcurrfp()); if(geometry == gFieldQuotient && quotient_field_changed) return current_quotient_field; if(geometry == gSpace535) { @@ -1156,6 +1157,7 @@ EX struct fpattern& getcurrfp() { DEBB(DF_FIELD, ("set prime = ", fp.Prime)); return fp; } + if(!hyperbolic) return fp_invalid; if(S7 == 8 && S3 == 3 && !bt::in()) { static fpattern fp(17); return fp; diff --git a/geom-exp.cpp b/geom-exp.cpp index dbd489a2..0f85e2cd 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -359,6 +359,7 @@ void ge_select_tiling() { if(arb::in() && (ISMOBILE || ISWEB)) continue; if(WDIM == 3 && MAXMDIM == 3) continue; if(geometry == gFieldQuotient && !CAP_FIELD) continue; + if(geometry == gFake) continue; if(!current_filter->test()) continue; if(orig_el) { for(int j=0; jtimestamp = ntimestamp; + if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp; if(isize(cgis) > 4) { vector> timestamps; diff --git a/hyper.cpp b/hyper.cpp index 102b242d..86faa56a 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -40,6 +40,7 @@ #include "arbitrile.cpp" #include "euclid.cpp" #include "sphere.cpp" +#include "fake.cpp" #include "quotient.cpp" #include "crystal.cpp" #include "reg3.cpp" diff --git a/landgen.cpp b/landgen.cpp index bea02466..4cd0aee0 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -2755,6 +2755,8 @@ EX void set_land_for_geometry(cell *c) { } EX void setdist(cell *c, int d, cell *from) { + + if(fake::in()) return FPIU(setdist(c, d, from)); if(c->mpdist <= d) return; if(c->mpdist > d+1 && d < BARLEV) setdist(c, d+1, from); diff --git a/pattern2.cpp b/pattern2.cpp index d6b10eee..e66599e3 100644 --- a/pattern2.cpp +++ b/pattern2.cpp @@ -58,14 +58,14 @@ int eupattern4(cell *c) { EX bool ishept(cell *c) { // EUCLIDEAN - if(euclid && PURE) return eupattern(c) == 0; + if(euc::in() && PURE) return eupattern(c) == 0; else if(hybri) { cell *c1 = hybrid::get_where(c).first; return c1 == c1->master->c7; } else return c == c->master->c7; } EX bool ishex1(cell *c) { // EUCLIDEAN - if(euclid && PURE) return eupattern(c) == 1; + if(euc::in() && PURE) return eupattern(c) == 1; #if CAP_GP else if(GOLDBERG) return c->master->c7 != c && !pseudohept(c->move(0)); #endif @@ -74,7 +74,7 @@ EX bool ishex1(cell *c) { bool ishex2(cell *c) { // EUCLIDEAN - if(euclid && PURE) return eupattern(c) == 1; + if(euc::in() && PURE) return eupattern(c) == 1; #if CAP_GP else if(GOLDBERG) return c->master->c7 != c && gp::pseudohept_val(c) == 1; #endif @@ -372,6 +372,7 @@ EX pair fieldval(cell *c) { } EX int fieldval_uniq(cell *c) { + if(fake::in()) return FPIU(fieldval_uniq(c)); if(experimental) return 0; else if(hybri) { auto c1 = hybrid::get_where(c).first; @@ -388,7 +389,7 @@ EX int fieldval_uniq(cell *c) { if(ctof(c)) return c->master->fieldval; else return createMov(c, 0)->master->fieldval + 256 * createMov(c,2)->master->fieldval + (1<<16) * createMov(c,4)->master->fieldval; } - else if(euclid && !kite::in() && !arcm::in()) { + else if(euc::in()) { auto p = euc2_coordinates(c); if(bounded) return p.first + (p.second << 16); return gmod(p.first - 22 * p.second, 3*127); @@ -1405,7 +1406,7 @@ EX bool pseudohept(cell *c) { #if MAXMDIM == 4 if(WDIM == 3) { if(geometry == gField435) return false; - else if(euclid) return euc::pseudohept(c); + else if(euc::in()) return euc::pseudohept(c); else return reg3::pseudohept(c); } #endif diff --git a/polygons.cpp b/polygons.cpp index 328f3dd8..0abf682a 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -1142,6 +1142,8 @@ void geometry_information::prepare_shapes() { if(GDIM == 3 && !floor_textures) make_floor_textures(); #endif + if(fake::in()) { FPIU( cgi.require_shapes() ); } + symmetriesAt.clear(); allshapes.clear(); #if CAP_GP diff --git a/raycaster.cpp b/raycaster.cpp index df1bd335..a37af553 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -41,6 +41,7 @@ EX int max_cells = 2048; EX bool rays_generate = true; EX ld& exp_decay_current() { + if(fake::in()) return *FPIU(&exp_decay_current()); return (sn::in() || hyperbolic || sl2) ? exp_decay_exp : exp_decay_poly; } @@ -90,8 +91,7 @@ EX bool requested() { #endif if(!available()) return false; if(want_use == 2) return true; - if(sphere) return false; /* currently incompatible with primitives */ - return racing::on || quotient; + return racing::on || quotient || fake::in(); } #if HDR diff --git a/reg3.cpp b/reg3.cpp index 4d71f5f0..86bea0e8 100644 --- a/reg3.cpp +++ b/reg3.cpp @@ -28,10 +28,16 @@ EX namespace reg3 { #endif EX bool in() { + if(fake::in()) return FPIU(in()); return GDIM == 3 && !euclid && !bt::in() && !nonisotropic && !hybri && !kite::in(); } EX void generate() { + + if(fake::in()) { + fake::generate(); + return; + } int& loop = cgi.loop; int& face = cgi.face; @@ -1330,6 +1336,7 @@ EX int celldistance(cell *c1, cell *c2) { EX bool pseudohept(cell *c) { auto m = regmap(); if(cgflags & qSINGLE) return true; + if(fake::in()) return FPIU(reg3::pseudohept(c)); if(sphere) { hyperpoint h = tC0(m->relative_matrix(c->master, regmap()->origin, C0)); if(S7 == 12) {