rug:: redesigned menus, hilbert rug, improved finger mode interface

This commit is contained in:
Zeno Rogue committed 2026-11-05 11:39:12 +01:00
1 parent 295329db52
commit 5cb531b89b
8 files changed
+581 -359

No files matched your search

-3
View File
@@ -1719,9 +1719,6 @@ EX int get_direction_key(int sym, int uni) {
}
EX bool gmodekeys(int sym, int uni) {
#if CAP_RUG
if(rug::rugged && rug::handlekeys(sym, uni)) return true;
#endif
if(NUMBERKEY == '6') { vid.grid = !vid.grid; return true; }
if(NUMBERKEY == '7') { vid.darkhepta = !vid.darkhepta; return true; }
+2 -4
View File
@@ -6958,8 +6958,7 @@ S(
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu.",
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect.",
"V tomto menu si můžeš vybrat z několika známých hladkých ploch s konstantní "
"zápornou křivostí. Vzhledem k tomu, že i hyperbolická rovina má konstantní "
@@ -6968,8 +6967,7 @@ S(
"pouze malou část hyperbolické roviny, takže na hraní HyperRogue se nehodí; "
"v některých směrech se však jejich pokrytí táhne velmi daleko, a proto se "
"dá efekt někdy vylepšit tím, že v konfiguraci grafiky zvýšíš dohled a "
"v nastavení hyperského koberce velikost textury. Kvůli pohodlí je z tohoto "
"menu možné vybrat i jiné 3D modely."
"v nastavení hyperského koberce velikost textury."
)
// coverage
+2 -4
View File
@@ -6626,8 +6626,7 @@ S(
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu.",
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect.",
"Dans ce menu vous pouvez choisir plusieurs surfaces lisses de courbure négative constante. "
"Comme la courbure d'un plan hyperbolique est aussi une constante négative, ces surfaces "
@@ -6635,8 +6634,7 @@ S(
"une petite partie du plan hyperbolique peut être représenté de cette manière, donc ils ne sont "
"pas pratiques pour jouer à HyperRogue ; cependant la représentation s'étend loin dans des directions, "
"spécifiées, donc augmenter d'abord la distance de vue dans la configuration graphique et taille de "
"texture dans les réglages du tapis hypersan peut augmenter l'effet. Vous pouvez aussi choisir d'autres "
"modèles 3D depuis ce menu."
"texture dans les réglages du tapis hypersan peut augmenter l'effet."
)
// coverage
+2 -4
View File
@@ -6737,8 +6737,7 @@ S(
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu.",
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect.",
"W tym trybie możesz wybrać spośród kilku znanych powierzchni o stałej ujemnej krzywiźnie. "
"Krzywizna płaszczyzny hiperbolicznej jest również stała ujemna, także powierzchnie te "
@@ -6746,8 +6745,7 @@ S(
"płaszczyzny hiperbolicznej może być pokazana w ten sposób, także nie nadają się "
"one zbyt dobrze do grania w HyperRogue; pokrycie zato wybiega daleko w określonych kierunkach, "
"także powiększenie pola widzenia w konfiguracji grafiki oraz rozmiaru tekstury w ustawieniach "
"hiperskiego dywanu może poprawić efekt. Dla wygody możesz również wybrać inne tryby 3D "
"z tego menu."
"hiperskiego dywanu może poprawić efekt."
)
// coverage
+2 -4
View File
@@ -7014,8 +7014,7 @@ S(
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu.",
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect.",
"В этом меню можно выбрать одну их нескольких известных гладких поверхностей постоянной отрицательной кривизны. "
"Так как кривизна гиперболической плоскости также постоянна и отрицательна, на таких поверхностях "
@@ -7023,8 +7022,7 @@ S(
"гиперболической плоскости будет при этом видна, так что играть на них "
"в HyperRogue неудобно; однако покрытие распространяется далеко в определённых направлениях, "
"увеличение дальности обзора в настройках графики и размера текстуры "
"в настройках гиперсидского ковра могут помочь. Для удобства можно выбрать другую "
"модель из этого меню."
"в настройках гиперсидского ковра могут помочь."
)
// coverage
+2 -3
View File
@@ -6040,10 +6040,9 @@ S(
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu.",
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect.",
"在该菜单中你可以选择多种负曲率的已知光滑曲面。由于双曲面的曲率也是负常数,这些曲面很适合用于绘制双曲密铺。虽然看起来不错,但只有一些双曲面能以这种方式表示出来,并不适合游玩HyperRogue;不过,在某些特定方向的覆盖会延伸很远,因此在图形设置中增加视野范围并在超波斯地毯设置中增加纹理尺寸可能会提升效果。出于方便考虑,你也可以在该菜单选择其他3D模型。"
"在该菜单中你可以选择多种负曲率的已知光滑曲面。由于双曲面的曲率也是负常数,这些曲面很适合用于绘制双曲密铺。虽然看起来不错,但只有一些双曲面能以这种方式表示出来,并不适合游玩HyperRogue;不过,在某些特定方向的覆盖会延伸很远,因此在图形设置中增加视野范围并在超波斯地毯设置中增加纹理尺寸可能会提升效果。"
)
// coverage
+267 -223
View File
@@ -32,6 +32,7 @@ bool computed = false;
#if HDR
struct edge {
struct rugpoint *target;
struct rugpoint *os, *ot;
ld len;
};
@@ -282,10 +283,10 @@ EX rugpoint *findOrAddRugpoint(shiftpoint h, double dist) {
return r ? r : addRugpoint(h, dist);
}
void addNewEdge(rugpoint *e1, rugpoint *e2, ld len = 1) {
void addNewEdge(rugpoint *e1, rugpoint *e2, rugpoint *os, rugpoint *ot, ld len = 1) {
edge e; e.len = len;
e.target = e2; e1->edges.push_back(e);
e.target = e1; e2->edges.push_back(e);
e.target = e2; e.os = os; e.ot = ot; e1->edges.push_back(e);
e.target = e1; e.os = ot; e.ot = os; e2->edges.push_back(e);
}
EX bool edge_exists(rugpoint *e1, rugpoint *e2) {
@@ -295,9 +296,9 @@ EX bool edge_exists(rugpoint *e1, rugpoint *e2) {
return false;
}
void addEdge(rugpoint *e1, rugpoint *e2, ld len = 1) {
void addEdge(rugpoint *e1, rugpoint *e2, rugpoint *os, rugpoint *ot, ld len = 1) {
if(!edge_exists(e1, e2))
addNewEdge(e1, e2, len);
addNewEdge(e1, e2, os, ot, len);
}
void add_anticusp_edge(rugpoint *e1, rugpoint *e2, ld len = 1) {
@@ -309,9 +310,16 @@ void add_anticusp_edge(rugpoint *e1, rugpoint *e2, ld len = 1) {
}
EX void addTriangle(rugpoint *t1, rugpoint *t2, rugpoint *t3, ld len IS(1)) {
addEdge(t1->getglue(), t2->getglue(), len);
addEdge(t2->getglue(), t3->getglue(), len);
addEdge(t3->getglue(), t1->getglue(), len);
addEdge(t1->getglue(), t2->getglue(), t1, t2, len);
addEdge(t2->getglue(), t3->getglue(), t2, t3, len);
addEdge(t3->getglue(), t1->getglue(), t3, t1, len);
triangles.push_back(triangle(t1,t2,t3));
}
EX void addTriangleCalc(rugpoint *t1, rugpoint *t2, rugpoint *t3) {
addEdge(t1->getglue(), t2->getglue(), t1, t2, hdist(t1->h, t2->h) * modelscale);
addEdge(t2->getglue(), t3->getglue(), t2, t3, hdist(t2->h, t3->h) * modelscale);
addEdge(t3->getglue(), t1->getglue(), t3, t1, hdist(t3->h, t1->h) * modelscale);
triangles.push_back(triangle(t1,t2,t3));
}
@@ -427,7 +435,7 @@ ld clifford_torus::compute_mx() {
return mx;
}
#if MAXMDIM >= 4
#if MAXMDIM >= 4 && CAP_SURFACE
EX void buildTorusRug() {
calcparam_rug();
@@ -448,6 +456,7 @@ EX void buildTorusRug() {
hyperpoint onscreen;
shiftpoint h1 = gmatrix[gs] * ct.torus_to_actual(h);
applymodel(h1, onscreen);
r->h = h1;
r->x1 = onscreen[0];
r->y1 = onscreen[1];
@@ -507,8 +516,8 @@ EX void buildTorusRug() {
for(int yy=0; yy<rugmax; yy++)
for(int xx=0; xx<rugmax; xx++)
addTriangle(rugarr[yy][xx], rugarr[yy+1][xx], rugarr[yy+1][xx+1], modelscale/rugmax),
addTriangle(rugarr[yy][xx+1], rugarr[yy][xx], rugarr[yy+1][xx+1], modelscale/rugmax);
addTriangleCalc(rugarr[yy][xx], rugarr[yy+1][xx], rugarr[yy+1][xx+1]),
addTriangleCalc(rugarr[yy][xx+1], rugarr[yy][xx], rugarr[yy+1][xx+1]);
}
double maxz = 0;
@@ -556,12 +565,16 @@ void comp(cell*& minimum, cell *next) {
minimum = next;
}
EX bool torus_available() {
return euclid && closed_manifold;
}
EX void buildRug() {
need_mouseh = true;
good_shape = false;
#if MAXMDIM >= 4
if(euclid && closed_manifold) {
#if MAXMDIM >= 4 && CAP_SURFACE
if(surface::sh == surface::dsTorus) {
good_shape = true;
buildTorusRug();
return;
@@ -1200,8 +1213,7 @@ EX void init_model() {
precision_increases = 0;
#if CAP_CRYSTAL
if(cryst && surface::sh == surface::dsNone) {
surface::sh = surface::dsCrystal;
if(cryst && surface::sh == surface::dsCrystal) {
crystal::init_rotation();
good_shape = true;
return;
@@ -1272,47 +1284,18 @@ ld protractor = 0;
#define CAP_HOLDKEYS (CAP_SDL && !ISWEB)
EX bool handlekeys(int sym, int uni) {
USING_NATIVE_GEOMETRY;
if(NUMBERKEY == '2') {
ld bdist = 1e12;
if(finger_center)
finger_center = NULL;
else {
for(auto p: points) {
ld cdist = hdist(p->getglue()->h, mouseh);
if(cdist < bdist)
bdist = cdist, finger_center = p->getglue();
}
EX void activate_finger() {
ld bdist = 1e12;
if(finger_center)
finger_center = NULL;
else {
for(auto p: points) {
ld cdist = hdist(p->getglue()->h, mouseh);
if(cdist < bdist)
bdist = cdist, finger_center = p->getglue();
}
if(renderonce) renderlate+=10;
return true;
}
else if(NUMBERKEY == '3') {
#if CAP_CRYSTAL
if(in_crystal())
crystal::switch_z_coordinate();
else
#endif
rotate_view(cspin180(0, 2));
return true;
}
else if(NUMBERKEY == '4') {
#if CAP_CRYSTAL
if(in_crystal())
crystal::flip_z();
else
#endif
rotate_view(cspin90(0, 2));
return true;
}
#if CAP_CRYSTAL
else if(sym == SDLK_HOME && in_crystal()) {
crystal::next_home_orientation();
return true;
}
#endif
else return false;
if(renderonce) renderlate+=10;
}
EX void finger_on(int coord, ld val) {
@@ -1374,8 +1357,10 @@ EX void actDraw() {
int besti;
#if HDR
static constexpr ld RADAR_INF = 1e12;
ld radar_distance = RADAR_INF;
#endif
EX ld radar_distance = RADAR_INF;
EX shiftpoint gethyper(ld x, ld y) {
@@ -1552,195 +1537,254 @@ EX void rug_geometry_choice() {
dialog::display();
}
EX void show() {
EX void init_rug_page(const string& s) {
cmode = sm::SIDE | sm::MAYDARK | sm::PANNING;
gamescreen();
dialog::init(XLAT("hypersian rug mode"), iinf[itPalace].color, 150, 100);
dialog::addBoolItem(XLAT("enable the Hypersian Rug mode"), rug::rugged, 'u');
dialog::addBoolItem(XLAT("render the texture only once"), (renderonce), 'o');
#if CAP_SDL
dialog::addBoolItem(XLAT("render texture without OpenGL"), (rendernogl), 'g');
#else
rendernogl = false;
#endif
dialog::addSelItem(XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's');
dialog::init(s, iinf[itPalace].color, 150, 100);
}
dialog::addSelItem(XLAT("vertex limit"), its(vertex_limit), 'v');
if(rug::rugged)
dialog::lastItem().value += " (" + its(qvalid) + ")";
dialog::addSelItem(XLAT("model distance"), fts(model_distance), 'd');
if(rug::rugged) {
dialog::addSelItem(XLAT("projection"), models::get_model_name(rconf.model), 'p');
}
else dialog::addBreak(100);
EX void close_rug_page() {
dialog::addBreak(50);
dialog::addBack();
dialog::display();
}
EX void show_hyperrug_settings() {
init_rug_page("Hypersian Rug");
#if CAP_SURFACE
surface::shape_option(surface::dsHypersian, 'u', false);
#else
dialog::addBoolItem(XLAT("enable"), rug::rugged, 'u');
dialog::add_action([] {
if(rug::rugged) rug::close();
else rug::init();
});
#endif
if(!rug::rugged) {
dynamicval<eGeometry> g(geometry, gwhere);
dialog::addSelItem(XLAT("native geometry"), geometry_name(), 'n');
if(gwhere == rgElliptic) dialog::lastItem().value += " (e)";
#if MAXMDIM >= 4
dialog::add_action([] { if(!rug::rugged) pushScreen(rug_geometry_choice); });
#endif
}
else
dialog::addSelItem(XLAT("radar"), radar_distance == RADAR_INF ? "∞" : fts(radar_distance, 4), 'r');
dialog::addSelItem(XLAT("model scale factor"), fts(modelscale), 'm');
else dialog::addBreak(100);
dialog::addSelItem(XLAT("maximum error"), fts(err_zero), 'e');
if(rug::rugged)
dialog::lastItem().value += " (" + fts(err_zero_current) + ")";
dialog::add_action([] {
if(good_shape) good_shape = false;
dialog::editNumber(err_zero, 1e-9, 1, .1, 1e-3, XLAT("maximum error"),
XLAT("New points are added when the current error in the model is smaller than this value.")
);
dialog::scaleLog();
dialog::get_di().reaction = [] () { err_zero_current = err_zero; };
});
if(rug::rugged)
dialog::addSelItem(XLAT("model iterations"), its(queueiter), 0);
dialog::addItem(XLAT("stereo vision config"), 'f');
// dialog::addSelItem(XLAT("protractor"), fts(protractor / degree) + "°", 'f');
if(!good_shape) {
dialog::addSelItem(XLAT("maximum error"), fts(err_zero), 'e');
if(rug::rugged)
dialog::lastItem().value += " (" + fts(err_zero_current) + ")";
}
#if ISMOBILE
dialog::addSelItem(XLAT("move on touch"), fts(move_on_touch), 'G');
#endif
dialog::addSelItem(XLAT("anti-crossing"), fts(anticusp_factor), 'A');
dialog::addBoolItem(XLAT("3D monsters/walls on the surface"), spatial_rug, 'S');
dialog::add_action([] () { spatial_rug = !spatial_rug; });
edit_levellines('L');
dialog::add_action([] {
dialog::editNumber(anticusp_factor, 0, 1.5, .1, 0, XLAT("anti-crossing"),
XLAT("The anti-crossing algorithm prevents the model from crossing itself, "
"by preventing points which should not be close from being close. "
"The bigger number, the more sensitive it is, but the embedding is slower. Set 0 to disable.")
);
});
#if CAP_SURFACE
if(hyperbolic)
dialog::addItem(XLAT("smooth surfaces"), 'c');
else
dialog::addBreak(100);
#endif
dialog::addBreak(50);
dialog::addHelp();
dialog::addBack();
dialog::add_action([] { gotoHelp(makehelp()); });
dialog::display();
keyhandler = [] (int sym, int uni) {
handlePanning(sym, uni);
dialog::handleNavigation(sym, uni);
close_rug_page();
}
if(uni == 'h' || uni == SDLK_F1) gotoHelp(makehelp());
else if(uni == 'u') {
if(rug::rugged) rug::close();
else {
#if CAP_SURFACE
surface::sh = surface::dsNone;
#endif
rug::init();
EX void show_torus_settings() {
init_rug_page("conformal torus");
#if CAP_SURFACE
surface::shape_option(surface::dsTorus, 'u', false);
#else
dialog::addBoolItem(XLAT("enable"), rug::rugged, 'u');
dialog::add_action([] {
if(rug::rugged) rug::close();
else rug::init();
});
#endif
if(!rug::rugged) {
dynamicval<eGeometry> g(geometry, gwhere);
dialog::addSelItem(XLAT("native geometry"), geometry_name(), 'n');
if(gwhere == rgElliptic) dialog::lastItem().value += " (e)";
#if MAXMDIM >= 4
dialog::add_action([] { if(!rug::rugged) pushScreen(rug_geometry_choice); });
#endif
}
else dialog::addBreak(100);
close_rug_page();
}
EX void show_crystal_settings() {
init_rug_page("crystal viewer");
#if CAP_SURFACE
surface::shape_option(surface::dsCrystal, 'u', false);
#endif
if(rug::in_crystal()) {
dialog::addItem("next home orientation", 'h');
dialog::add_action(crystal::next_home_orientation);
dialog::addItem("switch Z coordinate", 's');
dialog::add_action(crystal::switch_z_coordinate);
dialog::addItem("flip Z coordinate", 'f');
dialog::add_action(crystal::flip_z);
}
rug::close_rug_page();
}
EX void show_gfx_settings() {
init_rug_page("rug mode settings");
dialog::addBoolItem_action(XLAT("render the texture only once"), (renderonce), 'o');
#if CAP_SDL
if(!rug::rugged) dialog::addBoolItem_action(XLAT("render texture without OpenGL"), (rendernogl), 'g');
#else
rendernogl = false;
#endif
dialog::addSelItem(XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's');
dialog::add_action([] {
texturesize *= 2; if(texturesize == 8192) texturesize = 64;
change_texturesize();
});
dialog::addSelItem(XLAT("vertex limit"), its(vertex_limit), 'v');
if(rug::rugged)
dialog::lastItem().value += " (" + its(qvalid) + ")";
dialog::add_action([] {
dialog::editNumber(vertex_limit, 0, 50000, 500, 3000, ("vertex limit"),
XLAT("The more vertices, the more accurate the Hypersian Rug model is. "
"However, a number too high might make the model slow to compute and render.")
);
dialog::get_ne().reaction = [] () { err_zero_current = err_zero; };
});
dialog::addSelItem(XLAT("model distance"), fts(model_distance), 'd');
dialog::add_action([] {
dialog::editNumber(model_distance, -10, 10, .1, 1, XLAT("model distance"),
XLAT("In the perspective projection, this sets the distance from the camera to the center of the model. "
"In the orthogonal projection this just controls the scale.")
);
old_distance = model_distance;
dialog::get_di().reaction = [] () {
if(rug::rugged && perspective()) {
using_rugview rv;
shift_view(ztangent(old_distance - model_distance));
}
}
else if(uni == 'R')
dialog::editNumber(finger_range, 0, 1, .01, .1, XLAT("finger range"),
XLAT("Press 1 to enable the finger mode.")
);
else if(uni == 'F')
dialog::editNumber(finger_force, 0, 1, .01, .1, XLAT("finger force"),
XLAT("Press 1 to enable the finger force.")
);
else if(uni == 'o')
renderonce = !renderonce;
else if(uni == 'G') {
dialog::editNumber(move_on_touch, -1, 1, .1, 0, XLAT("move on touch"), "");
dialog::get_ne().extra_options = anims::rug_angle_options;
}
else if(uni == 'A') {
dialog::editNumber(anticusp_factor, 0, 1.5, .1, 0, XLAT("anti-crossing"),
XLAT("The anti-crossing algorithm prevents the model from crossing itself, "
"by preventing points which should not be close from being close. "
"The bigger number, the more sensitive it is, but the embedding is slower. Set 0 to disable.")
);
}
else if(uni == 'v') {
dialog::editNumber(vertex_limit, 0, 50000, 500, 3000, ("vertex limit"),
XLAT("The more vertices, the more accurate the Hypersian Rug model is. "
"However, a number too high might make the model slow to compute and render.")
);
dialog::get_ne().reaction = [] () { err_zero_current = err_zero; };
}
else if(uni == 'r')
addMessage(XLAT("This just shows the 'z' coordinate of the selected point."));
else if(uni == 'm') {
dialog::editNumber(modelscale, 0.1, 10, rugged ? .01 : .1, 1, XLAT("model scale factor"),
XLAT("This is relevant when the native geometry is not Euclidean. "
"For example, if the native geometry is spherical, and scale < 1, a 2d sphere will be rendered as a subsphere; "
"if the native geometry is hyperbolic, and scale > 1, a hyperbolic plane will be rendered as an equidistant surface. ")
);
dialog::scaleLog();
if(rug::rugged) {
static bool adjust_points = true;
static bool camera_center = false;
static bool adjust_edges = true;
static bool adjust_distance = true;
static ld last;
last = modelscale;
dialog::get_ne().extra_options = [] () {
dialog::addBoolItem_action(XLAT("adjust points"), adjust_points, 'P');
if(adjust_points)
dialog::addBoolItem_action(XLAT("center on camera"), camera_center, 'C');
else
dialog::addBreak(100);
dialog::addBoolItem_action(XLAT("adjust edges"), adjust_edges, 'E');
dialog::addBoolItem_action(XLAT("adjust distance"), adjust_distance, 'D');
};
dialog::get_ne().reaction = [] () {
if(!last || !modelscale) return;
if(!camera_center) push_all_points(2, model_distance);
for(auto p:points) {
if(adjust_edges) for(auto& e: p->edges) e.len *= modelscale / last;
if(adjust_points) p->native *= modelscale / last;
enqueue(p);
}
if(adjust_distance) model_distance = model_distance * modelscale / last;
last = modelscale;
good_shape = false;
if(!camera_center) push_all_points(2, -model_distance);
};
}
}
else if(uni == 'p') {
pushScreen(models::model_menu);
}
else if(uni == 'd') {
dialog::editNumber(model_distance, -10, 10, .1, 1, XLAT("model distance"),
XLAT("In the perspective projection, this sets the distance from the camera to the center of the model. "
"In the orthogonal projection this just controls the scale.")
);
old_distance = model_distance;
dialog::get_di().reaction = [] () {
if(rug::rugged && perspective()) {
using_rugview rv;
shift_view(ztangent(old_distance - model_distance));
};
});
dialog::addSelItem(XLAT("model scale factor"), fts(modelscale), 'm');
dialog::add_action([] {
dialog::editNumber(modelscale, 0.1, 10, rugged ? .01 : .1, 1, XLAT("model scale factor"),
XLAT("This is relevant when the native geometry is not Euclidean. "
"For example, if the native geometry is spherical, and scale < 1, a 2d sphere will be rendered as a subsphere; "
"if the native geometry is hyperbolic, and scale > 1, a hyperbolic plane will be rendered as an equidistant surface. ")
);
dialog::scaleLog();
if(rug::rugged) {
static bool adjust_points = true;
static bool camera_center = false;
static bool adjust_edges = true;
static bool adjust_distance = true;
static ld last;
last = modelscale;
dialog::get_ne().extra_options = [] () {
dialog::addBoolItem_action(XLAT("adjust points"), adjust_points, 'P');
if(adjust_points)
dialog::addBoolItem_action(XLAT("center on camera"), camera_center, 'C');
else
dialog::addBreak(100);
dialog::addBoolItem_action(XLAT("adjust edges"), adjust_edges, 'E');
dialog::addBoolItem_action(XLAT("adjust distance"), adjust_distance, 'D');
};
dialog::get_ne().reaction = [] () {
if(!last || !modelscale) return;
if(!camera_center) push_all_points(2, model_distance);
for(auto p:points) {
if(adjust_edges) for(auto& e: p->edges) e.len *= modelscale / last;
if(adjust_points) p->native *= modelscale / last;
enqueue(p);
}
old_distance = model_distance;
if(adjust_distance) model_distance = model_distance * modelscale / last;
last = modelscale;
good_shape = false;
if(!camera_center) push_all_points(2, -model_distance);
};
}
else if(uni == 'e') {
dialog::editNumber(err_zero, 1e-9, 1, .1, 1e-3, XLAT("maximum error"),
XLAT("New points are added when the current error in the model is smaller than this value.")
);
dialog::scaleLog();
dialog::get_di().reaction = [] () { err_zero_current = err_zero; };
}
else if(uni == 'f')
pushScreen(showStereo);
#if MAXMDIM >= 4
else if(uni == 'n' && !rug::rugged)
pushScreen(rug_geometry_choice);
});
if(rug::rugged) {
dialog::addSelItem(XLAT("projection"), models::get_model_name(rconf.model), 'p');
dialog::add_action_push(models::model_menu);
}
else dialog::addBreak(100);
dialog::addItem(XLAT("stereo vision config"), 'f');
dialog::add_action_push(showStereo);
#if ISMOBILE
dialog::addSelItem(XLAT("move on touch"), fts(move_on_touch), 'G');
dialog::add_action([] {
dialog::editNumber(move_on_touch, -1, 1, .1, 0, XLAT("move on touch"), "");
dialog::get_ne().extra_options = anims::rug_angle_options;
});
#endif
dialog::addBoolItem_action(XLAT("3D monsters/walls on the surface"), spatial_rug, 'S');
edit_levellines('L');
close_rug_page();
}
EX void show() {
init_rug_page("hypersian rug mode");
#if CAP_SURFACE
if(!rug::rugged && torus_available()) surface::sh = surface::dsTorus;
surface::shape_option(surface::sh, 'u');
dialog::addBreak(100);
#endif
dialog::addItem("Hypersian Rug settings", 'r');
dialog::add_action_push(show_hyperrug_settings);
if(torus_available()) {
dialog::addItem("torus settings", 't');
dialog::add_action_push(show_torus_settings);
}
if(cryst) {
dialog::addItem("Crystal settings", 'C');
dialog::add_action_push(show_crystal_settings);
}
if(hyperbolic) {
#if CAP_SURFACE
dialog::addItem("constant curvature surfaces", 'c');
dialog::add_action_push(surface::show_smooth);
dialog::addItem("Hilbert Rug", 'i');
dialog::add_action_push(surface::show_hilbert);
#endif
#if CAP_SDL
else if(uni == 'g' && !rug::rugged)
rendernogl = !rendernogl;
#endif
else if(uni == 's') {
texturesize *= 2;
if(texturesize == 8192) texturesize = 64;
change_texturesize();
}
#if CAP_SURFACE
else if(uni == 'c')
pushScreen(surface::show_surfaces);
#endif
else if(handlekeys(sym, uni)) ;
else if(doexiton(sym, uni)) popScreen();
};
}
dialog::addItem("general settings", 'g');
dialog::add_action_push(show_gfx_settings);
#if CAP_SURFACE
dialog::addItem("other features", 'o');
dialog::add_action_push(surface::show_other);
#endif
dialog::display();
}
EX void select() {
+304 -114
View File
@@ -16,11 +16,11 @@ EX namespace surface {
ld sech(ld d) { return 1 / cosh(d); }
#if HDR
enum eShape { dsNone, dsTractricoid, dsDini, dsKuen, dsHyperlike, dsHyperboloid, dsHemisphere, dsCrystal };
enum eShape { dsHypersian, dsTorus, dsTractricoid, dsDini, dsKuen, dsHyperlike, dsHyperboloid, dsHemisphere, dsCrystal, dsHilbert };
#endif
string shape_name[] = { "hypersian rug", "tractricoid", "Dini's surface", "Kuen surface", "concave barrel",
"hyperboloid", "hemisphere", "crystal" };
string shape_name[] = { "hypersian rug", "conformal torus", "tractricoid", "Dini's surface", "Kuen surface", "concave barrel",
"hyperboloid", "hemisphere", "crystal", "Hilbert rug" };
EX eShape sh;
@@ -367,7 +367,8 @@ int dexp_comb_colors[16] = {
0x123456, 0x123456, 0x123456, 0x123456, 0x123456, 0x123456, 0x123456
};
int coverage_style;
EX int coverage_style;
EX bool approx_quality;
EX vector<pair<hyperpoint, int> > coverage;
#if CAP_KUEN_MAP
@@ -409,7 +410,7 @@ void draw_kuen_map() {
}
#endif
void full_mesh() {
EX void full_mesh() {
rug::clear_model();
rug::buildRug();
rug::qvalid = 0;
@@ -596,21 +597,24 @@ void run_other() {
}
EX void run_shape(eShape s) {
if(s == sh && rug::rugged) { rug::close(); return; }
coverage.clear();
need_mouseh = true;
sh = s;
if(rug::rugged) rug::close();
rug::gwhere = rug::rgEuclid;
if(!among(s, dsHypersian, dsTorus, dsCrystal)) rug::gwhere = rug::rgEuclid;
rug::init();
// if(!rug::rugged) rug::reopen();
pushScreen(history::progress_screen);
if(sh != dsNone) rug::good_shape = true;
if(sh != dsHypersian) rug::good_shape = true;
switch(sh) {
case dsNone:
case dsHypersian:
case dsTorus:
case dsCrystal:
break;
case dsKuen:
@@ -631,16 +635,19 @@ EX void run_shape(eShape s) {
case dsHemisphere:
run_function([] (hyperpoint h) { h = h / h[2]; h[2] = sqrt(1 - sqhypot_d(2, h)); return h; });
break;
break;
case dsHilbert:
run_hilbert();
}
if(sh != dsNone) rug::good_shape = true;
if(sh != dsHypersian) rug::good_shape = true;
rug::qvalid = 0;
popScreen();
if(sh != dsHyperboloid && sh != dsHemisphere && sh != dsNone) {
if(sh != dsHyperboloid && sh != dsHemisphere && sh != dsHypersian) {
ld minx = 1e9, maxx = -1e9;
@@ -660,7 +667,7 @@ EX void run_shape(eShape s) {
void cancel_shape() {
if(sh) {
sh = dsNone;
sh = dsHypersian;
rug::good_shape = false;
rug::qvalid = 0;
for(auto p: rug::points)
@@ -674,108 +681,136 @@ void cancel_shape() {
cell *coverage_center;
transmatrix coverage_matrix;
EX void show_surfaces() {
cmode = sm::SIDE;
gamescreen();
dialog::init(XLAT("constant curvature surfaces"), iinf[itPalace].color, 150, 0);
bool b = rug::rugged || coverage_style;
// approximations
dialog::addBoolItem(XLAT("tractricoid"), b && sh == dsTractricoid, '1');
dialog::addBoolItem(XLAT("concave barrel"), b && sh == dsHyperlike, '2');
dialog::addSelItem(" " + XLAT("parameter"), fts(hyper_b), '@');
dialog::addBoolItem(XLAT("Dini's surface"), b && sh == dsDini, '3');
dialog::addSelItem(" " + XLAT("parameter") + " ", fts(dini_b), '#');
dialog::addBoolItem(XLAT("Kuen surface"), b && sh == dsKuen, '4');
dialog::addBreak(50);
dialog::addTitle(XLAT("other 3D models"), iinf[itPalace].color, 150);
hyperpoint hilbert_at(hyperpoint s, hyperpoint e, hyperpoint d, hyperpoint as, hyperpoint ae, ld steps, ld t) {
hyperpoint ctr = (e+d)/2;
hyperpoint f = e+(d-s);
if(steps == 0) return as * (1-t) * (1-t) + ae * t * t + ctr * (1 - t*t - (1-t) * (1-t));
if(steps < 1) return lerp(hilbert_at(s, e, d, as, ae, 0, t), hilbert_at(s, e, d, as, ae, 1, t), steps);
t *= 4;
int ti = int(t);
if(ti == 4) ti = 3;
t -= ti;
if(ti == 0) return hilbert_at(s, (s+d)/2, (s+e)/2, (s+as)/2, (s+d+e+d)/4, steps-1, t);
if(ti == 1) return hilbert_at((s+d)/2, ctr, d, (s+d+e+d)/4, (e+d+d+f)/4, steps-1, t);
if(ti == 2) return hilbert_at(ctr, (s+d)/2 + (e-s), d + (e-s)/2, (e+d+d+f)/4, (e+d+f+e)/4, steps-1, t);
if(ti == 3) return hilbert_at((s+d)/2 + (e-s), e, ctr, (e+d+f+e)/4, (e+ae)/2, steps-1, t);
throw hr_exception("hilbert_at error");
}
dialog::addBoolItem(XLAT("Hypersian Rug"), b && sh == dsNone, '5');
dialog::addBoolItem(XLAT("Minkowski hyperboloid"), b && sh == dsHyperboloid, '6');
dialog::addBoolItem(XLAT("hemisphere"), b && sh == dsHemisphere, '7');
ld approx_length_at(ld s) {
if(s < 0.5) s -= (s - 0.5) / 2;
return exp(0.5) * pow(2, s);;
}
dialog::addBreak(100);
ld approx_length_to_s(ld len) {
ld s = log(len / exp(0.5)) / log(2);
if(s < 0.5) s += (s - 0.5);
return s;
}
bool circular = false;
int qty = 7;
ld hscale = 1;
EX void run_hilbert() {
full_mesh();
rug::good_shape = true;
rug::qvalid = 0;
for(auto p: rug::points) {
hyperpoint& pt = p->native;
hyperpoint h = p->h.h;
if(circular) {
ld r = hdist0(h);
ld phi = atan2(h);
ld length_factor = r == 0 ? 1 : sinh(r) / r;
ld s = approx_length_to_s((approx_length_at(0.0) * length_factor));
ld t = phi * qty / TAU;
ld tz = floor(t);
ld x0 = -1 + 2 * tz;
ld x1 = +1 + 2 * tz;
hyperpoint h = hilbert_at(point3(x0,-1,1), point3(x1,-1,1), point3(x0,1,1), point3(x0,0,1), point3(x1,0,1), s, t-tz);
h *= TAU / (2 * qty);
pt = cspin(0, 1, h[0]) * cspin(1, 2, h[1]) * point3(0, 1, 0) * r;
}
else {
ld y = asin_auto(h[1]);
ld x = asin_auto_clamp(h[0] / cos_auto(y));
ld length_factor = cos_auto(y);
ld s = approx_length_to_s((approx_length_at(0.5) * length_factor));
ld t = x / approx_length_at(0.5) / hscale + .5;
ld tz = floor(t);
ld x0 = -1 + 2 * tz;
ld x1 = +1 + 2 * tz;
pt = hilbert_at(point3(x0,-1,1), point3(x1,-1,1), point3(x0,1,1), point3(x0,0,1), point3(x1,0,1), s, t-tz);
pt[0] *= hscale;
pt[1] *= hscale;
pt[2] = y;
}
pt *= rug::modelscale;
pt[3] = 1;
p->valid = true;
}
}
// menu
EX void shape_option(eShape s, char c, bool named IS(true)) {
bool b = rug::rugged || coverage_style || approx_quality;
dialog::addBoolItem(named ? XLAT(shape_name[s]) : XLAT("enable"), b && sh == s, c);
dialog::add_action([s] { run_shape(s); });
}
EX void show_smooth() {
rug::init_rug_page(XLAT("constant curvature surfaces"));
shape_option(dsTractricoid, 't');
shape_option(dsHyperlike, 'b');
dialog::addSelItem(" " + XLAT("parameter"), fts(hyper_b), 'B');
dialog::add_action([] {
dialog::editNumber(hyper_b, -1, 1, .05, 1, XLAT("parameter"),
XLAT("Controls the inner radius.")
);
dialog::get_ne().reaction = [] () {
if(sh == dsHyperlike) run_shape(sh);
};
});
shape_option(dsDini, 'd');
dialog::addSelItem(" " + XLAT("parameter") + " ", fts(dini_b), 'D');
dialog::add_action([] {
dialog::editNumber(dini_b, -1, 1, .05, .15, XLAT("parameter"),
XLAT("The larger the number, the more twisted it is.")
);
});
shape_option(dsKuen, 'k');
dialog::addSelItem(XLAT("precision"), its(precision), 'p');
dialog::add_action([] {
dialog::editNumber(precision, 1, 10000, 0, 100, XLAT("precision"),
XLAT("Computing these models involves integrals and differential equations, which are currently solved numerically. This controls the precision.")
);
dialog::get_ne().step = .1;
dialog::scaleLog();
});
string cstyles[4] = { "OFF", "on surface", "on H² (static)", "on H² (dynamic)" };
if((rug::rugged && sh && sh != dsHyperboloid && sh != dsHemisphere) || coverage_style)
if((rug::rugged && sh && sh != dsHyperboloid && sh != dsHemisphere) || coverage_style) {
dialog::addSelItem(XLAT("display coverage"), cstyles[coverage_style], 'c');
else dialog::addBreak(100);
#if CAP_FILES
if(rug::rugged)
dialog::addItem(XLAT("save the current embedding"), 's');
else
dialog::addItem(XLAT("load a saved embedding"), 's');
#endif
dialog::addHelp();
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'h' || uni == SDLK_F1)
gotoHelp(XLAT(
"In this menu you can choose from several known smooth surfaces of constant negative curvature. "
"Since the curvature of hyperbolic plane is also constant negative, these surfaces "
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect. "
"For convenience, you can also choose other 3D models from this menu."
));
#if CAP_FILES
else if(uni == 's') {
static string rugname = "saved.rug";
if(rug::rugged)
dialog::openFileDialog(rugname, XLAT("save embedding to:"), ".rug", [] () { rug::rug_save(rugname); return true; });
else
dialog::openFileDialog(rugname, XLAT("load embedding from:"), ".rug", [] () { rug::init(); rug::rug_load(rugname); return true; });
}
#endif
else if(uni == '1')
run_shape(dsTractricoid);
else if(uni == '2')
run_shape(dsHyperlike);
else if(uni == '3')
run_shape(dsDini);
else if(uni == '4')
run_shape(dsKuen);
else if(uni == '5')
run_shape(dsNone);
else if(uni == '6')
run_shape(dsHyperboloid);
else if(uni == '7')
run_shape(dsHemisphere);
else if(uni == '@') {
dialog::editNumber(hyper_b, -1, 1, .05, 1, XLAT("parameter"),
XLAT("Controls the inner radius.")
);
dialog::get_ne().reaction = [] () {
if(sh == dsHyperlike) run_shape(sh);
};
}
else if(uni == 'x')
for(auto p: rug::points)
p->native = p->surface_point.params;
else if(uni == '#')
dialog::editNumber(dini_b, -1, 1, .05, .15, XLAT("parameter"),
XLAT("The larger the number, the more twisted it is.")
);
else if(uni == 'p') {
dialog::editNumber(precision, 1, 10000, 0, 100, XLAT("precision"),
XLAT("Computing these models involves integrals and differential equations, which are currently solved numerically. This controls the precision.")
);
dialog::get_ne().step = .1;
dialog::scaleLog();
}
else if(uni == 'c') {
dialog::add_action([] {
coverage_style = (1 + coverage_style) % 4;
if(coverage_style == 0) {
rug::reopen();
@@ -784,10 +819,135 @@ EX void show_surfaces() {
if(rug::rugged) rug::close();
}
coverage_matrix = inverse(unshift(ggmatrix(coverage_center = cwt.at)));
});
}
else dialog::addBreak(100);
dialog::addHelp();
dialog::add_action([] {
gotoHelp(XLAT(
"In this menu you can choose from several known smooth surfaces of constant negative curvature. "
"Since the curvature of hyperbolic plane is also constant negative, these surfaces "
"are great to draw hyperbolic tesselations on. While they look great, only a small part "
"of the hyperbolic plane can be visibly represented in this way, so they are not "
"good for playing HyperRogue; however, the coverage extends far away in specific directions, "
"so first increasing sight range in graphics config and texture size in hypersian rug settings may improve the effect."
));
});
rug::close_rug_page();
}
EX void display_approx() {
dialog::addBoolItem_action(XLAT("approximation quality"), approx_quality, 'A');
dialog::addCustom(50, [] {
if(approx_quality) {
int mid = (dialog::top + dialog::tothei) / 2;
int siz = vid.fsize / 2;
for(int s=0; s<=10; s++) {
ld r = exp(lerp(log(v_approx_min), log(v_approx_max), s/10.));
color_t col = rainbow_color(1, s / 10.);
displaystr(dialog::dcenter + (dialog::dialogwidth - 3 * vid.fsize) * lerp(-0.5, 0.5, s/10.), mid, 0, siz, format("%.3f", r), col, 8);
}
}
else if(rug::handlekeys(sym, uni)) ;
else if(doexiton(sym, uni)) popScreen();
};
});
if(approx_quality) {
dialog::addSelItem(XLAT("set scale to"), format("%lf..%lf", approx_min, approx_max), 'S');
dialog::add_action([] { v_approx_max = approx_max; v_approx_min = approx_min; });
}
else dialog::addBreak(100);
}
EX void show_hilbert() {
rug::init_rug_page(XLAT("Hilbert Rug"));
shape_option(dsHilbert, 'u', false);
dialog::addBoolItem(XLAT("circular mode"), circular, 'C');
dialog::add_action([] { circular = !circular; if(sh == dsHilbert) run_shape(dsHilbert); });
if(circular) {
dialog::addSelItem(XLAT("scale"), its(qty), 'Q');
dialog::add_action([] {
dialog::editNumber(qty, 1, 100, 1, 7, XLAT("scale"),
XLAT("The smaller the number, the flatter it is.")
);
dialog::get_ne().reaction = [] { if(sh == dsHilbert) run_shape(dsHilbert); };
});
}
else {
dialog::addSelItem(XLAT("scale"), fts(hscale), 'S');
dialog::add_action([] {
dialog::editNumber(hscale, 1e-3, 1e3, .05, 1, XLAT("scale"),
XLAT("The smaller the number, the flatter it is.")
);
dialog::get_ne().reaction = [] { if(sh == dsHilbert) run_shape(dsHilbert); };
});
}
rug::close_rug_page();
}
string finger_help =
"Press 1 while mouse is over the surface to enable the finger force. In this mode, "
"pressing arrow keys affects the shape of the surface (simulated pulling).";
EX void show_other() {
rug::init_rug_page(XLAT("other features"));
dialog::addTitle(XLAT("other 3D models"), iinf[itPalace].color, 150);
shape_option(dsHyperboloid, 'h');
shape_option(dsHemisphere, 's');
dialog::addBreak(100);
#if CAP_FILES
if(rug::rugged)
dialog::addItem(XLAT("save the current embedding"), 's');
else
dialog::addItem(XLAT("load a saved embedding"), 's');
dialog::add_action([] {
static string rugname = "saved.rug";
if(rug::rugged)
dialog::openFileDialog(rugname, XLAT("save embedding to:"), ".rug", [] () { rug::rug_save(rugname); return true; });
else
dialog::openFileDialog(rugname, XLAT("load embedding from:"), ".rug", [] () { rug::init(); rug::rug_load(rugname); return true; });
});
#endif
if(rug::rugged) {
dialog::addSelItem(XLAT("radar"), rug::radar_distance == rug::RADAR_INF ? "∞" : fts(rug::radar_distance, 4), 'r');
dialog::add_action([] {
addMessage(XLAT("This just shows the 'z' coordinate of the selected point."));
});
}
else dialog::addBreak(100);
dialog::addItem(XLAT("surface_point to native"), 'x');
dialog::add_action([] {
for(auto p: rug::points)
p->native = p->surface_point.params;
});
display_approx();
dialog::add_key_action('1', rug::activate_finger);
dialog::addSelItem(XLAT("finger range"), fts(rug::finger_range), 'R');
dialog::add_action([] {
dialog::editNumber(rug::finger_range, 0, 1, .01, .1, XLAT("finger range"),
XLAT(finger_help)
);
});
dialog::addSelItem(XLAT("finger force"), fts(rug::finger_force), 'F');
dialog::add_action([] {
dialog::editNumber(rug::finger_force, 0, 1, .01, .1, XLAT("finger force"),
XLAT(finger_help)
);
});
rug::close_rug_page();
}
#if CAP_COMMANDLINE
@@ -835,8 +995,10 @@ int surface_args() {
run_shape(dsHyperboloid);
}
else if(argis("-d:surface"))
launch_dialog(show_surfaces);
else if(argis("-d:surface")) {
launch_dialog(rug::show);
launch_dialog(show_smooth);
}
else return 1;
return 0;
@@ -847,7 +1009,12 @@ int surface_args() {
auto surface_hook = addHook(hooks_args, 100, surface_args);
#endif
void display_coverage() {
EX ld approx_min;
EX ld approx_max;
EX ld v_approx_min = 0.5;
EX ld v_approx_max = 1.5;
void display_coverage_and_approx_quality() {
shiftmatrix M =
coverage_style == 3 ? ggmatrix(coverage_center) * coverage_matrix
@@ -858,9 +1025,32 @@ void display_coverage() {
queuestr(M * p.first, 10, s0+char(p.second), dexp_comb_colors[(p.second >> 8) & 15]);
/* if(p->valid && p->surface_point.remaining_distance == 0)
queuestr(p->h, 10, "x", dexp_colors[p->dexp_id]); */
if(approx_quality) {
vid.linewidth *= 4;
approx_min = INF, approx_max = 0;
for(auto p: rug::points) {
for(auto& e: p->edges) {
if(e.os > e.ot) continue;
if(!e.os->valid || !e.ot->valid) continue;
ld d;
if(1) {
USING_NATIVE_GEOMETRY;
d = hdist(e.ot->native, e.os->native);
}
ld ratio = d / e.len;
if(ratio < approx_min) approx_min = ratio;
if(ratio > approx_max) approx_max = ratio;
ld z = log(ratio) / log(v_approx_max / v_approx_min);
color_t col = (rainbow_color(1, z) << 8) | 0xFF;
queueline(e.ot->h, e.os->h, col, 0);
}
}
vid.linewidth /= 4;
}
}
auto surface_hook2 = addHook(hooks_frame, 0, display_coverage);
auto surface_hook2 = addHook(hooks_frame, 0, display_coverage_and_approx_quality);
EX }
#endif