mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-16 18:24:48 +00:00
674 lines
20 KiB
C++
674 lines
20 KiB
C++
#include "rogueviz.h"
|
|
|
|
namespace rogueviz {
|
|
|
|
namespace magic { void magic(int i); }
|
|
|
|
namespace crystal_sokoban { void run_sb(); }
|
|
|
|
namespace colorpicker {
|
|
|
|
int current_step;
|
|
color_t current_color;
|
|
cell *current_center;
|
|
|
|
color_t get_color_at(cell *c) {
|
|
crystal::coord oldc = crystal::get_coord(current_center->master);
|
|
crystal::coord newc = crystal::get_coord(c->master);
|
|
color_t res;
|
|
for(int i=0; i<3; i++) {
|
|
int val = part(current_color, 2-i) + current_step * ((newc[i] - oldc[i]) / 2);
|
|
if(val < 0) val = 0;
|
|
if(val > 255) val = 255;
|
|
part(res, 2-i) = val;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool color_markers(cell *c, const shiftmatrix& V) {
|
|
if(!centerover) return false;
|
|
if(centerover != current_center) {
|
|
current_color = get_color_at(centerover);
|
|
current_center = centerover;
|
|
}
|
|
c->landparam = get_color_at(c);
|
|
return false;
|
|
}
|
|
|
|
bool color_key(int sym, int uni) {
|
|
if((cmode & sm::NORMAL) && (uni >= '0' && uni <= '6')) {
|
|
current_step = (1 << (uni - '0'));
|
|
return true;
|
|
}
|
|
if((cmode & sm::NORMAL) && uni == '[') {
|
|
current_step = (1 + current_step) / 2;
|
|
return true;
|
|
}
|
|
if((cmode & sm::NORMAL) && uni == ']') {
|
|
current_step = 2 * current_step;
|
|
return true;
|
|
}
|
|
if((cmode & sm::NORMAL) && uni >= 1000 && uni < 1010) {
|
|
current_step = 1 << (uni - 1000);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool color_prestats() {
|
|
nohelp = true;
|
|
for(int k= 0; k <= 6; k++) {
|
|
int v = 1<< k;
|
|
if(displayButtonS(10 + k * vid.fsize * 2, 10 + vid.fsize, its(k), v == current_step ? 0xFF2020 : 0xC0C0C0, 0, vid.fsize))
|
|
getcstat = 1000 + k;
|
|
}
|
|
if(mouseover) {
|
|
displayButtonS(10 + 7 * vid.fsize * 2, 10 + vid.fsize, itsh(mouseover->landparam & 0xFFFFFF), mouseover->landparam, 0, vid.fsize);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void run_cpick() {
|
|
crystal::compass_probability = 0;
|
|
stop_game();
|
|
crystal::set_crystal(6);
|
|
set_variation(eVariation::pure);
|
|
enable_canvas();
|
|
patterns::whichCanvas = 'g';
|
|
patterns::canvasback = 0;
|
|
check_cgi();
|
|
start_game();
|
|
current_center = currentmap->gamestart();
|
|
current_color = 0x808080;
|
|
current_step = 32;
|
|
mapeditor::drawplayer = false;
|
|
vid.smart_range_detail = 1;
|
|
vid.use_smart_range = 2;
|
|
rv_hook(hooks_drawcell, 100, color_markers);
|
|
rv_hook(hooks_handleKey, 50, color_key);
|
|
rv_hook(hooks_prestats, 150, color_prestats);
|
|
}
|
|
|
|
auto cphook = addHook(hooks_args, 100, [] {
|
|
using namespace arg;
|
|
|
|
if(0) ;
|
|
else if(argis("-cpick")) {
|
|
PHASEFROM(2);
|
|
run_cpick();
|
|
}
|
|
else return 1;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
namespace sokoban {
|
|
|
|
bool on;
|
|
|
|
bool created;
|
|
|
|
hpcshape sokowall[10][7];
|
|
|
|
hyperpoint tpoint[6];
|
|
|
|
void push3(hyperpoint h) {
|
|
cgi.first = false;
|
|
cgi.hpc.push_back(h);
|
|
}
|
|
|
|
void create_sokowalls(cell *c) {
|
|
for(int a=0; a<6; a++) tpoint[a] = currentmap->adj(c, a) * C0;
|
|
const int qfr = 8;
|
|
auto v = [&] (int a, int b, int fr) {
|
|
hyperpoint h0 = get_corner_position(c, b);
|
|
hyperpoint h1 = get_corner_position(c, b+1);
|
|
hyperpoint h2 = normalize(h0 * (qfr-fr) + h1 * fr);
|
|
return orthogonal_move_fol(h2, 1 / (1 - a / 6.1));
|
|
};
|
|
|
|
for(int a=0; a<9; a++)
|
|
for(int b=0; b<6; b++) {
|
|
cgi.bshape(sokowall[a][b], PPR::FLOOR + 2 * a + 1);
|
|
for(int f=0; f<=qfr; f++)
|
|
push3(v(a, b, f));
|
|
for(int f=0; f<=qfr; f++)
|
|
push3(v(a+1, b, qfr-f));
|
|
push3(v(a, b, 0));
|
|
}
|
|
|
|
for(int a=0; a<9; a++) {
|
|
cgi.bshape(sokowall[a][6], PPR::FLOOR + 2 * a);
|
|
for(int b=0; b<6; b++)
|
|
for(int f=0; f<qfr; f++)
|
|
push3(v(a, b, f));
|
|
push3(v(a, 0, 0));
|
|
}
|
|
|
|
cgi.finishshape();
|
|
cgi.extra_vertices();
|
|
}
|
|
|
|
bool sokomap(cell *c, const shiftmatrix& V) {
|
|
if(!created) {
|
|
created = true;
|
|
create_sokowalls(c);
|
|
}
|
|
|
|
crystal::coord v = crystal::get_coord(c->master);
|
|
bool high = (v[0] ^ v[1] ^ v[2]) & 2;
|
|
|
|
|
|
color_t col = 0x00C000FF;
|
|
|
|
poly_outline = 0xFF;
|
|
|
|
int lev = 0;
|
|
|
|
int phase = ((v[0] ^ v[2] ^ v[4]) & 2) >> 1;
|
|
|
|
// high = v[0] >= 0 && v[0] <= 8 && v[1] >= 0 && v[1] <= 8 && v[2] == 0 && !(v[0] > 0 && v[0] < 8 && v[1] > 0 && v[1] < 8);
|
|
// if(high) col = 0xFFFF00FF, lev = 3;
|
|
|
|
high = true;
|
|
lev = ((v[0] + v[1] + v[2])/2+1) & 3;
|
|
if(lev == 0) col = 0x008000FF;
|
|
if(lev == 1) col = 0x00C000FF;
|
|
if(lev == 2) col = 0x40C000FF;
|
|
if(lev == 3) col = 0x80FF00FF;
|
|
|
|
// if(v[0] == 4 && v[1] == 4 && v[2] == 0) col = 0xFFFFFFFF;
|
|
|
|
c->landparam = lev;
|
|
|
|
if(high) {
|
|
ld d = hdist0(tC0(V));
|
|
color_t dark1 = col - ((col & 0xFCFCFC00) >> 1);
|
|
color_t dark2 = col - ((col & 0xFCFCFC00) >> 2);
|
|
for(int b=0; b<6; b++)
|
|
for(int a=c->move(b)->landparam; a<lev; a++) if(hdist0(V * tpoint[b]) < d) {
|
|
/* auto& p = */
|
|
queuepoly(V, sokowall[a][b], (((b+phase)&1) ? dark1 : dark2));
|
|
// p.flags |= POLY_PRECISE_WIDE;
|
|
// p.linewidth = 2;
|
|
}
|
|
queuepoly(V, sokowall[lev][6], col);
|
|
}
|
|
else {
|
|
queuepoly(V, sokowall[0][6], col);
|
|
}
|
|
|
|
c->wall = waInvisibleFloor;
|
|
return false;
|
|
}
|
|
|
|
void run_sb() {
|
|
crystal::compass_probability = 0;
|
|
stop_game();
|
|
crystal::set_crystal(6);
|
|
set_variation(eVariation::pure);
|
|
enable_canvas();
|
|
patterns::whichCanvas = 'g';
|
|
patterns::canvasback = 0;
|
|
check_cgi();
|
|
rv_hook(hooks_drawcell, 100, sokomap);
|
|
start_game();
|
|
mapeditor::drawplayer = false;
|
|
vid.smart_range_detail = 1;
|
|
vid.use_smart_range = 2;
|
|
}
|
|
|
|
auto sbhook = addHook(hooks_args, 100, [] {
|
|
using namespace arg;
|
|
|
|
if(0) ;
|
|
else if(argis("-sb")) {
|
|
PHASEFROM(2);
|
|
sokoban::run_sb();
|
|
}
|
|
else return 1;
|
|
return 0;
|
|
});
|
|
|
|
|
|
}
|
|
|
|
const flagtype VC = 1;
|
|
const flagtype NO_VC = 2;
|
|
const flagtype PLAYER = 4;
|
|
|
|
void sync(int mode, flagtype flags) {
|
|
using namespace tour;
|
|
if(mode == pmStart) {
|
|
crystal::compass_probability = 0;
|
|
crystal::crystal_period = 0;
|
|
enable_canvas();
|
|
mapeditor::drawplayer = (flags & PLAYER);
|
|
vid.smart_range_detail = 1;
|
|
vid.use_smart_range = 2;
|
|
crystal::view_coordinates = (flags & VC);
|
|
smooth_scrolling = true;
|
|
}
|
|
if(mode == pmKey && !(flags & NO_VC))
|
|
crystal::view_coordinates = !crystal::view_coordinates;
|
|
if(cwt.at != centerover && !playermoved && !(flags & PLAYER)) {
|
|
cwt.at = centerover;
|
|
current_display->which_copy = gmatrix[cwt.at].T;
|
|
}
|
|
}
|
|
|
|
vector<tour::slide> high_slides;
|
|
|
|
int shapeid;
|
|
|
|
int mycanvas(cell *c) {
|
|
|
|
int dim = crystal::get_dim();
|
|
|
|
auto d = crystal::get_coord(c->master);
|
|
for(int i=0; i<dim; i++) d[i] >>= 1;
|
|
|
|
color_t col = 0;
|
|
|
|
d[0] ++;
|
|
|
|
int ones = 0;
|
|
for(int i=0; i<dim; i++) if((d[i] & 1) == 1) ones++;
|
|
|
|
switch(shapeid) {
|
|
|
|
case 0: {
|
|
auto dx = d; dx[0] -= 2;
|
|
bool grid = false;
|
|
for(int i=1; i<dim; i++) if((dx[i] & 3) == 2) grid = true;
|
|
|
|
for(int i=0; i<3; i++) part(col, i) = 0xFF + 0x30 * (d[i]-2);
|
|
if(grid) col |= 0x1000000;
|
|
if(dx == crystal::c0) col = 0x1FFD500;
|
|
if(dx[0] == 2 && dx[1] == 0 && dx[2] == 0 && dx[3] == 0 && dx[4] == 0 && dx[5] == 0) col = 0;
|
|
if(dx[0] == 6 && dx[1] == 0 && dx[2] == 0 && dx[3] == 0 && dx[4] == 0 && dx[5] == 0) col = 0;
|
|
return col;
|
|
}
|
|
|
|
case 1: {
|
|
if(d[1] == 0 && d[2] == 0 && d[3] == 0 && d[4] == 0 && d[5] == 0) ;
|
|
else col = gradient(0x1FF0000, 0x1FFFF00, 9, d[0], 16);
|
|
part(col, 0) = 0x80 + d[1] * 0x70;
|
|
return col;
|
|
}
|
|
|
|
case 2: {
|
|
col = gradient(0xFFFF00, 0x00FF00, 17, d[0], 24);
|
|
for(int i=0; i<1; i++) part(col, i) = 0xFF + 0x30 * (d[i+1]-2);
|
|
c->landparam = col;
|
|
if(ones == dim-1) col |= 0x1000000;
|
|
return col;
|
|
}
|
|
|
|
case 3: {
|
|
if(d[3] == 1) col = (ones & 1) ? 0x1C0FFC0 : 0x180FF80;
|
|
if(d[3] == -1) col = (ones & 1) ? 0x18080FF : 0x14040FF;
|
|
if(d[2] == 1) col = (ones & 1) ? 0x1FFCC00 : 0x1FF8080;
|
|
if(d[2] == -1) col = (ones & 1) ? 0x180FFFF : 0x1408080;
|
|
|
|
if(d[4] == 1) col = 0x1FFFFFF;
|
|
if(d[4] == -1) col = 0x1FFFFFF;
|
|
if(d[5] == 1) col = 0x1FFFFFF;
|
|
if(d[5] == -1) col = 0x1FFFFFF;
|
|
return col;
|
|
}
|
|
|
|
case 4: {
|
|
if(d[3] == 1) col = (ones & 1) ? 0x1C0FFC0 : 0x180FF80;
|
|
if(d[3] == -1) col = (ones & 1) ? 0x18080FF : 0x14040FF;
|
|
|
|
if(d[4] == 1) col = 0x1FFFFFF;
|
|
if(d[4] == -1) col = 0x1FFFFFF;
|
|
return col;
|
|
}
|
|
|
|
case 5: {
|
|
if(d[3] == 1) col = (ones & 1) ? 0x1C0FFC0 : 0x180FF80;
|
|
if(d[3] == -2) col = (ones & 1) ? 0x180FFFF : 0x140FFFF;
|
|
return col;
|
|
}
|
|
|
|
case 6: {
|
|
if(d[3] == 1) col = (ones & 1) ? 0x1FF8080 : 0x1FF6060;
|
|
if(d[2] == -1) col = (ones & 1) ? 0x1FFFF70 : 0x1E0E060;
|
|
return col;
|
|
}
|
|
|
|
case 7: {
|
|
if(d[1] == 0 || d[2] == 0) ;
|
|
else if(d[1] > 0 && d[2] > 0) col = 0x1FF0000;
|
|
else if(d[1] > 0 && d[2] < 0) col = 0x1FFFF00;
|
|
else if(d[1] < 0 && d[2] < 0) col = 0x100FFFF;
|
|
else if(d[1] < 0 && d[2] > 0) col = 0x10000FF;
|
|
return col;
|
|
}
|
|
|
|
case 8: {
|
|
int s = d[1] + d[2] + d[3] + d[4] + d[5];
|
|
if(s > 0) col = 0x1FFD500;
|
|
else if (s < -1) col = 0x17851a9;
|
|
return col;
|
|
}
|
|
|
|
case 9: {
|
|
int s = d[1] + d[2] + d[3] + d[4] + d[5] + d[0];
|
|
if(s > 0) col = 0x1FF20FF;
|
|
else if (s < -1) col = 0x1C0C0C0;
|
|
return col;
|
|
}
|
|
|
|
case 10: /* house */ {
|
|
d[0]--;
|
|
int is0 = 0, is1 = 0, is2 = 0, ismore = 0;
|
|
for(int a=0; a<dim; a++) {
|
|
int v = abs(d[a]);
|
|
if(v == 0) is0++;
|
|
else if(v == 1) is1++;
|
|
else if(v == 2) is2++;
|
|
else if(v > 2) ismore++;
|
|
}
|
|
if(d[dim-1]) return 0x101010;
|
|
else if(ismore) return 0x101010;
|
|
else if(is2) return 0x1800000;
|
|
else if(is1) return 0xFFFF00;
|
|
else return 0x1FFFFFF;
|
|
}
|
|
|
|
case 11: /* orthoplex */ {
|
|
int s = abs(d[0] - 4) + abs(d[1] - 3) + abs(d[2] - 2) + abs(d[3] - 3);
|
|
if(s == 0) return 0x1FFFFFF;
|
|
else if(s == 12) return 0x1FF0000;
|
|
else if(s < 12) return 0x800000;
|
|
// else if(s == 13) return 0x1FF8000;
|
|
else return 0x202020;
|
|
}
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void enable() {
|
|
rv_hook(patterns::hooks_generate_canvas, 100, mycanvas);
|
|
}
|
|
|
|
auto explore_structure(int _shapeid) {
|
|
using namespace tour;
|
|
return [=] (presmode mode) {
|
|
sync(mode, NO_VC);
|
|
if(mode == pmStart) {
|
|
tour::slide_backup(mapeditor::drawplayer, false);
|
|
tour::slide_backup(smooth_scrolling, true);
|
|
stop_game();
|
|
set_geometry(geometry == gCrystal534 ? gCrystal534 : gCrystal344);
|
|
enable_canvas();
|
|
patterns::whichCanvas = ' ';
|
|
shapeid = _shapeid;
|
|
enable();
|
|
crystal::crystal_period = 4;
|
|
start_game();
|
|
#if CAP_RAY
|
|
ray::max_cells = 4096;
|
|
#endif
|
|
}
|
|
if(mode == pmKey || mode == pmGeometrySpecial) {
|
|
stop_game();
|
|
set_geometry(geometry == gCrystal534 ? gCrystal344 : gCrystal534);
|
|
enable();
|
|
start_game();
|
|
}
|
|
};
|
|
}
|
|
|
|
void add_explore_structure(vector<tour::slide>& v, int id, string nshort, string nlong) {
|
|
string hds = "high-dimensional shapes/";
|
|
v.emplace_back(
|
|
tour::slide{hds+nshort, 999, tour::LEGAL::SPECIAL,
|
|
nlong + "\n\n"
|
|
"In these slides, press 5 to switch between 4-dimensional and 6-dimensional space."
|
|
"Press End/Home to move forward/backward.",
|
|
explore_structure(id)
|
|
});
|
|
}
|
|
|
|
void house(int sides, int shape = 10) {
|
|
stop_game();
|
|
if(sides < 0)
|
|
set_geometry(gCrystal344);
|
|
else
|
|
crystal::set_crystal(sides);
|
|
set_variation(eVariation::pure);
|
|
enable_canvas();
|
|
patterns::whichCanvas = ' ';
|
|
shapeid = shape;
|
|
check_cgi();
|
|
enable();
|
|
start_game();
|
|
}
|
|
|
|
#if CAP_RVSLIDES
|
|
tour::slide *gen_high_demo() {
|
|
high_slides.clear();
|
|
using namespace tour;
|
|
auto& v = high_slides;
|
|
v.emplace_back(
|
|
slide{"Introduction/Three-dimensional space", 999, LEGAL::NONE,
|
|
"This is our 2D visualization of 3-dimensional space.\n\n"
|
|
"In most slides you can press '5' to enable or disable the coordinate display. "
|
|
"You can move the focus with numpad, arrowkeys, or clicking cells with mouse."
|
|
,
|
|
[] (presmode mode) {
|
|
sync(mode, VC);
|
|
if(mode == pmStart) {
|
|
crystal::set_crystal(6);
|
|
patterns::whichCanvas = 'K';
|
|
start_game();
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"Introduction/Four-dimensional space", 999, LEGAL::NONE,
|
|
"This is our 2D visualization of 4-dimensional space.",
|
|
[] (presmode mode) {
|
|
sync(mode, VC);
|
|
if(mode == pmStart) {
|
|
crystal::set_crystal(8);
|
|
patterns::whichCanvas = 'K';
|
|
start_game();
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"Introduction/Color picker", 999, LEGAL::NONE,
|
|
"Color picker. You can press '0' to '6' to adjust how fast the colors change.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart)
|
|
colorpicker::run_cpick();
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/house 3D", 999, LEGAL::NONE,
|
|
"A house in three dimensions. This is a 5x5 square, its center is white, rest of its interior is yellow, and its perimeter is red. "
|
|
"By using the third dimension, you can leave the square, or enter it back.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
house(6);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/house 4D", 999, LEGAL::NONE,
|
|
"A house in four dimensions. This is a 5x5x5 cube."
|
|
,
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
house(8);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/house, 3D visualization", 999, LEGAL::NONE,
|
|
"A house in four dimensions, using the 3D version of our visualization. This visualization is harder to understand. Press End/Home to move forward/backward.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
house(-1);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/4D orthoplex using 2D", 999, LEGAL::NONE,
|
|
"Try to find the center of the orthoplex in four dimensions. This is a 4D analog of the octahedron.\n\n"
|
|
"The faces of the orthoplex are bright red, the outside is dark gray, and the center is white."
|
|
,
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
house(8, 11);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/4D orthoplex using 3D", 999, LEGAL::NONE,
|
|
"The same visualization in 3D.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
house(8, 11);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/basic roguelike", 999, LEGAL::NONE,
|
|
"This is a basic roguelike in three dimensions. Even though it appears that it should be possible to move in such a way that "
|
|
"one of the enemies is no longer adjacent to us, it turns out that one of its other 'copies' will always manage "
|
|
"to chase us. This is clear when we imagine the actual higher-dimensional space.",
|
|
[] (presmode mode) {
|
|
sync(mode, NO_VC | PLAYER);
|
|
if(mode == pmStart) {
|
|
crystal::set_crystal(6);
|
|
patterns::whichCanvas = 'c';
|
|
colortables['c'][0] = 0x208020;
|
|
colortables['c'][1] = 0x105010;
|
|
patterns::canvasback = 0x101010;
|
|
start_game();
|
|
auto & us = vid.cs;
|
|
us.charid = 4;
|
|
/* us.skincolor = 0xB55239FF;
|
|
us.haircolor = 0xB55239FF;
|
|
us.dresscolor = 0xB55239FF; */
|
|
us.skincolor = 0x202020FF;
|
|
us.haircolor = 0x202020FF;
|
|
us.dresscolor = 0x202020FF;
|
|
us.eyecolor = 0xC000FF;
|
|
cwt.at->move(0)->monst = moRedFox;
|
|
cwt.at->move(1)->monst = moLavaWolf;
|
|
minf[moLavaWolf].color = 0x909090;
|
|
minf[moLavaWolf].name = "Wolf";
|
|
minf[moRedFox].name = "Fox";
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"games and puzzles/gravity mockup", 999, LEGAL::NONE,
|
|
"A mockup of a 4D game with gravity. We use the 3D version of our visualization, while the gravity dimension is shown using perspective.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
sokoban::run_sb();
|
|
}
|
|
}
|
|
});
|
|
|
|
add_explore_structure(v, 0, "Cage", "In this series of slides we show various structures in four-dimensional space, using the three-dimensional variant of our visualization.\n\nWe start with a 4x4x4x4 cage with a golden point in the center.");
|
|
add_explore_structure(v, 1, "Tunnel", "One-dimensional tunnel");
|
|
add_explore_structure(v, 2, "Skeleton", "The 1-skeleton of the tessellation of Z^4 with cubes of edge 2");
|
|
add_explore_structure(v, 3, "Tunnel2", "Two-dimensional tunnel");
|
|
add_explore_structure(v, 4, "Hyperplanes", "Two hyperplanes in distance 2, i.e., three-dimensional tunnel.");
|
|
add_explore_structure(v, 5, "Far hyperplanes", "Two hyperplanes in distance 3.");
|
|
add_explore_structure(v, 6, "Orthogonal", "Two orthogonal hyperplanes.");
|
|
add_explore_structure(v, 7, "Quarterspaces", "Four quarterspaces.");
|
|
add_explore_structure(v, 8, "Diagonal", "Diagonal tunnel in all coordinates except one.");
|
|
add_explore_structure(v, 9, "Diagonal", "Diagonal tunnel in all coordinates.");
|
|
|
|
v.emplace_back(
|
|
slide{"Magic cube/Standard magic cube", 999, LEGAL::NONE,
|
|
"Magic Cube (aka Rubik's Cube) using two dimensions. This is an example of a visualization which is difficult for our method, because we are moving complex objects in the 3D space."
|
|
"Press 'r' to rotate the face (while the mouse pointer is on its center -- two coodinates must be 0 and one must be non-zero), Shift+R to reset.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
magic::magic(6);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"Magic cube/Four-dimensional magic cube", 999, LEGAL::NONE,
|
|
"Magic Cube (4D) using two dimensions. Keys are the same as in the previous slide.\n\n"
|
|
"Use 'r' to rotate the 2D face under the mouse pointer (two coodinates must be 0 and two must be non-zero)."
|
|
,
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
magic::magic(8);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"Magic cube/Four-dimensional magic cube, 3D visualization", 999, LEGAL::NONE,
|
|
"Magic Cube (4D) using three dimensions.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
magic::magic(-1);
|
|
}
|
|
}
|
|
});
|
|
|
|
v.emplace_back(
|
|
slide{"3D Sokoban", 999, LEGAL::NONE,
|
|
"A three-dimensional Sokoban puzzle visualized using H2.\n\n"
|
|
"The puzzle is designed so that all three dimensions matter.\n\n"
|
|
"Press 'r' or Backspace to undo moves.",
|
|
[] (presmode mode) {
|
|
sync(mode, 0);
|
|
if(mode == pmStart) {
|
|
crystal_sokoban::run_sb();
|
|
}
|
|
}
|
|
});
|
|
|
|
callhooks(pres::hooks_build_rvtour, "highdim", high_slides);
|
|
pres::add_end(v);
|
|
return &high_slides[0];
|
|
}
|
|
#endif
|
|
|
|
auto highdim_hooks =
|
|
addHook_slideshows(120, [] (tour::ss::slideshow_callback cb) {
|
|
|
|
if(high_slides.empty()) gen_high_demo();
|
|
|
|
cb(XLAT("visualizing higher-dimensional spaces"), &high_slides[0], 'h');
|
|
});
|
|
|
|
} |