fractal geometry

This commit is contained in:
Zeno Rogue 2023-03-28 22:54:46 +02:00
parent 121df0d9c7
commit 511ffe8498
9 changed files with 190 additions and 12 deletions

135
cell.cpp
View File

@ -402,6 +402,141 @@ EX bool is_in_disk(cell *c) {
return *it == c;
}
bool sierpinski3(gp::loc g) {
int x = g.first;
int y = g.second;
set<pair<int, int>> visited;
while(true) {
if(visited.count({x,y})) return false;
visited.insert({x,y});
// if(x == -1 && y == -2) return false;
// if(x == -2 && y == -3) return false;
if(x == 0 && y == 0) return true;
if((x&1) == 1 && (y&1) == 1) return false;
x >>= 1;
y >>= 1;
// x--; y++;
tie(x, y) = make_pair(-x-y, x);
}
}
bool sierpinski46(gp::loc g) {
int x = g.first;
int y = g.second;
set<pair<int, int>> visited;
if(S7 == 6)
x += 2785684, y += 289080;
else
x += 75239892, y += 7913772;
while(true) {
if(visited.count({x,y})) return false;
visited.insert({x,y});
if(x == 0 && y == 0) return true;
int dx = gmod(x, 3);
int dy = gmod(y, 3);
if(dx == 1 && dy == 1) return false;
if(S7 == 6 && dx == dy) return false;
x = (x-dx) / 3;
y = (y-dy) / 3;
}
}
bool menger_sponge(euc::coord c) {
c[0] += 528120*9; c[1] += 438924*9; c[2] += 306712*9;
set<euc::coord> visited;
while(true) {
if(visited.count(c)) return false;
visited.insert(c);
if(c[0] == 0 && c[1] == 0 && c[2] == 0) return true;
int ones = 0;
for(int i=0; i<3; i++) {
int d = gmod(c[i], 3);
c[i] = (c[i] - d) / 3;
if(d == 1) ones++;
}
if(ones >= 2) return false;
}
}
bool sierpinski_tet(euc::coord c) {
set<euc::coord> visited;
c[0] += 16 * (1+8+64+512);
c[1] += 16 * (1+8+64+512);
c[1] += 32 * (1+8+64+512);
c[2] += 32 * (1+8+64+512);
c[0] += 64 * (1+8+64+512);
c[1] += 64 * (1+8+64+512);
while(true) {
if(visited.count(c)) return false;
visited.insert(c);
if(c[0] == 0 && c[1] == 0 && c[2] == 0) return true;
int ones = 0;
for(int i=0; i<3; i++) {
int d = gmod(c[i], 2);
c[i] = (c[i] - d) / 2;
if(d == 1) ones++;
}
if(ones & 1) return false;
}
}
EX bool is_in_fractal(cell *c) {
if(fake::in()) return FPIU(is_in_fractal(c));
if(mhybrid) { c = hybrid::get_where(c).first; return PIU(is_in_fractal(c)); }
switch(geometry) {
case gSierpinski3:
return sierpinski3(euc::full_coords2(c));
case gSierpinski4:
case gSixFlake:
return sierpinski46(euc::full_coords2(c));
case gMengerSponge:
return menger_sponge(euc::get_ispacemap()[c->master]);
case gSierpinskiTet: {
return sierpinski_tet(euc::get_ispacemap()[c->master]);
}
default:
return true;
}
}
EX cell *fractal_rep(cell *c) {
switch(geometry) {
case gSierpinski3: {
auto co = euc::full_coords2(c);
co.first += 4;
co.first &= ~15;
co.first -= 4;
co.second += 2;
co.second &= ~15;
co.second -= 2;
if(co.first == -4 && co.second == -2) co.first = 0, co.second = 0;
return euc::get_at(euc::to_coord(co))->c7;
}
case gSierpinski4:
case gSixFlake: {
auto co = euc::full_coords2(c);
if(S7 == 6) co.first += 4;
co.first -= gmod(co.first, 9);
co.second -= gmod(co.second, 9);
return euc::get_at(euc::to_coord(co))->c7;
}
case gSierpinskiTet: {
auto co = euc::get_ispacemap()[c->master];
co[0] &=~7;
co[1] &=~7;
co[2] &=~7;
return euc::get_at(co)->c7;
}
case gMengerSponge: {
auto co = euc::get_ispacemap()[c->master];
for(int i=0; i<3; i++) co[i] = co[i] - gmod(co[i], 9);
return euc::get_at(co)->c7;
}
default:
throw hr_exception("unknown fractal");
}
}
/** create a map in the current geometry */
EX void initcells() {
DEBB(DF_INIT, ("initcells"));

View File

@ -1751,6 +1751,22 @@ void celldrawer::draw_features_and_walls_3d() {
#if MAXMDIM >= 4
color_t dummy;
int ofs = currentmap->wall_offset(c);
if((cgflags & qFRACTAL) && c->wall == waChasm && c->land == laMemory) {
for(int a=0; a<c->type; a++) if(c->move(a) && c->move(a)->land != laMemory) {
if(anyshiftclick) {
auto& poly = queuepoly(V, cgi.shPlainWall3D[ofs+a], 0xFFFFFFFF - 0xF0F0F00 * get_darkval(c, a));
poly.tinf = &floor_texture_vertices[cgi.shFullFloor.id];
ensure_vertex_number(*poly.tinf, poly.cnt);
}
else {
auto& poly = queuepoly(V, cgi.shWireframe3D[ofs + a], 0);
poly.outline = 0xFFFFFFFF;
}
}
if(anyshiftclick) return;
}
if(isWall3(c, wcol)) {
if(!no_wall_rendering) {
if(c->wall == waChasm && c->land == laMemory && !in_perspective()) {

View File

@ -751,6 +751,7 @@ enum eGeometry {
gSpace345, gSpace353, gSpace354, gSpace355,
gHalfBring,
gAperiodicHat,
gSierpinski3, gSierpinski4, gSixFlake, gMengerSponge, gSierpinskiTet,
gGUARD};
enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSol, gcNIH, gcSolN, gcNil, gcProduct, gcSL2 };
@ -830,6 +831,8 @@ static const flagtype qCAT = Flag(28);
static const flagtype qAPERIODIC = Flag(29);
static const flagtype qHAT = Flag(30);
static const flagtype qFRACTAL = Flag(31);
// note: dnext assumes that x&7 equals 7
static const int SEE_ALL = 50;
// note: check_football_colorability in arbitrile.cpp assumes OINF is divisible by 3
@ -958,7 +961,12 @@ EX vector<geometryinfo> ginf = {
{"{3,5,4}","none", "{3,5,4} hyperbolic honeycomb", "354", 20, 5, qIDEAL | qULTRA, giHyperb3, {{7, 2}}, eVariation::pure},
{"{3,5,5}","none", "{3,5,5} hyperbolic honeycomb", "355", 20, 5, qIDEAL | qULTRA, giHyperb3, {{7, 2}}, eVariation::pure},
{"{5,4}", "pBring", "projective Bring's Surface", "pBring", 5, 4, qsSMALLN, giHyperb2, {{6, 4}}, eVariation::bitruncated},
{"hat", "none", "aperiodic hat", "hat", 14, 3, qAPERIODIC | qHAT, giEuclid2, {{7, 7}}, eVariation::pure},
{"hat", "none", "aperiodic hat", "hat", 14, 3, qAPERIODIC | qHAT, giEuclid2, {{7, 7}}, eVariation::pure},
{"triangle","none", "Sierpiński triangle", "S3", 6, 3, qFRACTAL, giEuclid2, {{10, 10}}, eVariation::pure},
{"carpet", "none", "Sierpiński carpet", "S4", 4, 4, qFRACTAL, giEuclid2, {{10, 10}}, eVariation::pure},
{"6-flake","none", "6-flake fractal", "S6", 6, 3, qFRACTAL, giEuclid2, {{10, 10}}, eVariation::pure},
{"{4,3,4}","none", "Menger sponge", "S8", 6, 4, qFRACTAL, giEuclid3, {{10, 10}}, eVariation::pure},
{"rh{4,3,4}","none", "Sierpiński tetrahedron", "S4b", 12, 3, qFRACTAL, giEuclid3, {{10, 10}}, eVariation::pure},
};
// bits: 9, 10, 15, 16, (reserved for later) 17, 18

View File

@ -46,10 +46,12 @@ EX namespace euc {
switch(g) {
case gCubeTiling:
case gMengerSponge:
shifttable = { +D0, +D1, +D2 };
break;
case gRhombic3:
case gSierpinskiTet:
shifttable = { D0+D1, D0+D2, D1+D2, D1-D2, D0-D2, D0-D1 };
break;
@ -58,10 +60,13 @@ EX namespace euc {
break;
case gEuclid:
case gSierpinski3:
case gSixFlake:
shifttable = { D0, D1, D1-D0, -D0, -D1, D0-D1 };
break;
case gEuclidSquare:
case gSierpinski4:
shifttable = { D0, D1, -D0, -D1 };
break;

View File

@ -346,6 +346,8 @@ void set_or_configure_geometry(eGeometry g) {
/** is g2 the same tiling as the current geometry (geometry)? */
bool same_tiling(eGeometry g2) {
/* no quotients for fractals */
if(cgflags & qFRACTAL) return g2 == geometry;
if(g2 == gCrystal)
return S3 == 4;
if(g2 == gFieldQuotient && (hyperbolic || (geometry == gCubeTiling && reg3::cubes_reg3)) && standard_tiling())
@ -602,7 +604,7 @@ EX void select_quotient_screen() {
}
EX void select_quotient() {
if(meuclid && !aperiodic && !arcm::in() && !reg3::cubes_reg3) {
if(meuclid && !aperiodic && !arcm::in() && !reg3::cubes_reg3 && !(cgflags & qFRACTAL)) {
euc::prepare_torus3();
pushScreen(euc::show_torus3);
}
@ -1029,6 +1031,8 @@ EX void showEuclideanMenu() {
menuitem_nilwidth('v');
}
else if((WDIM == 3 || aperiodic || arb::in()) && !reg3::in() && geometry != gCubeTiling) dialog::addBreak(100);
else if(cgclass && qFRACTAL)
dialog::addBreak(100);
else
menuitem_change_variation('v');

View File

@ -3970,7 +3970,7 @@ EX int colorhash(color_t i) {
EX bool isWall3(cell *c, color_t& wcol) {
if(isWall(c)) return true;
if(c->wall == waChasm && c->land == laMemory) { wcol = 0x606000; return true; }
if(c->wall == waChasm && c->land == laMemory && (anyshiftclick || !(cgflags & qFRACTAL))) { wcol = 0x606000; return true; }
if(c->wall == waInvisibleFloor) return false;
// if(chasmgraph(c)) return true;
if(among(c->wall, waMirror, waCloud, waMineUnknown, waMineMine)) return true;

View File

@ -240,7 +240,7 @@ EX void buildCredits() {
"Kojiguchi Kazuki, baconcow, Alan, SurelyYouJest, hotdogPi, DivisionByZero, xXxWeedGokuxXx, jpystynen, Dmitry Marakasov, Alexandre Moine, Arthur O'Dwyer, "
"Triple_Agent_AAA, bluetailedgnat, Allalinor, Shitford, KittyTac, Christopher King, KosGD, TravelDemon, Bubbles, rdococ, frozenlake, MagmaMcFry, "
"Snakebird Priestess, roaringdragon2, Stopping Dog, bengineer8, Sir Light IJIJ, ShadeBlade, Saplou, shnourok, Ralith, madasa, 6% remaining, Chimera245, Remik Pi, alien foxcat thing, "
"Piotr Grochowski, Ann, still-flow, tyzone, Paradoxica, LottieRatWorld, aismallard, albatross, EncodedSpirit, Jacob Mandelson, CrashTuvai, cvoight, jennlbw, Kali Ranya"
"Piotr Grochowski, Ann, still-flow, tyzone, Paradoxica, LottieRatWorld, aismallard, albatross, EncodedSpirit, Jacob Mandelson, CrashTuvai, cvoight, jennlbw, Kali Ranya, spiritbackup"
);
#ifdef EXTRALICENSE
help += EXTRALICENSE;

View File

@ -2226,7 +2226,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
int hardchance = items[itRuby] + yendor::hardness();
if(hardchance > 25) hardchance = 25;
bool hardivy = hrand(100) < hardchance;
if(hat::in() ? buildIvy(c, 0, 4) : (hardivy ? buildIvy(c, 1, 9) : buildIvy(c, 0, c->type)) && !peace::on)
if((cgflags & qFRACTAL) ? buildIvy(c, 0, 2) : hat::in() ? buildIvy(c, 0, 4) : (hardivy ? buildIvy(c, 1, 9) : buildIvy(c, 0, c->type)) && !peace::on)
c->item = itRuby;
}
}
@ -2826,7 +2826,9 @@ EX void setland_randomwalk(cell *c) {
if(c->land) return;
if(hrand(10) == 0) setland(c, currentlands[hrand(isize(currentlands))]);
else {
cell *c2 = c->cmove(hrand(c->type));
cell *c2 = nullptr;
for(int i=0; i<10 && !(c2 && (!disksize || is_in_disk(c2)) && is_in_fractal(c2)); i++)
c2 = c->cmove(hrand(c->type));
setland_randomwalk(c2);
c->land = c2->land;
}
@ -2836,6 +2838,11 @@ EX eLand random_land() {
return hrand_elt(isize(cheatdest_list) ? cheatdest_list : currentlands);
}
EX void share_land(cell *c, cell *c2) {
if(!c2->land) setland(c2, random_land());
c->land = c2->land;
}
EX void set_land_for_geometry(cell *c) {
if(!c->land && isize(currentlands)) {
@ -2844,15 +2851,17 @@ EX void set_land_for_geometry(cell *c) {
return;
}
/* note: Nil patched chaos done in setLandNil */
if(ls::patched_chaos() && stdeuc) {
if(ls::patched_chaos() && (cgflags & qFRACTAL)) {
share_land(c, fractal_rep(c));
}
else if(ls::patched_chaos() && stdeuc) {
cell *c2 = c;
while(true) {
forCellCM(c3, c2) if(cdist50(c3) < cdist50(c2)) { c2 = c3; goto again; }
break;
again: ;
}
if(!c2->land) setland(c2, random_land());
c->land = c2->land;
share_land(c, c2);
return;
}
if(ls::patched_chaos() && aperiodic) {
@ -2861,8 +2870,7 @@ EX void set_land_for_geometry(cell *c) {
c2 = c->master->cmove(0)->cmove(0)->cmove(1)->cmove(1)->c7;
else
c2 = c->master->cmove(0)->cmove(0)->cmove(0)->cmove(0)->cmove(0)->cmove(1)->cmove(1)->cmove(1)->cmove(1)->cmove(1)->c7;
if(!c2->land) setland(c2, random_land());
c->land = c2->land;
share_land(c, c2);
return;
}
if(land_structure == lsChaosRW) {
@ -3058,7 +3066,7 @@ EX void setdist(cell *c, int d, cell *from) {
}
}
if(disksize && !is_in_disk(c)) {
if((disksize && !is_in_disk(c)) || ((cgflags & qFRACTAL) && !is_in_fractal(c))) {
setland(c, laMemory);
if(!isMultitile(c)) c->monst = moNone;
c->item = itNone;

View File

@ -159,6 +159,8 @@ EX void fix_land_structure_choice() {
land_structure = lsSingle;
if(aperiodic && !among(land_structure, lsChaosRW, lsTotalChaos, lsPatchedChaos, lsSingle))
land_structure = lsPatchedChaos;
if((cgflags & qFRACTAL) && !among(land_structure, lsChaosRW, lsTotalChaos, lsPatchedChaos, lsSingle))
land_structure = lsPatchedChaos;
}
EX bool landUnlockedRPM(eLand n) {