1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-05-16 22:24:07 +00:00

Merge branch 'master' into mymake

This commit is contained in:
Zeno Rogue 2020-05-15 12:14:04 +02:00 committed by GitHub
commit 5ec85100e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
108 changed files with 6543 additions and 4322 deletions

View File

@ -623,16 +623,16 @@ void geometry_information::slimetriangle(hyperpoint a, hyperpoint b, hyperpoint
texture_order([&] (ld x, ld y) { texture_order([&] (ld x, ld y) {
ld z = 1-x-y; ld z = 1-x-y;
ld r = scalefactor * hcrossf7 * (0 + pow(max(x,max(y,z)), .3) * 0.8) * (hybri ? .5 : 1); ld r = scalefactor * hcrossf7 * (0 + pow(max(x,max(y,z)), .3) * 0.8) * (hybri ? .5 : 1);
hyperpoint h = direct_exp(tangent_length(a*x+b*y+c*z, r), 10); hyperpoint h = direct_exp(tangent_length(a*x+b*y+c*z, r));
hpcpush(h); hpcpush(h);
}); });
} }
void geometry_information::balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) { void geometry_information::balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) {
if(lev == 0) { if(lev == 0) {
hpcpush(direct_exp(a, 10)); hpcpush(direct_exp(a));
hpcpush(direct_exp(b, 10)); hpcpush(direct_exp(b));
hpcpush(direct_exp(c, 10)); hpcpush(direct_exp(c));
} }
else { else {
auto midpoint = [&] (hyperpoint h1, hyperpoint h2) { auto midpoint = [&] (hyperpoint h1, hyperpoint h2) {
@ -1254,7 +1254,16 @@ hpcshape& geometry_information::generate_pipe(ld length, ld width) {
} }
} }
last->flags |= POLY_TRIANGLES; for(int a=0; a<MAX_R; a++) {
hpcpush(at(MAX_X, a));
hpcpush(at(MAX_X, a+1));
hpcpush(xpush0(length));
hpcpush(at(MAX_X, a+1));
hpcpush(at(MAX_X, a));
hpcpush(C0);
}
last->flags |= POLY_TRIANGLES | POLY_PRINTABLE;
add_texture(*last); add_texture(*last);
finishshape(); finishshape();
extra_vertices(); extra_vertices();

View File

@ -952,12 +952,12 @@ EX void achievement_display() {
col /= 10; col *= 0x10101; col /= 10; col *= 0x10101;
displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, achievementMessage[0], col & 0xFFFF00, 8); displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, achievementMessage[0], col & 0xFFFF00, 8);
int w = 2 * vid.fsize; int w = 2 * vid.fsize;
#if ISMOBILE==0 #if !ISMOBILE
while(w>3 && textwidth(w, achievementMessage[1]) > vid.xres) w--; while(w>3 && textwidth(w, achievementMessage[1]) > vid.xres) w--;
#endif #endif
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, w, achievementMessage[1], col, 8); displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, w, achievementMessage[1], col, 8);
w = vid.fsize; w = vid.fsize;
#if ISMOBILE==0 #if !ISMOBILE
while(w>3 && textwidth(w, achievementMessage[2]) > vid.xres) w--; while(w>3 && textwidth(w, achievementMessage[2]) > vid.xres) w--;
#endif #endif
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*4, 2, w, achievementMessage[2], col, 8); displayfr(vid.xres/2, vid.yres/4 + vid.fsize*4, 2, w, achievementMessage[2], col, 8);

View File

@ -49,18 +49,74 @@ EX arbi_tiling current;
EX short& id_of(heptagon *h) { return h->zebraval; } EX short& id_of(heptagon *h) { return h->zebraval; }
struct hr_polygon_error : hr_exception {
vector<transmatrix> v;
eGeometryClass c;
int id;
map<string, cld> params;
hr_polygon_error(const vector<transmatrix>& _v, int _id) : v(_v), c(cgclass), id(_id) {}
~hr_polygon_error() noexcept(true) {}
};
struct connection_debug_request : hr_exception {
int id;
eGeometryClass c;
connection_debug_request(int i): id(i), c(cgclass) {}
};
void ensure_geometry(eGeometryClass c) {
stop_game();
if(c != cgclass) {
if(c == gcEuclid) set_geometry(gEuclid);
if(c == gcHyperbolic) set_geometry(gNormal);
if(c == gcSphere) set_geometry(gSphere);
}
if(specialland != laCanvas) {
canvas_default_wall = waInvisibleFloor;
patterns::whichCanvas = 'g';
patterns::canvasback = 0xFFFFFF;
firstland = specialland = laCanvas;
}
start_game();
}
void start_poly_debugger(hr_polygon_error& err) {
ensure_geometry(err.c);
drawthemap();
mapeditor::drawing_tool = true;
pushScreen(mapeditor::showDrawEditor);
mapeditor::initdraw(cwt.at);
int n = isize(err.v);
mapeditor::dtcolor = 0xFF0000FF;
mapeditor::dtwidth = 0.02;
for(int i=0; i<n-1; i++)
mapeditor::dt_add_line(tC0(err.v[i]), tC0(err.v[i+1]), 0);
mapeditor::dtcolor = 0xFFFFFFFF;
for(int i=0; i<n; i++)
mapeditor::dt_add_text(tC0(err.v[i]), 0.5, its(i));
}
void shape::build_from_angles_edges() { void shape::build_from_angles_edges() {
transmatrix at = Id; transmatrix at = Id;
vertices.clear(); vertices.clear();
int n = isize(angles); int n = isize(angles);
hyperpoint ctr = Hypc; hyperpoint ctr = Hypc;
vector<transmatrix> matrices;
for(int i=0; i<n; i++) { for(int i=0; i<n; i++) {
matrices.push_back(at);
println(hlog, "at = ", at); println(hlog, "at = ", at);
vertices.push_back(tC0(at)); vertices.push_back(tC0(at));
ctr += tC0(at); ctr += tC0(at);
at = at * xpush(edges[i]) * spin(angles[i]); at = at * xpush(edges[i]) * spin(angles[i]);
} }
if(!eqmatrix(at, Id)) throw hr_parse_exception("polygon error"); matrices.push_back(at);
if(!eqmatrix(at, Id)) throw hr_polygon_error(matrices, id);
if(sqhypot_d(3, ctr) < 1e-2) { if(sqhypot_d(3, ctr) < 1e-2) {
// this may happen for some spherical tilings // this may happen for some spherical tilings
// try to move towards the center // try to move towards the center
@ -78,10 +134,43 @@ void shape::build_from_angles_edges() {
bool correct_index(int index, int size) { return index >= 0 && index < size; } bool correct_index(int index, int size) { return index >= 0 && index < size; }
template<class T> bool correct_index(int index, const T& v) { return correct_index(index, isize(v)); } template<class T> bool correct_index(int index, const T& v) { return correct_index(index, isize(v)); }
template<class T> void verify_index(int index, const T& v) { if(!correct_index(index, v)) throw hr_parse_exception("bad index"); } template<class T> void verify_index(int index, const T& v, exp_parser& ep) { if(!correct_index(index, v)) throw hr_parse_exception("bad index: " + its(index) + " at " + ep.where()); }
string unnamed = "unnamed"; string unnamed = "unnamed";
EX void load_tile(exp_parser& ep, bool unit) {
current.shapes.emplace_back();
auto& cc = current.shapes.back();
cc.id = isize(current.shapes) - 1;
cc.flags = 0;
while(ep.next() != ')') {
cld dist = 1;
if(!unit) {
dist = ep.parse(0);
ep.force_eat(",");
}
cld angle = ep.parse(0);
cc.edges.push_back(ep.validate_real(dist * ep.extra_params["distunit"]));
cc.angles.push_back(ep.validate_real(angle * ep.extra_params["angleunit"] + ep.extra_params["angleofs"]));
if(ep.eat(",")) continue;
else if(ep.eat(")")) break;
else throw hr_parse_exception("expecting , or )");
}
try {
cc.build_from_angles_edges();
}
catch(hr_parse_exception& ex) {
throw hr_parse_exception(ex.s + ep.where());
}
catch(hr_polygon_error& poly) {
poly.params = ep.extra_params;
throw;
}
cc.connections.resize(cc.size());
for(int i=0; i<isize(cc.connections); i++)
cc.connections[i] = make_tuple(cc.id, i, false);
}
EX void load(const string& fname) { EX void load(const string& fname) {
fhstream f(fname, "rt"); fhstream f(fname, "rt");
string s; string s;
@ -102,7 +191,7 @@ EX void load(const string& fname) {
int ai; int ai;
if(ep.next() == ')') ai = isize(c.shapes)-1; if(ep.next() == ')') ai = isize(c.shapes)-1;
else ai = ep.iparse(); else ai = ep.iparse();
verify_index(ai, c.shapes); verify_index(ai, c.shapes, ep);
c.shapes[ai].flags |= f; c.shapes[ai].flags |= f;
ep.force_eat(")"); ep.force_eat(")");
}; };
@ -131,16 +220,19 @@ EX void load(const string& fname) {
ginf[gArbitrary].g = giEuclid2; ginf[gArbitrary].g = giEuclid2;
ginf[gArbitrary].sides = 7; ginf[gArbitrary].sides = 7;
set_flag(ginf[gArbitrary].flags, qBOUNDED, false); set_flag(ginf[gArbitrary].flags, qBOUNDED, false);
set_flag(ginf[gArbitrary].flags, qAFFINE, true);
} }
else if(ep.eat("h2.")) { else if(ep.eat("h2.")) {
ginf[gArbitrary].g = giHyperb2; ginf[gArbitrary].g = giHyperb2;
ginf[gArbitrary].sides = 7; ginf[gArbitrary].sides = 7;
set_flag(ginf[gArbitrary].flags, qBOUNDED, false); set_flag(ginf[gArbitrary].flags, qBOUNDED, false);
set_flag(ginf[gArbitrary].flags, qAFFINE, false);
} }
else if(ep.eat("s2.")) { else if(ep.eat("s2.")) {
ginf[gArbitrary].g = giSphere2; ginf[gArbitrary].g = giSphere2;
ginf[gArbitrary].sides = 5; ginf[gArbitrary].sides = 5;
set_flag(ginf[gArbitrary].flags, qBOUNDED, false); set_flag(ginf[gArbitrary].flags, qBOUNDED, false);
set_flag(ginf[gArbitrary].flags, qAFFINE, false);
} }
else if(ep.eat("angleunit(")) angleunit = real(ep.parsepar()); else if(ep.eat("angleunit(")) angleunit = real(ep.parsepar());
else if(ep.eat("angleofs(")) angleofs = real(ep.parsepar()); else if(ep.eat("angleofs(")) angleofs = real(ep.parsepar());
@ -157,43 +249,11 @@ EX void load(const string& fname) {
string tok = ep.next_token(); string tok = ep.next_token();
ep.force_eat("="); ep.force_eat("=");
ep.extra_params[tok] =ep.parsepar(); ep.extra_params[tok] =ep.parsepar();
if(debugflags & DF_GEOM)
println(hlog, "let ", tok, " = ", real(ep.extra_params[tok]));
} }
else if(ep.eat("unittile(")) { else if(ep.eat("unittile(")) load_tile(ep, true);
c.shapes.emplace_back(); else if(ep.eat("tile(")) load_tile(ep, false);
auto& cc = c.shapes.back();
cc.id = isize(c.shapes) - 1;
cc.flags = 0;
while(ep.next() != ')') {
ld angle = ep.rparse(0);
cc.edges.push_back(distunit);
cc.angles.push_back(angle * angleunit + angleofs);
if(ep.eat(",")) continue;
else if(ep.eat(")")) break;
else throw hr_parse_exception("expecting , or )");
}
cc.build_from_angles_edges();
cc.connections.resize(cc.size());
for(int i=0; i<isize(cc.connections); i++)
cc.connections[i] = make_tuple(cc.id, i, false);
}
else if(ep.eat("tile(")) {
c.shapes.emplace_back();
auto& cc = c.shapes.back();
cc.id = isize(c.shapes) - 1;
cc.flags = 0;
while(ep.next() != ')') {
ld dist = ep.rparse(0);
ep.force_eat(",");
ld angle = ep.rparse(0);
cc.edges.push_back(dist * distunit);
cc.angles.push_back(angle * angleunit + angleofs);
if(ep.eat(",")) continue;
else if(ep.eat(")")) break;
else throw hr_parse_exception("expecting , or )");
}
cc.build_from_angles_edges();
cc.connections.resize(cc.size());
}
else if(ep.eat("conway(\"")) { else if(ep.eat("conway(\"")) {
string s = ""; string s = "";
while(true) { while(true) {
@ -201,7 +261,7 @@ EX void load(const string& fname) {
if(ep.eat("(")) m = 0; if(ep.eat("(")) m = 0;
else if(ep.eat("[")) m = 1; else if(ep.eat("[")) m = 1;
else if(ep.eat("\"")) break; else if(ep.eat("\"")) break;
else throw hr_parse_exception("cannot parse Conway notation"); else throw hr_parse_exception("cannot parse Conway notation, " + ep.where());
int ai = 0; int ai = 0;
int as = ep.iparse(); int as = ep.iparse();
@ -221,18 +281,18 @@ EX void load(const string& fname) {
ep.force_eat(")"); ep.force_eat(")");
} }
else if(ep.eat("c(")) { else if(ep.eat("c(")) {
int ai = ep.iparse(); verify_index(ai, c.shapes); ep.force_eat(","); int ai = ep.iparse(); verify_index(ai, c.shapes, ep); ep.force_eat(",");
int as = ep.iparse(); verify_index(as, c.shapes[ai]); ep.force_eat(","); int as = ep.iparse(); verify_index(as, c.shapes[ai], ep); ep.force_eat(",");
int bi = ep.iparse(); verify_index(bi, c.shapes); ep.force_eat(","); int bi = ep.iparse(); verify_index(bi, c.shapes, ep); ep.force_eat(",");
int bs = ep.iparse(); verify_index(bs, c.shapes[bi]); ep.force_eat(","); int bs = ep.iparse(); verify_index(bs, c.shapes[bi], ep); ep.force_eat(",");
int m = ep.iparse(); ep.force_eat(")"); int m = ep.iparse(); ep.force_eat(")");
c.shapes[ai].connections[as] = make_tuple(bi, bs, m); c.shapes[ai].connections[as] = make_tuple(bi, bs, m);
c.shapes[bi].connections[bs] = make_tuple(ai, as, m); c.shapes[bi].connections[bs] = make_tuple(ai, as, m);
} }
else if(ep.eat("subline(")) { else if(ep.eat("subline(")) {
int ai = ep.iparse(); verify_index(ai, c.shapes); ep.force_eat(","); int ai = ep.iparse(); verify_index(ai, c.shapes, ep); ep.force_eat(",");
int as = ep.iparse(); verify_index(as, c.shapes[ai]); ep.force_eat(","); int as = ep.iparse(); verify_index(as, c.shapes[ai], ep); ep.force_eat(",");
int bs = ep.iparse(); verify_index(bs, c.shapes[ai]); ep.force_eat(")"); int bs = ep.iparse(); verify_index(bs, c.shapes[ai], ep); ep.force_eat(")");
c.shapes[ai].sublines.emplace_back(as, bs); c.shapes[ai].sublines.emplace_back(as, bs);
} }
else if(ep.eat("sublines(")) { else if(ep.eat("sublines(")) {
@ -252,10 +312,93 @@ EX void load(const string& fname) {
} }
} }
} }
else throw hr_parse_exception("expecting command"); else if(ep.eat("debug(")) {
int i = ep.iparse(0);
verify_index(i, c.shapes, ep);
ep.force_eat(")");
throw connection_debug_request(i);
}
else throw hr_parse_exception("expecting command, " + ep.where());
} }
} }
arbi_tiling debugged;
vector<pair<transmatrix, int> > debug_polys;
string primes(int i) {
string res;
while(i--) res += "'";
return res;
}
void connection_debugger() {
cmode = sm::SIDE | sm::DIALOG_STRICT_X;
gamescreen(0);
auto& last = debug_polys.back();
initquickqueue();
for(auto& p: debug_polys) {
int id = p.second;
transmatrix V = gmatrix[cwt.at] * p.first;
auto& sh = debugged.shapes[id].vertices;
for(auto& v: sh)
curvepoint(V * v);
curvepoint(V * sh[0]);
color_t col = colortables['A'][id];
col = darkena(col, 0, 0xFF);
if(&p == &last) {
vid.linewidth *= 2;
queuecurve(0xFFFF00FF, col, PPR::LINE);
vid.linewidth /= 2;
for(int i=0; i<isize(sh); i++)
queuestr(V * sh[i], vid.fsize, its(i), 0xFFFFFFFF);
}
else
queuecurve(0xFFFFFFFF, col, PPR::LINE);
}
quickqueue();
dialog::init(XLAT("connection debugger"));
dialog::addInfo(debugged.name);
dialog::addHelp(debugged.comment);
dialog::addBreak(50);
dialog::addInfo("face index " + its(last.second));
dialog::addBreak(50);
auto& sh = debugged.shapes[last.second];
int N = isize(sh.edges);
for(int k=0; k<N; k++) {
auto con = sh.connections[k];
string cap = its(k) + primes(last.second) + " -> " + its(get<1>(con)) + primes(get<0>(con)) + (get<2>(con) ? " (m) " : "");
dialog::addSelItem(cap, "go", '0' + k);
dialog::add_action([k, last, con] {
debug_polys.emplace_back(last.first * get_adj(debugged, last.second, k, -1), get<0>(con));
});
}
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
handlePanning(sym, uni);
dialog::handleNavigation(sym, uni);
if(doexiton(sym, uni)) popScreen();
};
}
geometryinfo1& arbi_tiling::get_geometry() { geometryinfo1& arbi_tiling::get_geometry() {
return ginf[gEuclid].g; return ginf[gEuclid].g;
} }
@ -268,6 +411,56 @@ EX hrmap *current_altmap;
heptagon *build_child(heptspin p, pair<int, int> adj); heptagon *build_child(heptspin p, pair<int, int> adj);
EX transmatrix get_adj(arbi_tiling& c, int t, int dl, int xdl) {
auto& sh = c.shapes[t];
int dr = gmod(dl+1, sh.size());
auto& co = sh.connections[dl];
int xt = get<0>(co);
if(xdl == -1) xdl = get<1>(co);
int m = get<2>(co);
auto& xsh = c.shapes[xt];
int xdr = gmod(xdl+1, xsh.size());
hyperpoint vl = sh.vertices[dl];
hyperpoint vr = sh.vertices[dr];
hyperpoint vm = mid(vl, vr);
transmatrix rm = gpushxto0(vm);
hyperpoint xvl = xsh.vertices[xdl];
hyperpoint xvr = xsh.vertices[xdr];
hyperpoint xvm = mid(xvl, xvr);
transmatrix xrm = gpushxto0(xvm);
transmatrix Res = rgpushxto0(vm) * rspintox(rm*vr);
if(cgflags & qAFFINE) {
ld sca = hdist(vl, vr) / hdist(xvl, xvr);
transmatrix Tsca = Id;
Tsca[0][0] = Tsca[1][1] = sca;
Res = Res * Tsca;
}
if(m) Res = Res * MirrorX;
Res = Res * spintox(xrm*xvl) * xrm;
if(m) swap(vl, vr);
if(hdist(vl, Res*xvr) + hdist(vr, Res*xvl) > .1) {
println(hlog, "s1 = ", kz(spintox(rm*vr)), " s2 = ", kz(rspintox(xrm*xvr)));
println(hlog, tie(t, dl), " = ", kz(Res));
println(hlog, hdist(vl, Res * xvr), " # ", hdist(vr, Res * xvl));
exit(3);
}
return Res;
}
struct hrmap_arbi : hrmap { struct hrmap_arbi : hrmap {
heptagon *origin; heptagon *origin;
heptagon *getOrigin() override { return origin; } heptagon *getOrigin() override { return origin; }
@ -309,6 +502,8 @@ struct hrmap_arbi : hrmap {
celllister cl(origin->c7, 1000, 200, NULL); celllister cl(origin->c7, 1000, 200, NULL);
ginf[geometry].distlimit[0] = cgi.base_distlimit = cl.dists.back(); ginf[geometry].distlimit[0] = cgi.base_distlimit = cl.dists.back();
if(sphere) cgi.base_distlimit = SEE_ALL; if(sphere) cgi.base_distlimit = SEE_ALL;
if(cgflags & qAFFINE) cgi.base_distlimit = 3;
} }
~hrmap_arbi() { ~hrmap_arbi() {
@ -330,58 +525,13 @@ struct hrmap_arbi : hrmap {
void verify() override { } void verify() override { }
transmatrix adj(heptagon *h, int dl) override { transmatrix adj(heptagon *h, int dl) override {
auto& c = current; return get_adj(current, id_of(h), dl, h->c.move(dl) ? h->c.spin(dl) : -1);
int t = id_of(h);
auto& sh = c.shapes[t];
int dr = gmod(dl+1, sh.size());
auto& co = sh.connections[dl];
int xt = get<0>(co);
int xdl = get<1>(co);
int m = get<2>(co);
if(h->c.move(dl)) xdl = h->c.spin(dl);
auto& xsh = c.shapes[xt];
int xdr = gmod(xdl+1, xsh.size());
hyperpoint vl = sh.vertices[dl];
hyperpoint vr = sh.vertices[dr];
hyperpoint vm = mid(vl, vr);
transmatrix rm = gpushxto0(vm);
hyperpoint xvl = xsh.vertices[xdl];
hyperpoint xvr = xsh.vertices[xdr];
hyperpoint xvm = mid(xvl, xvr);
transmatrix xrm = gpushxto0(xvm);
transmatrix Res = rgpushxto0(vm) * rspintox(rm*vr);
if(m) Res = Res * MirrorX;
Res = Res * spintox(xrm*xvl) * xrm;
if(m) swap(vl, vr);
if(hdist(vl, Res*xvr) + hdist(vr, Res*xvl) > .1) {
println(hlog, "s1 = ", kz(spintox(rm*vr)), " s2 = ", kz(rspintox(xrm*xvr)));
println(hlog, tie(t, dl), " = ", kz(Res));
println(hlog, hdist(vl, Res * xvr), " # ", hdist(vr, Res * xvl));
exit(3);
}
return Res;
} }
heptagon *create_step(heptagon *h, int d) override { heptagon *create_step(heptagon *h, int d) override {
int t = id_of(h); int t = id_of(h);
const auto& p = arbi_matrix[h];
heptagon *alt = p.first;
auto& sh = current.shapes[t]; auto& sh = current.shapes[t];
auto& co = sh.connections[d]; auto& co = sh.connections[d];
@ -391,6 +541,48 @@ struct hrmap_arbi : hrmap {
int m = get<2>(co); int m = get<2>(co);
auto& xsh = current.shapes[xt]; auto& xsh = current.shapes[xt];
if(cgflags & qAFFINE) {
set<heptagon*> visited;
vector<pair<heptagon*, transmatrix> > v;
visited.insert(h);
v.emplace_back(h, Id);
transmatrix goal = adj(h, d);
for(int i=0; i<200 && i < isize(v); i++) {
transmatrix T = v[i].second;
heptagon *h2 = v[i].first;
if(eqmatrix(T, goal)) {
h->c.connect(d, h2, e, m);
return h2;
}
for(int i=0; i<h2->type; i++) {
heptagon *h3 = h2->move(i);
if(!h3) continue;
if(visited.count(h3)) continue;
visited.insert(h3);
v.emplace_back(h3, T * adj(h2, i));
}
}
auto h1 = tailored_alloc<heptagon> (current.shapes[xt].size());
h1->distance = h->distance + 1;
h1->zebraval = xt;
h1->c7 = newCell(h1->type, h1);
h1->alt = nullptr;
h1->cdata = nullptr;
h1->emeraldval = h->emeraldval ^ m;
h->c.connect(d, h1, e, m);
return h1;
}
const auto& p = arbi_matrix[h];
heptagon *alt = p.first;
transmatrix T = p.second * adj(h, d); transmatrix T = p.second * adj(h, d);
if(hyperbolic) { if(hyperbolic) {
@ -470,6 +662,46 @@ struct hrmap_arbi : hrmap {
EX hrmap *new_map() { return new hrmap_arbi; } EX hrmap *new_map() { return new hrmap_arbi; }
void run(string fname) {
stop_game();
eGeometry g = geometry;
arbi_tiling t = current;
auto v = variation;
set_geometry(gArbitrary);
try {
load(fname);
ginf[gArbitrary].tiling_name = current.name;
}
catch(hr_polygon_error& poly) {
set_geometry(g);
set_variation(v);
current = t;
start_poly_debugger(poly);
string help = XLAT("Polygon number %1 did not close correctly. Here is the picture to help you understand the issue.\n\n", its(poly.id));
showstartmenu = false;
for(auto& p: poly.params)
help += lalign(-1, p.first, " = ", p.second, "\n");
gotoHelp(help);
}
catch(hr_parse_exception& ex) {
println(hlog, "failed: ", ex.s);
set_geometry(g);
current = t;
start_game();
addMessage("failed: " + ex.s);
}
catch(connection_debug_request& cr) {
set_geometry(g);
debugged = current;
current = t;
ensure_geometry(cr.c);
debug_polys.clear();
debug_polys.emplace_back(Id, cr.id);
pushScreen(connection_debugger);
}
start_game();
}
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
int readArgs() { int readArgs() {
using namespace arg; using namespace arg;
@ -477,17 +709,8 @@ int readArgs() {
if(0) ; if(0) ;
else if(argis("-arbi")) { else if(argis("-arbi")) {
PHASEFROM(2); PHASEFROM(2);
stop_game();
shift(); shift();
set_geometry(gArbitrary); run(args());
try {
load(args());
}
catch(hr_parse_exception& ex) {
println(hlog, "failed: ", ex.s);
exit(3);
}
ginf[gArbitrary].tiling_name = current.name;
} }
else return 1; else return 1;
return 0; return 0;
@ -498,7 +721,7 @@ auto hook = addHook(hooks_args, 100, readArgs);
EX bool in() { return geometry == gArbitrary; } EX bool in() { return geometry == gArbitrary; }
EX string tes = "tessellations/marjorie-rice.tes"; EX string tes = "tessellations/sample/marjorie-rice.tes";
EX bool linespattern(cell *c) { EX bool linespattern(cell *c) {
return current.shapes[id_of(c->master)].flags & arcm::sfLINE; return current.shapes[id_of(c->master)].flags & arcm::sfLINE;
@ -511,20 +734,10 @@ EX bool pseudohept(cell *c) {
EX void choose() { EX void choose() {
dialog::openFileDialog(tes, XLAT("open a tiling"), ".tes", dialog::openFileDialog(tes, XLAT("open a tiling"), ".tes",
[] () { [] () {
stop_game(); run(tes);
set_geometry(gArbitrary);
try {
load(tes);
ginf[gArbitrary].tiling_name = current.name;
}
catch(hr_parse_exception& ex) {
println(hlog, "failed: ", ex.s);
set_geometry(gNormal);
}
start_game();
return true; return true;
}); });
} }
EX } EX }
} }

View File

@ -402,29 +402,27 @@ void archimedean_tiling::compute_geometry() {
ld alpha_total = 0; ld alpha_total = 0;
for(int i=0; i<N; i++) { for(int i=0; i<N; i++) {
ld crmin = 0, crmax = sphere ? M_PI/2 : 10;
ld el = 0; ld gamma = M_PI / faces[i];
for(int q=0; q<100; q++) {
circumradius[i] = (crmin + crmax) / 2; auto& c = circumradius[i];
hyperpoint p1 = xpush0(circumradius[i]);
hyperpoint p2 = spin(2 * M_PI / faces[i]) * p1; c = asin_auto(sin_auto(edgelength/2) / sin(gamma));
inradius[i] = hdist0(mid(p1, p2)); inradius[i] = hdist0(mid(xpush0(circumradius[i]), xspinpush0(2*gamma, circumradius[i])));
el = hdist(p1, p2);
if(el > edgelength) crmax = circumradius[i]; hyperpoint h = xpush(c) * spin(M_PI - 2*gamma) * xpush0(c);
else crmin = circumradius[i]; ld a = atan2(h);
} cyclefix(a, 0);
if(el < edgelength - 1e-3) alpha_total += 100; // could not make an edge that long if(a < 0) a = -a;
hyperpoint h = xpush(edgelength/2) * xspinpush0(M_PI/2, inradius[i]);
ld a = atan2(-h[1], h[0]);
if(a < 0) a += 2 * M_PI;
alphas[i] = a; alphas[i] = a;
// printf(" H = %s alp = %f\n", display(h), (float) atan2(-h[1], h[0]));
alpha_total += alphas[i]; alpha_total += alphas[i];
} }
// printf("el = %f alpha = %f\n", float(edgelength), float(alpha_total)); if(debugflags & DF_GEOM)
println(hlog, "edgelength = ", edgelength, " angles = ", alphas, " inradius = ", inradius, " circumradius = ", circumradius);
if(sphere ^ (alpha_total > M_PI)) elmin = edgelength;
if(isnan(alpha_total)) elmax = edgelength;
else if(sphere ^ (alpha_total > M_PI)) elmin = edgelength;
else elmax = edgelength; else elmax = edgelength;
if(euclid) break; if(euclid) break;
} }

View File

@ -219,7 +219,7 @@ void setcameraangle(bool b) { }
#if CAP_GL #if CAP_GL
#if CAP_VR #if CAP_VR
EX hookset<bool()> *hooks_vr_eye_view, *hooks_vr_eye_projection; EX hookset<bool()> hooks_vr_eye_view, hooks_vr_eye_projection;
#endif #endif
EX void eyewidth_translate(int ed) { EX void eyewidth_translate(int ed) {
@ -492,8 +492,6 @@ int gl_width(int size, const char *s) {
return x; return x;
} }
vector<glhr::textured_vertex> tver;
glhr::textured_vertex charvertex(int x1, int y1, ld tx, ld ty) { glhr::textured_vertex charvertex(int x1, int y1, ld tx, ld ty) {
glhr::textured_vertex res; glhr::textured_vertex res;
res.coords[0] = x1; res.coords[0] = x1;
@ -842,12 +840,12 @@ EX ld realradius() {
ld vradius = current_display->radius; ld vradius = current_display->radius;
if(sphere) { if(sphere) {
if(sphereflipped()) if(sphereflipped())
vradius /= sqrt(vid.alpha*vid.alpha - 1); vradius /= sqrt(pconf.alpha*pconf.alpha - 1);
else else
vradius = 1e12; // use the following vradius = 1e12; // use the following
} }
if(euclid) if(euclid)
vradius = current_display->radius * get_sightrange() / (1 + vid.alpha) / 2.5; vradius = current_display->radius * get_sightrange() / (1 + pconf.alpha) / 2.5;
vradius = min<ld>(vradius, min(vid.xres, vid.yres) / 2); vradius = min<ld>(vradius, min(vid.xres, vid.yres) / 2);
return vradius; return vradius;
} }
@ -857,14 +855,14 @@ EX void drawmessage(const string& s, int& y, color_t col) {
int space; int space;
if(dual::state) if(dual::state)
space = vid.xres; space = vid.xres;
else if(y > current_display->ycenter + rrad * vid.stretch) else if(y > current_display->ycenter + rrad * pconf.stretch)
space = vid.xres; space = vid.xres;
else if(y > current_display->ycenter) else if(y > current_display->ycenter)
space = current_display->xcenter - rhypot(rrad, (y-current_display->ycenter) / vid.stretch); space = current_display->xcenter - rhypot(rrad, (y-current_display->ycenter) / pconf.stretch);
else if(y > current_display->ycenter - vid.fsize) else if(y > current_display->ycenter - vid.fsize)
space = current_display->xcenter - rrad; space = current_display->xcenter - rrad;
else if(y > current_display->ycenter - vid.fsize - rrad * vid.stretch) else if(y > current_display->ycenter - vid.fsize - rrad * pconf.stretch)
space = current_display->xcenter - rhypot(rrad, (current_display->ycenter-vid.fsize-y) / vid.stretch); space = current_display->xcenter - rhypot(rrad, (current_display->ycenter-vid.fsize-y) / pconf.stretch);
else else
space = vid.xres; space = vid.xres;
@ -949,7 +947,7 @@ EX void drawCircle(int x, int y, int size, color_t color, color_t fillcolor IS(0
if(ISMOBILE && pts > 72) pts = 72; if(ISMOBILE && pts > 72) pts = 72;
for(int r=0; r<pts; r++) { for(int r=0; r<pts; r++) {
float rr = (M_PI * 2 * r) / pts; float rr = (M_PI * 2 * r) / pts;
glcoords.push_back(glhr::makevertex(x + size * sin(rr), y + size * vid.stretch * cos(rr), 0)); glcoords.push_back(glhr::makevertex(x + size * sin(rr), y + size * pconf.stretch * cos(rr), 0));
} }
current_display->set_all(0); current_display->set_all(0);
glhr::vertices(glcoords); glhr::vertices(glcoords);
@ -969,13 +967,13 @@ EX void drawCircle(int x, int y, int size, color_t color, color_t fillcolor IS(0
#if CAP_XGD #if CAP_XGD
gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size); gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size);
#elif CAP_SDLGFX #elif CAP_SDLGFX
if(vid.stretch == 1) { if(pconf.stretch == 1) {
if(fillcolor) filledCircleColor(s, x, y, size, fillcolor); if(fillcolor) filledCircleColor(s, x, y, size, fillcolor);
if(color) ((vid.antialias && AA_NOGL)?aacircleColor:circleColor) (s, x, y, size, color); if(color) ((vid.antialias && AA_NOGL)?aacircleColor:circleColor) (s, x, y, size, color);
} }
else { else {
if(fillcolor) filledEllipseColor(s, x, y, size, size * vid.stretch, fillcolor); if(fillcolor) filledEllipseColor(s, x, y, size, size * pconf.stretch, fillcolor);
if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * vid.stretch, color); if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * pconf.stretch, color);
} }
#elif CAP_SDL #elif CAP_SDL
int pts = size * 4; int pts = size * 4;
@ -1022,7 +1020,7 @@ EX void displayColorButton(int x, int y, const string& name, int key, int align,
} }
ld textscale() { ld textscale() {
return vid.fsize / (current_display->radius * cgi.crossf) * (1+vid.alpha) * 2; return vid.fsize / (current_display->radius * cgi.crossf) * (1+pconf.alpha) * 2;
} }
EX bool setfsize = true; EX bool setfsize = true;

View File

@ -1332,7 +1332,7 @@ EX bool quickfind(eLand l) {
#define I2000 (INVLUCK?600:2000) #define I2000 (INVLUCK?600:2000)
#define I10000 (INVLUCK?3000:10000) #define I10000 (INVLUCK?3000:10000)
EX hookset<int(cell*, bool)> *hooks_wallchance; EX hookset<int(cell*, bool)> hooks_wallchance;
EX int wallchance(cell *c, bool deepOcean) { EX int wallchance(cell *c, bool deepOcean) {
int i = callhandlers(-1, hooks_wallchance, c, deepOcean); int i = callhandlers(-1, hooks_wallchance, c, deepOcean);

View File

@ -9,11 +9,17 @@
namespace hr { namespace hr {
EX namespace bt { EX namespace bt {
#if CAP_BT
/** note: nihsolv and kd3 tilings return bt::in(). They are defined elsewhere, although some of bt:: functions are used for them */ /** note: nihsolv and kd3 tilings return bt::in(). They are defined elsewhere, although some of bt:: functions are used for them */
EX bool in() { return cgflags & qBINARY; } EX bool in() {
#if CAP_BT
return cgflags & qBINARY;
#else
return false;
#endif
}
#if CAP_BT
#if HDR #if HDR
enum bindir { enum bindir {
bd_right = 0, bd_right = 0,

View File

@ -251,7 +251,7 @@ EX void drawArrowTraps() {
} }
} }
auto ccm_blizzard = addHook(clearmemory, 0, [] () { auto ccm_blizzard = addHook(hooks_clearmemory, 0, [] () {
arrowtraps.clear(); arrowtraps.clear();
blizzardcells.clear(); blizzardcells.clear();
bcells.clear(); bcells.clear();

View File

@ -202,6 +202,9 @@ EX cell *createMov(cell *c, int d) {
else if(kite::in()) else if(kite::in())
kite::find_cell_connection(c, d); kite::find_cell_connection(c, d);
#endif #endif
else if(fake::in()) {
return FPIU(createMov(c, d));
}
#if CAP_IRR #if CAP_IRR
else if(IRREGULAR) { else if(IRREGULAR) {
irr::link_cell(c, d); irr::link_cell(c, d);
@ -279,7 +282,7 @@ EX void eumerge(cell* c1, int s1, cell *c2, int s2, bool mirror) {
// map<pair<eucoord, eucoord>, cell*> euclidean; // map<pair<eucoord, eucoord>, cell*> euclidean;
EX hookset<hrmap*()> *hooks_newmap; EX hookset<hrmap*()> hooks_newmap;
/** create a map in the current geometry */ /** create a map in the current geometry */
EX void initcells() { EX void initcells() {
@ -287,6 +290,7 @@ EX void initcells() {
hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap); hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap);
if(res) currentmap = res; if(res) currentmap = res;
else if(fake::in()) currentmap = fake::new_map();
else if(asonov::in()) currentmap = asonov::new_map(); else if(asonov::in()) currentmap = asonov::new_map();
else if(nonisotropic || hybri) currentmap = nisot::new_map(); else if(nonisotropic || hybri) currentmap = nisot::new_map();
#if CAP_CRYSTAL #if CAP_CRYSTAL
@ -1175,7 +1179,7 @@ EX void clearCellMemory() {
gp::gp_adj.clear(); gp::gp_adj.clear();
} }
auto cellhooks = addHook(clearmemory, 500, clearCellMemory); auto cellhooks = addHook(hooks_clearmemory, 500, clearCellMemory);
EX bool isNeighbor(cell *c1, cell *c2) { EX bool isNeighbor(cell *c1, cell *c2) {
for(int i=0; i<c1->type; i++) if(c1->move(i) == c2) return true; for(int i=0; i<c1->type; i++) if(c1->move(i) == c2) return true;

View File

@ -143,12 +143,22 @@ void celldrawer::setcolors() {
case laCrossroads2: case laCrossroads3: case laCrossroads4: case laCrossroads5: case laCrossroads2: case laCrossroads3: case laCrossroads4: case laCrossroads5:
case laRose: case laPower: case laWildWest: case laHalloween: case laRedRock: case laRose: case laPower: case laWildWest: case laHalloween: case laRedRock:
case laDragon: case laStorms: case laTerracotta: case laMercuryRiver: case laDragon: case laStorms: case laTerracotta: case laMercuryRiver:
case laDesert: case laKraken: case laDocks: case laCA: case laDesert: case laKraken: case laDocks:
case laMotion: case laGraveyard: case laWineyard: case laLivefjord: case laMotion: case laGraveyard: case laWineyard: case laLivefjord:
case laRlyeh: case laHell: case laCrossroads: case laJungle: case laRlyeh: case laHell: case laCrossroads: case laJungle:
case laAlchemist: case laFrog: case laAlchemist: case laFrog:
fcol = floorcolors[c->land]; break; fcol = floorcolors[c->land]; break;
case laCA:
fcol = floorcolors[c->land];
if(geosupport_chessboard()) {
if(chessvalue(c)) fcol += 0x202020;
}
else if(geosupport_threecolor()) {
fcol += 0x202020 * pattern_threecolor(c);
}
break;
case laWet: case laWet:
fcol = 0x40FF40; break; fcol = 0x40FF40; break;
@ -831,7 +841,17 @@ void celldrawer::draw_grid() {
if(bt::in() && !sn::in() && !among(t, 5, 6, 8)) continue; if(bt::in() && !sn::in() && !among(t, 5, 6, 8)) continue;
if(!bt::in() && c->move(t) < c) continue; if(!bt::in() && c->move(t) < c) continue;
dynamicval<color_t> g(poly_outline, gridcolor(c, c->move(t))); dynamicval<color_t> g(poly_outline, gridcolor(c, c->move(t)));
queuepoly(V, cgi.shWireframe3D[ofs + t], 0); if(fat_edges && reg3::in()) {
for(int i=0; i<S7; i++) if(c < c->move(i)) {
for(int j=0; j<cgi.face-1; j++) {
gridline(V, cgi.cellshape[i*cgi.face+j], cgi.cellshape[i*cgi.face+j+1], gridcolor(c, c->move(t)), prec);
}
gridline(V, cgi.cellshape[i*cgi.face], cgi.cellshape[(i+1)*cgi.face-1], gridcolor(c, c->move(t)), prec);
}
}
else {
queuepoly(V, cgi.shWireframe3D[ofs + t], 0);
}
} }
} }
#endif #endif
@ -1580,6 +1600,10 @@ void celldrawer::draw_features_and_walls_3d() {
if(c->move(a) && (among(pmodel, mdPerspective, mdGeodesic) || gmatrix0.count(c->move(a)))) if(c->move(a) && (among(pmodel, mdPerspective, mdGeodesic) || gmatrix0.count(c->move(a))))
b = (patterns::innerwalls && (tC0(V)[2] < tC0(V * currentmap->adj(c, a))[2])) || !isWall3(c->move(a), dummy); b = (patterns::innerwalls && (tC0(V)[2] < tC0(V * currentmap->adj(c, a))[2])) || !isWall3(c->move(a), dummy);
if(b) { if(b) {
#if CAP_WRL
/* always render */
if(wrl::in && wrl::print) ; else
#endif
if(pmodel == mdPerspective && !sphere && !quotient && !kite::in() && !nonisotropic && !hybri && !experimental && !nih) { if(pmodel == mdPerspective && !sphere && !quotient && !kite::in() && !nonisotropic && !hybri && !experimental && !nih) {
if(a < 4 && among(geometry, gHoroTris, gBinary3) && celldistAlt(c) >= celldistAlt(centerover)) continue; if(a < 4 && among(geometry, gHoroTris, gBinary3) && celldistAlt(c) >= celldistAlt(centerover)) continue;
else if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(centerover)) continue; else if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(centerover)) continue;
@ -1792,7 +1816,7 @@ void celldrawer::bookkeeping() {
else { else {
playerV = V * ddspin(c, cwt.spin, 0); playerV = V * ddspin(c, cwt.spin, 0);
if(cwt.mirrored) playerV = playerV * Mirror; 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; current_display->which_copy = V;
if(orig) cwtV = playerV; if(orig) cwtV = playerV;
} }

View File

@ -3904,3 +3904,44 @@ Geometries:
- reverse FPP mode on iOS should be fixed - reverse FPP mode on iOS should be fixed
- fixed the missing message when PC could not stay in place - fixed the missing message when PC could not stay in place
- fixed a potential rare crash - fixed a potential rare crash
2020-04-17 21:00 Update 11.3j:
- the Hypersian Rug mode now uses HyperRogue's 3D engine. As a result, its controls are consistent with HyperRogue 3D (which has received shift-strafe for this consistency), you can now view it in all models, and embed in Solv and Nil geometries.
- a drawing tool (see creative mode). Contrary to Vector Graphics Editor (now called Shape Editor) and texture editor, this simulates on using a huge non-Euclidean blackboard (unless played in Euclidean geometry or in a quotient space, of course).
- WRL export (export e.g. your Hypersian Rug or a hyperbolic honeycomb in ball model to a 3D modelling tool or a 3D printer)
- fixed some issues with 'vertical stretch' (acted inconsistently, stretched text)
- fixed central inversion in 3D models
- better error information when trying to read an illegal tes file
- CA mode now automatically awards Orb of Aether (so you can step space) and is simulated on every step in animation mode
- more accurate Solv rendering (using RK4 method instead of buggy midpoint)
- when giving values, you can use "..|" (animate with sharp changes); "../" can be also used for spline interpolation
2020-04-22 01:46 Update 11.3k:
- Orb of the Sword now destroys shrubs
- more accurate mouse when in 2D projections
- fixed horocycles in bitruncated {4,x}
- fixed Great Walls in {5,3,4}
- Hypersian Rug/camera improvements:
- - camera rotation now works better in 3D scenes with gravity (including product geometries, 2D-in-3D and gravity lands)
- - now displays geometry correctly
- - removed outdated documentation
- - panning enabled in the menu
- - shift+mousewheel for zoom works in rug
- - zoom hotkeys now change FOV in perspective modes
- - lshift+arrows now rotate the model in rug
- - shift+PageUp/Down now zooms
- - rug can now be mouse-rotated with Ctrl
- - fixed in standard binary tiling and in *.tes tilings
2020-05-01 18:57 Update 11.3l:
- Tessellation files:
- - added a debug screen if the polygon does not end correctly
- - added a command debug(tilenumber) to debug connections
- - if you define constants, they take precedence over predefined constants
- - expression parser now accepts whitespace in expressions
- fixed a bug when scrolling with arrows then moving in quotient spaces
- fixed 'smooth scrolling' option not making scrolling very smooth
- an option to fatten regular (isotropic non-Euclidean) honeycomb edges
- better centering in screenshot, 'rotate PC' option
- in animation, 'monster turns' can be configured (useful for animating CA or butterflies)
- fixed Butterflies in non-orientable geometries

View File

@ -730,7 +730,7 @@ enum eGeometry {
gTernary, gNIH, gSolN, gInfOrder, gSpace336, gSpace344, gCrystal344, gTernary, gNIH, gSolN, gInfOrder, gSpace336, gSpace344, gCrystal344,
gArnoldCat, gArbitrary, gInfOrder4, gCrystal534, gArnoldCat, gArbitrary, gInfOrder4, gCrystal534,
gSpace535, gSpace536, gSeifertCover, gSeifertWeber, gHomologySphere, gSpace535, gSpace536, gSeifertCover, gSeifertWeber, gHomologySphere,
gInfOrderMixed, gSpace436, gInfOrderMixed, gSpace436, gFake,
gGUARD}; gGUARD};
enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSolNIH, gcNil, gcProduct, gcSL2 }; enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSolNIH, gcNil, gcProduct, gcSL2 };
@ -798,6 +798,7 @@ static const flagtype qDEPRECATED = Flag(21);
static const flagtype qINFMIXED = Flag(22); static const flagtype qINFMIXED = Flag(22);
static const flagtype qRAYONLY = Flag(23); static const flagtype qRAYONLY = Flag(23);
static const flagtype qAFFINE = Flag(24);
// note: dnext assumes that x&7 equals 7 // note: dnext assumes that x&7 equals 7
static const int SEE_ALL = 50; static const int SEE_ALL = 50;
@ -875,13 +876,13 @@ EX vector<geometryinfo> ginf = {
{"{4,3,5}","none", "{4,3,5} hyperbolic honeycomb", "435", 6, 5, 0, giHyperb3, 0x31600, {{7, 2}}, eVariation::pure}, {"{4,3,5}","none", "{4,3,5} hyperbolic honeycomb", "435", 6, 5, 0, giHyperb3, 0x31600, {{7, 2}}, eVariation::pure},
{"{3,3,3}","none", "{3,3,3} 5-cell", "333", 4, 3, qsSMALLB, giSphere3, 0x38000, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,3,3}","none", "{3,3,3} 5-cell", "333", 4, 3, qsSMALLB, giSphere3, 0x38000, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,3,4}","none", "{3,3,4} 16-cell", "334", 4, 4, qsSMALLB, giSphere3, 0x38200, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,3,4}","none", "{3,3,4} 16-cell", "334", 4, 4, qsSMALLB, giSphere3, 0x38200, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,3,4}","elliptic","{3,3,4} 16-cell (elliptic)", "e334", 4, 4, qsSMALLBE, giSphere3, 0x39200, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,3,4}","elliptic","{3,3,4} 16-cell (elliptic space)", "e334", 4, 4, qsSMALLBE, giSphere3, 0x39200, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{4,3,3}","none", "{4,3,3} 8-cell", "433", 6, 4, qsSMALLB, giSphere3, 0x38400, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{4,3,3}","none", "{4,3,3} 8-cell", "433", 6, 4, qsSMALLB, giSphere3, 0x38400, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{4,3,3}","elliptic","{4,3,3} 8-cell (elliptic)", "e433", 6, 4, qsSMALLBE, giSphere3, 0x39400, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{4,3,3}","elliptic","{4,3,3} 8-cell (elliptic space)", "e433", 6, 4, qsSMALLBE, giSphere3, 0x39400, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,4,3}","none", "{3,4,3} 24-cell", "343", 8, 3, qsSMALLB, giSphere3, 0x38600, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,4,3}","none", "{3,4,3} 24-cell", "343", 8, 3, qsSMALLB, giSphere3, 0x38600, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,4,3}","elliptic","{3,4,3} 24-cell (elliptic)", "e343", 8, 3, qsSMALLBE, giSphere3, 0x39600, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,4,3}","elliptic","{3,4,3} 24-cell (elliptic space)", "e343", 8, 3, qsSMALLBE, giSphere3, 0x39600, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,3,5}","none", "{3,3,5} 600-cell", "335", 4, 3, qsSMALLB, giSphere3, 0x41000, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,3,5}","none", "{3,3,5} 600-cell", "335", 4, 3, qsSMALLB, giSphere3, 0x41000, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"{3,3,5}","elliptic","{3,3,5} 600-cell (elliptic)", "e335", 4, 3, qsSMALLBE, giSphere3, 0x41200, {{SEE_ALL, SEE_ALL}}, eVariation::pure}, {"{3,3,5}","elliptic","{3,3,5} 600-cell (elliptic space)", "e335", 4, 3, qsSMALLBE, giSphere3, 0x41200, {{SEE_ALL, SEE_ALL}}, eVariation::pure},
{"bin{3,6}", "none", "{3,6} on horospheres", "bin36", 8, 3, qBINARY, giHyperb3, 0x40000, {{7, 3}}, eVariation::pure}, {"bin{3,6}", "none", "{3,6} on horospheres", "bin36", 8, 3, qBINARY, giHyperb3, 0x40000, {{7, 3}}, eVariation::pure},
{"bin-rect", "none", "rectangles on horospheres", "bin44/2", 7, 3, qBINARY, giHyperb3, 0x40200, {{7, 3}}, eVariation::pure}, {"bin-rect", "none", "rectangles on horospheres", "bin44/2", 7, 3, qBINARY, giHyperb3, 0x40200, {{7, 3}}, eVariation::pure},
{"bin{6,3}", "none", "{6,3} on horospheres", "bin63", 14, 3, qBINARY, giHyperb3, 0x40400, {{7, 3}}, eVariation::pure}, {"bin{6,3}", "none", "{6,3} on horospheres", "bin63", 14, 3, qBINARY, giHyperb3, 0x40400, {{7, 3}}, eVariation::pure},
@ -912,6 +913,7 @@ EX vector<geometryinfo> ginf = {
{"{5,3,3}","SW", "Poincaré homology sphere", "533s", 12, 3, qsSINGLE, giSphere3, 0x31400, {{7, 2}}, eVariation::pure}, {"{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}, {"{?,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}, {"{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 // bits: 9, 10, 15, 16, (reserved for later) 17, 18

View File

@ -347,7 +347,7 @@ int arg::readCommon() {
EX purehookset hooks_config; EX purehookset hooks_config;
EX hookset<int()> *hooks_args; EX hookset<int()> hooks_args;
namespace arg { namespace arg {
@ -357,12 +357,13 @@ namespace arg {
curphase = phase; curphase = phase;
callhooks(hooks_config); callhooks(hooks_config);
while(pos < isize(argument)) { while(pos < isize(argument)) {
for(auto& h: *hooks_args) { int r = callhandlers(1, hooks_args);
int r = h.second(); if(r == 2) return; if(r == 0) { lshift(); goto cont; } switch (r) {
case 0: lshift(); break;
case 1: printf("Unknown option: %s\n", argcs()); exit(3); break;
case 2: return;
default: assert(false);
} }
printf("Unknown option: %s\n", argcs());
exit(3);
cont: ;
} }
} }
} }

View File

@ -3347,6 +3347,14 @@ EX namespace ca {
EX eWall wlive = waFloorA; EX eWall wlive = waFloorA;
EX unordered_set<cell*> changed;
EX void list_adj(cell *c) {
changed.insert(c);
for(cell* c1: adj_minefield_cells(c))
changed.insert(c1);
}
// you can also do -mineadj // you can also do -mineadj
EX string fillup(string s) { EX string fillup(string s) {
@ -3372,6 +3380,12 @@ EX namespace ca {
shift(); wlive = eWall(argi()); shift(); wlive = eWall(argi());
return 0; return 0;
} }
if(argis("-carun")) {
shift(); int iter = argi();
start_game();
for(int i=0; i<iter; i++) simulate();
return 0;
}
if(args()[0] != '-') return 1; if(args()[0] != '-') return 1;
if(args()[1] != 'c') return 1; if(args()[1] != 'c') return 1;
int livedead = args()[2] - '0'; int livedead = args()[2] - '0';
@ -3395,13 +3409,16 @@ EX namespace ca {
EX void simulate() { EX void simulate() {
if(cwt.at->land != laCA) return; if(cwt.at->land != laCA) return;
vector<cell*>& allcells = currentmap->allcells(); if(items[itOrbAether] < 2) items[itOrbAether] = 2;
vector<cell*> allcells;
for(cell *c: changed) allcells.push_back(c);
changed.clear();
int dcs = isize(allcells); int dcs = isize(allcells);
std::vector<bool> willlive(dcs); std::vector<bool> willlive(dcs);
int old = 0, xold = 0; int old = 0, xold = 0;
for(int i=0; i<dcs; i++) { for(int i=0; i<dcs; i++) {
cell *c = allcells[i]; cell *c = allcells[i];
if(c->land != laCA) return; if(c->land != laCA) continue;
int nei = 0, live = 0; int nei = 0, live = 0;
for(cell *c2: adj_minefield_cells(c)) { for(cell *c2: adj_minefield_cells(c)) {
nei++; if(c2->wall == wlive) live++; nei++; if(c2->wall == wlive) live++;
@ -3412,13 +3429,19 @@ EX namespace ca {
} }
for(int i=0; i<dcs; i++) { for(int i=0; i<dcs; i++) {
cell *c = allcells[i]; cell *c = allcells[i];
auto last = c->wall;
c->wall = willlive[i] ? wlive : waNone; c->wall = willlive[i] ? wlive : waNone;
if(c->wall != last) {
dynamicval<ld> d(prob, 0);
setdist(c, 7, nullptr);
list_adj(c);
}
} }
println(hlog, tie(dcs, old, xold)); println(hlog, make_tuple(dcs, old, xold, isize(changed)));
} }
EX } EX }
auto ccm = addHook(clearmemory, 0, [] () { auto ccm = addHook(hooks_clearmemory, 0, [] () {
heat::offscreen_heat.clear(); heat::offscreen_heat.clear();
heat::offscreen_fire.clear(); heat::offscreen_fire.clear();
princess::clear(); princess::clear();
@ -3825,12 +3848,12 @@ EX namespace halloween {
} }
int id = hrand(100); int id = hrand(100);
if(items[itTreat] == 1) { if(items[itTreat] == 1) {
#if ISMOBILE==0 #if !ISMOBILE
addMessage(XLAT("Hint: use arrow keys to scroll.")); addMessage(XLAT("Hint: use arrow keys to scroll."));
#endif #endif
} }
else if(items[itTreat] == 2) { else if(items[itTreat] == 2) {
#if ISMOBILE==0 #if !ISMOBILE
addMessage(XLAT("Hint: press 1 2 3 4 to change the projection.")); addMessage(XLAT("Hint: press 1 2 3 4 to change the projection."));
#endif #endif
} }

View File

@ -8,7 +8,7 @@
*/ */
#include "hyper.h" #include "hyper.h"
#ifdef CAP_COMPLEX2 #if CAP_COMPLEX2
namespace hr { namespace hr {
@ -198,7 +198,7 @@ EX namespace brownian {
vector<cell*> to_remove; vector<cell*> to_remove;
for(auto p: futures) if(is_cell_removed(p.first)) to_remove.push_back(p.first); for(auto p: futures) if(is_cell_removed(p.first)) to_remove.push_back(p.first);
for(auto r: to_remove) futures.erase(r); for(auto r: to_remove) futures.erase(r);
}) + addHook(clearmemory, 0, [] () { futures.clear(); }) }) + addHook(hooks_clearmemory, 0, [] () { futures.clear(); })
+ addHook(hooks_gamedata, 0, [] (gamedata* gd) { gd->store(futures); }); + addHook(hooks_gamedata, 0, [] (gamedata* gd) { gd->store(futures); });
EX } EX }
@ -554,15 +554,19 @@ EX bool mightBeMine(cell *c) {
return c->wall == waMineUnknown || c->wall == waMineMine; return c->wall == waMineUnknown || c->wall == waMineMine;
} }
EX hookset<bool(cell*)> *hooks_mark; EX hookset<bool(cell*)> hooks_mark;
EX void performMarkCommand(cell *c) { EX void performMarkCommand(cell *c) {
if(!c) return; if(!c) return;
if(callhandlers(false, hooks_mark, c)) return; if(callhandlers(false, hooks_mark, c)) return;
if(c->land == laCA && c->wall == waNone) if(c->land == laCA && c->wall == waNone) {
c->wall = waFloorA; c->wall = ca::wlive;
else if(c->land == laCA && c->wall == waFloorA) ca::list_adj(c);
}
else if(c->land == laCA && c->wall == ca::wlive) {
c->wall = waNone; c->wall = waNone;
ca::list_adj(c);
}
if(c->land != laMinefield) return; if(c->land != laMinefield) return;
if(c->item) return; if(c->item) return;
if(!mightBeMine(c)) return; if(!mightBeMine(c)) return;

View File

@ -305,10 +305,10 @@ EX void initConfig() {
addsaver(precise_width, "precisewidth", .5); addsaver(precise_width, "precisewidth", .5);
addsaver(linepatterns::width, "pattern-linewidth", 1); addsaver(linepatterns::width, "pattern-linewidth", 1);
addsaver(fat_edges, "fat-edges"); addsaver(fat_edges, "fat-edges");
addsaver(vid.scale, "scale", 1); addsaver(pconf.scale, "scale", 1);
addsaver(vid.xposition, "xposition", 0); addsaver(pconf.xposition, "xposition", 0);
addsaver(vid.yposition, "yposition", 0); addsaver(pconf.yposition, "yposition", 0);
addsaver(vid.alpha, "projection", 1); addsaver(pconf.alpha, "projection", 1);
addsaver(vid.sspeed, "scrollingspeed", 0); addsaver(vid.sspeed, "scrollingspeed", 0);
addsaver(vid.mspeed, "movement speed", 1); addsaver(vid.mspeed, "movement speed", 1);
addsaver(vid.full, "fullscreen", false); addsaver(vid.full, "fullscreen", false);
@ -329,14 +329,14 @@ EX void initConfig() {
// special graphics // special graphics
addsaver(vid.ballangle, "ball angle", 20); addsaver(pconf.ballangle, "ball angle", 20);
addsaver(vid.yshift, "Y shift", 0); addsaver(vid.yshift, "Y shift", 0);
addsaver(vid.use_wall_radar, "wallradar", true); addsaver(vid.use_wall_radar, "wallradar", true);
addsaver(vid.fixed_facing, "fixed facing", 0); addsaver(vid.fixed_facing, "fixed facing", 0);
addsaver(vid.fixed_facing_dir, "fixed facing dir", 90); addsaver(vid.fixed_facing_dir, "fixed facing dir", 90);
addsaver(vid.fixed_yz, "fixed YZ", true); addsaver(vid.fixed_yz, "fixed YZ", true);
addsaver(vid.camera_angle, "camera angle", 0); addsaver(pconf.camera_angle, "camera angle", 0);
addsaver(vid.ballproj, "ballproj", 1); addsaver(pconf.ballproj, "ballproj", 1);
addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE); addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE);
addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE); addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE);
@ -362,6 +362,20 @@ EX void initConfig() {
addsaver(reserve_limit, "memory_reserve", 128); addsaver(reserve_limit, "memory_reserve", 128);
addsaver(show_memory_warning, "show_memory_warning"); addsaver(show_memory_warning, "show_memory_warning");
auto& rconf = vid.rug_config;
addsaverenum(rconf.model, "rug-projection", mdEquidistant);
addsaver(rconf.scale, "rug-projection-scale", 1);
addsaver(rconf.alpha, "rug-projection-alpha", 1);
addsaver(rconf.clip_min, "rug-projection-clip-min", -100);
addsaver(rconf.clip_max, "rug-projection-clip-max", +10);
addsaver(rconf.stretch, "rug-projection-stretch", 1);
addsaver(rconf.halfplane_scale, "rug-projection-halfplane scale", 1);
addsaver(rconf.collignon_parameter, "rug-collignon-parameter", 1);
addsaver(rconf.collignon_reflected, "rug-collignon-reflect", false);
addsaver(rconf.euclid_to_sphere, "rug-euclid to sphere projection", 1.5);
addsaver(rconf.twopoint_param, "rug-twopoint parameter", 1);
addsaver(rconf.fisheye_param, "rug-fisheye parameter", 1);
addsaver(rconf.model_transition, "rug-model transition", 1);
addsaver(rug::renderonce, "rug-renderonce"); addsaver(rug::renderonce, "rug-renderonce");
addsaver(rug::rendernogl, "rug-rendernogl"); addsaver(rug::rendernogl, "rug-rendernogl");
addsaver(rug::texturesize, "rug-texturesize"); addsaver(rug::texturesize, "rug-texturesize");
@ -389,16 +403,16 @@ EX void initConfig() {
addsaver(models::rotation_xz, "conformal rotation_xz"); addsaver(models::rotation_xz, "conformal rotation_xz");
addsaver(models::rotation_xy2, "conformal rotation_2"); addsaver(models::rotation_xy2, "conformal rotation_2");
addsaver(models::do_rotate, "conformal rotation mode", 1); addsaver(models::do_rotate, "conformal rotation mode", 1);
addsaver(models::model_orientation, "model orientation", 0); addsaver(pconf.model_orientation, "model orientation", 0);
addsaver(models::model_orientation_yz, "model orientation-yz", 0); addsaver(pconf.model_orientation_yz, "model orientation-yz", 0);
addsaver(models::top_z, "topz", 5); addsaver(pconf.top_z, "topz", 5);
addsaver(models::model_transition, "model transition", 1); addsaver(pconf.model_transition, "model transition", 1);
addsaver(models::halfplane_scale, "halfplane scale", 1); addsaver(pconf.halfplane_scale, "halfplane scale", 1);
addsaver(history::autoband, "automatic band"); addsaver(history::autoband, "automatic band");
addsaver(history::autobandhistory, "automatic band history"); addsaver(history::autobandhistory, "automatic band history");
addsaver(history::dospiral, "do spiral"); addsaver(history::dospiral, "do spiral");
addsaver(models::clip_min, "clip-min", -1); addsaver(pconf.clip_min, "clip-min", -1);
addsaver(models::clip_max, "clip-max", +1); addsaver(pconf.clip_max, "clip-max", +1);
addsaver(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true); addsaver(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true);
// control // control
@ -468,13 +482,13 @@ EX void initConfig() {
addsaver(vid.fov, "field-of-vision", 90); addsaver(vid.fov, "field-of-vision", 90);
addsaver(vid.desaturate, "desaturate", 0); addsaver(vid.desaturate, "desaturate", 0);
addsaverenum(vid.stereo_mode, "stereo-mode"); addsaverenum(vid.stereo_mode, "stereo-mode");
addsaver(vid.euclid_to_sphere, "euclid to sphere projection", 1.5); addsaver(pconf.euclid_to_sphere, "euclid to sphere projection", 1.5);
addsaver(vid.twopoint_param, "twopoint parameter", 1); addsaver(pconf.twopoint_param, "twopoint parameter", 1);
addsaver(vid.fisheye_param, "fisheye parameter", 1); addsaver(pconf.fisheye_param, "fisheye parameter", 1);
addsaver(vid.stretch, "stretch", 1); addsaver(pconf.stretch, "stretch", 1);
addsaver(vid.binary_width, "binary-tiling-width", 1); addsaver(vid.binary_width, "binary-tiling-width", 1);
addsaver(vid.collignon_parameter, "collignon-parameter", 1); addsaver(pconf.collignon_parameter, "collignon-parameter", 1);
addsaver(vid.collignon_reflected, "collignon-reflect", false); addsaver(pconf.collignon_reflected, "collignon-reflect", false);
addsaver(vid.plevel_factor, "plevel_factor", 0.7); addsaver(vid.plevel_factor, "plevel_factor", 0.7);
@ -521,7 +535,7 @@ EX void initConfig() {
#if CAP_SHOT #if CAP_SHOT
addsaver(shot::shotx, "shotx"); addsaver(shot::shotx, "shotx");
addsaver(shot::shoty, "shoty"); addsaver(shot::shoty, "shoty");
addsaver(shot::make_svg, "shotsvg"); addsaverenum(shot::format, "shotsvg");
addsaver(shot::transparent, "shottransparent"); addsaver(shot::transparent, "shottransparent");
addsaver(shot::gamma, "shotgamma"); addsaver(shot::gamma, "shotgamma");
addsaver(shot::caption, "shotcaption"); addsaver(shot::caption, "shotcaption");
@ -545,11 +559,11 @@ EX void initConfig() {
addsaver(slr::steps, "slr-steps"); addsaver(slr::steps, "slr-steps");
addsaver(slr::range_xy, "slr-range-xy"); addsaver(slr::range_xy, "slr-range-xy");
addsaver(vid.skiprope, "mobius", 0); addsaver(pconf.skiprope, "mobius", 0);
addsaver(models::formula, "formula"); addsaver(pconf.formula, "formula");
addsaverenum(models::basic_model, "basic model"); addsaverenum(pconf.basic_model, "basic model");
addsaver(models::use_atan, "use_atan"); addsaver(pconf.use_atan, "use_atan");
addsaver(arcm::current.symbol, "arcm-symbol", "4^5"); addsaver(arcm::current.symbol, "arcm-symbol", "4^5");
addsaverenum(hybrid::underlying, "product-underlying"); addsaverenum(hybrid::underlying, "product-underlying");
@ -670,7 +684,7 @@ EX bool inSpecialMode() {
tour::on || tour::on ||
#endif #endif
yendor::on || tactic::on || randomPatternsMode || yendor::on || tactic::on || randomPatternsMode ||
geometry != gNormal || pmodel != mdDisk || vid.alpha != 1 || vid.scale != 1 || geometry != gNormal || pmodel != mdDisk || pconf.alpha != 1 || pconf.scale != 1 ||
rug::rugged || vid.monmode != DEFAULT_MONMODE || rug::rugged || vid.monmode != DEFAULT_MONMODE ||
vid.wallmode != DEFAULT_WALLMODE; vid.wallmode != DEFAULT_WALLMODE;
} }
@ -697,7 +711,7 @@ EX bool have_current_settings() {
} }
EX bool have_current_graph_settings() { EX bool have_current_graph_settings() {
if(vid.xposition || vid.yposition || vid.alpha != 1 || vid.scale != 1) if(pconf.xposition || pconf.yposition || pconf.alpha != 1 || pconf.scale != 1)
return true; return true;
if(pmodel != mdDisk || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE) if(pmodel != mdDisk || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE)
return true; return true;
@ -708,8 +722,8 @@ EX bool have_current_graph_settings() {
} }
EX void reset_graph_settings() { EX void reset_graph_settings() {
pmodel = mdDisk; vid.alpha = 1; vid.scale = 1; pmodel = mdDisk; pconf.alpha = 1; pconf.scale = 1;
vid.xposition = vid.yposition = 0; pconf.xposition = pconf.yposition = 0;
#if CAP_RUG #if CAP_RUG
if(rug::rugged) rug::close(); if(rug::rugged) rug::close();
#endif #endif
@ -786,7 +800,7 @@ EX void saveConfig() {
fprintf(f, "%s=%s\n", s->name.c_str(), s->save().c_str()); fprintf(f, "%s=%s\n", s->name.c_str(), s->save().c_str());
fclose(f); fclose(f);
#if ISMOBILE==0 #if !ISMOBILE
addMessage(s0 + "Configuration saved to: " + conffile); addMessage(s0 + "Configuration saved to: " + conffile);
#else #else
addMessage(s0 + "Configuration saved"); addMessage(s0 + "Configuration saved");
@ -893,6 +907,7 @@ string solhelp() {
} }
EX void edit_sightrange() { EX void edit_sightrange() {
USING_NATIVE_GEOMETRY_IN_RUG;
if(vid.use_smart_range) { if(vid.use_smart_range) {
ld& det = WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3; ld& det = WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3;
dialog::editNumber(det, 1, 50, 1, WDIM == 2 ? 8 : 30, XLAT("minimum visible cell in pixels"), ""); dialog::editNumber(det, 1, 50, 1, WDIM == 2 ? 8 : 30, XLAT("minimum visible cell in pixels"), "");
@ -1254,36 +1269,34 @@ EX void configureOther() {
// dialog::addBoolItem_action(XLAT("forget faraway cells"), memory_saving_mode, 'y'); // dialog::addBoolItem_action(XLAT("forget faraway cells"), memory_saving_mode, 'y');
#if CAP_AUDIO #if CAP_AUDIO
if(CAP_AUDIO) { dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b');
dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b'); dialog::add_action([] {
dialog::add_action([] { dialog::editNumber(musicvolume, 0, 128, 10, 60, XLAT("background music volume"), "");
dialog::editNumber(musicvolume, 0, 128, 10, 60, XLAT("background music volume"), ""); dialog::reaction = [] () {
dialog::reaction = [] () { #if CAP_SDLAUDIO
#if CAP_SDLAUDIO Mix_VolumeMusic(musicvolume);
Mix_VolumeMusic(musicvolume); #endif
#endif #if ISANDROID
#if ISANDROID settingsChanged = true;
settingsChanged = true; #endif
#endif };
}; dialog::bound_low(0);
dialog::bound_low(0); dialog::bound_up(MIX_MAX_VOLUME);
dialog::bound_up(MIX_MAX_VOLUME); });
});
dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e'); dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e');
dialog::add_action([] { dialog::add_action([] {
dialog::editNumber(effvolume, 0, 128, 10, 60, XLAT("sound effects volume"), ""); dialog::editNumber(effvolume, 0, 128, 10, 60, XLAT("sound effects volume"), "");
dialog::reaction = [] () { dialog::reaction = [] () {
#if ISANDROID #if ISANDROID
settingsChanged = true; settingsChanged = true;
#endif #endif
}; };
dialog::bound_low(0); dialog::bound_low(0);
dialog::bound_up(MIX_MAX_VOLUME); dialog::bound_up(MIX_MAX_VOLUME);
}); });
} #endif
#endif
menuitem_sightrange('r'); menuitem_sightrange('r');
@ -1302,12 +1315,10 @@ EX void configureInterface() {
gamescreen(3); gamescreen(3);
dialog::init(XLAT("interface")); dialog::init(XLAT("interface"));
#if CAP_TRANS #if CAP_TRANS
if(CAP_TRANS) { dialog::addSelItem(XLAT("language"), XLAT("EN"), 'l');
dialog::addSelItem(XLAT("language"), XLAT("EN"), 'l'); dialog::add_action_push(selectLanguageScreen);
dialog::add_action_push(selectLanguageScreen); #endif
}
#endif
dialog::addSelItem(XLAT("player character"), numplayers() > 1 ? "" : csname(vid.cs), 'g'); dialog::addSelItem(XLAT("player character"), numplayers() > 1 ? "" : csname(vid.cs), 'g');
dialog::add_action_push(showCustomizeChar); dialog::add_action_push(showCustomizeChar);
@ -1431,7 +1442,7 @@ EX void showJoyConfig() {
EX void projectionDialog() { EX void projectionDialog() {
vid.tc_alpha = ticks; vid.tc_alpha = ticks;
dialog::editNumber(vid.alpha, -5, 5, .1, 1, dialog::editNumber(vpconf.alpha, -5, 5, .1, 1,
XLAT("projection"), XLAT("projection"),
XLAT("HyperRogue uses the Minkowski hyperboloid model internally. " XLAT("HyperRogue uses the Minkowski hyperboloid model internally. "
"Klein and Poincaré models can be obtained by perspective, " "Klein and Poincaré models can be obtained by perspective, "
@ -1449,17 +1460,17 @@ EX void projectionDialog() {
"tanh(g)/tanh(c) units below the center. This in turn corresponds to " "tanh(g)/tanh(c) units below the center. This in turn corresponds to "
"the Poincaré model for g=c, and Klein-Beltrami model for g=0.")); "the Poincaré model for g=c, and Klein-Beltrami model for g=0."));
dialog::addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'P'); dialog::addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'P');
dialog::add_action([] () { *dialog::ne.editwhat = 1; vid.scale = 1; dialog::ne.s = "1"; }); dialog::add_action([] () { *dialog::ne.editwhat = 1; vpconf.scale = 1; dialog::ne.s = "1"; });
dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K'); dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K');
dialog::add_action([] () { *dialog::ne.editwhat = 0; vid.scale = 1; dialog::ne.s = "0"; }); dialog::add_action([] () { *dialog::ne.editwhat = 0; vpconf.scale = 1; dialog::ne.s = "0"; });
if(hyperbolic) { if(hyperbolic) {
dialog::addSelItem("inverted Poincaré model", "-1", 'I'); dialog::addSelItem("inverted Poincaré model", "-1", 'I');
dialog::add_action([] () { *dialog::ne.editwhat = -1; vid.scale = 1; dialog::ne.s = "-1"; }); dialog::add_action([] () { *dialog::ne.editwhat = -1; vpconf.scale = 1; dialog::ne.s = "-1"; });
} }
dialog::addItem(sphere ? "orthographic" : "Gans model", 'O'); dialog::addItem(sphere ? "orthographic" : "Gans model", 'O');
dialog::add_action([] () { vid.alpha = vid.scale = 999; dialog::ne.s = dialog::disp(vid.alpha); }); dialog::add_action([] () { vpconf.alpha = vpconf.scale = 999; dialog::ne.s = dialog::disp(vpconf.alpha); });
dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T'); dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T');
dialog::add_action([] () { double d = 1.1; vid.alpha *= d; vid.scale *= d; dialog::ne.s = dialog::disp(vid.alpha); }); dialog::add_action([] () { double d = 1.1; vpconf.alpha *= d; vpconf.scale *= d; dialog::ne.s = dialog::disp(vpconf.alpha); });
}; };
} }
@ -1564,7 +1575,7 @@ EX void showStereo() {
} }
EX void config_camera_rotation() { EX void config_camera_rotation() {
dialog::editNumber(vid.ballangle, 0, 90, 5, 0, XLAT("camera rotation in 3D models"), dialog::editNumber(pconf.ballangle, 0, 90, 5, 0, XLAT("camera rotation in 3D models"),
"Rotate the camera in 3D models (ball model, hyperboloid, and hemisphere). " "Rotate the camera in 3D models (ball model, hyperboloid, and hemisphere). "
"Note that hyperboloid and hemisphere models are also available in the " "Note that hyperboloid and hemisphere models are also available in the "
"Hypersian Rug surfaces menu, but they are rendered differently there -- " "Hypersian Rug surfaces menu, but they are rendered differently there -- "
@ -1627,12 +1638,12 @@ EX void show3D() {
gamescreen(0); gamescreen(0);
dialog::init(XLAT("3D configuration")); dialog::init(XLAT("3D configuration"));
#if MAXMDIM >= 4 #if MAXMDIM >= 4
if(WDIM == 2) { if(WDIM == 2) {
dialog::addBoolItem(XLAT("use the full 3D models"), vid.always3, 'U'); dialog::addBoolItem(XLAT("use the full 3D models"), vid.always3, 'U');
dialog::add_action(geom3::switch_always3); dialog::add_action(geom3::switch_always3);
} }
#endif #endif
if(vid.use_smart_range == 0 && GDIM == 2) { if(vid.use_smart_range == 0 && GDIM == 2) {
dialog::addSelItem(XLAT("High detail range"), fts(vid.highdetail), 'n'); dialog::addSelItem(XLAT("High detail range"), fts(vid.highdetail), 'n');
dialog::addSelItem(XLAT("Mid detail range"), fts(vid.middetail), 'm'); dialog::addSelItem(XLAT("Mid detail range"), fts(vid.middetail), 'm');
@ -1648,9 +1659,9 @@ EX void show3D() {
if(GDIM == 2) if(GDIM == 2)
dialog::addSelItem(XLAT("Projection at the ground level"), fts(vid.alpha), 'p'); dialog::addSelItem(XLAT("Projection at the ground level"), fts(pconf.alpha), 'p');
else if(!in_perspective()) else if(!in_perspective())
dialog::addSelItem(XLAT("Projection distance"), fts(vid.alpha), 'p'); dialog::addSelItem(XLAT("Projection distance"), fts(pconf.alpha), 'p');
dialog::addBreak(50); dialog::addBreak(50);
dialog::addSelItem(XLAT("Height of walls"), fts(vid.wall_height), 'w'); dialog::addSelItem(XLAT("Height of walls"), fts(vid.wall_height), 'w');
@ -1676,7 +1687,7 @@ EX void show3D() {
dialog::editNumber(mouseaim_sensitivity, -1, 1, 0.002, 0.01, XLAT("mouse aiming sensitivity"), "set to 0 to disable"); dialog::editNumber(mouseaim_sensitivity, -1, 1, 0.002, 0.01, XLAT("mouse aiming sensitivity"), "set to 0 to disable");
}); });
} }
dialog::addSelItem(XLAT("camera rotation"), fts(vid.camera_angle), 's'); dialog::addSelItem(XLAT("camera rotation"), fts(vpconf.camera_angle), 's');
if(GDIM == 2) { if(GDIM == 2) {
dialog::addSelItem(XLAT("fixed facing"), vid.fixed_facing ? fts(vid.fixed_facing_dir) : XLAT("OFF"), 'f'); dialog::addSelItem(XLAT("fixed facing"), vid.fixed_facing ? fts(vid.fixed_facing_dir) : XLAT("OFF"), 'f');
dialog::add_action([] () { vid.fixed_facing = !vid.fixed_facing; dialog::add_action([] () { vid.fixed_facing = !vid.fixed_facing;
@ -1730,16 +1741,16 @@ EX void show3D() {
} }
#endif #endif
if(GDIM == 2) { if(GDIM == 2) {
dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && vid.camera_angle, 'T'); dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && pconf.camera_angle, 'T');
dialog::add_action(geom3::switch_tpp); dialog::add_action(geom3::switch_tpp);
} }
#if MAXMDIM >=4 #if MAXMDIM >=4
if(WDIM == 2) { if(WDIM == 2) {
dialog::addBoolItem(XLAT("configure FPP automatically"), GDIM == 3, 'F'); dialog::addBoolItem(XLAT("configure FPP automatically"), GDIM == 3, 'F');
dialog::add_action(geom3::switch_fpp); dialog::add_action(geom3::switch_fpp);
} }
#endif #endif
if(0); if(0);
#if CAP_RUG #if CAP_RUG
@ -1902,7 +1913,7 @@ EX void show3D() {
}; };
} }
else if(uni == 's') else if(uni == 's')
dialog::editNumber(vid.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"), dialog::editNumber(vpconf.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"),
XLAT("Rotate the camera. Can be used to obtain a first person perspective, " XLAT("Rotate the camera. Can be used to obtain a first person perspective, "
"or third person perspective when combined with Y shift.") "or third person perspective when combined with Y shift.")
); );
@ -1959,11 +1970,7 @@ EX void showCustomizeChar() {
int firsty = dialog::items[0].position / 2; int firsty = dialog::items[0].position / 2;
int scale = firsty - 2 * vid.fsize; int scale = firsty - 2 * vid.fsize;
dynamicval<eModel> pm(pmodel, flat_model()); flat_model_enabler fme;
glClear(GL_DEPTH_BUFFER_BIT);
dynamicval<ld> va(vid.alpha, 1);
dynamicval<ld> vs(vid.scale, 1);
dynamicval<ld> vc(vid.camera_angle, 0);
initquickqueue(); initquickqueue();
transmatrix V = atscreenpos(vid.xres/2, firsty, scale); transmatrix V = atscreenpos(vid.xres/2, firsty, scale);
@ -2301,11 +2308,9 @@ EX void showSettings() {
dialog::addItem(XLAT("colors & aura"), 'c'); dialog::addItem(XLAT("colors & aura"), 'c');
dialog::add_action_push(show_color_dialog); dialog::add_action_push(show_color_dialog);
#if CAP_SHMUP #if CAP_SHMUP && !ISMOBILE
if(CAP_SHMUP && !ISMOBILE) { dialog::addSelItem(XLAT("keyboard & joysticks"), "", 'k');
dialog::addSelItem(XLAT("keyboard & joysticks"), "", 'k'); dialog::add_action(multi::configure);
dialog::add_action(multi::configure);
}
#endif #endif
dialog::addSelItem(XLAT("mouse & touchscreen"), "", 'm'); dialog::addSelItem(XLAT("mouse & touchscreen"), "", 'm');
@ -2411,22 +2416,22 @@ EX int read_config_args() {
PHASEFROM(2); PHASEFROM(2);
nomenukey = true; nomenukey = true;
} }
#if MAXMDIM >= 4 #if MAXMDIM >= 4
else if(argis("-switch-fpp")) { else if(argis("-switch-fpp")) {
PHASEFROM(2); PHASEFROM(2);
geom3::switch_fpp(); geom3::switch_fpp();
} }
#endif #endif
else if(argis("-switch-tpp")) { else if(argis("-switch-tpp")) {
PHASEFROM(2); PHASEFROM(2);
geom3::switch_tpp(); geom3::switch_tpp();
} }
#if MAXMDIM >= 4 #if MAXMDIM >= 4
else if(argis("-switch-3d")) { else if(argis("-switch-3d")) {
PHASEFROM(2); PHASEFROM(2);
geom3::switch_always3(); geom3::switch_always3();
} }
#endif #endif
else if(argis("-nohelp")) { else if(argis("-nohelp")) {
PHASEFROM(2); PHASEFROM(2);
nohelp = true; nohelp = true;
@ -2465,7 +2470,7 @@ EX int read_config_args() {
else if(argis("-yca")) { else if(argis("-yca")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(vid.yshift); shift_arg_formula(vid.yshift);
shift_arg_formula(vid.camera_angle); shift_arg_formula(pconf.camera_angle);
} }
else if(argis("-pside")) { else if(argis("-pside")) {
PHASEFROM(2); PHASEFROM(2);
@ -2473,8 +2478,8 @@ EX int read_config_args() {
} }
else if(argis("-xy")) { else if(argis("-xy")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(vid.xposition); shift_arg_formula(pconf.xposition);
shift_arg_formula(vid.yposition); shift_arg_formula(pconf.yposition);
} }
else if(argis("-fixdir")) { else if(argis("-fixdir")) {
PHASEFROM(2); PHASEFROM(2);
@ -2534,6 +2539,10 @@ EX int read_config_args() {
PHASEFROM(2); PHASEFROM(2);
shift(); neon_mode = eNeon(argi()); shift(); neon_mode = eNeon(argi());
} }
else if(argis("-smooths")) {
PHASEFROM(2);
shift(); smooth_scrolling = argi();
}
else if(argis("-neonnf")) { else if(argis("-neonnf")) {
PHASEFROM(2); PHASEFROM(2);
shift(); neon_nofill = argi(); shift(); neon_nofill = argi();
@ -2578,15 +2587,15 @@ auto ah_config = addHook(hooks_args, 0, read_config_args) + addHook(hooks_args,
EX unordered_map<string, ld&> params = { EX unordered_map<string, ld&> params = {
{"linewidth", vid.linewidth}, {"linewidth", vid.linewidth},
{"patternlinewidth", linepatterns::width}, {"patternlinewidth", linepatterns::width},
{"scale", vid.scale}, {"scale", pconf.scale},
{"xposition", vid.xposition}, {"xposition", pconf.xposition},
{"yposition", vid.yposition}, {"yposition", pconf.yposition},
{"projection", vid.alpha}, {"projection", pconf.alpha},
{"sspeed", vid.sspeed}, {"sspeed", vid.sspeed},
{"mspeed", vid.mspeed}, {"mspeed", vid.mspeed},
{"ballangle", vid.ballangle}, {"ballangle", pconf.ballangle},
{"yshift", vid.yshift}, {"yshift", vid.yshift},
{"cameraangle", vid.camera_angle}, {"cameraangle", pconf.camera_angle},
{"eye", vid.eye}, {"eye", vid.eye},
{"depth", vid.depth}, {"depth", vid.depth},
{"camera", vid.camera}, {"camera", vid.camera},
@ -2603,22 +2612,22 @@ EX unordered_map<string, ld&> params = {
{"star", polygonal::STAR}, {"star", polygonal::STAR},
{"lvspeed", history::lvspeed}, {"lvspeed", history::lvspeed},
{"rotation", models::rotation}, {"rotation", models::rotation},
{"mori", models::model_orientation}, {"mori", pconf.model_orientation},
{"mori_yz", models::model_orientation_yz}, {"mori_yz", pconf.model_orientation_yz},
{"clipmin", models::clip_min}, {"clipmin", pconf.clip_min},
{"clipmax", models::clip_max}, {"clipmax", pconf.clip_max},
{"topz", models::top_z}, {"topz", pconf.top_z},
{"mtrans", models::model_transition}, {"mtrans", pconf.model_transition},
{"hp", models::halfplane_scale}, {"hp", pconf.halfplane_scale},
{"back", backbrightness}, {"back", backbrightness},
{"ipd", vid.ipd}, {"ipd", vid.ipd},
{"lr", vid.lr_eyewidth}, {"lr", vid.lr_eyewidth},
{"anaglyph", vid.anaglyph_eyewidth}, {"anaglyph", vid.anaglyph_eyewidth},
{"fov", vid.fov}, {"fov", vid.fov},
{"ets", vid.euclid_to_sphere}, {"ets", pconf.euclid_to_sphere},
{"stretch", vid.stretch}, {"stretch", pconf.stretch},
{"twopoint", vid.twopoint_param}, {"twopoint", pconf.twopoint_param},
{"fisheye", vid.fisheye_param}, {"fisheye", pconf.fisheye_param},
{"bwidth", vid.binary_width}, {"bwidth", vid.binary_width},
#if CAP_ANIMATIONS #if CAP_ANIMATIONS
{"aperiod", anims::period}, {"aperiod", anims::period},
@ -2630,10 +2639,10 @@ EX unordered_map<string, ld&> params = {
{"a", anims::a}, {"a", anims::a},
{"b", anims::b}, {"b", anims::b},
#endif #endif
{"mobius", vid.skiprope}, {"mobius", pconf.skiprope},
{"sang", models::spiral_angle}, {"sang", pconf.spiral_angle},
{"spiralx", models::spiral_x}, {"spiralx", pconf.spiral_x},
{"spiraly", models::spiral_y}, {"spiraly", pconf.spiral_y},
#if CAP_CRYSTAL #if CAP_CRYSTAL
{"cprob", crystal::compass_probability}, {"cprob", crystal::compass_probability},
#endif #endif
@ -2642,7 +2651,7 @@ EX unordered_map<string, ld&> params = {
{"fade", shot::fade}, {"fade", shot::fade},
{"mgrid", vid.multiplier_grid}, {"mgrid", vid.multiplier_grid},
{"mring", vid.multiplier_ring}, {"mring", vid.multiplier_ring},
{"collignon", vid.collignon_parameter}, {"collignon", pconf.collignon_parameter},
{"levellines", levellines}, {"levellines", levellines},
#endif #endif
}; };

View File

@ -322,7 +322,7 @@ MONSTER( 'Y', 0xFF8000, "Yendorian Researcher", moResearcher, ZERO, RESERVED, mo
"They have no special features, other than wearing a strange hat." "They have no special features, other than wearing a strange hat."
) )
MONSTER( 'K', 0xA8A8A8, "Sparrowhawk", moSparrowhawk, CF_FACE_SIDE | CF_BIRD | CF_FLYING | CF_IGNORE_PLATE, RESERVED, moEagle, MONSTER( 'K', 0xA8A8A8, "Sparrowhawk", moSparrowhawk, CF_FACE_SIDE | CF_BIRD | CF_FLYING | CF_IGNORE_PLATE, RESERVED, moEagle,
"A bird who hunts in the treetops of Yendorian Forest." "A bird who hunts in the treetops of the Yendorian Forest."
) )
MONSTER( 'K', 0xD0A0A0, "Kraken", moKrakenH, ZERO | CF_NOGHOST | CF_NOBLOW | CF_MULTITILE | CF_KRAKEN | CF_FACING, RESERVED, moNone, krakendesc) MONSTER( 'K', 0xD0A0A0, "Kraken", moKrakenH, ZERO | CF_NOGHOST | CF_NOBLOW | CF_MULTITILE | CF_KRAKEN | CF_FACING, RESERVED, moNone, krakendesc)
MONSTER( 'K', 0xC07070, "Kraken Tentacle", moKrakenT, ZERO | CF_NOGHOST | CF_NOBLOW | CF_PART | CF_MULTITILE | CF_KRAKEN, RESERVED, moNone, krakendesc) MONSTER( 'K', 0xC07070, "Kraken Tentacle", moKrakenT, ZERO | CF_NOGHOST | CF_NOBLOW | CF_PART | CF_MULTITILE | CF_KRAKEN, RESERVED, moNone, krakendesc)
@ -398,7 +398,7 @@ MONSTER( 'W', 0x202020, "Hunting Dog (regrouping)", moHunterChanging, ZERO, RESE
"When your plan has clearly failed, it is better to abandon it and go to a safe place, to have a chance of succeeding next time. This dog clearly knows this.") "When your plan has clearly failed, it is better to abandon it and go to a safe place, to have a chance of succeeding next time. This dog clearly knows this.")
MONSTER( 'B', 0xC00000, "North Pole", moNorthPole, ZERO | CF_MAGNETIC | CF_FACING, RESERVED, moYeti, NODESCYET) MONSTER( 'B', 0xC00000, "North Pole", moNorthPole, ZERO | CF_MAGNETIC | CF_FACING, RESERVED, moYeti, NODESCYET)
MONSTER( 'B', 0x0000C0, "South Pole", moSouthPole, ZERO | CF_MAGNETIC | CF_FACING, RESERVED, moYeti, NODESCYET) MONSTER( 'B', 0x0000C0, "South Pole", moSouthPole, ZERO | CF_MAGNETIC | CF_FACING, RESERVED, moYeti, NODESCYET)
MONSTER( 'P', 0xC03000, "Red Raider", moPair, CF_FACE_UP | CF_RAIDER | CF_FACING, RESERVED, moYeti, "Red Raiders travel in pairs. They have promised to always watch another one's back. They are able to destroy walls on their way.") MONSTER( 'P', 0xC03000, "Red Raider", moPair, CF_FACE_UP | CF_RAIDER | CF_FACING, RESERVED, moYeti, "Red Raiders travel in pairs. They have promised to always watch each other's backs. They are able to destroy walls on their way.")
MONSTER( 'H', 0xC0C0C0, "Gray Raider", moHexDemon, CF_FACE_UP | CF_RAIDER, RESERVED, moHexDemon, "Gray Raiders never step on gray cells.") MONSTER( 'H', 0xC0C0C0, "Gray Raider", moHexDemon, CF_FACE_UP | CF_RAIDER, RESERVED, moHexDemon, "Gray Raiders never step on gray cells.")
MONSTER( 'A', 0x80B080, "Green Raider", moAltDemon, CF_FACE_UP | CF_RAIDER, RESERVED, moAltDemon, "Green Raiders never step from one green cell to another.") MONSTER( 'A', 0x80B080, "Green Raider", moAltDemon, CF_FACE_UP | CF_RAIDER, RESERVED, moAltDemon, "Green Raiders never step from one green cell to another.")
MONSTER( 'M', 0x904000, "Brown Raider", moMonk, CF_FACE_UP | CF_RAIDER, RESERVED, moMonk, "Brown Raiders never move adjacent to an item.") MONSTER( 'M', 0x904000, "Brown Raider", moMonk, CF_FACE_UP | CF_RAIDER, RESERVED, moMonk, "Brown Raiders never move adjacent to an item.")
@ -665,7 +665,7 @@ ITEM( '%', 0x606060, "Black Lotus", itLotus, IC_TREASURE, ZERO, RESERVED, osNone
"without preparation.\n" "without preparation.\n"
) )
ITEM( 'o', 0x505050, "Orb of Undeath", itOrbUndeath, IC_ORB, ZERO | IF_EMPATHY | IF_SHMUPLIFE | IF_REVIVAL, RESERVED, osFriend, ITEM( 'o', 0x505050, "Orb of Undeath", itOrbUndeath, IC_ORB, ZERO | IF_EMPATHY | IF_SHMUPLIFE | IF_REVIVAL, RESERVED, osFriend,
"Monsters slain by you in melee are turned into friendly ghosts, " "Monsters slain by you in melee are turned into friendly ghosts. "
"Does not affect plants and friends." "Does not affect plants and friends."
) )
ITEM( '*', 0x8080FF, "White Dove Feather", itWindstone, IC_TREASURE, ZERO, RESERVED, osNone, ITEM( '*', 0x8080FF, "White Dove Feather", itWindstone, IC_TREASURE, ZERO, RESERVED, osNone,
@ -733,7 +733,7 @@ ITEM( 'o', 0x900000, "Orb of Domination", itOrbDomination, IC_ORB, ZERO | IF_RAN
"Simply move onto such a creature to ride them; while riding, you are protected from dangerous terrains " "Simply move onto such a creature to ride them; while riding, you are protected from dangerous terrains "
"and partially from attacks (they cause you to lose half of your Domination power), " "and partially from attacks (they cause you to lose half of your Domination power), "
"but you cannot collect items.\n\n" "but you cannot collect items.\n\n"
/*When only one charge is left, " /*"When only one charge is left, "
"you have to dismount this turn -- be very careful to make this possible, " "you have to dismount this turn -- be very careful to make this possible, "
"as your mount could attack you immediately!\n\n" */ "as your mount could attack you immediately!\n\n" */
"While riding, " "While riding, "
@ -849,7 +849,7 @@ ITEM( '*', 0xFFA860, "Tiger's Eye", itBrownian, IC_TREASURE, ZERO, RESERVED, osN
ITEM( '$', 0xF0C0C0, "Meteorite", itWest, IC_TREASURE, ZERO, RESERVED, osNone, ITEM( '$', 0xF0C0C0, "Meteorite", itWest, IC_TREASURE, ZERO, RESERVED, osNone,
"These rocks falling from the sky have been captured to fall forever in the artificial gravity. Meteorite iron is believed to be a valuable material for magical weapons.") "These rocks falling from the sky have been captured to fall forever in the artificial gravity. Meteorite iron is believed to be a valuable material for magical weapons.")
ITEM( '*', 0x30FF30, "Torbernite", itVarTreasure, IC_TREASURE, ZERO, RESERVED, osNone, ITEM( '*', 0x30FF30, "Torbernite", itVarTreasure, IC_TREASURE, ZERO, RESERVED, osNone,
"Crystals emiting magical radiation.") "Crystals emitting magical radiation.")
ITEM( 'o', 0x703800, "Orb of Intensity", itOrbIntensity, IC_ORB, ZERO, RESERVED, osNone, ITEM( 'o', 0x703800, "Orb of Intensity", itOrbIntensity, IC_ORB, ZERO, RESERVED, osNone,
"When you have this, initial and maximal charge amounts of all Orbs are increased by 20%." "When you have this, initial and maximal charge amounts of all Orbs are increased by 20%."
) )

View File

@ -14,8 +14,11 @@ EX bool outoffocus = false;
EX int mousex, mousey; EX int mousex, mousey;
EX hyperpoint mouseh, mouseoh; EX hyperpoint mouseh, mouseoh;
EX bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick; EX bool pandora_leftclick, pandora_rightclick;
EX bool forcetarget, lshiftclick, lctrlclick, numlock_on;
EX bool lshiftclick, rshiftclick, lctrlclick, rctrlclick, anyshiftclick, anyctrlclick, wheelclick;
EX bool targetclick, hiliteclick, forcetarget, numlock_on;
EX bool gtouched; EX bool gtouched;
EX bool holdmouse; EX bool holdmouse;
@ -76,7 +79,7 @@ EX movedir vectodir(hyperpoint P) {
transmatrix U = ggmatrix(cwt.at); transmatrix U = ggmatrix(cwt.at);
if(GDIM == 3 && WDIM == 2) U = radar_transform * U; if(GDIM == 3 && WDIM == 2) U = radar_transform * U;
P = direct_exp(lp_iapply(P), 100); P = direct_exp(lp_iapply(P));
hyperpoint H = sphereflip * tC0(U); hyperpoint H = sphereflip * tC0(U);
transmatrix Centered = sphereflip * rgpushxto0(H); transmatrix Centered = sphereflip * rgpushxto0(H);
@ -87,8 +90,8 @@ EX movedir vectodir(hyperpoint P) {
for(int i=0; i<cwt.at->type; i++) { for(int i=0; i<cwt.at->type; i++) {
transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin); transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin);
ld d1 = geo_dist(U * T * C0, Centered * P, iTable); ld d1 = geo_dist(U * T * C0, Centered * P);
ld d2 = geo_dist(U * T * C0, Centered * C0, iTable); ld d2 = geo_dist(U * T * C0, Centered * C0);
dirdist[i] = d1 - d2; dirdist[i] = d1 - d2;
//xspinpush0(-i * 2 * M_PI /cwt.at->type, .5), P); //xspinpush0(-i * 2 * M_PI /cwt.at->type, .5), P);
} }
@ -301,6 +304,80 @@ transmatrix zforward_push(ld z) {
return T; return T;
} }
EX void zoom_or_fov(ld t) {
if(in_perspective()) {
auto tanfov = tan(vid.fov * degree / 2);
tanfov *= t;
vid.fov = atan(tanfov) * 2 / degree;
}
else
vpconf.scale *= t;
}
EX void full_forward_camera(ld t) {
if(anyshiftclick)
zoom_or_fov(exp(-t/10.));
else if(GDIM == 3) {
shift_view(ctangent(2, t));
didsomething = true;
playermoved = false;
}
}
EX void full_rotate_camera(int dir, ld val) {
if(rug::rug_control() && lshiftclick) {
hyperpoint h;
if(nonisotropic) {
transmatrix T2 = eupush( tC0(inverse(View)) );
transmatrix nlp = View * T2;
auto rV = inverse(nlp) * View;
h = nlp * inverse_exp(tC0(rV));
}
else h = inverse_exp(tC0(View));
shift_view(-h);
rotate_view(cspin(dir, 2, val));
shift_view(h);
}
else if(history::on)
history::lvspeed += (dir?1:-1) * val / 2;
else if(GDIM == 3 && rshiftclick)
shift_view(ctangent(dir, -val)), didsomething = true, playermoved = false; /* -val because shift reverses */
#if CAP_CRYSTAL
else if(rug::rug_control() && rug::in_crystal())
crystal::apply_rotation(cspin(dir, 2, val));
#endif
else if(GDIM == 3) {
if(keep_vertical()) {
hyperpoint vv = vertical_vector();
ld alpha = -atan2(vv[2], vv[1]);
rotate_view(cspin(2, 1, alpha));
ld max_angle = quarter_circle - 1e-4;
if(dir == 1 && alpha + val > max_angle)
val = max_angle - alpha;
if(dir == 1 && alpha + val < -max_angle)
val = -max_angle - alpha;
rotate_view(cspin(dir, 2, val));
rotate_view(cspin(1, 2, alpha));
}
else
rotate_view(cspin(dir, 2, val));
if(!rug::rug_control()) didsomething = true;
}
else
shift_view(ctangent(dir, val)), playermoved = false, didsomething = true;
}
EX void full_rotate_view(ld h, ld v) {
if(history::on && !rug::rug_control())
models::rotation += h;
else {
rotate_view(spin(v));
didsomething = true;
if(isGravityLand(cwt.at->land) && !rug::rug_control())
playermoved = false;
}
}
EX void handlePanning(int sym, int uni) { EX void handlePanning(int sym, int uni) {
if(mousepan && dual::split([=] { handlePanning(sym, uni); })) return; if(mousepan && dual::split([=] { handlePanning(sym, uni); })) return;
if(GDIM == 3) { if(GDIM == 3) {
@ -308,71 +385,30 @@ EX void handlePanning(int sym, int uni) {
if(sym == PSEUDOKEY_WHEELDOWN) shift_view(ztangent(0.05*shiftmul)), didsomething = true, playermoved = false; if(sym == PSEUDOKEY_WHEELDOWN) shift_view(ztangent(0.05*shiftmul)), didsomething = true, playermoved = false;
} }
if(rug::rugged || smooth_scrolling) { rug::using_rugview urv;
return;
}
#if !ISPANDORA #if !ISPANDORA
if(sym == SDLK_END && GDIM == 3) { if(!smooth_scrolling) {
shift_view(ztangent(-0.2*shiftmul)), didsomething = true, playermoved = false; if(sym == SDLK_END) full_forward_camera(-0.2*shiftmul);
} if(sym == SDLK_HOME) full_forward_camera(0.2*shiftmul);
if(sym == SDLK_HOME && GDIM == 3) { if(sym == SDLK_RIGHT) full_rotate_camera(0, -0.2*shiftmul);
shift_view(ztangent(+0.2*shiftmul)), didsomething = true, playermoved = false; if(sym == SDLK_LEFT) full_rotate_camera(0, 0.2*shiftmul);
} if(sym == SDLK_UP) full_rotate_camera(1, 0.2*shiftmul);
if(sym == SDLK_RIGHT) { if(sym == SDLK_DOWN) full_rotate_camera(1, -0.2*shiftmul);
if(history::on)
history::lvspeed += 0.1 * shiftmul;
else if(GDIM == 3)
rotate_view(cspin(0, 2, -0.2*shiftmul)), didsomething = true;
else
View = xpush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_LEFT) {
if(history::on)
history::lvspeed -= 0.1 * shiftmul;
else if(GDIM == 3)
rotate_view(cspin(0, 2, 0.2*shiftmul)), didsomething = true;
else
View = xpush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_UP) {
if(history::on)
history::lvspeed += 0.1 * shiftmul;
else if(GDIM == 3)
rotate_view(cspin(1, 2, 0.2*shiftmul)), didsomething = true;
else
View = ypush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_DOWN) {
if(history::on)
history::lvspeed -= 0.1 * shiftmul;
else if(GDIM == 3)
rotate_view(cspin(1, 2, -0.2*shiftmul)), didsomething = true;
else
View = ypush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
} }
#endif #endif
if(sym == SDLK_PAGEUP) { if(!smooth_scrolling) {
if(history::on) if(sym == SDLK_PAGEUP) full_rotate_view(1, M_PI/cgi.S21/2*shiftmul);
models::rotation++; if(sym == SDLK_PAGEDOWN) full_rotate_view(-1, -M_PI/cgi.S21/2*shiftmul);
else if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
rotate_view(spin(M_PI/cgi.S21/2*shiftmul)), didsomething = true; if(isGravityLand(cwt.at->land) && !rug::rug_control()) playermoved = false;
} }
if(sym == SDLK_PAGEDOWN) {
if(history::on)
models::rotation++;
else
rotate_view(spin(-M_PI/cgi.S21/2*shiftmul)), didsomething = true;
}
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
if(isGravityLand(cwt.at->land)) playermoved = false;
if(sym == PSEUDOKEY_WHEELUP && GDIM == 2) { if(sym == PSEUDOKEY_WHEELUP && GDIM == 2) {
ld jx = (mousex - current_display->xcenter - .0) / current_display->radius / 10; ld jx = (mousex - current_display->xcenter - .0) / current_display->radius / 10;
ld jy = (mousey - current_display->ycenter - .0) / current_display->radius / 10; ld jy = (mousey - current_display->ycenter - .0) / current_display->radius / 10;
playermoved = false; playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View; rotate_view(gpushxto0(hpxy(jx, jy)));
sym = 1; sym = 1;
} }
} }
@ -433,8 +469,8 @@ EX void handleKeyNormal(int sym, int uni) {
if(DEFAULTCONTROL) { if(DEFAULTCONTROL) {
if(sym == SDLK_RIGHT) movepckeydir(0); if(sym == SDLK_RIGHT) movepckeydir(0);
if(sym == SDLK_LEFT) movepckeydir(4); if(sym == SDLK_LEFT) movepckeydir(4);
if(sym == SDLK_DOWN) movepckeydir(2 + (leftclick?1:0) - (rightclick?1:0)); if(sym == SDLK_DOWN) movepckeydir(2 + (pandora_leftclick?1:0) - (pandora_rightclick?1:0));
if(sym == SDLK_UP) movepckeydir(6 - (leftclick?1:0) + (rightclick?1:0)); if(sym == SDLK_UP) movepckeydir(6 - (pandora_leftclick?1:0) + (pandora_rightclick?1:0));
} }
#endif #endif
@ -493,8 +529,12 @@ EX void handleKeyNormal(int sym, int uni) {
pushScreen(inv::show); pushScreen(inv::show);
#endif #endif
if(((sym == SDLK_HOME && GDIM == 2) || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) if((sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) {
fullcenter(); if(rug::rug_control())
rug::reset_view();
else
fullcenter();
}
if(sym == 'v' && DEFAULTNOR(sym)) if(sym == 'v' && DEFAULTNOR(sym))
pushScreen(showMainMenu); pushScreen(showMainMenu);
@ -611,7 +651,7 @@ EX void mainloopiter() {
if(cwt.mirrored) playerV = playerV * Mirror; if(cwt.mirrored) playerV = playerV * Mirror;
} }
mousepan = (cmode & (sm::NORMAL | sm::DRAW | sm::MAP)) && GDIM == 3 && mouseaim_sensitivity; mousepan = (cmode & (sm::NORMAL | sm::DRAW | sm::MAP)) && (GDIM == 3 || (rug::rugged && lctrlclick)) && mouseaim_sensitivity;
if(mousepan != oldmousepan) { if(mousepan != oldmousepan) {
oldmousepan = mousepan; oldmousepan = mousepan;
#if CAP_MOUSEGRAB #if CAP_MOUSEGRAB
@ -657,13 +697,20 @@ EX void mainloopiter() {
} }
Uint8 *keystate = SDL_GetKeyState(NULL); Uint8 *keystate = SDL_GetKeyState(NULL);
rightclick = keystate[SDLK_RCTRL];
leftclick = keystate[SDLK_RSHIFT]; pandora_rightclick = keystate[SDLK_RCTRL];
lctrlclick = keystate[SDLK_LCTRL]; pandora_leftclick = keystate[SDLK_RSHIFT];
lshiftclick = keystate[SDLK_LSHIFT]; lshiftclick = keystate[SDLK_LSHIFT];
forcetarget = (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]); rshiftclick = keystate[SDLK_RSHIFT];
anyshiftclick = lshiftclick | rshiftclick;
lctrlclick = keystate[SDLK_LCTRL];
rctrlclick = keystate[SDLK_RCTRL];
anyctrlclick = lctrlclick | rctrlclick;
forcetarget = anyshiftclick;
hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT]; hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT];
anyshiftclick = keystate[SDLK_LSHIFT] | keystate[SDLK_RSHIFT];
wheelclick = false; wheelclick = false;
getcshift = 1; getcshift = 1;
@ -674,11 +721,14 @@ EX void mainloopiter() {
didsomething = false; didsomething = false;
if(vid.shifttarget&1) { if(vid.shifttarget&1) {
leftclick = false; #if ISPANDORA
targetclick = pandora_leftclick | pandora_rightclick;
pandora_leftclick = pandora_rightclick = 0;
#else
targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]; targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT];
#endif
} }
else { else {
leftclick = keystate[SDLK_RSHIFT];
targetclick = true; targetclick = true;
} }
@ -689,42 +739,31 @@ EX void mainloopiter() {
SDL_Event ev; SDL_Event ev;
DEBB(DF_GRAPH, ("polling for events\n")); DEBB(DF_GRAPH, ("polling for events\n"));
if(GDIM == 3 && !shmup::on && !rug::rugged) { if((GDIM == 3 && !shmup::on) || (lctrlclick && rug::rugged)) {
#if CAP_MOUSEGRAB #if CAP_MOUSEGRAB
rotate_view(cspin(0, 2, -mouseaim_x) * cspin(1, 2, -mouseaim_y)); rug::using_rugview urv;
dynamicval<bool> ds(didsomething, didsomething);
full_rotate_camera(0, -mouseaim_x);
full_rotate_camera(1, -mouseaim_y);
mouseaim_x = mouseaim_y = 0; mouseaim_x = mouseaim_y = 0;
#endif #endif
} }
if(smooth_scrolling && !shmup::on && !rug::rugged) { if(smooth_scrolling && !shmup::on) {
rug::using_rugview urv;
static int lastticks; static int lastticks;
ld t = (ticks - lastticks) * shiftmul / 1000.; ld t = (ticks - lastticks) * shiftmul / 1000.;
lastticks = ticks; lastticks = ticks;
Uint8 *keystate = SDL_GetKeyState(NULL); Uint8 *keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_END] && GDIM == 3 && DEFAULTNOR(SDLK_END))
shift_view(ctangent(2, -t)), didsomething = true, playermoved = false; if(keystate[SDLK_END] && GDIM == 3 && DEFAULTNOR(SDLK_END)) full_forward_camera(-t);
if(keystate[SDLK_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME)) if(keystate[SDLK_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME)) full_forward_camera(t);
shift_view(ctangent(2, t)), didsomething = true, playermoved = false; if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) full_rotate_camera(0, -t);
if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) full_rotate_camera(0, t);
rotate_view(GDIM == 2 ? xpush(-t) : cspin(0, 2, -t)), didsomething = true, playermoved = playermoved && GDIM == 3; if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) full_rotate_camera(1, t);
if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) full_rotate_camera(1, -t);
rotate_view(GDIM == 2 ? xpush(t) : cspin(0, 2, t)), didsomething = true, playermoved = playermoved && GDIM == 3; if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) full_rotate_view(t * 180 / M_PI, t);
if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) if(keystate[SDLK_PAGEDOWN] && DEFAULTNOR(SDLK_PAGEDOWN)) full_rotate_view(-t * 180 / M_PI, t);
rotate_view(GDIM == 2 ? ypush(t) : cspin(1, 2, t)), didsomething = true, playermoved = playermoved && GDIM == 3;
if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN))
rotate_view(GDIM == 2 ? ypush(-t) : cspin(1, 2, -t)), didsomething = true, playermoved = playermoved && GDIM == 3;
if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) {
if(history::on)
models::rotation+=t;
else
rotate_view(spin(t)), didsomething = true;
}
if(keystate[SDLK_PAGEDOWN] && DEFAULTNOR(SDLK_PAGEDOWN)) {
if(history::on)
models::rotation-=t;
else
rotate_view(spin(-t)), didsomething = true;
}
} }
achievement_pump(); achievement_pump();
@ -737,8 +776,6 @@ EX void mainloopiter() {
EX void handle_event(SDL_Event& ev) { EX void handle_event(SDL_Event& ev) {
bool normal = cmode & sm::NORMAL; bool normal = cmode & sm::NORMAL;
Uint8 *keystate = SDL_GetKeyState(NULL);
DEBB(DF_GRAPH, ("got event type #%d\n", ev.type)); DEBB(DF_GRAPH, ("got event type #%d\n", ev.type));
int sym = 0; int sym = 0;
int uni = 0; int uni = 0;
@ -833,9 +870,6 @@ EX void handle_event(SDL_Event& ev) {
bool rollchange = (cmode & sm::OVERVIEW) && getcstat >= 2000 && cheater; bool rollchange = (cmode & sm::OVERVIEW) && getcstat >= 2000 && cheater;
bool anyctrl = keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL];
bool anyshift = keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT];
if(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) { if(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) {
mousepressed = ev.type == SDL_MOUSEBUTTONDOWN; mousepressed = ev.type == SDL_MOUSEBUTTONDOWN;
if(mousepressed) flashMessages(); if(mousepressed) flashMessages();
@ -858,49 +892,47 @@ EX void handle_event(SDL_Event& ev) {
if(was_holdmouse && ev.type == SDL_MOUSEBUTTONUP) if(was_holdmouse && ev.type == SDL_MOUSEBUTTONUP)
sym = uni = PSEUDOKEY_RELEASE; sym = uni = PSEUDOKEY_RELEASE;
/* simulate RMB and MMB for Mac users etc. */
if(ev.button.button == SDL_BUTTON_LEFT) {
if(ISPANDORA ? pandora_rightclick : lctrlclick)
ev.button.button = SDL_BUTTON_MIDDLE;
else if((ISPANDORA ? pandora_leftclick : lshiftclick) && !(vid.shifttarget&1))
ev.button.button = SDL_BUTTON_RIGHT;
}
if(!act) ; if(!act) ;
else if(ev.button.button==SDL_BUTTON_RIGHT || leftclick) else if(ev.button.button==SDL_BUTTON_RIGHT)
sym = SDLK_F1; sym = SDLK_F1;
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick) { else if(ev.button.button==SDL_BUTTON_MIDDLE)
sym = 1, didsomething = true; sym = 1, didsomething = true;
if(anyshift)
vid.xposition = vid.yposition = 0;
}
else if(ev.button.button == SDL_BUTTON_LEFT) { else if(ev.button.button == SDL_BUTTON_LEFT) {
sym = getcstat, uni = getcstat, shiftmul = getcshift; sym = getcstat, uni = getcstat, shiftmul = getcshift;
} }
else if(ev.button.button==SDL_BUTTON_WHEELDOWN) { else if(ev.button.button==SDL_BUTTON_WHEELDOWN || ev.button.button == SDL_BUTTON_WHEELUP) {
if(anyctrl && anyshift && !rug::rugged && GDIM == 2) { ld dir = ev.button.button == SDL_BUTTON_WHEELUP ? 0.25 : -0.25;
mapeditor::scaleall(1/1.2); if(lshiftclick && rshiftclick && !rug::rugged && GDIM == 2) {
vid.alpha /= 1.2; mapeditor::scaleall(pow(2, dir), lctrlclick);
pconf.alpha *= pow(2, dir);
}
else if(lshiftclick && GDIM == 2)
mapeditor::scaleall(pow(2, dir), lctrlclick);
else if(rshiftclick && !rug::rugged && GDIM == 2)
pconf.alpha -= dir;
else if(lctrlclick) {
if(dir>0) {
pconf.xposition += (.0 + mousex - current_display->xcenter) / vpconf.scale / current_display->scrsize;
pconf.yposition += (.0 + mousey - current_display->ycenter) / vpconf.scale / current_display->scrsize;
}
else
pconf.xposition = pconf.yposition = 0;
} }
else if(anyctrl && !rug::rugged && GDIM == 2)
mapeditor::scaleall(pow(2, -.25));
else if(anyshift && !rug::rugged && GDIM == 2)
vid.alpha -= 0.25;
else if(rollchange) { else if(rollchange) {
sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true; sym = getcstat, uni = getcstat, shiftmul = -dir*4*getcshift, wheelclick = true;
} }
else { else {
sym = uni = PSEUDOKEY_WHEELDOWN; sym = uni = dir > 0 ? PSEUDOKEY_WHEELUP : PSEUDOKEY_WHEELDOWN;
}
}
if(ev.button.button==SDL_BUTTON_WHEELUP) {
if(anyctrl && anyshift && !rug::rugged && GDIM == 2) {
mapeditor::scaleall(1.2);
vid.alpha *= 1.2;
}
else if(anyctrl && !rug::rugged && GDIM == 2)
mapeditor::scaleall(pow(2, .25));
else if(anyshift && !rug::rugged && GDIM == 2)
vid.alpha += 0.25;
else if(rollchange) {
sym = getcstat, uni = getcstat, shiftmul = -getcshift, wheelclick = true;
}
else {
sym = uni = PSEUDOKEY_WHEELUP;
} }
} }
} }
@ -926,11 +958,11 @@ EX void handle_event(SDL_Event& ev) {
if(holdmouse && getcstat == '-') sym = uni = getcstat, fix_mouseh(); if(holdmouse && getcstat == '-') sym = uni = getcstat, fix_mouseh();
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2()) { if(((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2()) {
fix_mouseh(); fix_mouseh();
if(anyctrl) { if(lctrlclick) {
vid.xposition += (mousex - lmousex) * 1. / current_display->scrsize, pconf.xposition += (mousex - lmousex) * 1. / current_display->scrsize,
vid.yposition += (mousey - lmousey) * 1. / current_display->scrsize; pconf.yposition += (mousey - lmousey) * 1. / current_display->scrsize;
} }
else if(mouseh[LDIM] < 50 && mouseoh[LDIM] < 50) { else if(mouseh[LDIM] < 50 && mouseoh[LDIM] < 50) {
panning(mouseoh, mouseh); panning(mouseoh, mouseh);
@ -959,17 +991,17 @@ EX void handle_event(SDL_Event& ev) {
else quitmainloop = true; else quitmainloop = true;
} }
if(sym == SDLK_F4 && anyshift) { if(sym == SDLK_F4 && anyshiftclick) {
nomap = !nomap; nomap = !nomap;
sym = 0; sym = 0;
} }
if(sym == SDLK_F2 && anyshift) { if(sym == SDLK_F2 && anyshiftclick) {
nohud = !nohud; nohud = !nohud;
sym = 0; sym = 0;
} }
if(sym == SDLK_F3 && anyshift) { if(sym == SDLK_F3 && anyshiftclick) {
nofps = !nofps; nofps = !nofps;
sym = 0; sym = 0;
} }
@ -989,7 +1021,7 @@ EX void mainloop() {
#endif #endif
} }
#if ISMOBILE==1 #if ISMOBILE
EX void displayabutton(int px, int py, string s, int col) { EX void displayabutton(int px, int py, string s, int col) {
// TMP // TMP
int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2; int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2;
@ -1047,7 +1079,7 @@ EX bool gmodekeys(int sym, int uni) {
if(GDIM == 2) { if(GDIM == 2) {
if(among(NUMBERKEY, '1', '2', '3') && !rug::rugged && euclid && WDIM == 2) { if(among(NUMBERKEY, '1', '2', '3') && !rug::rugged && euclid && WDIM == 2) {
vid.xposition = vid.yposition = 0; pconf.xposition = pconf.yposition = 0;
ld maxs = 0; ld maxs = 0;
auto& cd = current_display; auto& cd = current_display;
for(auto& p: gmatrix) for(int i=0; i<p.first->type; i++) { for(auto& p: gmatrix) for(int i=0; i<p.first->type; i++) {
@ -1057,15 +1089,15 @@ EX bool gmodekeys(int sym, int uni) {
maxs = max(maxs, onscreen[0] / cd->xsize); maxs = max(maxs, onscreen[0] / cd->xsize);
maxs = max(maxs, onscreen[1] / cd->ysize); maxs = max(maxs, onscreen[1] / cd->ysize);
} }
vid.alpha = 1; pconf.alpha = 1;
vid.scale = vid.scale / 2 / maxs / cd->radius; pconf.scale = pconf.scale / 2 / maxs / cd->radius;
if(NUMBERKEY == '3') vid.scale *= 2; if(NUMBERKEY == '3') pconf.scale *= 2;
if(NUMBERKEY == '1') vid.scale /= 2; if(NUMBERKEY == '1') pconf.scale /= 2;
} }
else if(NUMBERKEY == '1' && !rug::rugged) { vid.alpha = 999; vid.scale = 998; vid.xposition = vid.yposition = 0; } else if(NUMBERKEY == '1' && !rug::rugged) { pconf.alpha = 999; pconf.scale = 998; pconf.xposition = pconf.yposition = 0; }
else if(NUMBERKEY == '2' && !rug::rugged) { vid.alpha = 1; vid.scale = 0.4; vid.xposition = vid.yposition = 0; } else if(NUMBERKEY == '2' && !rug::rugged) { pconf.alpha = 1; pconf.scale = 0.4; pconf.xposition = pconf.yposition = 0; }
else if(NUMBERKEY == '3' && !rug::rugged) { vid.alpha = 1; vid.scale = 1; vid.xposition = vid.yposition = 0; } else if(NUMBERKEY == '3' && !rug::rugged) { pconf.alpha = 1; pconf.scale = 1; pconf.xposition = pconf.yposition = 0; }
else if(NUMBERKEY == '4' && !rug::rugged) { vid.alpha = 0; vid.scale = 1; vid.xposition = vid.yposition = 0; } else if(NUMBERKEY == '4' && !rug::rugged) { pconf.alpha = 0; pconf.scale = 1; pconf.xposition = pconf.yposition = 0; }
else if(NUMBERKEY == '5') { vid.wallmode += 60 + (shiftmul > 0 ? 1 : -1); vid.wallmode %= 7; } else if(NUMBERKEY == '5') { vid.wallmode += 60 + (shiftmul > 0 ? 1 : -1); vid.wallmode %= 7; }
else if(NUMBERKEY == '8') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 6; } else if(NUMBERKEY == '8') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 6; }
else if(uni == '%') { else if(uni == '%') {
@ -1183,10 +1215,10 @@ EX void show() {
dialog::addItem(XLAT("experiment with geometry"), 'g'); dialog::addItem(XLAT("experiment with geometry"), 'g');
dialog::add_action([] () { runGeometryExperiments(); }); dialog::add_action([] () { runGeometryExperiments(); });
dialog::addSelItem(XLAT("projection"), fts(vid.alpha), 'p'); dialog::addSelItem(XLAT("projection"), fts(vpconf.alpha), 'p');
dialog::add_action([] () { projectionDialog(); }); dialog::add_action([] () { projectionDialog(); });
dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z'); dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z');
dialog::add_action([] () { editScale(); }); dialog::add_action([] () { editScale(); });
dialog::addItem(XLAT("spherical VR"), 'v'); dialog::addItem(XLAT("spherical VR"), 'v');
@ -1195,7 +1227,7 @@ EX void show() {
mode = 0; fullcenter(); mode = 0; fullcenter();
mode = 2; sensitivity = 1; mode = 2; sensitivity = 1;
vid.stereo_mode = sLR; vid.ipd = 0.2; vid.stereo_mode = sLR; vid.ipd = 0.2;
vid.alpha = 0; vid.scale = 1; vpconf.alpha = 0; vpconf.scale = 1;
}); });
dialog::addBreak(100); dialog::addBreak(100);

View File

@ -461,7 +461,7 @@ shifttable get_canonical(coord co) {
} }
#endif #endif
int crystal_period = 0; EX int crystal_period = 0;
struct hrmap_crystal : hrmap_standard { struct hrmap_crystal : hrmap_standard {
heptagon *getOrigin() override { return get_heptagon_at(c0, S7); } heptagon *getOrigin() override { return get_heptagon_at(c0, S7); }
@ -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; for(int a=0; a<3; a++) co[a] = i%5, i /= 5;
} }
#endif #endif
else if(euclid) { else if(euc::in()) {
auto tab = euc::get_ispacemap()[c->master]; auto tab = euc::get_ispacemap()[c->master];
for(int a=0; a<3; a++) co[a] = tab[a]; for(int a=0; a<3; a++) co[a] = tab[a];
if(PURE) for(int a=0; a<3; a++) co[a] *= 2; if(PURE) for(int a=0; a<3; a++) co[a] *= 2;
@ -822,12 +822,12 @@ EX bool crystal_cell(cell *c, transmatrix V) {
if(!cryst) return false; if(!cryst) return false;
if(view_east && cheater) { if(view_east && allowIncreasedSight()) {
int d = dist_alt(c); int d = dist_alt(c);
queuestr(V, 0.3, its(d), 0xFFFFFF, 1); queuestr(V, 0.3, its(d), 0xFFFFFF, 1);
} }
if(view_coordinates && cheater && WDIM == 2) { if(view_coordinates && WDIM == 2 && allowIncreasedSight()) {
auto m = crystal_map(); auto m = crystal_map();
@ -1230,8 +1230,8 @@ void cut_triangle2(const hyperpoint pa, const hyperpoint pb, const hyperpoint pc
rug::rugpoint *rac = rug::addRugpoint(hac, 0); rug::rugpoint *rac = rug::addRugpoint(hac, 0);
rug::rugpoint *rbc = rug::addRugpoint(hbc, 0); rug::rugpoint *rbc = rug::addRugpoint(hbc, 0);
rac->flat = pac; rac->native = pac;
rbc->flat = pbc; rbc->native = pbc;
rac->valid = true; rac->valid = true;
rbc->valid = true; rbc->valid = true;
rug::triangles.push_back(rug::triangle(rac, rbc, NULL)); rug::triangles.push_back(rug::triangle(rac, rbc, NULL));
@ -1270,7 +1270,7 @@ EX void build_rugdata() {
if(!draw_cut) { if(!draw_cut) {
rugpoint *v = addRugpoint(tC0(V), 0); rugpoint *v = addRugpoint(tC0(V), 0);
v->flat = coord_to_flat(co); v->native = coord_to_flat(co);
v->valid = true; v->valid = true;
rugpoint *p[MAX_EDGE_CRYSTAL]; rugpoint *p[MAX_EDGE_CRYSTAL];
@ -1278,7 +1278,7 @@ EX void build_rugdata() {
for(int i=0; i<c->type; i++) { for(int i=0; i<c->type; i++) {
p[i] = addRugpoint(V * get_corner_position(c, i), 0); p[i] = addRugpoint(V * get_corner_position(c, i), 0);
p[i]->valid = true; p[i]->valid = true;
p[i]->flat = coord_to_flat(vcoord[i]); p[i]->native = coord_to_flat(vcoord[i]);
} }
for(int i=0; i<c->type; i++) addTriangle(v, p[i], p[(i+1) % c->type]); for(int i=0; i<c->type; i++) addTriangle(v, p[i], p[(i+1) % c->type]);

View File

@ -6,6 +6,20 @@ namespace hr {
namespace tests { namespace tests {
string test_eq(hyperpoint h1, hyperpoint h2, ld err = 1e-6) {
if(sqhypot_d(MDIM, h1 -h2) < err)
return "OK";
else
return "ERROR";
}
string test_eq(transmatrix T1, transmatrix T2, ld err = 1e-6) {
if(eqmatrix(T1, T2, err))
return "OK";
else
return "ERROR";
}
int readArgs() { int readArgs() {
using namespace arg; using namespace arg;
@ -58,16 +72,30 @@ int readArgs() {
int cx = (co + 1) % WDIM; int cx = (co + 1) % WDIM;
int cy = (co + 2) % WDIM; int cy = (co + 2) % WDIM;
auto oxy = [&] (ld x, ld y, ld z) { hyperpoint h = Hypc; h[co] = z; h[cx] = x; if(WDIM == 3) h[cy] = y; return tC0(bt::normalized_at(h)); }; auto oxy = [&] (ld x, ld y, ld z) { hyperpoint h = Hypc; h[co] = z; h[cx] = x; if(WDIM == 3) h[cy] = y; return tC0(bt::normalized_at(h)); };
ld shrunk_x = geo_dist(oxy(0,0,-1), oxy(.01,0,-1), iTable); ld shrunk_x = geo_dist(oxy(0,0,-1), oxy(.01,0,-1));
ld shrunk_y = geo_dist(oxy(0,0,-1), oxy(0,.01,-1), iTable); ld shrunk_y = geo_dist(oxy(0,0,-1), oxy(0,.01,-1));
ld expand_x = geo_dist(oxy(0,0,+1), oxy(.01,0,+1), iTable); ld expand_x = geo_dist(oxy(0,0,+1), oxy(.01,0,+1));
ld expand_y = geo_dist(oxy(0,0,+1), oxy(0,.01,+1), iTable); ld expand_y = geo_dist(oxy(0,0,+1), oxy(0,.01,+1));
if(WDIM == 2) shrunk_y = expand_y = 1; if(WDIM == 2) shrunk_y = expand_y = 1;
println(hlog, "should be 1: ", lalign(10, (shrunk_x * shrunk_y * bt::area_expansion_rate()) / (expand_x * expand_y)), " : ", tie(shrunk_x, shrunk_y, expand_x, expand_y, aer)); println(hlog, "should be 1: ", lalign(10, (shrunk_x * shrunk_y * bt::area_expansion_rate()) / (expand_x * expand_y)), " : ", tie(shrunk_x, shrunk_y, expand_x, expand_y, aer));
if(geometry == gArnoldCat) if(geometry == gArnoldCat)
println(hlog, "(but not in Arnold's cat)"); println(hlog, "(but not in Arnold's cat)");
} }
} }
else if(argis("-test-push")) {
PHASEFROM(3);
for(eGeometry g: {gSol, gNil, gCubeTiling, gSpace534, gCell120}) {
stop_game();
set_geometry(g);
println(hlog, "testing geometry: ", geometry_name());
hyperpoint h = hyperpoint(.1, .2, .3, 1);
h = normalize(h);
println(hlog, "h = ", h);
println(hlog, "test rgpushxto0: ", test_eq(rgpushxto0(h) * C0, h));
println(hlog, "test gpushxto0: ", test_eq(gpushxto0(h) * h, C0));
println(hlog, "test inverses: ", test_eq(inverse(rgpushxto0(h)), gpushxto0(h)));
}
}
else return 1; else return 1;
return 0; return 0;
} }

View File

@ -803,7 +803,7 @@ EX namespace dialog {
else else
addSlider(ne.sc.direct(ne.vmin), ne.sc.direct(*ne.editwhat), ne.sc.direct(ne.vmax), 500); addSlider(ne.sc.direct(ne.vmin), ne.sc.direct(*ne.editwhat), ne.sc.direct(ne.vmax), 500);
addBreak(100); addBreak(100);
#if ISMOBILE==0 #if !ISMOBILE
addHelp(XLAT("You can scroll with arrow keys -- Ctrl to fine-tune")); addHelp(XLAT("You can scroll with arrow keys -- Ctrl to fine-tune"));
addBreak(100); addBreak(100);
#endif #endif
@ -1263,6 +1263,10 @@ EX namespace dialog {
else act(); else act();
} }
inline void push_confirm_dialog(const reaction_t& act, const string& s) {
pushScreen([act, s] () { confirm_dialog(s, act); });
}
inline reaction_t add_confirmation(const reaction_t& act) { inline reaction_t add_confirmation(const reaction_t& act) {
return [act] { do_if_confirmed(act); }; return [act] { do_if_confirmed(act); };
} }

View File

@ -66,7 +66,7 @@ void launch(int seed, int elimit, int hlimit) {
dual::switch_to(0); dual::switch_to(0);
specialland = firstland = laCanvas; specialland = firstland = laCanvas;
canvas_default_wall = waSea; canvas_default_wall = waSea;
vid.scale = .5; pconf.scale = .5;
dual::switch_to(1); dual::switch_to(1);
specialland = firstland = laCanvas; specialland = firstland = laCanvas;
shrand(seed); shrand(seed);

View File

@ -34,6 +34,9 @@ static const int POLY_ALWAYS_IN = (1<<21); // always draw this
static const int POLY_TRIANGLES = (1<<22); // made of TRIANGLES, not TRIANGLE_FAN static const int POLY_TRIANGLES = (1<<22); // made of TRIANGLES, not TRIANGLE_FAN
static const int POLY_INTENSE = (1<<23); // extra intense colors static const int POLY_INTENSE = (1<<23); // extra intense colors
static const int POLY_DEBUG = (1<<24); // debug this shape static const int POLY_DEBUG = (1<<24); // debug this shape
static const int POLY_PRINTABLE = (1<<25); // these walls are printable
static const int POLY_FAT = (1<<26); // fatten this model in WRL export (used for Rug)
static const int POLY_SHADE_TEXTURE = (1<<27); // texture has 'z' coordinate for shading
/** \brief A graphical element that can be drawn. Objects are not drawn immediately but rather queued. /** \brief A graphical element that can be drawn. Objects are not drawn immediately but rather queued.
* *
@ -181,6 +184,7 @@ vector<glvertex> line_vertices;
#endif #endif
EX void glflush() { EX void glflush() {
DEBBI(DF_GRAPH, ("glflush"));
#if MINIMIZE_GL_CALLS #if MINIMIZE_GL_CALLS
if(isize(triangle_vertices)) { if(isize(triangle_vertices)) {
// printf("%08X %08X | %d shapes, %d/%d vertices\n", triangle_color, line_color, shapes_merged, isize(triangle_vertices), isize(line_vertices)); // printf("%08X %08X | %d shapes, %d/%d vertices\n", triangle_color, line_color, shapes_merged, isize(triangle_vertices), isize(line_vertices));
@ -235,7 +239,7 @@ EX void glflush() {
} }
#endif #endif
#if ISMOBILE==0 #if !ISMOBILE
SDL_Surface *aux; SDL_Surface *aux;
#endif #endif
@ -262,13 +266,13 @@ void add1(const hyperpoint& H) {
} }
bool is_behind(const hyperpoint& H) { bool is_behind(const hyperpoint& H) {
return pmodel == mdDisk && (hyperbolic ? H[2] >= 0 : true) && (nonisotropic ? false : vid.alpha + H[2] <= BEHIND_LIMIT); return pmodel == mdDisk && (hyperbolic ? H[2] >= 0 : true) && (nonisotropic ? false : pconf.alpha + H[2] <= BEHIND_LIMIT);
} }
hyperpoint be_just_on_view(const hyperpoint& H1, const hyperpoint &H2) { hyperpoint be_just_on_view(const hyperpoint& H1, const hyperpoint &H2) {
// H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - vid.alpha // H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - pconf.alpha
// H2[2]- BEHIND_LIMIT + vid.alpha = t * (H2[2] - H1[2]) // H2[2]- BEHIND_LIMIT + pconf.alpha = t * (H2[2] - H1[2])
ld t = (H2[2] - BEHIND_LIMIT + vid.alpha) / (H2[2] - H1[2]); ld t = (H2[2] - BEHIND_LIMIT + pconf.alpha) / (H2[2] - H1[2]);
return H1 * t + H2 * (1-t); return H1 * t + H2 * (1-t);
} }
@ -289,14 +293,14 @@ EX bool two_sided_model() {
if(pmodel == mdDisk) return sphere; if(pmodel == mdDisk) return sphere;
if(pmodel == mdHemisphere) return true; if(pmodel == mdHemisphere) return true;
if(pmodel == mdRotatedHyperboles) return true; if(pmodel == mdRotatedHyperboles) return true;
if(pmodel == mdSpiral && models::spiral_cone < 360) return true; if(pmodel == mdSpiral && pconf.spiral_cone < 360) return true;
return false; return false;
} }
EX int get_side(const hyperpoint& H) { EX int get_side(const hyperpoint& H) {
if(pmodel == mdDisk && sphere) { if(pmodel == mdDisk && sphere) {
double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2];
double horizon = curnorm / vid.alpha; double horizon = curnorm / pconf.alpha;
return (H[2] <= -horizon) ? -1 : 1; return (H[2] <= -horizon) ? -1 : 1;
} }
if(pmodel == mdRotatedHyperboles) if(pmodel == mdRotatedHyperboles)
@ -310,7 +314,7 @@ EX int get_side(const hyperpoint& H) {
applymodel(H, res); applymodel(H, res);
return res[2] < 0 ? -1 : 1; return res[2] < 0 ? -1 : 1;
} }
if(pmodel == mdSpiral && models::spiral_cone < 360) { if(pmodel == mdSpiral && pconf.spiral_cone < 360) {
return cone_side(H); return cone_side(H);
} }
return 0; return 0;
@ -334,13 +338,13 @@ void fixpoint(glvertex& hscr, hyperpoint H) {
} }
hyperpoint Hscr; hyperpoint Hscr;
applymodel(good, Hscr); applymodel(good, Hscr);
hscr = glhr::makevertex(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*current_display->radius); hscr = glhr::makevertex(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*current_display->radius);
} }
void addpoint(const hyperpoint& H) { void addpoint(const hyperpoint& H) {
if(true) { if(true) {
ld z = current_display->radius; ld z = current_display->radius;
// if(vid.alpha + H[2] <= BEHIND_LIMIT && pmodel == mdDisk) poly_flags |= POLY_BEHIND; // if(pconf.alpha + H[2] <= BEHIND_LIMIT && pmodel == mdDisk) poly_flags |= POLY_BEHIND;
if(spherespecial) { if(spherespecial) {
@ -350,7 +354,7 @@ void addpoint(const hyperpoint& H) {
} }
else if(sphere && (poly_flags & POLY_ISSIDE)) { else if(sphere && (poly_flags & POLY_ISSIDE)) {
double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2];
double horizon = curnorm / vid.alpha; double horizon = curnorm / pconf.alpha;
poly_flags |= POLY_NOTINFRONT; poly_flags |= POLY_NOTINFRONT;
if(last_infront && nif_error_in(glcoords.back()[0], glcoords.back()[1], H[0], H[1])) if(last_infront && nif_error_in(glcoords.back()[0], glcoords.back()[1], H[0], H[1]))
poly_flags |= POLY_NIF_ERROR; poly_flags |= POLY_NIF_ERROR;
@ -358,8 +362,8 @@ void addpoint(const hyperpoint& H) {
last_infront = true; last_infront = true;
z *= z *=
(sqrt(curnorm - horizon*horizon) / (vid.alpha - horizon)) / (sqrt(curnorm - horizon*horizon) / (pconf.alpha - horizon)) /
(sqrt(curnorm - H[2]*H[2]) / (vid.alpha+H[2])); (sqrt(curnorm - H[2]*H[2]) / (pconf.alpha+H[2]));
} }
else { else {
poly_flags |= POLY_NOTINFRONT; poly_flags |= POLY_NOTINFRONT;
@ -385,12 +389,12 @@ void addpoint(const hyperpoint& H) {
} }
if(GDIM == 2) { if(GDIM == 2) {
for(int i=0; i<3; i++) Hscr[i] *= z; for(int i=0; i<3; i++) Hscr[i] *= z;
Hscr[1] *= vid.stretch; Hscr[1] *= pconf.stretch;
} }
else { else {
Hscr[0] *= z; Hscr[0] *= z;
Hscr[1] *= z * vid.stretch; Hscr[1] *= z * pconf.stretch;
Hscr[2] = 1 - 2 * (-Hscr[2] - models::clip_min) / (models::clip_max - models::clip_min); Hscr[2] = 1 - 2 * (-Hscr[2] - pconf.clip_min) / (pconf.clip_max - pconf.clip_min);
} }
add1(Hscr); add1(Hscr);
} }
@ -409,7 +413,7 @@ void coords_to_poly() {
bool behind3(hyperpoint h) { bool behind3(hyperpoint h) {
if(pmodel == mdGeodesic) if(pmodel == mdGeodesic)
h = lp_apply(inverse_exp(h, iTable)); h = lp_apply(inverse_exp(h));
return h[2] < 0; return h[2] < 0;
} }
@ -482,11 +486,11 @@ void addpoly(const transmatrix& V, const vector<glvertex> &tab, int ofs, int cnt
/* /*
hyperpoint Hscr; hyperpoint Hscr;
applymodel(goodpoint, Hscr); applymodel(goodpoint, Hscr);
glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius));
glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch+10, Hscr[2]*vid.radius)); glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch+10, Hscr[2]*vid.radius));
glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius-10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius-10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius));
glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*vid.stretch-10, Hscr[2]*vid.radius)); glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius, Hscr[1]*current_display->radius*pconf.stretch-10, Hscr[2]*vid.radius));
glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*vid.stretch, Hscr[2]*vid.radius)); */ glcoords.push_back(make_array<GLfloat>(Hscr[0]*current_display->radius+10, Hscr[1]*current_display->radius*pconf.stretch, Hscr[2]*vid.radius)); */
} }
} }
@ -594,6 +598,7 @@ void dqi_poly::gldraw() {
if(tinf) { if(tinf) {
glhr::be_textured(); glhr::be_textured();
if(flags & POLY_SHADE_TEXTURE) current_display->next_shader_flags |= GF_TEXTURE_SHADED;
glBindTexture(GL_TEXTURE_2D, tinf->texture_id); glBindTexture(GL_TEXTURE_2D, tinf->texture_id);
glhr::vertices_texture(v, tinf->tvertices, offset, offset_texture); glhr::vertices_texture(v, tinf->tvertices, offset, offset_texture);
ioffset = 0; ioffset = 0;
@ -715,7 +720,7 @@ EX ld scale_at(const transmatrix& T) {
EX ld linewidthat(const hyperpoint& h) { EX ld linewidthat(const hyperpoint& h) {
if(!(vid.antialias & AA_LINEWIDTH)) return 1; if(!(vid.antialias & AA_LINEWIDTH)) return 1;
else if(hyperbolic && pmodel == mdDisk && vid.alpha == 1 && !ISWEB) { else if(hyperbolic && pmodel == mdDisk && pconf.alpha == 1 && !ISWEB) {
double dz = h[LDIM]; double dz = h[LDIM];
if(dz < 1) return 1; if(dz < 1) return 1;
else { else {
@ -750,7 +755,7 @@ vector<ld> periods;
ld period_at(ld y) { ld period_at(ld y) {
ld m = current_display->radius; ld m = current_display->radius;
y /= (m * vid.stretch); y /= (m * pconf.stretch);
switch(pmodel) { switch(pmodel) {
case mdBand: case mdBand:
@ -760,8 +765,8 @@ ld period_at(ld y) {
case mdMollweide: case mdMollweide:
return m * 2 * sqrt(1 - y*y*4); return m * 2 * sqrt(1 - y*y*4);
case mdCollignon: { case mdCollignon: {
if(vid.collignon_reflected && y > 0) y = -y; if(pconf.collignon_reflected && y > 0) y = -y;
y += signed_sqrt(vid.collignon_parameter); y += signed_sqrt(pconf.collignon_parameter);
return abs(m*y*2/1.2); return abs(m*y*2/1.2);
} }
default: default:
@ -785,7 +790,7 @@ void adjust(bool tinf) {
ld cmin = -chypot/2, cmax = chypot/2, dmin = -chypot, dmax = chypot; ld cmin = -chypot/2, cmax = chypot/2, dmin = -chypot, dmax = chypot;
ld z = vid.stretch * current_display->radius; ld z = pconf.stretch * current_display->radius;
switch(pmodel) { switch(pmodel) {
case mdSinusoidal: case mdBandEquidistant: case mdMollweide: case mdSinusoidal: case mdBandEquidistant: case mdMollweide:
@ -797,9 +802,9 @@ void adjust(bool tinf) {
break; break;
case mdCollignon: case mdCollignon:
dmin = z * (signed_sqrt(vid.collignon_parameter - 1) - signed_sqrt(vid.collignon_parameter)); dmin = z * (signed_sqrt(pconf.collignon_parameter - 1) - signed_sqrt(pconf.collignon_parameter));
if(vid.collignon_reflected) dmax = -dmin; if(pconf.collignon_reflected) dmax = -dmin;
else dmax = z * (signed_sqrt(vid.collignon_parameter + 1) - signed_sqrt(vid.collignon_parameter)); else dmax = z * (signed_sqrt(pconf.collignon_parameter + 1) - signed_sqrt(pconf.collignon_parameter));
break; break;
default: ; default: ;
@ -889,7 +894,7 @@ void compute_side_by_centerin(dqi_poly *p, bool& nofill) {
else else
nofill = true; nofill = true;
} }
applymodel(h1, hscr); hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * vid.stretch; applymodel(h1, hscr); hscr[0] *= current_display->radius; hscr[1] *= current_display->radius * pconf.stretch;
for(int i=0; i<isize(glcoords)-1; i++) { for(int i=0; i<isize(glcoords)-1; i++) {
double x1 = glcoords[i][0] - hscr[0]; double x1 = glcoords[i][0] - hscr[0];
double y1 = glcoords[i][1] - hscr[1]; double y1 = glcoords[i][1] - hscr[1];
@ -914,11 +919,11 @@ void compute_side_by_centerin(dqi_poly *p, bool& nofill) {
/* /*
if(poly_flags & POLY_BADCENTERIN) { if(poly_flags & POLY_BADCENTERIN) {
glcoords.push_back(glhr::makevertex(hscr[0]+10, hscr[1]*vid.stretch, hscr[2])); glcoords.push_back(glhr::makevertex(hscr[0]+10, hscr[1]*pconf.stretch, hscr[2]));
glcoords.push_back(glhr::makevertex(hscr[0], hscr[1]*vid.stretch+10, hscr[2])); glcoords.push_back(glhr::makevertex(hscr[0], hscr[1]*pconf.stretch+10, hscr[2]));
glcoords.push_back(glhr::makevertex(hscr[0]-10, hscr[1]*vid.stretch, hscr[2])); glcoords.push_back(glhr::makevertex(hscr[0]-10, hscr[1]*pconf.stretch, hscr[2]));
glcoords.push_back(glhr::makevertex(hscr[0], hscr[1]*vid.stretch-10, hscr[2])); glcoords.push_back(glhr::makevertex(hscr[0], hscr[1]*pconf.stretch-10, hscr[2]));
glcoords.push_back(glhr::makevertex(hscr[0]+10, hscr[1]*vid.stretch, hscr[2])); glcoords.push_back(glhr::makevertex(hscr[0]+10, hscr[1]*pconf.stretch, hscr[2]));
} */ } */
} }
@ -1381,7 +1386,12 @@ EX namespace ods {
void dqi_poly::draw() { void dqi_poly::draw() {
if(flags & POLY_DEBUG) debug_this(); if(flags & POLY_DEBUG) debug_this();
if(debugflags & DF_VERTEX) {
println(hlog, tie(band_shift, V, offset, cnt, offset_texture, outline, linewidth, flags, intester, cache), (cell*) tinf);
for(int i=0; i<cnt; i++) print(hlog, (*tab)[i]);
println(hlog);
}
#if CAP_ODS #if CAP_ODS
if(vid.stereo_mode == sODS) { if(vid.stereo_mode == sODS) {
ods::draw_ods(this); ods::draw_ods(this);
@ -1430,7 +1440,7 @@ void dqi_poly::draw() {
for(int j=0; j<MAX_PHASE; j++) { for(int j=0; j<MAX_PHASE; j++) {
twopoint_sphere_flips = j; twopoint_sphere_flips = j;
hyperpoint h2; applymodel(h1, h2); hyperpoint h2; applymodel(h1, h2);
glvertex h = glhr::pointtogl(h2 * current_display->radius); h[1] *= vid.stretch; glvertex h = glhr::pointtogl(h2 * current_display->radius); h[1] *= pconf.stretch;
if(i == 0) if(i == 0)
phases[j].push_back(h); phases[j].push_back(h);
else { else {
@ -1458,7 +1468,7 @@ void dqi_poly::draw() {
for(int i=0; i<cnt; i++) { for(int i=0; i<cnt; i++) {
hyperpoint h1 = V * glhr::gltopoint((*tab)[offset+i]); hyperpoint h1 = V * glhr::gltopoint((*tab)[offset+i]);
hyperpoint mh1; applymodel(h1, mh1); mh1[1] *= vid.stretch; hyperpoint mh1; applymodel(h1, mh1); mh1[1] *= pconf.stretch;
phases[cpha].push_back(glhr::pointtogl(mh1 * current_display->radius)); phases[cpha].push_back(glhr::pointtogl(mh1 * current_display->radius));
// check if the i-th edge intersects the boundary of the ellipse // check if the i-th edge intersects the boundary of the ellipse
@ -1474,7 +1484,7 @@ void dqi_poly::draw() {
if(c1 < 0) c1 = -c1, c2 = -c2; if(c1 < 0) c1 = -c1, c2 = -c2;
hyperpoint h = ah1 * c1 + ah2 * c2; hyperpoint h = ah1 * c1 + ah2 * c2;
h /= hypot_d(3, h); h /= hypot_d(3, h);
if(h[2] < 0 && abs(h[0]) < sin(vid.twopoint_param)) cpha = 1-cpha, pha = 2; if(h[2] < 0 && abs(h[0]) < sin(pconf.twopoint_param)) cpha = 1-cpha, pha = 2;
} }
if(cpha == 1) pha = 0; if(cpha == 1) pha = 0;
} }
@ -1530,7 +1540,7 @@ void dqi_poly::draw() {
last_infront = false; last_infront = false;
addpoly(V, *tab, offset, cnt); addpoly(V, *tab, offset, cnt);
if(!(sphere && vid.alpha < .9)) if(pmodel != mdJoukowsky) if(!(flags & POLY_ALWAYS_IN)) for(int i=1; i<isize(glcoords); i++) { if(!(sphere && pconf.alpha < .9)) if(pmodel != mdJoukowsky) if(!(flags & POLY_ALWAYS_IN)) for(int i=1; i<isize(glcoords); i++) {
ld dx = glcoords[i][0] - glcoords[i-1][0]; ld dx = glcoords[i][0] - glcoords[i-1][0];
ld dy = glcoords[i][1] - glcoords[i-1][1]; ld dy = glcoords[i][1] - glcoords[i-1][1];
if(dx > vid.xres * 2 || dy > vid.yres * 2) return; if(dx > vid.xres * 2 || dy > vid.yres * 2) return;
@ -1556,7 +1566,7 @@ void dqi_poly::draw() {
if(poly_flags & POLY_NIF_ERROR) return; if(poly_flags & POLY_NIF_ERROR) return;
if(spherespecial == 1 && sphere && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && vid.alpha <= 1) { if(spherespecial == 1 && sphere && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && pconf.alpha <= 1) {
bool around_center = false; bool around_center = false;
for(int i=0; i<isize(glcoords)-1; i++) { for(int i=0; i<isize(glcoords)-1; i++) {
double x1 = glcoords[i][0]; double x1 = glcoords[i][0];
@ -1574,9 +1584,9 @@ void dqi_poly::draw() {
bool can_have_inverse = false; bool can_have_inverse = false;
if(sphere && pmodel == mdDisk && (spherespecial > 0 || equi)) can_have_inverse = true; if(sphere && pmodel == mdDisk && (spherespecial > 0 || equi)) can_have_inverse = true;
if(pmodel == mdJoukowsky) can_have_inverse = true; if(pmodel == mdJoukowsky) can_have_inverse = true;
if(pmodel == mdJoukowskyInverted && vid.skiprope) can_have_inverse = true; if(pmodel == mdJoukowskyInverted && pconf.skiprope) can_have_inverse = true;
if(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) can_have_inverse = true; if(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) can_have_inverse = true;
if(pmodel == mdSpiral && vid.skiprope) can_have_inverse = true; if(pmodel == mdSpiral && pconf.skiprope) can_have_inverse = true;
if(pmodel == mdCentralInversion) can_have_inverse = true; if(pmodel == mdCentralInversion) can_have_inverse = true;
if(can_have_inverse && !(poly_flags & POLY_ISSIDE)) { if(can_have_inverse && !(poly_flags & POLY_ISSIDE)) {
@ -1591,7 +1601,7 @@ void dqi_poly::draw() {
} }
if(poly_flags & POLY_INVERSE) { if(poly_flags & POLY_INVERSE) {
if(curradius < vid.alpha - 1e-6) return; if(curradius < pconf.alpha - 1e-6) return;
if(!sphere) return; if(!sphere) return;
} }
@ -1620,7 +1630,7 @@ void dqi_poly::draw() {
ld h = atan2(glcoords[0][0], glcoords[0][1]); ld h = atan2(glcoords[0][0], glcoords[0][1]);
for(int i=0; i<=360; i++) { for(int i=0; i<=360; i++) {
ld a = i * degree + h; ld a = i * degree + h;
glcoords.push_back(glhr::makevertex(current_display->radius * sin(a), current_display->radius * vid.stretch * cos(a), 0)); glcoords.push_back(glhr::makevertex(current_display->radius * sin(a), current_display->radius * pconf.stretch * cos(a), 0));
} }
poly_flags ^= POLY_INVERSE; poly_flags ^= POLY_INVERSE;
} }
@ -1651,7 +1661,7 @@ void dqi_poly::draw() {
} }
#endif #endif
#if CAP_SVG==1 #if CAP_SVG
if(svg::in) { if(svg::in) {
coords_to_poly(); coords_to_poly();
color_t col = color; color_t col = color;
@ -1668,10 +1678,10 @@ void dqi_poly::draw() {
coords_to_poly(); coords_to_poly();
#if CAP_XGD==1 #if CAP_XGD
gdpush(1); gdpush(color); gdpush(outline); gdpush(polyi); gdpush(1); gdpush(color); gdpush(outline); gdpush(polyi);
for(int i=0; i<polyi; i++) gdpush(polyx[i]), gdpush(polyy[i]); for(int i=0; i<polyi; i++) gdpush(polyx[i]), gdpush(polyy[i]);
#elif CAP_SDLGFX==1 #elif CAP_SDLGFX
if(tinf) { if(tinf) {
#if CAP_TEXTURE #if CAP_TEXTURE
@ -1793,7 +1803,7 @@ void dqi_string::draw() {
return; return;
} }
#endif #endif
#if ISMOBILE==0 #if !ISMOBILE
int fr = frame & 255; int fr = frame & 255;
displayfrSP(x, y, shift, fr, size, str, color, align, frame >> 8); displayfrSP(x, y, shift, fr, size, str, color, align, frame >> 8);
#else #else
@ -1846,14 +1856,14 @@ ld xintval(const hyperpoint& h) {
EX ld backbrightness = .25; EX ld backbrightness = .25;
purehookset hook_drawqueue; purehookset hooks_drawqueue;
constexpr int PMAX = int(PPR::MAX); constexpr int PMAX = int(PPR::MAX);
int qp[PMAX], qp0[PMAX]; int qp[PMAX], qp0[PMAX];
color_t darken_color(color_t& color, bool outline) { color_t darken_color(color_t& color, bool outline) {
int alpha = color & 255; int alpha = color & 255;
if(sphere && pmodel == mdDisk && vid.alpha <= 1) if(sphere && pmodel == mdDisk && pconf.alpha <= 1)
return 0; return 0;
else { else {
if(outline && alpha < 255) if(outline && alpha < 255)
@ -1875,6 +1885,8 @@ void dqi_line::draw_back() {
} }
EX void sort_drawqueue() { EX void sort_drawqueue() {
DEBBI(DF_GRAPH, ("sort_drawqueue"));
for(int a=0; a<PMAX; a++) qp[a] = 0; for(int a=0; a<PMAX; a++) qp[a] = 0;
@ -1925,6 +1937,7 @@ EX void reverse_side_priorities() {
// on the sphere, parts on the back are drawn first // on the sphere, parts on the back are drawn first
EX void draw_backside() { EX void draw_backside() {
DEBBI(DF_GRAPH, ("draw_backside"));
if(pmodel == mdHyperboloid && hyperbolic) { if(pmodel == mdHyperboloid && hyperbolic) {
dynamicval<eModel> dv (pmodel, mdHyperboloidFlat); dynamicval<eModel> dv (pmodel, mdHyperboloidFlat);
for(auto& ptd: ptds) for(auto& ptd: ptds)
@ -1964,7 +1977,17 @@ EX void reverse_transparent_walls() {
} }
EX void draw_main() { EX void draw_main() {
DEBBI(DF_GRAPH, ("draw_main"));
if(sphere && GDIM == 3 && pmodel == mdPerspective) { if(sphere && GDIM == 3 && pmodel == mdPerspective) {
if(ray::in_use && !ray::comparison_mode) {
ray::cast();
reset_projection();
/* currently incompatible with primitive-based renderer */
/* also not implemented in stretch */
return;
}
for(int p: {1, 0, 2, 3}) { for(int p: {1, 0, 2, 3}) {
if(elliptic && p < 2) continue; if(elliptic && p < 2) continue;
glhr::set_depthwrite(true); glhr::set_depthwrite(true);
@ -1998,17 +2021,21 @@ EX void draw_main() {
} }
} }
else { else {
DEBB(DF_GRAPH, ("draw_main1"));
if(ray::in_use && !ray::comparison_mode) { if(ray::in_use && !ray::comparison_mode) {
ray::cast(); ray::cast();
reset_projection(); reset_projection();
if(stretch::in()) return; /*primitive not implemented */
} }
DEBB(DF_GRAPH, ("outcircle"));
for(auto& ptd: ptds) if(ptd->prio == PPR::OUTCIRCLE) for(auto& ptd: ptds) if(ptd->prio == PPR::OUTCIRCLE)
ptd->draw(); ptd->draw();
if(two_sided_model()) draw_backside(); if(two_sided_model()) draw_backside();
for(auto& ptd: ptds) if(ptd->prio != PPR::OUTCIRCLE) { for(auto& ptd: ptds) if(ptd->prio != PPR::OUTCIRCLE) {
DEBBI(DF_VERTEX, ("prio: ", int(ptd->prio), " color ", ptd->color));
dynamicval<int> ss(spherespecial, among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE) ? 0 : spherespecial); dynamicval<int> ss(spherespecial, among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE) ? 0 : spherespecial);
ptd->draw(); ptd->draw();
} }
@ -2028,11 +2055,18 @@ EX void draw_main() {
} }
#if CAP_VR #if CAP_VR
EX hookset<bool()> *hooks_vr_draw_all; EX hookset<bool()> hooks_vr_draw_all;
#endif #endif
EX void drawqueue() { EX void drawqueue() {
callhooks(hook_drawqueue);
DEBBI(DF_GRAPH, ("drawqueue"));
#if CAP_WRL
if(wrl::in) { wrl::render(); return; }
#endif
callhooks(hooks_drawqueue);
current_display->next_shader_flags = 0; current_display->next_shader_flags = 0;
reset_projection(); reset_projection();
// reset_projection() is not sufficient here, because we need to know shaderside_projection // reset_projection() is not sufficient here, because we need to know shaderside_projection
@ -2045,6 +2079,8 @@ EX void drawqueue() {
profile_start(3); profile_start(3);
sort_drawqueue(); sort_drawqueue();
DEBB(DF_GRAPH, ("sort walls"));
if(GDIM == 2) if(GDIM == 2)
for(PPR p: {PPR::REDWALLs, PPR::REDWALLs2, PPR::REDWALLs3, PPR::WALL3s, for(PPR p: {PPR::REDWALLs, PPR::REDWALLs2, PPR::REDWALLs3, PPR::WALL3s,
@ -2353,7 +2389,7 @@ EX void getcoord0(const hyperpoint& h, int& xc, int &yc, int &sc) {
hyperpoint hscr; hyperpoint hscr;
applymodel(h, hscr); applymodel(h, hscr);
xc = current_display->xcenter + current_display->radius * hscr[0]; xc = current_display->xcenter + current_display->radius * hscr[0];
yc = current_display->ycenter + current_display->radius * vid.stretch * hscr[1]; yc = current_display->ycenter + current_display->radius * pconf.stretch * hscr[1];
sc = 0; sc = 0;
// EYETODO sc = vid.eye * current_display->radius * hscr[2]; // EYETODO sc = vid.eye * current_display->radius * hscr[2];
} }

View File

@ -306,10 +306,13 @@ EX namespace euc {
}; };
hrmap_euclidean* cubemap() { hrmap_euclidean* cubemap() {
if(fake::in()) return FPIU(cubemap());
return ((hrmap_euclidean*) currentmap); return ((hrmap_euclidean*) currentmap);
} }
hrmap_euclidean* eucmap() { return cubemap(); } hrmap_euclidean* eucmap() {
return cubemap();
}
EX vector<coord>& get_current_shifttable() { return cubemap()->shifttable; } EX vector<coord>& get_current_shifttable() { return cubemap()->shifttable; }
EX map<coord, heptagon*>& get_spacemap() { return cubemap()->spacemap; } EX map<coord, heptagon*>& get_spacemap() { return cubemap()->spacemap; }
@ -1169,7 +1172,84 @@ EX int cyldist(gp::loc a, gp::loc b) {
return best; return best;
} }
EX bool in() { return euclid && standard_tiling(); } EX void generate() {
if(fake::in()) {
fake::generate();
return;
}
auto v = euc::get_shifttable();
auto& cs = cgi.cellshape;
if(S7 == 6) {
cgi.adjcheck = 1;
cgi.face = 4;
for(int w=0; w<6; w++) {
for(int a=0; a<4; a++) {
int t[3];
t[0] = (w>=3) ? -1 : 1;
t[1] = among(a, 0, 3) ? -1 : 1;
t[2] = among(a, 2, 3) ? -1 : 1;
int x = w%3;
int y = (x+2)%3;
int z = (y+2)%3;
cs.push_back(hpxy3(t[x]/2., t[y]/2., t[z]/2.));
}
}
}
if(S7 == 12) {
cgi.adjcheck = sqrt(2);
cgi.face = 4;
for(int w=0; w<12; w++) {
auto co = v[w];
vector<int> valid;
for(int c=0; c<3; c++) if(co[c]) valid.push_back(c);
int third = 3 - valid[1] - valid[0];
hyperpoint v0 = cpush0(valid[0], co[valid[0]] > 0 ? 1 : -1);
hyperpoint v1 = cpush0(valid[1], co[valid[1]] > 0 ? 1 : -1);
cs.push_back(v0);
cs.push_back(v0/2 + v1/2 + cpush0(third, .5) - C0);
cs.push_back(v1);
cs.push_back(v0/2 + v1/2 + cpush0(third, -.5) - C0);
}
}
if(S7 == 14) {
cgi.adjcheck = 2;
cgi.face = 4; /* the first face */
auto v = euc::get_shifttable();
for(int w=0; w<14; w++) {
if(w%7 < 3) {
int z = w>=7?-1:1;
cs.push_back(cpush0(w%7, z) + cpush0((w%7+1)%3, 1/2.) - C0);
cs.push_back(cpush0(w%7, z) + cpush0((w%7+2)%3, 1/2.) - C0);
cs.push_back(cpush0(w%7, z) + cpush0((w%7+1)%3,-1/2.) - C0);
cs.push_back(cpush0(w%7, z) + cpush0((w%7+2)%3,-1/2.) - C0);
}
else {
auto t = v[w];
ld x = t[0], y = t[1], z = t[2];
for(hyperpoint h: {
hpxy3(x, y/2, 0), hpxy3(x/2, y, 0), hpxy3(0, y, z/2),
hpxy3(0, y/2, z), hpxy3(x/2, 0, z), hpxy3(x, 0, z/2)
}) cs.push_back(h);
}
}
}
}
/** @brief returns true if the current geometry is based on this module
* (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();
}
EX bool in(int dim) { return in() && WDIM == dim; } EX bool in(int dim) { return in() && WDIM == dim; }
EX bool in(int dim, int s7) { return in(dim) && S7 == s7; } EX bool in(int dim, int s7) { return in(dim) && S7 == s7; }

View File

@ -376,7 +376,7 @@ EX int curr_dist(cell *c) {
case dfWorld: case dfWorld:
if(!mod_allowed() && !among(c->land, laOcean, laIvoryTower, laEndorian, laDungeon, laTemple, laWhirlpool, laCanvas)) if(!mod_allowed() && !among(c->land, laOcean, laIvoryTower, laEndorian, laDungeon, laTemple, laWhirlpool, laCanvas))
return 0; return 0;
if(isCyclic(c->land) && (eubinary || c->master->alt)) return celldistAlt(c); if((isCyclic(c->land) || c->land == laCanvas) && (eubinary || c->master->alt)) return celldistAlt(c);
return inmirror(c) ? (c->landparam & 255) : c->landparam; return inmirror(c) ? (c->landparam & 255) : c->landparam;
} }
return 0; return 0;

385
fake.cpp Normal file
View File

@ -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<class T> auto in_underlying(const T& t) -> decltype(t()) {
pcgip = cgip;
dynamicval<hrmap*> gpm(pmap, this);
dynamicval<eGeometry> gag(actual_geometry, geometry);
dynamicval<eGeometry> g(geometry, underlying);
dynamicval<geometry_information*> gc(cgip, underlying_cgip);
dynamicval<hrmap*> 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; i<c->type; 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; a<h1->type; 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<S6; i++) queuepoly(ggmatrix(cwt.at), shWall3D[i], 0xFF0000FF);
if(pmodel == mdDisk && WDIM == 2) {
draw_recursive(centerover, cview(), -1, -1, nullptr, 0);
return;
}
bool bymatrix = true;
dq::visited_c.clear();
dq::visited_by_matrix.clear();
auto enqueue = (bymatrix ? dq::enqueue_by_matrix_c : dq::enqueue_c);
enqueue(centerover, cview());
while(!dq::drawqueue_c.empty()) {
auto& p = dq::drawqueue_c.front();
cell *c = get<0>(p);
transmatrix V = get<1>(p);
dynamicval<ld> 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; i<S7; i++) if(c->move(i)) {
enqueue(c->move(i), V * adj(c, i));
}
}
}
};
EX hrmap* new_map() { return new hrmap_fake; };
EX hrmap* get_umap() { if(!dynamic_cast<hrmap_fake*>(currentmap)) return nullptr; else return ((hrmap_fake*)currentmap)->underlying_map; }
#if HDR
template<class T> auto in_underlying_geometry(const T& f) -> decltype(f()) {
if(!fake::in()) return f();
dynamicval<eGeometry> g(geometry, underlying);
dynamicval<eGeometry> gag(actual_geometry, geometry);
dynamicval<geometry_information*> gc(cgip, underlying_cgip);
dynamicval<hrmap*> gpm(pmap, currentmap);
dynamicval<hrmap*> 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<hyperpoint> befake(const vector<hyperpoint>& v) {
vector<hyperpoint> 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<ucgi.face; i++) h += fcs[i];
if(material(h) > 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 }
}

File diff suppressed because it is too large Load Diff

View File

@ -1011,6 +1011,8 @@ auto floor_hook =
#if MAXMDIM >= 4 #if MAXMDIM >= 4
EX ld floor_texture_square_size;
void draw_shape_for_texture(floorshape* sh) { void draw_shape_for_texture(floorshape* sh) {
int id = sh->id; int id = sh->id;
@ -1064,23 +1066,30 @@ void draw_shape_for_texture(floorshape* sh) {
hyperpoint inmodel; hyperpoint inmodel;
applymodel(center, inmodel); applymodel(center, inmodel);
glvertex tmap; glvertex tmap;
tmap[0] = (1 + inmodel[0] * vid.scale) / 2; tmap[0] = (1 + inmodel[0] * pconf.scale) / 2;
tmap[1] = (1 - inmodel[1] * vid.scale) / 2; tmap[1] = (1 - inmodel[1] * pconf.scale) / 2;
applymodel(center + v1, inmodel); applymodel(center + v1, inmodel);
tmap[2] = (1 + inmodel[0] * vid.scale) / 2 - tmap[0]; tmap[2] = (1 + inmodel[0] * pconf.scale) / 2 - tmap[0];
floor_texture_map[sh->id] = tmap; floor_texture_map[sh->id] = tmap;
} }
// SL2 needs 6 times more auto tvec_at = [&] (ld x, ld y) {
texture_order([&] (ld x, ld y) {
hyperpoint h = center + v1 * x + v2 * y; hyperpoint h = center + v1 * x + v2 * y;
hyperpoint inmodel; hyperpoint inmodel;
applymodel(h, inmodel); applymodel(h, inmodel);
glvec2 v; glvec2 v;
v[0] = (1 + inmodel[0] * vid.scale) / 2; v[0] = (1 + inmodel[0] * pconf.scale) / 2;
v[1] = (1 - inmodel[1] * vid.scale) / 2; v[1] = (1 - inmodel[1] * pconf.scale) / 2;
return v;
};
// SL2 needs 6 times more
texture_order([&] (ld x, ld y) {
auto v = tvec_at(x, y);
ftv.tvertices.push_back(glhr::makevertex(v[0], v[1], 0)); ftv.tvertices.push_back(glhr::makevertex(v[0], v[1], 0));
}); });
floor_texture_square_size = 2 * (tvec_at(1, 0)[0] - tvec_at(0, 0)[0]);
} }
/** copy the texture vertices so that there are at least qty of them */ /** copy the texture vertices so that there are at least qty of them */
@ -1102,7 +1111,9 @@ EX void bind_floor_texture(hpcshape& li, int id) {
ensure_vertex_number(li); ensure_vertex_number(li);
} }
#if HDR
const int FLOORTEXTURESIZE = 4096; const int FLOORTEXTURESIZE = 4096;
#endif
void geometry_information::make_floor_textures_here() { void geometry_information::make_floor_textures_here() {
require_shapes(); require_shapes();
@ -1110,9 +1121,9 @@ void geometry_information::make_floor_textures_here() {
dynamicval<videopar> vi(vid, vid); dynamicval<videopar> vi(vid, vid);
vid.xres = FLOORTEXTURESIZE; vid.xres = FLOORTEXTURESIZE;
vid.yres = FLOORTEXTURESIZE; vid.yres = FLOORTEXTURESIZE;
vid.scale = 0.125; pconf.scale = 0.125;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.alpha = 1; pconf.alpha = 1;
dynamicval<ld> lw(vid.linewidth, 2); dynamicval<ld> lw(vid.linewidth, 2);
floor_textures = new renderbuffer(vid.xres, vid.yres, vid.usingGL); floor_textures = new renderbuffer(vid.xres, vid.yres, vid.usingGL);
@ -1127,7 +1138,7 @@ void geometry_information::make_floor_textures_here() {
cd->xsize = cd->ysize = FLOORTEXTURESIZE; cd->xsize = cd->ysize = FLOORTEXTURESIZE;
cd->xcenter = cd->ycenter = cd->scrsize = FLOORTEXTURESIZE/2; cd->xcenter = cd->ycenter = cd->scrsize = FLOORTEXTURESIZE/2;
cd->radius = cd->scrsize * vid.scale; cd->radius = cd->scrsize * pconf.scale;
floor_textures->enable(); floor_textures->enable();
floor_textures->clear(0); // 0xE8E8E8 = 1 floor_textures->clear(0); // 0xE8E8E8 = 1

View File

@ -359,6 +359,7 @@ void ge_select_tiling() {
if(arb::in() && (ISMOBILE || ISWEB)) continue; if(arb::in() && (ISMOBILE || ISWEB)) continue;
if(WDIM == 3 && MAXMDIM == 3) continue; if(WDIM == 3 && MAXMDIM == 3) continue;
if(geometry == gFieldQuotient && !CAP_FIELD) continue; if(geometry == gFieldQuotient && !CAP_FIELD) continue;
if(geometry == gFake) continue;
if(!current_filter->test()) continue; if(!current_filter->test()) continue;
if(orig_el) { if(orig_el) {
for(int j=0; j<isize(ginf); j++) for(int j=0; j<isize(ginf); j++)
@ -381,21 +382,21 @@ void ge_select_tiling() {
EX string current_proj_name() { EX string current_proj_name() {
bool h = hyperbolic || sn::in(); bool h = hyperbolic || sn::in();
if(pmodel != mdDisk) if(vpconf.model != mdDisk)
return models::get_model_name(pmodel); return models::get_model_name(vpconf.model);
else if(h && vid.alpha == 1) else if(h && vpconf.alpha == 1)
return XLAT("Poincaré model"); return XLAT("Poincaré model");
else if(h && vid.alpha == 0) else if(h && vpconf.alpha == 0)
return XLAT("Klein-Beltrami model"); return XLAT("Klein-Beltrami model");
else if(h && vid.alpha == -1) else if(h && vpconf.alpha == -1)
return XLAT("inverted Poincaré model"); return XLAT("inverted Poincaré model");
else if(sphere && vid.alpha == 1) else if(sphere && vpconf.alpha == 1)
return XLAT("stereographic projection"); return XLAT("stereographic projection");
else if(sphere && vid.alpha == 0) else if(sphere && vpconf.alpha == 0)
return XLAT("gnomonic projection"); return XLAT("gnomonic projection");
else if(sphere && vid.alpha >= 999) else if(sphere && vpconf.alpha >= 999)
return XLAT("orthographic projection"); return XLAT("orthographic projection");
else if(h && vid.alpha >= 999) else if(h && vpconf.alpha >= 999)
return XLAT("Gans model"); return XLAT("Gans model");
else else
return XLAT("general perspective"); return XLAT("general perspective");
@ -405,7 +406,7 @@ EX string dim_name() {
return " (" + its(WDIM) + "D)"; return " (" + its(WDIM) + "D)";
} }
#if CAP_THREAD #if CAP_THREAD && MAXMDIM >= 4
EX void showQuotientConfig3() { EX void showQuotientConfig3() {
using namespace fieldpattern; using namespace fieldpattern;
@ -512,6 +513,7 @@ EX void select_quotient_screen() {
char key = 'a'; char key = 'a';
for(int i=0; i<isize(ginf); i++) { for(int i=0; i<isize(ginf); i++) {
auto g = eGeometry(i); auto g = eGeometry(i);
if(ginf[g].flags & qDEPRECATED) continue;
if(same_tiling(g)) { if(same_tiling(g)) {
dialog::addBoolItem( dialog::addBoolItem(
(ginf[g].flags & qANYQ) ? (ginf[g].flags & qANYQ) ?
@ -528,7 +530,7 @@ EX void select_quotient_screen() {
println(hlog, "set prime = ", currfp.Prime); println(hlog, "set prime = ", currfp.Prime);
start_game(); start_game();
} }
#if CAP_THREAD #if CAP_THREAD && MAXMDIM >= 4
pushScreen(showQuotientConfig3); pushScreen(showQuotientConfig3);
#endif #endif
} }
@ -742,6 +744,16 @@ EX void showEuclideanMenu() {
dialog::add_action(select_quotient); dialog::add_action(select_quotient);
if(arcm::in()) {
dialog::addItem(XLAT("advanced parameters"), '4');
dialog::add_action_push(arcm::show);
}
if(cryst) {
dialog::addItem(XLAT("advanced parameters"), '4');
dialog::add_action_push(crystal::show);
}
#if CAP_IRR #if CAP_IRR
if(hyperbolic && IRREGULAR) { if(hyperbolic && IRREGULAR) {
nom = isize(irr::cells); nom = isize(irr::cells);
@ -813,7 +825,7 @@ EX void showEuclideanMenu() {
dialog::add_action([] { dialog::add_action([] {
dialog::editNumber(rots::underlying_scale, 0, 1, 0.05, 0.25, XLAT("view the underlying geometry"), dialog::editNumber(rots::underlying_scale, 0, 1, 0.05, 0.25, XLAT("view the underlying geometry"),
XLAT( XLAT(
geometry == gRotSpace ? "The space you are currently in the space of rotations of the underlying hyperbolic or spherical geometry. " geometry == gRotSpace ? "The space you are currently in is the space of rotations of the underlying hyperbolic or spherical geometry. "
: "You are currently in a product space.") + : "You are currently in a product space.") +
XLAT( XLAT(
"This option lets you see the underlying space. Lands and some walls (e.g. in the Graveyard) are based on " "This option lets you see the underlying space. Lands and some walls (e.g. in the Graveyard) are based on "
@ -826,6 +838,20 @@ EX void showEuclideanMenu() {
}); });
} }
if(stretch::applicable()) {
dialog::addSelItem(XLAT("stretched geometry"), fts(stretch::factor), 'S');
dialog::add_action([] {
dialog::editNumber(stretch::factor, -1, 9, 0.1, 0, XLAT("stretched geometry"),
XLAT(
"Stretch the metric along the fibers. This can currently be done in rotation spaces and in 8-cell, 24-cell and 120-cell. "
"Value of 0 means not stretched, -1 means S2xE or H2xE (works only in the limit). "
"Only the raycaster is implemented for stretched geometry, so you will see only walls. (Must be > -1)"
)
);
dialog::reaction = ray::reset_raycaster;
});
}
dialog::addBreak(100); dialog::addBreak(100);
dialog::addSelItem(XLAT("land"), XLAT1(linf[specialland].name), 'l'); dialog::addSelItem(XLAT("land"), XLAT1(linf[specialland].name), 'l');
dialog::add_action_push(ge_land_selection); dialog::add_action_push(ge_land_selection);
@ -940,7 +966,7 @@ EX void runGeometryExperiments() {
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
eGeometry readGeo(const string& ss) { EX eGeometry readGeo(const string& ss) {
for(int i=0; i<isize(ginf); i++) if(ginf[i].shortname == ss) return eGeometry(i); for(int i=0; i<isize(ginf); i++) if(ginf[i].shortname == ss) return eGeometry(i);
bool numeric = true; bool numeric = true;
for(char c: ss) if(c < '0' || c > '9') numeric = false; for(char c: ss) if(c < '0' || c > '9') numeric = false;

View File

@ -329,7 +329,7 @@ hpcshape
int SD3, SD6, SD7, S12, S14, S21, S28, S42, S36, S84; int SD3, SD6, SD7, S12, S14, S21, S28, S42, S36, S84;
vector<int> walloffsets; vector<pair<int, cell*>> walloffsets;
vector<array<int, 3>> symmetriesAt; vector<array<int, 3>> symmetriesAt;
@ -433,16 +433,16 @@ hpcshape
ld alpha; ld alpha;
int area; int area;
}; };
shared_ptr<gpdata_t> gpdata; shared_ptr<gpdata_t> gpdata = nullptr;
#endif #endif
int state; int state = 0;
int usershape_state; int usershape_state = 0;
/** contains the texture point coordinates for 3D models */ /** contains the texture point coordinates for 3D models */
basic_textureinfo models_texture; basic_textureinfo models_texture;
geometry_information() { last = NULL; state = usershape_state = 0; gpdata = NULL; } geometry_information() { last = NULL; }
void require_basics() { if(state & 1) return; state |= 1; prepare_basics(); } void require_basics() { if(state & 1) return; state |= 1; prepare_basics(); }
void require_shapes() { if(state & 2) return; state |= 2; prepare_shapes(); } void require_shapes() { if(state & 2) return; state |= 2; prepare_shapes(); }
@ -573,6 +573,7 @@ void geometry_information::prepare_basics() {
#endif #endif
#if MAXMDIM >= 4 #if MAXMDIM >= 4
if(reg3::in()) reg3::generate(); if(reg3::in()) reg3::generate();
if(euc::in(3)) euc::generate();
#endif #endif
hybrid_finish: hybrid_finish:
@ -710,24 +711,24 @@ EX namespace geom3 {
void geometry_information::prepare_compute3() { void geometry_information::prepare_compute3() {
using namespace geom3; using namespace geom3;
DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute")); DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute"));
// tanh(depth) / tanh(camera) == vid.alpha // tanh(depth) / tanh(camera) == pconf.alpha
invalid = ""; invalid = "";
if(GDIM == 3) ; if(GDIM == 3) ;
else if(vid.tc_alpha < vid.tc_depth && vid.tc_alpha < vid.tc_camera) else if(vid.tc_alpha < vid.tc_depth && vid.tc_alpha < vid.tc_camera)
vid.alpha = tan_auto(vid.depth) / tan_auto(vid.camera); pconf.alpha = tan_auto(vid.depth) / tan_auto(vid.camera);
else if(vid.tc_depth < vid.tc_alpha && vid.tc_depth < vid.tc_camera) { else if(vid.tc_depth < vid.tc_alpha && vid.tc_depth < vid.tc_camera) {
ld v = vid.alpha * tan_auto(vid.camera); ld v = pconf.alpha * tan_auto(vid.camera);
if(hyperbolic && (v<1e-6-12 || v>1-1e-12)) invalid = "cannot adjust depth", vid.depth = vid.camera; if(hyperbolic && (v<1e-6-12 || v>1-1e-12)) invalid = "cannot adjust depth", vid.depth = vid.camera;
else vid.depth = atan_auto(v); else vid.depth = atan_auto(v);
} }
else { else {
ld v = tan_auto(vid.depth) / vid.alpha; ld v = tan_auto(vid.depth) / pconf.alpha;
if(hyperbolic && (v<1e-12-1 || v>1-1e-12)) invalid = "cannot adjust camera", vid.camera = vid.depth; if(hyperbolic && (v<1e-12-1 || v>1-1e-12)) invalid = "cannot adjust camera", vid.camera = vid.depth;
else vid.camera = atan_auto(v); else vid.camera = atan_auto(v);
} }
if(fabs(vid.alpha) < 1e-6) invalid = "does not work with perfect Klein"; if(fabs(pconf.alpha) < 1e-6) invalid = "does not work with perfect Klein";
if(invalid != "") { if(invalid != "") {
INFDEEP = .7; INFDEEP = .7;
@ -851,20 +852,20 @@ EX void switch_always3() {
EX void switch_tpp() { EX void switch_tpp() {
if(dual::split(switch_fpp)) return; if(dual::split(switch_fpp)) return;
if(pmodel == mdDisk && vid.camera_angle) { if(pmodel == mdDisk && pconf.camera_angle) {
vid.yshift = 0; vid.yshift = 0;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = 0; pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
vid.fixed_facing = false; vid.fixed_facing = false;
} }
else { else {
vid.yshift = -0.3; vid.yshift = -0.3;
vid.camera_angle = -45; pconf.camera_angle = -45;
vid.scale = 18/16. * vid.xres / vid.yres / multi::players; pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = -0.9; pconf.yposition = -0.9;
vid.fixed_facing = true; vid.fixed_facing = true;
vid.fixed_facing_dir = 90; vid.fixed_facing_dir = 90;
} }
@ -926,12 +927,19 @@ EX int last_texture_step;
int ntimestamp; int ntimestamp;
EX void check_cgi() { EX string cgi_string() {
string s; string s;
auto V = [&] (string a, string b) { s += a; s += ": "; s += b; s += "; "; }; auto V = [&] (string a, string b) { s += a; s += ": "; s += b; s += "; "; };
V("GEO", its(int(geometry))); V("GEO", its(int(geometry)));
V("VAR", its(int(variation))); V("VAR", its(int(variation)));
if(fake::in()) {
if(hyperbolic) V("H", fts(fake::scale));
if(euclid) V("E", fts(fake::scale));
if(sphere) V("S", fts(fake::scale));
V("G", FPIU(cgi_string()));
}
if(GOLDBERG) V("GP", its(gp::param.first) + "," + its(gp::param.second)); if(GOLDBERG) V("GP", its(gp::param.first) + "," + its(gp::param.second));
if(IRREGULAR) V("IRR", its(irr::irrid)); if(IRREGULAR) V("IRR", its(irr::irrid));
@ -976,9 +984,16 @@ EX void check_cgi() {
V("LQ", its(vid.linequality)); V("LQ", its(vid.linequality));
return s;
}
EX void check_cgi() {
string s = cgi_string();
cgip = &cgis[s]; cgip = &cgis[s];
cgi.timestamp = ++ntimestamp; cgi.timestamp = ++ntimestamp;
if(hybri) hybrid::underlying_cgip->timestamp = ntimestamp; if(hybri) hybrid::underlying_cgip->timestamp = ntimestamp;
if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp;
if(isize(cgis) > 4) { if(isize(cgis) > 4) {
vector<pair<int, string>> timestamps; vector<pair<int, string>> timestamps;

View File

@ -287,6 +287,23 @@ void virtualRebase_cell(cell*& base, T& at, const U& check) {
template<class T, class U> template<class T, class U>
void virtualRebase(cell*& base, T& at, const U& check) { void virtualRebase(cell*& base, T& at, const U& check) {
if(nil) {
hyperpoint h = check(at);
auto step = [&] (int i) {
at = currentmap->iadj(base, i) * at;
base = base->cmove(i);
h = check(at);
};
while(h[1] < -0.5) step(1);
while(h[1] >= 0.5) step(4);
while(h[0] < -0.5) step(0);
while(h[0] >= 0.5) step(3);
while(h[2] < -0.5) step(2);
while(h[2] >= 0.5) step(5);
return;
}
if(prod) { if(prod) {
auto d = product_decompose(check(at)).first; auto d = product_decompose(check(at)).first;
while(d > cgi.plevel / 2) { while(d > cgi.plevel / 2) {

View File

@ -24,10 +24,6 @@ EX void glError(const char* GLcall, const char* file, const int line) {
} }
} }
#ifndef CAP_VERTEXBUFFER
#define CAP_VERTEXBUFFER (ISWEB)
#endif
#if HDR #if HDR
#if CAP_SHADER && CAP_NOSHADER #if CAP_SHADER && CAP_NOSHADER
#define WITHSHADER(x, y) if(glhr::noshaders) y else x #define WITHSHADER(x, y) if(glhr::noshaders) y else x
@ -42,6 +38,12 @@ EX void glError(const char* GLcall, const char* file, const int line) {
EX namespace glhr { EX namespace glhr {
EX string to_glsl(ld x) {
char buf[64];
snprintf(buf, 64, "float(%.10e)", x);
return buf;
}
#if HDR #if HDR
struct glmatrix { struct glmatrix {
GLfloat a[4][4]; GLfloat a[4][4];

View File

@ -40,8 +40,8 @@ EX bool hide_player() {
#define ADC(V,c) for(const transmatrix& V: current_display->all_drawn_copies[c]) #define ADC(V,c) for(const transmatrix& V: current_display->all_drawn_copies[c])
EX hookset<bool(int sym, int uni)> *hooks_handleKey; EX hookset<bool(int sym, int uni)> hooks_handleKey;
EX hookset<bool(cell *c, const transmatrix& V)> *hooks_drawcell; EX hookset<bool(cell *c, const transmatrix& V)> hooks_drawcell;
EX purehookset hooks_frame, hooks_markers; EX purehookset hooks_frame, hooks_markers;
EX ld animation_factor = 1; EX ld animation_factor = 1;
@ -1390,7 +1390,7 @@ EX bool drawMonsterType(eMonster m, cell *where, const transmatrix& V1, color_t
} }
else { else {
queuepoly(VHEAD1, cgi.shPHead, 0xF0A0D0FF); queuepoly(VHEAD1, cgi.shPHead, 0xF0A0D0FF);
queuepoly(VBS, cgi.shFlowerHand, 0xC00000FF); queuepoly(VBODY * VBS, cgi.shFlowerHand, 0xC00000FF);
queuepoly(VBODY2 * VBS, cgi.shSuspenders, 0xC00000FF); queuepoly(VBODY2 * VBS, cgi.shSuspenders, 0xC00000FF);
} }
} }
@ -2858,10 +2858,10 @@ int haveaura_cached;
EX int haveaura() { EX int haveaura() {
if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return 0; if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return 0;
if(sphere && mdAzimuthalEqui()) return 0; if(sphere && mdAzimuthalEqui()) return 0;
if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && models::model_transition < 1) if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && pconf.model_transition < 1)
return 2; return 2;
if(pmodel == mdFisheye) return 1; if(pmodel == mdFisheye) return 1;
return pmodel == mdDisk && (!sphere || vid.alpha > 10) && !euclid; return pmodel == mdDisk && (!sphere || pconf.alpha > 10) && !euclid;
} }
vector<pair<int, int> > auraspecials; vector<pair<int, int> > auraspecials;
@ -2884,7 +2884,7 @@ void apply_joukowsky_aura(hyperpoint& h) {
h = ret; h = ret;
} }
if(nonisotropic) { if(nonisotropic) {
h = lp_apply(inverse_exp(h, iTable, true)); h = lp_apply(inverse_exp(h, pfNO_DISTANCE));
} }
} }
@ -2927,13 +2927,14 @@ void sumaura(int v) {
vector<glhr::colored_vertex> auravertices; vector<glhr::colored_vertex> auravertices;
void drawaura() { void drawaura() {
DEBBI(DF_GRAPH, ("draw aura"));
if(!haveaura()) return; if(!haveaura()) return;
if(vid.stereo_mode) return; if(vid.stereo_mode) return;
double rad = current_display->radius; double rad = current_display->radius;
if(sphere && !mdAzimuthalEqui()) rad /= sqrt(vid.alpha*vid.alpha - 1); if(sphere && !mdAzimuthalEqui()) rad /= sqrt(pconf.alpha*pconf.alpha - 1);
if(hyperbolic && pmodel == mdFisheye) { if(hyperbolic && pmodel == mdFisheye) {
ld h = 1; ld h = 1;
h /= vid.fisheye_param; h /= pconf.fisheye_param;
ld nrad = h / sqrt(2 + h*h); ld nrad = h / sqrt(2 + h*h);
rad *= nrad; rad *= nrad;
} }
@ -2959,9 +2960,9 @@ void drawaura() {
for(int x=0; x<vid.xres; x++) { for(int x=0; x<vid.xres; x++) {
ld hx = (x * 1. - current_display->xcenter) / rad; ld hx = (x * 1. - current_display->xcenter) / rad;
ld hy = (y * 1. - current_display->ycenter) / rad / vid.stretch; ld hy = (y * 1. - current_display->ycenter) / rad / pconf.stretch;
if(vid.camera_angle) camrotate(hx, hy); if(pconf.camera_angle) camrotate(hx, hy);
ld fac = sqrt(hx*hx+hy*hy); ld fac = sqrt(hx*hx+hy*hy);
if(fac < 1) continue; if(fac < 1) continue;
@ -3007,8 +3008,8 @@ void drawaura() {
facs[10] = 10; facs[10] = 10;
cmul[1] = cmul[0]; cmul[1] = cmul[0];
bool inversion = vid.alpha <= -1 || pmodel == mdJoukowsky; bool inversion = pconf.alpha <= -1 || pmodel == mdJoukowsky;
bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && models::model_transition < 1; bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && pconf.model_transition < 1;
for(int r=0; r<=AURA; r++) for(int z=0; z<11; z++) { for(int r=0; r<=AURA; r++) for(int z=0; z<11; z++) {
float rr = (M_PI * 2 * r) / AURA; float rr = (M_PI * 2 * r) / AURA;
@ -3024,7 +3025,7 @@ void drawaura() {
else else
models::apply_orientation(c1, s1); models::apply_orientation(c1, s1);
ld& mt = models::model_transition; ld& mt = pconf.model_transition;
ld mt2 = 1 - mt; ld mt2 = 1 - mt;
ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2); ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2);
@ -3034,7 +3035,7 @@ void drawaura() {
} }
cx[r][z][0] = rad0 * c; cx[r][z][0] = rad0 * c;
cx[r][z][1] = rad0 * s * vid.stretch; cx[r][z][1] = rad0 * s * pconf.stretch;
for(int u=0; u<3; u++) for(int u=0; u<3; u++)
cx[r][z][u+2] = bak[u] + (aurac[rm][u] / (aurac[rm][3]+.1) - bak[u]) * cmul[z]; cx[r][z][u+2] = bak[u] + (aurac[rm][u] / (aurac[rm][3]+.1) - bak[u]) * cmul[z];
@ -3362,7 +3363,7 @@ bool openorsafe(cell *c) {
EX color_t stdgridcolor = 0x202020FF; EX color_t stdgridcolor = 0x202020FF;
EX int gridcolor(cell *c1, cell *c2) { EX int gridcolor(cell *c1, cell *c2) {
if(cmode & sm::DRAW) return Dark(forecolor); if(cmode & sm::DRAW && !mapeditor::drawing_tool) return Dark(forecolor);
if(!c2) if(!c2)
return 0x202020 >> darken; return 0x202020 >> darken;
int rd1 = rosedist(c1), rd2 = rosedist(c2); int rd1 = rosedist(c1), rd2 = rosedist(c2);
@ -3667,8 +3668,8 @@ bool celldrawer::cell_clipped() {
hyperpoint H = tC0(V); hyperpoint H = tC0(V);
if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ; if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ;
else { else {
hyperpoint H2 = inverse_exp(H, iLazy); hyperpoint H2 = inverse_exp(H, pQUICK);
for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -.4) return true; for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -.6) return true;
} }
noclipped++; noclipped++;
} }
@ -3676,7 +3677,7 @@ bool celldrawer::cell_clipped() {
hyperpoint H = tC0(V); hyperpoint H = tC0(V);
if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ; if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ;
else { else {
hyperpoint H2 = inverse_exp(H, iLazy); hyperpoint H2 = inverse_exp(H, pQUICK);
for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -2) return true; for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -2) return true;
} }
noclipped++; noclipped++;
@ -3697,7 +3698,8 @@ EX void gridline(const transmatrix& V1, const hyperpoint h1, const transmatrix&
if(WDIM == 3 && fat_edges) { if(WDIM == 3 && fat_edges) {
transmatrix T = V1 * rgpushxto0(h1); transmatrix T = V1 * rgpushxto0(h1);
transmatrix S = rspintox(inverse(T) * V2 * h2); transmatrix S = rspintox(inverse(T) * V2 * h2);
queuepoly(T * S, cgi.generate_pipe(d, vid.linewidth), col); auto& p = queuepoly(T * S, cgi.generate_pipe(d, vid.linewidth), col);
p.intester = xpush0(d/2);
return; return;
} }
#endif #endif
@ -4050,7 +4052,7 @@ EX void queuecircleat(cell *c, double rad, color_t col) {
} }
#endif #endif
#if ISMOBILE==1 #if ISMOBILE
#define MOBON (clicked) #define MOBON (clicked)
#else #else
#define MOBON true #define MOBON true
@ -4470,12 +4472,12 @@ EX void precise_mouseover() {
if(WDIM == 3) { if(WDIM == 3) {
mouseover2 = mouseover = centerover; mouseover2 = mouseover = centerover;
ld best = HUGE_VAL; ld best = HUGE_VAL;
hyperpoint h = direct_exp(lp_iapply(ztangent(0.01)), 100); hyperpoint h = direct_exp(lp_iapply(ztangent(0.01)));
transmatrix cov = ggmatrix(mouseover2); transmatrix cov = ggmatrix(mouseover2);
forCellIdEx(c1, i, mouseover2) { forCellIdEx(c1, i, mouseover2) {
hyperpoint h1 = tC0(cov * currentmap->adj(mouseover2, i)); hyperpoint h1 = tC0(cov * currentmap->adj(mouseover2, i));
ld dist = geo_dist(h, h1, iTable) - geo_dist(C0, h1, iTable); ld dist = geo_dist(h, h1) - geo_dist(C0, h1);
if(dist < best) mouseover = c1, best = dist; if(dist < best) mouseover = c1, best = dist;
} }
return; return;
@ -4559,7 +4561,7 @@ EX void drawthemap() {
#endif #endif
if(non_spatial_model()) if(non_spatial_model())
spatial_graphics = false; spatial_graphics = false;
if(pmodel == mdDisk && abs(vid.alpha) < 1e-6) spatial_graphics = false; if(pmodel == mdDisk && abs(pconf.alpha) < 1e-6) spatial_graphics = false;
if(!spatial_graphics) wmspatial = mmspatial = false; if(!spatial_graphics) wmspatial = mmspatial = false;
if(GDIM == 3) wmspatial = mmspatial = true; if(GDIM == 3) wmspatial = mmspatial = true;
@ -4628,6 +4630,8 @@ EX void drawthemap() {
profile_stop(4); profile_stop(4);
drawFlashes(); drawFlashes();
mapeditor::draw_dtshapes();
if(multi::players > 1 && !shmup::on) { if(multi::players > 1 && !shmup::on) {
if(multi::centerplayer != -1) if(multi::centerplayer != -1)
cwtV = multi::whereis[multi::centerplayer]; cwtV = multi::whereis[multi::centerplayer];
@ -4762,7 +4766,7 @@ EX void calcparam() {
cd->xcenter = cd->xtop + cd->xsize / 2; cd->xcenter = cd->xtop + cd->xsize / 2;
cd->ycenter = cd->ytop + cd->ysize / 2; cd->ycenter = cd->ytop + cd->ysize / 2;
if(vid.scale > -1e-2 && vid.scale < 1e-2) vid.scale = 1; if(pconf.scale > -1e-2 && pconf.scale < 1e-2) pconf.scale = 1;
ld realradius = min(cd->xsize / 2, cd->ysize / 2); ld realradius = min(cd->xsize / 2, cd->ysize / 2);
@ -4784,11 +4788,11 @@ EX void calcparam() {
if(current_display->sidescreen) cd->xcenter = vid.yres/2; if(current_display->sidescreen) cd->xcenter = vid.yres/2;
} }
cd->radius = vid.scale * cd->scrsize; cd->radius = pconf.scale * cd->scrsize;
if(GDIM == 3 && in_perspective()) cd->radius = cd->scrsize; if(GDIM == 3 && in_perspective()) cd->radius = cd->scrsize;
realradius = min(realradius, cd->radius); realradius = min(realradius, cd->radius);
ld aradius = sphere ? cd->radius / (vid.alpha - 1) : cd->radius; ld aradius = sphere ? cd->radius / (pconf.alpha - 1) : cd->radius;
if(dronemode) { cd->ycenter -= cd->radius; cd->ycenter += vid.fsize/2; cd->ycenter += vid.fsize/2; cd->radius *= 2; } if(dronemode) { cd->ycenter -= cd->radius; cd->ycenter += vid.fsize/2; cd->ycenter += vid.fsize/2; cd->radius *= 2; }
@ -4800,8 +4804,8 @@ EX void calcparam() {
cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius; cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius;
} }
cd->xcenter += cd->scrsize * vid.xposition; cd->xcenter += cd->scrsize * pconf.xposition;
cd->ycenter += cd->scrsize * vid.yposition; cd->ycenter += cd->scrsize * pconf.yposition;
cd->tanfov = tan(vid.fov * degree / 2); cd->tanfov = tan(vid.fov * degree / 2);
@ -5150,7 +5154,7 @@ EX void clearAnimations() {
fallanims.clear(); fallanims.clear();
} }
auto graphcm = addHook(clearmemory, 0, [] () { auto graphcm = addHook(hooks_clearmemory, 0, [] () {
DEBBI(DF_MEMORY, ("clear graph memory")); DEBBI(DF_MEMORY, ("clear graph memory"));
mouseover = centerover = lmouseover = NULL; mouseover = centerover = lmouseover = NULL;
gmatrix.clear(); gmatrix0.clear(); current_display->all_drawn_copies.clear(); gmatrix.clear(); gmatrix0.clear(); current_display->all_drawn_copies.clear();

View File

@ -26,19 +26,25 @@ struct help_extension {
EX vector<help_extension> help_extensions; EX vector<help_extension> help_extensions;
vector<string> extra_keys = { vector<string> quick_keys = {
"1 = orthogonal/Gans model", "1 = orthogonal/Gans model/FPP",
"2 = small Poincare model/stereographic projection", "2 = small Poincare model/stereographic projection/SPP",
"3 = big Poincare model/stereographic projection", "3 = big Poincare model/stereographic projection/TPP",
"4 = Klein model/gnomonic projection", "4 = Klein model/gnomonic projection",
"5 = change wall display mode", "5 = change wall display mode",
"6 = change grid", "6 = change grid",
"7 = change heptagon marking", "7 = change heptagon marking",
// "8 = change background color", "8 = monster display mode"
// "9 = hyperboloid model", };
vector<string> normal_keys = {
"qweasdzxc, hjklyubn, numpad = move/skip turn", "qweasdzxc, hjklyubn, numpad = move/skip turn",
"arrows = panning", "g = drop a Dead Orb",
"o = world overview", "t = use a ranged Orb (target center of the screen)"
};
vector<string> extra_keys = {
"o = world overview (or another meaning in special modes)",
"v = menu", "v = menu",
"F1 = help", "F1 = help",
"F5 = restart game", "F5 = restart game",
@ -46,22 +52,37 @@ vector<string> extra_keys = {
"Esc = quest status", "Esc = quest status",
"Alt+Enter = full screen", "Alt+Enter = full screen",
"Alt = highlight interesting stuff", "Alt = highlight interesting stuff",
"t = use a ranged Orb (target center of the screen)",
"g = drop a Dead Orb",
"click left mouse button = move/skip", "click left mouse button = move/skip",
"shift+click left mouse button = use ranged Orb", "[shift+]click left mouse button = use ranged Orb (depending on mouse settings)",
"click right mouse button = context help", "click right mouse button = context help",
"mousewheel up = panning", "mousewheel up = panning",
"hold middle mouse button = panning", "hold middle mouse button = panning",
"lctrl + hold middle button = move the screen",
"mousewheel down = move/skip", "mousewheel down = move/skip",
"shift + mousewheel = change projection", "rshift + mousewheel = change projection",
"ctrl + mousewheel = change zoom", "lshift + mousewheel = change zoom (lctrl to keep center)",
"ctrl + shift + mousewheel = change both projection and zoom", "lctrl + mousewheel = reset the map center",
"ctrl + hold middle button = move the screen",
"shift + middle button = reset position",
"shift + F2 = disable the HUD", "shift + F2 = disable the HUD",
"shift + F3 = disable the FPS", "shift + F3 = disable the FPS",
"shift + F4 = disable the map" "shift + F4 = disable the map",
"space = recenter",
"ctrl + <key> = more precision"
};
vector<string> extra_keys_2d = {
"arrows = panning",
"PageUp/Down = rotate the screen",
};
vector<string> extra_keys_3d = {
"arrows = rotate the camera",
"rshift+arrows = strafe",
"lshift+arrows = rotate the model (in rug mode)",
"end = move camera forward",
"home = move camera backward",
"shift+Home/End = zoom",
"PageUp/Down = rotate the screen",
"move mouse = rotate camera (in rug, only with lctrl)",
}; };
void buildHelpText() { void buildHelpText() {
@ -147,7 +168,7 @@ void buildHelpText() {
#else #else
if(DEFAULTCONTROL) if(DEFAULTCONTROL)
help += XLAT( help += XLAT(
"Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " "Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n" "To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n"
); );
help += XLAT( help += XLAT(
@ -173,7 +194,18 @@ void buildHelpText() {
#if ISMOBILE == 0 #if ISMOBILE == 0
help_extensions.push_back(help_extension{'k', XLAT("advanced keyboard shortcuts"), [] () { help_extensions.push_back(help_extension{'k', XLAT("advanced keyboard shortcuts"), [] () {
help = ""; help = "";
for(string s: extra_keys) help += s, help += "\n\n"; for(string s: normal_keys) help += s, help += "\n";
for(string s: extra_keys) help += s, help += "\n";
help += "\n\nQuick keys:\n";
for(string s: quick_keys) help += s, help += "\n";
if(GDIM == 3 || rug::rugged) {
help += "\n\nIn 3D modes:\n";
for(string s: extra_keys_3d) help += s, help += "\n";
}
else {
help += "\n\nIn 2D modes:\n";
for(string s: extra_keys_2d) help += s, help += "\n";
}
}}); }});
#endif #endif
} }
@ -325,7 +357,7 @@ EX string generateHelpForItem(eItem it) {
" You need to go deep to collect lots of them."); " You need to go deep to collect lots of them.");
} }
#if ISMOBILE==1 #if ISMOBILE
if(it == itOrbSafety) if(it == itOrbSafety)
help += XLAT("This might be very useful for devices with limited memory."); help += XLAT("This might be very useful for devices with limited memory.");
#else #else
@ -493,7 +525,7 @@ void addMinefieldExplanation(string& s) {
); );
s += "\n\n"; s += "\n\n";
#if ISMOBILE==0 #if !ISMOBILE
s += XLAT("Known mines may be marked by pressing 'm'. Your allies won't step on marked mines."); s += XLAT("Known mines may be marked by pressing 'm'. Your allies won't step on marked mines.");
#else #else
s += XLAT("Known mines may be marked by touching while in drag mode. Your allies won't step on marked mines."); s += XLAT("Known mines may be marked by touching while in drag mode. Your allies won't step on marked mines.");
@ -772,7 +804,7 @@ EX void appendHelp(string s) {
unsigned char lastval; unsigned char lastval;
int windtotal; int windtotal;
EX hookset<void(cell*)> *hooks_mouseover; EX hookset<void(cell*)> hooks_mouseover;
EX void describeMouseover() { EX void describeMouseover() {
DEBBI(DF_GRAPH, ("describeMouseover")); DEBBI(DF_GRAPH, ("describeMouseover"));
@ -1017,7 +1049,7 @@ EX void showHelp() {
}; };
} }
EX hookset<bool()> *hooks_default_help; EX hookset<bool()> hooks_default_help;
EX void gotoHelp(const string& h) { EX void gotoHelp(const string& h) {
help = h; help = h;
@ -1030,6 +1062,13 @@ EX void gotoHelp(const string& h) {
#if CAP_RUG #if CAP_RUG
if(rug::rugged) { if(rug::rugged) {
help = rug::makehelp(); help = rug::makehelp();
help += "\n\n";
for(string s: extra_keys_3d) help += s, help += "\n";
help += "\n\n";
help_extensions.push_back(help_extension{'m', XLAT("Hypersian Rug menu"), [] () { popScreen(); rug::select(); }}); help_extensions.push_back(help_extension{'m', XLAT("Hypersian Rug menu"), [] () { popScreen(); rug::select(); }});
help_extensions.push_back(help_extension{'h', XLAT("HyperRogue help"), [] () { buildHelpText(); }}); help_extensions.push_back(help_extension{'h', XLAT("HyperRogue help"), [] () { buildHelpText(); }});
return; return;

View File

@ -119,9 +119,16 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0, int fix
if(h->c.spin(0) == 2 && h->move(0)) { if(h->c.spin(0) == 2 && h->move(0)) {
int d = h->c.spin(0); int d = h->c.spin(0);
int d1 = (d+S7-1)%S7; int d1 = (d+S7-1)%S7;
heptagon* h1 = createStep(h->move(0), d1); bool missing0 = !h->move(0)->move(d1);
if(h1->distance <= h->move(0)->distance) if(missing0) {
h->distance = h->move(0)->distance+1; if(s == 1)
h->distance = h->move(0)->distance + 1;
}
else {
heptagon* h1 = createStep(h->move(0), d1);
if(h1->distance <= h->move(0)->distance)
h->distance = h->move(0)->distance+1;
}
} }
if((h->s == hsB && h->move(0)->s == hsB) || h->move(0)->s == hsA) { if((h->s == hsB && h->move(0)->s == hsB) || h->move(0)->s == hsA) {
int d = h->c.spin(0); int d = h->c.spin(0);
@ -129,11 +136,23 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0, int fix
if(h1->distance <= h->move(0)->distance) if(h1->distance <= h->move(0)->distance)
h->distance = h->move(0)->distance+1; h->distance = h->move(0)->distance+1;
} }
if(h->c.spin(0) == S7-1 && h->move(0)->distance != 0) if(h->c.spin(0) == S7-1 && (h->move(0)->s != hsOrigin) && BITRUNCATED) {
h->distance = min( bool missing = !h->move(S7-1);
h->move(0)->move(0)->distance + 2, if(missing) {
createStep(h, S7-1)->distance + 1 h->distance = parent->distance;
); if(
parent->distance - h->move(0)->move(0)->distance == 1 &&
h->c.spin(0) == S7 - 1 &&
h->move(0)->c.spin(0) == 2)
h->distance++;
}
else {
h->distance = min(
h->move(0)->move(0)->distance + 2,
createStep(h, S7-1)->distance + 1
);
}
}
} }
else if(parent->s == hsOrigin) h->distance = parent->distance + gp::dist_2(); else if(parent->s == hsOrigin) h->distance = parent->distance + gp::dist_2();
#if CAP_GP #if CAP_GP
@ -190,6 +209,9 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0, int fix
} }
else { else {
h->distance = parent->distance - gp::dist_2(); h->distance = parent->distance - gp::dist_2();
if(S3 == 4 && S7 > 5 && BITRUNCATED) {
h->distance = parent->distance - 2;
}
if(S3 == 4 && S7 == 5) { if(S3 == 4 && S7 == 5) {
if(h->s == hsOrigin) { if(h->s == hsOrigin) {
printf("had to cheat!\n"); printf("had to cheat!\n");
@ -217,7 +239,7 @@ void addSpin(heptagon *h, int d, heptagon *from, int rot, int spin) {
extern int hrand(int); extern int hrand(int);
EX hookset<void(heptagon*, int)> *hooks_createStep; EX hookset<void(heptagon*, int)> hooks_createStep;
// create h->move(d) if not created yet // create h->move(d) if not created yet
heptagon *createStep(heptagon *h, int d) { heptagon *createStep(heptagon *h, int d) {

View File

@ -362,7 +362,7 @@ EX namespace history {
} }
ld measureLength() { ld measureLength() {
ld r = bandhalf * vid.scale; ld r = bandhalf * pconf.scale;
ld tpixels = 0; ld tpixels = 0;
int siz = isize(v); int siz = isize(v);
@ -526,7 +526,7 @@ EX namespace history {
dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i'); dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i');
// bool notconformal0 = (pmodel >= 5 && pmodel <= 6) && !euclid; // bool notconformal0 = (pmodel >= 5 && pmodel <= 6) && !euclid;
// bool notconformal = notconformal0 || abs(vid.alpha-1) > 1e-3; // bool notconformal = notconformal0 || abs(pconf.alpha-1) > 1e-3;
dialog::addSelItem(XLAT("projection"), current_proj_name(), 'm'); dialog::addSelItem(XLAT("projection"), current_proj_name(), 'm');
@ -689,7 +689,7 @@ EX namespace history {
auto hookArg = addHook(hooks_args, 100, readArgs); auto hookArg = addHook(hooks_args, 100, readArgs);
#endif #endif
auto hooks = addHook(clearmemory, 0, [] () { auto hooks = addHook(hooks_clearmemory, 0, [] () {
history::renderAutoband(); history::renderAutoband();
history::on = false; history::on = false;
history::killhistory.clear(); history::killhistory.clear();

View File

@ -25,7 +25,8 @@ EX FILE *debugfile;
#define DF_GP 2048 #define DF_GP 2048
#define DF_POLY 4096 #define DF_POLY 4096
#define DF_LOG 8192 #define DF_LOG 8192
#define DF_KEYS "imwesxufgbtopl" #define DF_VERTEX 16384
#define DF_KEYS "imwesxufgbtoplv"
#endif #endif
EX int debugflags = DF_INIT | DF_ERROR | DF_WARN | DF_MSG | DF_TIME | DF_LOG; EX int debugflags = DF_INIT | DF_ERROR | DF_WARN | DF_MSG | DF_TIME | DF_LOG;
@ -278,6 +279,22 @@ struct indenter_finish : indenter {
#endif #endif
EX void print(hstream& hs, cld x) {
int parts = 0;
if(kz(real(x))) {
print(hs, real(x));
parts++;
}
if(kz(imag(x))) {
if(parts && imag(x) > 0) print(hs, "+");
parts++;
print(hs, imag(x), "i");
}
if(!parts) print(hs, 0);
}
EX string fts_fixed(ld x, int prec IS(6)) { EX string fts_fixed(ld x, int prec IS(6)) {
std::stringstream ss; std::stringstream ss;
ss.precision(prec); ss.precision(prec);
@ -354,6 +371,13 @@ EX transmatrix kz(transmatrix h) {
return h; return h;
} }
#if HDR
template<class T> vector<T> kz(vector<T> v) {
for(auto& el: v) el = kz(el);
return v;
}
#endif
EX string pick123() { return cts('1' + rand() % 3); } EX string pick123() { return cts('1' + rand() % 3); }
EX string pick12() { return cts('1' + rand() % 2); } EX string pick12() { return cts('1' + rand() % 2); }

16
hud.cpp
View File

@ -328,7 +328,7 @@ void displayglyph2(int cx, int cy, int buttonsize, int i) {
EX bool nohud, nomenukey; EX bool nohud, nomenukey;
EX hookset<bool()> *hooks_prestats; EX hookset<bool()> hooks_prestats;
#if CAP_SHAPES #if CAP_SHAPES
void drawMobileArrow(int i) { void drawMobileArrow(int i) {
@ -401,20 +401,12 @@ EX void drawStats() {
if(geometry == gRotSpace || geometry == gProduct) rots::draw_underlying(!cornermode); if(geometry == gRotSpace || geometry == gProduct) rots::draw_underlying(!cornermode);
{ {
dynamicval<eModel> pm(pmodel, flat_model());
glClear(GL_DEPTH_BUFFER_BIT);
// dynamicval<videopar> v(vid, vid);
// vid.alpha = vid.scale = 1;
dynamicval<ld> va(vid.alpha, 1);
dynamicval<ld> vs(vid.scale, 1);
dynamicval<ld> vc(vid.camera_angle, 0);
if(prod) vid.alpha = 30, vid.scale = 30;
auto& cd = current_display; auto& cd = current_display;
auto xc = cd->xcenter; auto xc = cd->xcenter;
auto yc = cd->ycenter; auto yc = cd->ycenter;
calcparam(); flat_model_enabler fme;
if(crosshair_color && crosshair_size > 0) { if(crosshair_color && crosshair_size > 0) {
initquickqueue(); initquickqueue();
@ -469,7 +461,7 @@ EX void drawStats() {
int spots = 0; int spots = 0;
for(int u=vid.fsize; u<vid.xres/2-s; u += s) for(int u=vid.fsize; u<vid.xres/2-s; u += s)
for(int v=vid.fsize; v<vid.yres/2-s; v += s) for(int v=vid.fsize; v<vid.yres/2-s; v += s)
if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / vid.stretch) > rad) { if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / pconf.stretch) > rad) {
spots++; spots++;
} }
if(spots >= bycorner[cor] && spots >= 3) { if(spots >= bycorner[cor] && spots >= 3) {
@ -482,7 +474,7 @@ EX void drawStats() {
} }
for(int u=vid.fsize; u<vid.xres/2-s; u += s) for(int u=vid.fsize; u<vid.xres/2-s; u += s)
for(int v=vid.fsize; v<vid.yres/2-s; v += s) for(int v=vid.fsize; v<vid.yres/2-s; v += s)
if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / vid.stretch) > rad) { if(hypot(vid.xres/2-u-s, (vid.yres/2-v-s) / pconf.stretch) > rad) {
if(next >= isize(glyphstoshow)) break; if(next >= isize(glyphstoshow)) break;
int cx = u; int cx = u;

View File

@ -29,7 +29,7 @@ void moreStack() {
#endif #endif
namespace hr { namespace hr {
EX hookset<bool(int argc, char** argv)> *hooks_main; EX hookset<bool(int argc, char** argv)> hooks_main;
EX int hyper_main(int argc, char **argv) { EX int hyper_main(int argc, char **argv) {
using namespace hr; using namespace hr;

View File

@ -40,6 +40,7 @@
#include "arbitrile.cpp" #include "arbitrile.cpp"
#include "euclid.cpp" #include "euclid.cpp"
#include "sphere.cpp" #include "sphere.cpp"
#include "fake.cpp"
#include "quotient.cpp" #include "quotient.cpp"
#include "crystal.cpp" #include "crystal.cpp"
#include "reg3.cpp" #include "reg3.cpp"

122
hyper.h
View File

@ -13,8 +13,8 @@
#define _HYPER_H_ #define _HYPER_H_
// version numbers // version numbers
#define VER "11.3i" #define VER "11.3l"
#define VERNUM_HEX 0xA829 #define VERNUM_HEX 0xA82C
#include "sysconfig.h" #include "sysconfig.h"
@ -142,6 +142,7 @@ void addMessage(string s, char spamtype = 0);
#define sl2 (cgclass == gcSL2) #define sl2 (cgclass == gcSL2)
#define prod (cgclass == gcProduct) #define prod (cgclass == gcProduct)
#define hybri (cgflags & qHYBRID) #define hybri (cgflags & qHYBRID)
#define rotspace (geometry == gRotSpace)
#define hyperbolic (cgclass == gcHyperbolic) #define hyperbolic (cgclass == gcHyperbolic)
#define nonisotropic (among(cgclass, gcSolNIH, gcNil, gcSL2)) #define nonisotropic (among(cgclass, gcSolNIH, gcNil, gcSL2))
#define translatable (euclid || nonisotropic) #define translatable (euclid || nonisotropic)
@ -228,9 +229,46 @@ enum eStereo { sOFF, sAnaglyph, sLR, sODS };
enum eModel : int; enum eModel : int;
/** configuration of the projection */
struct projection_configuration {
eModel model; /**< which projection, see classes.cpp */
ld xposition, yposition; /**< move the center to another position */
ld scale, alpha, camera_angle, fisheye_param, twopoint_param, stretch, ballangle, ballproj, euclid_to_sphere;
ld clip_min, clip_max;
ld model_orientation, halfplane_scale, model_orientation_yz;
ld collignon_parameter;
bool collignon_reflected;
string formula;
eModel basic_model;
ld top_z;
ld model_transition;
ld spiral_angle;
ld spiral_x;
ld spiral_y;
bool use_atan;
ld right_spiral_multiplier;
ld any_spiral_multiplier;
ld sphere_spiral_multiplier;
ld spiral_cone;
ld skiprope;
ld product_z_scale;
projection_configuration() {
formula = "z^2"; top_z = 5; model_transition = 1; spiral_angle = 70; spiral_x = 10; spiral_y = 7;
right_spiral_multiplier = 1;
any_spiral_multiplier = 1;
sphere_spiral_multiplier = 2;
spiral_cone = 360;
use_atan = false;
product_z_scale = 1;
}
};
struct videopar { struct videopar {
ld scale, alpha, sspeed, mspeed, yshift, camera_angle; projection_configuration projection_config, rug_config;
ld ballangle, ballproj, euclid_to_sphere, twopoint_param, fisheye_param, stretch, binary_width, fixed_facing_dir; ld yshift;
ld sspeed, mspeed;
ld binary_width, fixed_facing_dir;
int mobilecompasssize; int mobilecompasssize;
int radarsize; // radar for 3D geometries int radarsize; // radar for 3D geometries
ld radarrange; ld radarrange;
@ -251,8 +289,6 @@ struct videopar {
int xscr, yscr; int xscr, yscr;
ld xposition, yposition;
bool grid; bool grid;
bool particles; bool particles;
@ -304,8 +340,6 @@ struct videopar {
int cells_drawn_limit; int cells_drawn_limit;
int cells_generated_limit; // limit on cells generated per frame int cells_generated_limit; // limit on cells generated per frame
ld skiprope;
eStereo stereo_mode; eStereo stereo_mode;
ld ipd; ld ipd;
ld lr_eyewidth, anaglyph_eyewidth; ld lr_eyewidth, anaglyph_eyewidth;
@ -319,7 +353,6 @@ struct videopar {
ld depth; // world level below the plane ld depth; // world level below the plane
ld camera; // camera level above the plane ld camera; // camera level above the plane
ld wall_height, creature_scale, height_width; ld wall_height, creature_scale, height_width;
eModel vpmodel;
ld lake_top, lake_bottom; ld lake_top, lake_bottom;
ld rock_wall_ratio; ld rock_wall_ratio;
ld human_wall_ratio; ld human_wall_ratio;
@ -331,7 +364,6 @@ struct videopar {
ld eye; ld eye;
bool auto_eye; bool auto_eye;
ld collignon_parameter; bool collignon_reflected;
ld plevel_factor; ld plevel_factor;
bool bubbles_special, bubbles_threshold, bubbles_all; bool bubbles_special, bubbles_threshold, bubbles_all;
int joysmooth; int joysmooth;
@ -341,7 +373,7 @@ extern videopar vid;
#define WDIM cginf.g.gameplay_dimension #define WDIM cginf.g.gameplay_dimension
#define GDIM cginf.g.graphical_dimension #define GDIM cginf.g.graphical_dimension
#define MDIM cginf.g.homogeneous_dimension #define MDIM (MAXMDIM == 3 ? 3 : cginf.g.homogeneous_dimension)
#define LDIM (MDIM-1) #define LDIM (MDIM-1)
#define cclass g.kind #define cclass g.kind
@ -407,8 +439,41 @@ struct movedir {
// shmup // shmup
template<class T> class hookset : public map<int, function<T>> {}; template<class T>
typedef hookset<void()> *purehookset; class hookset {
std::map<int, std::function<T>> *map_ = nullptr;
public:
template<class U>
int add(int prio, U&& hook) {
if (map_ == nullptr) map_ = new std::map<int, std::function<T>>();
while (map_->count(prio)) {
prio++;
}
map_->emplace(prio, static_cast<U&&>(hook));
return 0;
}
template<class... U>
void callhooks(U&&... args) const {
if (map_ == nullptr) return;
for (const auto& p : *map_) {
p.second(static_cast<U&&>(args)...);
}
}
template<class V, class... U>
V callhandlers(V zero, U&&... args) const {
if (map_ == nullptr) return zero;
for (const auto& p : *map_) {
auto z = p.second(static_cast<U&&>(args)...);
if (z != zero) return z;
}
return zero;
}
};
using purehookset = hookset<void()>;
static const int NOHINT = -1; static const int NOHINT = -1;
@ -621,33 +686,24 @@ enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMul
#define MODELCOUNT ((int) mdGUARD) #define MODELCOUNT ((int) mdGUARD)
#define pmodel (vid.vpmodel) #define pconf vid.projection_config
#define vpconf (rug::rugged ? vid.rug_config : vid.projection_config)
#define pmodel (pconf.model)
color_t darkena(color_t c, int lev, int a); color_t darkena(color_t c, int lev, int a);
static const int DISTANCE_UNKNOWN = 127; static const int DISTANCE_UNKNOWN = 127;
#include <functional> template<class T, class U> int addHook(hookset<T>& m, int prio, U&& hook) {
return m.add(prio, static_cast<U&&>(hook));
template<class T, class U> int addHook(hookset<T>*& m, int prio, const U& hook) {
if(!m) m = new hookset<T> ();
while(m->count(prio)) {
prio++;
}
(*m)[prio] = hook;
return 0;
} }
template<class T, class... U> void callhooks(hookset<T> *h, U&&... args) { template<class T, class... U> void callhooks(const hookset<T>& h, U&&... args) {
if(h) for(auto& p: *h) p.second(std::forward<U>(args)...); return h.callhooks(static_cast<U&&>(args)...);
} }
template<class T, class V, class... U> V callhandlers(V zero, hookset<T> *h, U&&... args) { template<class T, class V, class... U> V callhandlers(V zero, const hookset<T>& h, U&&... args) {
if(h) for(auto& p: *h) { return h.callhandlers(zero, static_cast<U&&>(args)...);
auto z = p.second(std::forward<U>(args)...);
if(z != zero) return z;
}
return zero;
} }
string XLAT(string); string XLAT(string);
@ -670,7 +726,7 @@ struct colortable: vector<color_t> {
namespace scores { void load(); } namespace scores { void load(); }
#if ISMOBILE==1 #if ISMOBILE
namespace leader { void showMenu(); void handleKey(int sym, int uni); } namespace leader { void showMenu(); void handleKey(int sym, int uni); }
#endif #endif
@ -679,7 +735,7 @@ int textwidth(int siz, const string &str);
int gl_width(int size, const char *s); int gl_width(int size, const char *s);
#endif #endif
#ifdef ISMOBILE #if ISMOBILE
extern int andmode; extern int andmode;
extern bool longclick; extern bool longclick;
extern bool useRangedOrb; extern bool useRangedOrb;

View File

@ -381,7 +381,14 @@ EX ld sqhypot_d(int d, const hyperpoint& h) {
EX ld hypot_d(int d, const hyperpoint& h) { EX ld hypot_d(int d, const hyperpoint& h) {
return sqrt(sqhypot_d(d, h)); return sqrt(sqhypot_d(d, h));
} }
/** @brief positive for a material vertex, 0 for ideal vertex, negative for ultra-ideal vertex */
EX ld material(const hyperpoint& h) {
if(sphere) return intval(h, Hypc);
else if(hyperbolic) return -intval(h, Hypc);
else return h[LDIM] - 1;
}
EX ld zlevel(const hyperpoint &h) { EX ld zlevel(const hyperpoint &h) {
if(sl2) return sqrt(-intval(h, Hypc)); if(sl2) return sqrt(-intval(h, Hypc));
else if(translatable) return h[LDIM]; else if(translatable) return h[LDIM];
@ -412,6 +419,14 @@ EX hyperpoint normalize(hyperpoint H) {
return H; return H;
} }
/** like normalize but makes (ultra)ideal points material */
EX hyperpoint ultra_normalize(hyperpoint H) {
if(material(H) <= 0) {
H[MDIM-1] = hypot_d(MDIM-1, H) + 1e-6;
}
return normalize(H);
}
/** normalize, and in product geometry, also flatten */ /** normalize, and in product geometry, also flatten */
EX hyperpoint normalize_flat(hyperpoint h) { EX hyperpoint normalize_flat(hyperpoint h) {
if(prod) return product_decompose(h).second; if(prod) return product_decompose(h).second;
@ -702,6 +717,8 @@ EX transmatrix rpushxto0(const hyperpoint& H) {
EX transmatrix ggpushxto0(const hyperpoint& H, ld co) { EX transmatrix ggpushxto0(const hyperpoint& H, ld co) {
if(translatable) { if(translatable) {
if(nonisotropic)
return co > 0 ? eupush(H) : inverse(eupush(H));
return eupush(co * H); return eupush(co * H);
} }
if(prod) { if(prod) {
@ -742,6 +759,7 @@ EX transmatrix rgpushxto0(const hyperpoint& H) {
EX void fixmatrix(transmatrix& T) { EX void fixmatrix(transmatrix& T) {
if(nonisotropic) ; // T may be inverse... do not do that if(nonisotropic) ; // T may be inverse... do not do that
else if(cgflags & qAFFINE) ; // affine
else if(prod) { else if(prod) {
auto z = zlevel(tC0(T)); auto z = zlevel(tC0(T));
T = mscale(T, -z); T = mscale(T, -z);
@ -1030,13 +1048,13 @@ EX bool asign(ld y1, ld y2) { return signum(y1) != signum(y2); }
EX ld xcross(ld x1, ld y1, ld x2, ld y2) { return x1 + (x2 - x1) * y1 / (y1 - y2); } EX ld xcross(ld x1, ld y1, ld x2, ld y2) { return x1 + (x2 - x1) * y1 / (y1 - y2); }
EX transmatrix parallel_transport(const transmatrix Position, const transmatrix& ori, const hyperpoint direction, int precision IS(100)) { EX transmatrix parallel_transport(const transmatrix Position, const transmatrix& ori, const hyperpoint direction) {
if(nonisotropic) return nisot::parallel_transport(Position, direction); if(nonisotropic) return nisot::parallel_transport(Position, direction);
else if(prod) { else if(prod) {
hyperpoint h = product::direct_exp(ori * direction); hyperpoint h = product::direct_exp(ori * direction);
return Position * rgpushxto0(h); return Position * rgpushxto0(h);
} }
else return Position * rgpushxto0(direct_exp(direction, precision)); else return Position * rgpushxto0(direct_exp(direction));
} }
EX void apply_parallel_transport(transmatrix& Position, const transmatrix orientation, const hyperpoint direction) { EX void apply_parallel_transport(transmatrix& Position, const transmatrix orientation, const hyperpoint direction) {
@ -1151,8 +1169,8 @@ EX hyperpoint tangent_length(hyperpoint dir, ld length) {
} }
/** exponential function: follow the geodesic given by v */ /** exponential function: follow the geodesic given by v */
EX hyperpoint direct_exp(hyperpoint v, int steps) { EX hyperpoint direct_exp(hyperpoint v) {
if(sn::in()) return nisot::numerical_exp(v, steps); if(sn::in()) return nisot::numerical_exp(v);
if(nil) return nilv::formula_exp(v); if(nil) return nilv::formula_exp(v);
if(sl2) return slr::formula_exp(v); if(sl2) return slr::formula_exp(v);
if(prod) return product::direct_exp(v); if(prod) return product::direct_exp(v);
@ -1163,20 +1181,26 @@ EX hyperpoint direct_exp(hyperpoint v, int steps) {
} }
#if HDR #if HDR
enum iePrecision { iLazy, iTable }; constexpr flagtype pfNO_INTERPOLATION = 1; /**< in tables (sol/nih geometries), do not use interpolations */
constexpr flagtype pfNO_DISTANCE = 2; /**< we just need the directions -- this makes it a bit faster in sol/nih geometries */
constexpr flagtype pfLOW_BS_ITER = 4; /**< low iterations in binary search (nil geometry, sl2 not affected currently) */
constexpr flagtype pQUICK = pfNO_INTERPOLATION | pfLOW_BS_ITER;
constexpr flagtype pNORMAL = 0;
#endif #endif
/** inverse exponential function \see hr::direct_exp */ /** inverse exponential function \see hr::direct_exp */
EX hyperpoint inverse_exp(const hyperpoint h, iePrecision p, bool just_direction IS(true)) { EX hyperpoint inverse_exp(const hyperpoint h, flagtype prec IS(pNORMAL)) {
#if CAP_SOLV #if CAP_SOLV
if(sn::in()) { if(sn::in()) {
if(nih) if(nih)
return sn::get_inverse_exp_nsym(h, p == iLazy, just_direction); return sn::get_inverse_exp_nsym(h, prec);
else else
return sn::get_inverse_exp_symsol(h, p == iLazy, just_direction); return sn::get_inverse_exp_symsol(h, prec);
} }
#endif #endif
if(nil) return nilv::get_inverse_exp(h, p == iLazy ? 5 : 20); if(nil) return nilv::get_inverse_exp(h, prec);
if(sl2) return slr::get_inverse_exp(h); if(sl2) return slr::get_inverse_exp(h);
if(prod) return product::inverse_exp(h); if(prod) return product::inverse_exp(h);
ld d = acos_auto_clamp(h[GDIM]); ld d = acos_auto_clamp(h[GDIM]);
@ -1186,9 +1210,15 @@ EX hyperpoint inverse_exp(const hyperpoint h, iePrecision p, bool just_direction
return v; return v;
} }
EX ld geo_dist(const hyperpoint h1, const hyperpoint h2, iePrecision p) { EX ld geo_dist(const hyperpoint h1, const hyperpoint h2, flagtype prec IS(pNORMAL)) {
if(!nonisotropic) return hdist(h1, h2); if(!nonisotropic) return hdist(h1, h2);
return hypot_d(3, inverse_exp(inverse(nisot::translate(h1)) * h2, p, false)); return hypot_d(3, inverse_exp(inverse(nisot::translate(h1)) * h2, prec));
}
EX ld geo_dist_q(const hyperpoint h1, const hyperpoint h2, flagtype prec IS(pNORMAL)) {
auto d = geo_dist(h1, h2, prec);
if(elliptic && d > M_PI/2) return M_PI - d;
return d;
} }
EX hyperpoint lp_iapply(const hyperpoint h) { EX hyperpoint lp_iapply(const hyperpoint h) {

View File

@ -30,10 +30,6 @@
#define EMSCRIPTEN #define EMSCRIPTEN
#endif #endif
#ifndef CAP_ORIENTATION
#define CAP_ORIENTATION 1
#endif
#ifdef FAKEWEB #ifdef FAKEWEB
namespace hr { void mainloopiter(); } namespace hr { void mainloopiter(); }
template<class A, class B, class C> void emscripten_set_main_loop(A a, B b, C c) { while(true) mainloopiter(); } template<class A, class B, class C> void emscripten_set_main_loop(A a, B b, C c) { while(true) mainloopiter(); }

View File

@ -8,21 +8,20 @@
#include "hyper.h" #include "hyper.h"
namespace hr { namespace hr {
ld ghx, ghy, ghgx, ghgy; hyperpoint ghxy, ghgxy, ghpm = C0;
hyperpoint ghpm = C0;
#if HDR #if HDR
inline bool sphereflipped() { return sphere && vid.alpha > 1.1 && GDIM == 3; } inline bool sphereflipped() { return sphere && pconf.alpha > 1.1 && GDIM == 3; }
#endif #endif
void ghcheck(hyperpoint &ret, const hyperpoint &H) { void ghcheck(hyperpoint &ret, const hyperpoint &H) {
if(hypot(ret[0]-ghx, ret[1]-ghy) < hypot(ghgx-ghx, ghgy-ghy)) { if(hypot_d(2, ret-ghxy) < hypot_d(2, ghgxy-ghxy)) {
ghpm = H; ghgx = ret[0]; ghgy = ret[1]; ghpm = H; ghgxy = ret;
} }
} }
EX void camrotate(ld& hx, ld& hy) { EX void camrotate(ld& hx, ld& hy) {
ld cam = vid.camera_angle * degree; ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam); GLfloat cc = cos(cam);
GLfloat ss = sin(cam); GLfloat ss = sin(cam);
ld ux = hx, uy = hy * cc + ss, uz = cc - ss * hy; ld ux = hx, uy = hy * cc + ss, uz = cc - ss * hy;
@ -37,7 +36,7 @@ EX bool non_spatial_model() {
return pmodel && vid.consider_shader_projection && (get_shader_flags() & SF_DIRECT); return pmodel && vid.consider_shader_projection && (get_shader_flags() & SF_DIRECT);
} }
EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeometryClass gc IS(ginf[geometry].cclass)) { EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(pconf.alpha), eGeometryClass gc IS(ginf[geometry].cclass)) {
ld hx = h[0], hy = h[1]; ld hx = h[0], hy = h[1];
if(gc == gcEuclid) if(gc == gcEuclid)
@ -59,7 +58,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet
B /= A; C /= A; B /= A; C /= A;
ld rootsign = 1; ld rootsign = 1;
// if(gc == gcSphere && vid.alpha > 1) rootsign = -1; // if(gc == gcSphere && pconf.alpha > 1) rootsign = -1;
ld hz = B / 2 + rootsign * sqrt(C + B*B/4); ld hz = B / 2 + rootsign * sqrt(C + B*B/4);
@ -72,7 +71,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet
return H; return H;
} }
EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(vid.alpha)) { EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(pconf.alpha)) {
ld s = 1 / (alpha + z[LDIM]); ld s = 1 / (alpha + z[LDIM]);
z[0] *= s; z[0] *= s;
z[1] *= s; z[1] *= s;
@ -88,21 +87,49 @@ EX hyperpoint space_to_perspective(hyperpoint z, ld alpha IS(vid.alpha)) {
EX hyperpoint gethyper(ld x, ld y) { EX hyperpoint gethyper(ld x, ld y) {
ld hx = (x - current_display->xcenter) / current_display->radius; ld hx = (x - current_display->xcenter) / current_display->radius;
ld hy = (y - current_display->ycenter) / current_display->radius / vid.stretch; ld hy = (y - current_display->ycenter) / current_display->radius / pconf.stretch;
hyperpoint hxy = point3(hx, hy, 0);
if(pmodel) { if(pmodel) {
ghx = hx, ghy = hy; ghxy = hxy;
return ghpm;
transmatrix T = rgpushxto0(ghpm);
auto distance_at = [&] (const transmatrix& T1) {
hyperpoint h1;
applymodel(tC0(T1), h1);
return sqhypot_d(2, hxy - h1);
};
ld best = distance_at(T);
for(int it=0; it<50; it++)
for(int s=0; s<4; s++) {
transmatrix T1 = T * spin(s * quarter_circle) * xpush(pow(1.2, -it));
ld dist = distance_at(T1);
if(dist < best) best = dist, T = T1;
if(mdBandAny()) {
band_shift += 2 * M_PI;
dist = distance_at(T1);
if(dist < best) best = dist, T = T1;
band_shift -= 4 * M_PI;
dist = distance_at(T1);
if(dist < best) best = dist, T = T1;
band_shift += 2 * M_PI;
}
}
return tC0(T);
} }
if(vid.camera_angle) camrotate(hx, hy); if(pconf.camera_angle) camrotate(hx, hy);
return perspective_to_space(hpxyz(hx, hy, 0)); return perspective_to_space(hpxyz(hx, hy, 0));
} }
void ballmodel(hyperpoint& ret, double alpha, double d, double zl) { void ballmodel(hyperpoint& ret, double alpha, double d, double zl) {
hyperpoint H = ypush(vid.camera) * xpush(d) * ypush(zl) * C0; hyperpoint H = ypush(vid.camera) * xpush(d) * ypush(zl) * C0;
ld tzh = vid.ballproj + H[LDIM]; ld tzh = pconf.ballproj + H[LDIM];
ld ax = H[0] / tzh; ld ax = H[0] / tzh;
ld ay = H[1] / tzh; ld ay = H[1] / tzh;
@ -165,7 +192,7 @@ ld find_zlev(hyperpoint& H) {
} }
ld get_tz(hyperpoint H) { ld get_tz(hyperpoint H) {
ld tz = vid.alpha+H[LDIM]; ld tz = pconf.alpha+H[LDIM];
if(tz < BEHIND_LIMIT && tz > -BEHIND_LIMIT) tz = BEHIND_LIMIT; if(tz < BEHIND_LIMIT && tz > -BEHIND_LIMIT) tz = BEHIND_LIMIT;
return tz; return tz;
} }
@ -237,7 +264,7 @@ void band_conformal(ld& x, ld& y) {
} }
void make_twopoint(ld& x, ld& y) { void make_twopoint(ld& x, ld& y) {
auto p = vid.twopoint_param; auto p = pconf.twopoint_param;
ld dleft = hypot_auto(x-p, y); ld dleft = hypot_auto(x-p, y);
ld dright = hypot_auto(x+p, y); ld dright = hypot_auto(x+p, y);
if(sphere) { if(sphere) {
@ -264,7 +291,7 @@ hyperpoint mobius(hyperpoint h, ld angle, ld scale = 1) {
} }
hyperpoint compute_hybrid(hyperpoint H, int rootid) { hyperpoint compute_hybrid(hyperpoint H, int rootid) {
auto& t = vid.twopoint_param; auto& t = pconf.twopoint_param;
hyperpoint Hl = xpush(+t) * H; hyperpoint Hl = xpush(+t) * H;
hyperpoint Hr = xpush(-t) * H; hyperpoint Hr = xpush(-t) * H;
ld g = (Hl[0] + 1e-7) / (Hl[1] + 1e-8); ld g = (Hl[0] + 1e-7) / (Hl[1] + 1e-8);
@ -315,11 +342,11 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
hyperpoint H_orig = H; hyperpoint H_orig = H;
if(models::product_model()) { if(models::product_model(pmodel)) {
ld zlev = zlevel(H); ld zlev = zlevel(H);
H /= exp(zlev); H /= exp(zlev);
hybrid::in_underlying_geometry([&] { applymodel(H, ret); }); hybrid::in_underlying_geometry([&] { applymodel(H, ret); });
ret[2] = zlev * models::product_z_scale; ret[2] = zlev * pconf.product_z_scale;
ret = NLP * ret; ret = NLP * ret;
return; return;
} }
@ -337,7 +364,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
} }
case mdGeodesic: { case mdGeodesic: {
auto S = lp_apply(inverse_exp(H, iTable)); auto S = lp_apply(inverse_exp(H, pNORMAL | pfNO_DISTANCE));
ld ratio = vid.xres / current_display->tanfov / current_display->radius / 2; ld ratio = vid.xres / current_display->tanfov / current_display->radius / 2;
ret[0] = S[0]/S[2] * ratio; ret[0] = S[0]/S[2] * ratio;
ret[1] = S[1]/S[2] * ratio; ret[1] = S[1]/S[2] * ratio;
@ -360,23 +387,23 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdDisk: { case mdDisk: {
if(nonisotropic) { if(nonisotropic) {
ret = lp_apply(inverse_exp(H, iTable, true)); ret = lp_apply(inverse_exp(H, pNORMAL | pfNO_DISTANCE));
ld w; ld w;
if(sn::in()) { if(sn::in()) {
// w = 1 / sqrt(1 - sqhypot_d(3, ret)); // w = 1 / sqrt(1 - sqhypot_d(3, ret));
// w = w / (vid.alpha + w); // w = w / (pconf.alpha + w);
w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * vid.alpha + 1); w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * pconf.alpha + 1);
} }
else { else {
w = hypot_d(3, ret); w = hypot_d(3, ret);
w = sinh(w) / ((vid.alpha + cosh(w)) * w); w = sinh(w) / ((pconf.alpha + cosh(w)) * w);
} }
for(int i=0; i<3; i++) ret[i] *= w; for(int i=0; i<3; i++) ret[i] *= w;
ret[3] = 1; ret[3] = 1;
break; break;
} }
ld tz = get_tz(H); ld tz = get_tz(H);
if(!vid.camera_angle) { if(!pconf.camera_angle) {
ret[0] = H[0] / tz; ret[0] = H[0] / tz;
ret[1] = H[1] / tz; ret[1] = H[1] / tz;
if(GDIM == 3) ret[2] = H[2] / tz; if(GDIM == 3) ret[2] = H[2] / tz;
@ -386,7 +413,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
else { else {
ld tx = H[0]; ld tx = H[0];
ld ty = H[1]; ld ty = H[1];
ld cam = vid.camera_angle * degree; ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam); GLfloat cc = cos(cam);
GLfloat ss = sin(cam); GLfloat ss = sin(cam);
ld ux = tx, uy = ty * cc - ss * tz, uz = tz * cc + ss * ty; ld ux = tx, uy = ty * cc - ss * tz, uz = tz * cc + ss * ty;
@ -400,6 +427,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdCentralInversion: { case mdCentralInversion: {
ld tz = get_tz(H); ld tz = get_tz(H);
for(int d=0; d<GDIM; d++) ret[d] = H[d] / tz; for(int d=0; d<GDIM; d++) ret[d] = H[d] / tz;
for(int d=GDIM; d<MAXMDIM; d++) ret[d] = 1;
ld r = 0; ld r = 0;
for(int d=0; d<GDIM; d++) r += ret[d]*ret[d]; for(int d=0; d<GDIM; d++) r += ret[d]*ret[d];
for(int d=0; d<GDIM; d++) ret[d] /= r; for(int d=0; d<GDIM; d++) ret[d] /= r;
@ -422,7 +450,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
if(GDIM == 3) { if(GDIM == 3) {
// a bit simpler when we do not care about 3D // a bit simpler when we do not care about 3D
H *= models::halfplane_scale; H *= pconf.halfplane_scale;
ret[0] = -H[0]; ret[0] = -H[0];
ret[1] = 1 + H[1]; ret[1] = 1 + H[1];
ret[2] = H[2]; ret[2] = H[2];
@ -434,7 +462,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
models::apply_orientation(H[0], H[1]); models::apply_orientation(H[0], H[1]);
H *= models::halfplane_scale; H *= pconf.halfplane_scale;
ret[0] = -models::osin - H[0]; ret[0] = -models::osin - H[0];
if(zlev != 1) { if(zlev != 1) {
@ -485,14 +513,14 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case gcEuclid: default: { case gcEuclid: default: {
// stereographic projection to a sphere // stereographic projection to a sphere
auto hd = hdist0(H) / vid.euclid_to_sphere; auto hd = hdist0(H) / pconf.euclid_to_sphere;
if(hd == 0) ret = hpxyz(0, 0, -1); if(hd == 0) ret = hpxyz(0, 0, -1);
else { else {
ld x = 2 * hd / (1 + hd * hd); ld x = 2 * hd / (1 + hd * hd);
ld y = x / hd; ld y = x / hd;
ret = H * x / hd / vid.euclid_to_sphere; ret = H * x / hd / pconf.euclid_to_sphere;
ret[2] = (1 - y); ret[2] = (1 - y);
ret = ret * (1 + (H[2]-1) * y / vid.euclid_to_sphere); ret = ret * (1 + (H[2]-1) * y / pconf.euclid_to_sphere);
} }
break; break;
} }
@ -523,7 +551,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
break; break;
} }
if(pmodel == mdHyperboloid) { if(pmodel == mdHyperboloid) {
ld& topz = models::top_z; ld& topz = pconf.top_z;
if(H[2] > topz) { if(H[2] > topz) {
ld scale = sqrt(topz*topz-1) / hypot_d(2, H); ld scale = sqrt(topz*topz-1) / hypot_d(2, H);
H *= scale; H *= scale;
@ -531,8 +559,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
} }
} }
else { else {
H = space_to_perspective(H, vid.alpha); H = space_to_perspective(H, pconf.alpha);
H[2] = 1 - vid.alpha; H[2] = 1 - pconf.alpha;
} }
ret[0] = H[0] / 3; ret[0] = H[0] / 3;
@ -546,14 +574,14 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdFisheye: { case mdFisheye: {
ld zlev; ld zlev;
if(nonisotropic) { if(nonisotropic) {
H = lp_apply(inverse_exp(H, iTable, false)); H = lp_apply(inverse_exp(H));
zlev = 1; zlev = 1;
} }
else { else {
zlev = find_zlev(H); zlev = find_zlev(H);
H = space_to_perspective(H); H = space_to_perspective(H);
} }
H /= vid.fisheye_param; H /= pconf.fisheye_param;
H[LDIM] = zlev; H[LDIM] = zlev;
ret = H / sqrt(1 + sqhypot_d(GDIM+1, H)); ret = H / sqrt(1 + sqhypot_d(GDIM+1, H));
if(GDIM == 3) ret[LDIM] = zlev; if(GDIM == 3) ret[LDIM] = zlev;
@ -564,8 +592,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
models::apply_orientation_yz(H[1], H[2]); models::apply_orientation_yz(H[1], H[2]);
models::apply_orientation(H[0], H[1]); models::apply_orientation(H[0], H[1]);
auto yz = move_z_to_y(H); auto yz = move_z_to_y(H);
hyperpoint Hl = xpush(-vid.twopoint_param) * H; hyperpoint Hl = xpush(-pconf.twopoint_param) * H;
hyperpoint Hr = xpush(+vid.twopoint_param) * H; hyperpoint Hr = xpush(+pconf.twopoint_param) * H;
ld lyx = (Hl[1] + 1e-7) / (Hl[0] + 1e-8); ld lyx = (Hl[1] + 1e-7) / (Hl[0] + 1e-8);
ld ryx = (Hr[1] + 1e-7) / (Hr[0] + 1e-8); ld ryx = (Hr[1] + 1e-7) / (Hr[0] + 1e-8);
// (r.x + t) * lyx = (r.x - t) * ryx = r.y // (r.x + t) * lyx = (r.x - t) * ryx = r.y
@ -574,8 +602,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
// r.x = -t * (ryx+lyx) / (lyx-ryx) // r.x = -t * (ryx+lyx) / (lyx-ryx)
// r.x = - 2 * t * lyx * ryx / lyx / ryx // r.x = - 2 * t * lyx * ryx / lyx / ryx
ret[0] = -vid.twopoint_param * (ryx + lyx) / (lyx - ryx); ret[0] = -pconf.twopoint_param * (ryx + lyx) / (lyx - ryx);
ret[1] = (ret[0] + vid.twopoint_param) * lyx; ret[1] = (ret[0] + pconf.twopoint_param) * lyx;
ret[2] = 0; ret[2] = 0;
move_y_to_z(ret, yz); move_y_to_z(ret, yz);
@ -603,11 +631,11 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
models::apply_orientation(H[0], H[1]); models::apply_orientation(H[0], H[1]);
// with equal speed skiprope: models::apply_orientation(H[1], H[0]); // with equal speed skiprope: models::apply_orientation(H[1], H[0]);
if(vid.skiprope) { if(pconf.skiprope) {
static ld last_skiprope = 0; static ld last_skiprope = 0;
static transmatrix lastmatrix; static transmatrix lastmatrix;
if(vid.skiprope != last_skiprope) { if(pconf.skiprope != last_skiprope) {
ret = mobius(C0, -vid.skiprope, 2); ret = mobius(C0, -pconf.skiprope, 2);
const cld c1(1, 0); const cld c1(1, 0);
const cld c2(2, 0); const cld c2(2, 0);
const cld c4(4, 0); const cld c4(4, 0);
@ -617,7 +645,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
hyperpoint zr = hpxyz(real(z), imag(z), 0); hyperpoint zr = hpxyz(real(z), imag(z), 0);
hyperpoint inhyp = perspective_to_space(zr, 1, gcHyperbolic); hyperpoint inhyp = perspective_to_space(zr, 1, gcHyperbolic);
last_skiprope = vid.skiprope; last_skiprope = pconf.skiprope;
lastmatrix = rgpushxto0(inhyp); lastmatrix = rgpushxto0(inhyp);
} }
H = lastmatrix * H; H = lastmatrix * H;
@ -628,7 +656,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ld r = hypot_d(2, H); ld r = hypot_d(2, H);
ld c = H[0] / r; ld c = H[0] / r;
ld s = H[1] / r; ld s = H[1] / r;
ld& mt = models::model_transition; ld& mt = pconf.model_transition;
ld a = 1 - .5 * mt, b = .5 * mt; ld a = 1 - .5 * mt, b = .5 * mt;
swap(a, b); swap(a, b);
@ -636,8 +664,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = (a * r - b/r) * s / 2; ret[1] = (a * r - b/r) * s / 2;
ret[2] = 0; ret[2] = 0;
if(vid.skiprope) if(pconf.skiprope)
ret = mobius(ret, vid.skiprope, 2); ret = mobius(ret, pconf.skiprope, 2);
if(pmodel == mdJoukowskyInverted) { if(pmodel == mdJoukowskyInverted) {
ld r2 = sqhypot_d(2, ret); ld r2 = sqhypot_d(2, ret);
@ -681,8 +709,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
} }
case mdBand: case mdBand:
if(models::model_transition != 1) { if(pconf.model_transition != 1) {
ld& mt = models::model_transition; ld& mt = pconf.model_transition;
H = space_to_perspective(H); H = space_to_perspective(H);
@ -745,10 +773,10 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
find_zlev(H); find_zlev(H);
makeband(H, ret, [] (ld& x, ld& y) { makeband(H, ret, [] (ld& x, ld& y) {
ld sgn = 1; ld sgn = 1;
if(vid.collignon_reflected && y > 0) y = -y, sgn = -1; if(pconf.collignon_reflected && y > 0) y = -y, sgn = -1;
y = signed_sqrt(sin_auto(y) + vid.collignon_parameter); y = signed_sqrt(sin_auto(y) + pconf.collignon_parameter);
x *= y / 1.2; x *= y / 1.2;
y -= signed_sqrt(vid.collignon_parameter); y -= signed_sqrt(pconf.collignon_parameter);
y *= sgn; y *= sgn;
y *= M_PI; y *= M_PI;
}); });
@ -768,7 +796,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdEquidistant: case mdEquiarea: case mdEquivolume: { case mdEquidistant: case mdEquiarea: case mdEquivolume: {
if(nonisotropic || prod) { if(nonisotropic || prod) {
ret = lp_apply(inverse_exp(H, iTable, false)); ret = lp_apply(inverse_exp(H));
ret[3] = 1; ret[3] = 1;
break; break;
} }
@ -813,7 +841,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = cosh(x) * factor; ret[1] = cosh(x) * factor;
ret[2] = 0; ret[2] = 0;
if(models::use_atan) { if(pconf.use_atan) {
ret[0] = atan(ret[0]); ret[0] = atan(ret[0]);
ret[1] = atan(ret[1]); ret[1] = atan(ret[1]);
} }
@ -822,7 +850,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
} }
case mdFormula: { case mdFormula: {
dynamicval<eModel> m(pmodel, models::basic_model); dynamicval<eModel> m(pmodel, pconf.basic_model);
applymodel(H, ret); applymodel(H, ret);
exp_parser ep; exp_parser ep;
ep.extra_params["z"] = cld(ret[0], ret[1]); ep.extra_params["z"] = cld(ret[0], ret[1]);
@ -832,7 +860,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ep.extra_params["ux"] = H[0]; ep.extra_params["ux"] = H[0];
ep.extra_params["uy"] = H[1]; ep.extra_params["uy"] = H[1];
ep.extra_params["uz"] = H[2]; ep.extra_params["uz"] = H[2];
ep.s = models::formula; ep.s = pconf.formula;
cld res; cld res;
try { try {
res = ep.parse(); res = ep.parse();
@ -852,15 +880,15 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
else ret = H; else ret = H;
z = cld(ret[0], ret[1]) * models::spiral_multiplier; z = cld(ret[0], ret[1]) * models::spiral_multiplier;
if(models::spiral_cone < 360) { if(pconf.spiral_cone < 360) {
ld alpha = imag(z) * 360 / models::spiral_cone; ld alpha = imag(z) * 360 / pconf.spiral_cone;
ld r = real(z); ld r = real(z);
r = exp(r); r = exp(r);
ret[0] = -sin(alpha) * r; ret[0] = -sin(alpha) * r;
ret[1] = cos(alpha) * r; ret[1] = cos(alpha) * r;
if(euclid) ret = models::euclidean_spin * ret; if(euclid) ret = models::euclidean_spin * ret;
ret[2] = (r-1) * sqrt( pow(360/models::spiral_cone, 2) - 1); ret[2] = (r-1) * sqrt( pow(360/pconf.spiral_cone, 2) - 1);
models::apply_ball(ret[2], ret[1]); models::apply_ball(ret[2], ret[1]);
} }
@ -870,8 +898,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = imag(z); ret[1] = imag(z);
if(euclid) ret = models::euclidean_spin * ret; if(euclid) ret = models::euclidean_spin * ret;
if(vid.skiprope) if(pconf.skiprope)
ret = mobius(ret, vid.skiprope, 1); ret = mobius(ret, pconf.skiprope, 1);
} }
} }
@ -929,12 +957,12 @@ EX bool behindsphere(const hyperpoint& h) {
if(mdBandAny()) return false; if(mdBandAny()) return false;
if(vid.alpha > 1) { if(pconf.alpha > 1) {
if(h[LDIM] > -1/vid.alpha) return true; if(h[LDIM] > -1/pconf.alpha) return true;
} }
if(vid.alpha <= 1) { if(pconf.alpha <= 1) {
if(h[LDIM] < .2-vid.alpha) return true; if(h[LDIM] < .2-pconf.alpha) return true;
} }
return false; return false;
@ -949,11 +977,11 @@ ld to01(ld a0, ld a1, ld x) {
EX ld spherity(const hyperpoint& h) { EX ld spherity(const hyperpoint& h) {
if(!sphere) return 1; if(!sphere) return 1;
if(vid.alpha > 1) { if(pconf.alpha > 1) {
return to01(1/vid.alpha, 1, abs(h[2])); return to01(1/pconf.alpha, 1, abs(h[2]));
} }
if(vid.alpha <= 1) { if(pconf.alpha <= 1) {
return to01(-1.5, 1, h[2]); return to01(-1.5, 1, h[2]);
} }
@ -1002,7 +1030,7 @@ EX transmatrix actualV(const heptspin& hs, const transmatrix& V) {
EX bool point_behind(hyperpoint h) { EX bool point_behind(hyperpoint h) {
if(sphere) return false; if(sphere) return false;
if(!in_perspective()) return false; if(!in_perspective()) return false;
if(pmodel == mdGeodesic) h = inverse_exp(h, iLazy); if(pmodel == mdGeodesic) h = inverse_exp(h, pQUICK);
if(pmodel == mdPerspective && prod) h = product::inverse_exp(h); if(pmodel == mdPerspective && prod) h = product::inverse_exp(h);
h = lp_apply(h); h = lp_apply(h);
return h[2] < 1e-8; return h[2] < 1e-8;
@ -1039,15 +1067,15 @@ EX bool in_smart_range(const transmatrix& T) {
applymodel(h, h1); applymodel(h, h1);
if(invalid_point(h1)) return false; if(invalid_point(h1)) return false;
ld x = current_display->xcenter + current_display->radius * h1[0]; ld x = current_display->xcenter + current_display->radius * h1[0];
ld y = current_display->ycenter + current_display->radius * h1[1] * vid.stretch; ld y = current_display->ycenter + current_display->radius * h1[1] * pconf.stretch;
if(x > current_display->xtop + current_display->xsize * 2) return false; if(x > current_display->xtop + current_display->xsize * 2) return false;
if(x < current_display->xtop - current_display->xsize * 1) return false; if(x < current_display->xtop - current_display->xsize * 1) return false;
if(y > current_display->ytop + current_display->ysize * 2) return false; if(y > current_display->ytop + current_display->ysize * 2) return false;
if(y < current_display->ytop - current_display->ysize * 1) return false; if(y < current_display->ytop - current_display->ysize * 1) return false;
if(GDIM == 3) { if(GDIM == 3) {
if(-h1[2] < models::clip_min * 2 - models::clip_max) return false; if(-h1[2] < pconf.clip_min * 2 - pconf.clip_max) return false;
if(-h1[2] > models::clip_max * 2 - models::clip_min) return false; if(-h1[2] > pconf.clip_max * 2 - pconf.clip_min) return false;
} }
ld epsilon = 0.01; ld epsilon = 0.01;
@ -1057,14 +1085,14 @@ EX bool in_smart_range(const transmatrix& T) {
hyperpoint h2; hyperpoint h2;
applymodel(T * cpush0(i, epsilon), h2); applymodel(T * cpush0(i, epsilon), h2);
ld x1 = current_display->radius * abs(h2[0] - h1[0]) / epsilon; ld x1 = current_display->radius * abs(h2[0] - h1[0]) / epsilon;
ld y1 = current_display->radius * abs(h2[1] - h1[1]) * vid.stretch / epsilon; ld y1 = current_display->radius * abs(h2[1] - h1[1]) * pconf.stretch / epsilon;
dx = max(dx, x1); dy = max(dy, y1); dx = max(dx, x1); dy = max(dy, y1);
if(GDIM == 3) dz = max(dz, abs(h2[2] - h1[2])); if(GDIM == 3) dz = max(dz, abs(h2[2] - h1[2]));
dh[i] = hypot(x1, y1); dh[i] = hypot(x1, y1);
} }
if(GDIM == 3) { if(GDIM == 3) {
if(-h1[2] + 2 * dz < models::clip_min || -h1[2] - 2 * dz > models::clip_max) return false; if(-h1[2] + 2 * dz < pconf.clip_min || -h1[2] - 2 * dz > pconf.clip_max) return false;
sort(dh, dh+GDIM); sort(dh, dh+GDIM);
ld scale = sqrt(dh[1] * dh[2]) * cgi.scalefactor * hcrossf7; ld scale = sqrt(dh[1] * dh[2]) * cgi.scalefactor * hcrossf7;
if(scale <= (WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3)) return false; if(scale <= (WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3)) return false;
@ -1191,7 +1219,7 @@ void hrmap_standard::draw() {
if(sphere && pmodel == mdSpiral && !in_multi) { if(sphere && pmodel == mdSpiral && !in_multi) {
in_multi = true; in_multi = true;
if(models::ring_not_spiral) { if(models::ring_not_spiral) {
int qty = ceil(1. / models::sphere_spiral_multiplier); int qty = ceil(1. / pconf.sphere_spiral_multiplier);
if(qty > 100) qty = 100; if(qty > 100) qty = 100;
for(int i=-qty; i < qty; i++) { for(int i=-qty; i < qty; i++) {
band_shift = 2 * M_PI * i; band_shift = 2 * M_PI * i;
@ -1251,6 +1279,24 @@ void hrmap_standard::draw() {
} }
} }
EX bool keep_vertical() {
if(CAP_ORIENTATION) return false;
if((WDIM == 2 || prod) && GDIM == 3 && vid.fixed_yz) return true;
if(downseek.qty) return true;
return false;
}
EX hyperpoint vertical_vector() {
auto& ds = downseek;
if((WDIM == 2 || prod) && GDIM == 3 && vid.fixed_yz)
return get_view_orientation() * ztangent(1);
else if(ds.qty && prod)
return get_view_orientation() * product::inverse_exp(ds.point);
else if(ds.qty)
return ds.point;
return C0;
}
EX void spinEdge(ld aspd) { EX void spinEdge(ld aspd) {
ld downspin = 0; ld downspin = 0;
auto& ds = downseek; auto& ds = downseek;
@ -1271,33 +1317,20 @@ EX void spinEdge(ld aspd) {
downspin = atan2(H[1], H[0]); downspin = atan2(H[1], H[0]);
downspin += vid.fixed_facing_dir * degree; downspin += vid.fixed_facing_dir * degree;
if(flipplayer) downspin += M_PI; if(flipplayer) downspin += M_PI;
while(downspin < -M_PI) downspin += 2*M_PI; cyclefix(downspin, 0);
while(downspin > +M_PI) downspin -= 2*M_PI;
aspd = (1 + 2 * abs(downspin)) * aspd; aspd = (1 + 2 * abs(downspin)) * aspd;
} }
else if((WDIM == 2 || prod) && GDIM == 3 && vid.fixed_yz && !CAP_ORIENTATION) { else if(keep_vertical()) {
aspd = 999999; hyperpoint h = vertical_vector();
auto& vo = get_view_orientation(); downspin = -atan2(h[0], h[1]);
// does not work well (also need change auto& to auto) if(ds.qty && GDIM == 2) {
// if(hybri && !prod) vo = vo * inverse(nisot::translate(tC0(vo))); downspin += models::rotation * degree;
}
if(ds.qty) { if(ds.qty) {
auto sdp = ds.point; cyclefix(downspin, 0);
if(prod) sdp = vo * product::inverse_exp(sdp); downspin = downspin * min(ds.speed, (double)1);
if(sdp[0])
downspin = models::rotation * degree - atan2(sdp[0], sdp[1]);
} }
else { else aspd = 999999;
if(vo[0][2])
downspin = -atan2(vo[0][2], vo[1][2]);
}
}
else if(ds.qty) {
downspin = atan2(ds.point[1], ds.point[0]);
downspin -= M_PI/2;
downspin += models::rotation * degree;
while(downspin < -M_PI) downspin += 2*M_PI;
while(downspin > +M_PI) downspin -= 2*M_PI;
downspin = downspin * min(ds.speed, (double)1);
} }
if(downspin > aspd) downspin = aspd; if(downspin > aspd) downspin = aspd;
if(downspin < -aspd) downspin = -aspd; if(downspin < -aspd) downspin = -aspd;
@ -1505,8 +1538,8 @@ EX void fullcenter() {
transmatrix screenpos(ld x, ld y) { transmatrix screenpos(ld x, ld y) {
transmatrix V = Id; transmatrix V = Id;
V[0][2] += (x - current_display->xcenter) / current_display->radius * (1+vid.alpha); V[0][2] += (x - current_display->xcenter) / current_display->radius * (1+pconf.alpha);
V[1][2] += (y - current_display->ycenter) / current_display->radius * (1+vid.alpha); V[1][2] += (y - current_display->ycenter) / current_display->radius * (1+pconf.alpha);
return V; return V;
} }
@ -1517,6 +1550,27 @@ transmatrix screenpos(ld x, ld y) {
EX eModel flat_model() { return MDIM == 4 ? mdPixel : mdDisk; } EX eModel flat_model() { return MDIM == 4 ? mdPixel : mdDisk; }
/** \brief enable the 'flat' model for drawing HUD. See hr::flat_model_enabler */
EX void enable_flat_model() {
glClear(GL_DEPTH_BUFFER_BIT);
pmodel = flat_model();
pconf.alpha = 1;
pconf.scale = 1;
pconf.camera_angle = 0;
pconf.stretch = 1;
if(prod) pconf.alpha = 30, pconf.scale = 30;
calcparam();
}
#if HDR
/** \brief enable the 'flat' model for drawing HUD. Use RAII so it will be switched back later */
struct flat_model_enabler {
projection_configuration bak;
flat_model_enabler() { bak = pconf; enable_flat_model(); }
~flat_model_enabler() { pconf = bak; calcparam(); }
};
#endif
EX transmatrix atscreenpos(ld x, ld y, ld size) { EX transmatrix atscreenpos(ld x, ld y, ld size) {
transmatrix V = Id; transmatrix V = Id;
@ -1541,7 +1595,7 @@ EX transmatrix atscreenpos(ld x, ld y, ld size) {
void circle_around_center(ld radius, color_t linecol, color_t fillcol, PPR prio) { void circle_around_center(ld radius, color_t linecol, color_t fillcol, PPR prio) {
#if CAP_QUEUE #if CAP_QUEUE
if(among(pmodel, mdDisk, mdEquiarea, mdEquidistant, mdFisheye) && !(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) && vid.camera_angle == 0) { if(among(pmodel, mdDisk, mdEquiarea, mdEquidistant, mdFisheye) && !(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) && pconf.camera_angle == 0) {
hyperpoint ret; hyperpoint ret;
applymodel(xpush0(radius), ret); applymodel(xpush0(radius), ret);
ld r = hypot_d(2, ret); ld r = hypot_d(2, ret);
@ -1552,7 +1606,7 @@ void circle_around_center(ld radius, color_t linecol, color_t fillcol, PPR prio)
#if CAP_QUEUE #if CAP_QUEUE
for(int i=0; i<=360; i++) curvepoint(xspinpush0(i * degree, 10)); for(int i=0; i<=360; i++) curvepoint(xspinpush0(i * degree, 10));
auto& c = queuecurve(linecol, fillcol, prio); auto& c = queuecurve(linecol, fillcol, prio);
if(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) if(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1)
c.flags |= POLY_FORCE_INVERTED; c.flags |= POLY_FORCE_INVERTED;
if(pmodel == mdJoukowsky) if(pmodel == mdJoukowsky)
c.flags |= POLY_FORCE_INVERTED; c.flags |= POLY_FORCE_INVERTED;
@ -1571,7 +1625,7 @@ EX void draw_model_elements() {
switch(pmodel) { switch(pmodel) {
case mdRotatedHyperboles: { case mdRotatedHyperboles: {
queuestr(current_display->xcenter, current_display->ycenter + current_display->radius * vid.alpha, 0, vid.fsize, "X", ringcolor, 1, 8); queuestr(current_display->xcenter, current_display->ycenter + current_display->radius * pconf.alpha, 0, vid.fsize, "X", ringcolor, 1, 8);
return; return;
} }
@ -1581,11 +1635,11 @@ EX void draw_model_elements() {
for(int mode=0; mode<4; mode++) { for(int mode=0; mode<4; mode++) {
for(int s=-200; s<=200; s ++) { for(int s=-200; s<=200; s ++) {
ld p = tanh(s / 40.); ld p = tanh(s / 40.);
ld a = vid.twopoint_param * (1+p); ld a = pconf.twopoint_param * (1+p);
ld b = vid.twopoint_param * (1-p); ld b = pconf.twopoint_param * (1-p);
ld h = ((mode & 2) ? -1 : 1) * sqrt(asin_auto(tan_auto(a) * tan_auto(b))); ld h = ((mode & 2) ? -1 : 1) * sqrt(asin_auto(tan_auto(a) * tan_auto(b)));
hyperpoint H = xpush(p * vid.twopoint_param) * ypush0(h); hyperpoint H = xpush(p * pconf.twopoint_param) * ypush0(h);
hyperpoint res = compute_hybrid(H, 2 | mode); hyperpoint res = compute_hybrid(H, 2 | mode);
models::apply_orientation(res[0], res[1]); models::apply_orientation(res[0], res[1]);
@ -1600,9 +1654,9 @@ EX void draw_model_elements() {
} }
case mdTwoPoint: case mdSimulatedPerspective: { case mdTwoPoint: case mdSimulatedPerspective: {
ld a = -models::model_orientation * degree; ld a = -pconf.model_orientation * degree;
queuestr(xspinpush0(a, +vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); queuestr(xspinpush0(a, +pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
queuestr(xspinpush0(a, -vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8); queuestr(xspinpush0(a, -pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
return; return;
} }
@ -1616,10 +1670,10 @@ EX void draw_model_elements() {
if(hyperbolic) { if(hyperbolic) {
#if CAP_QUEUE #if CAP_QUEUE
curvepoint(point3(0,0,1)); curvepoint(point3(0,0,1));
curvepoint(point3(0,0,-vid.alpha)); curvepoint(point3(0,0,-pconf.alpha));
queuecurve(ringcolor, 0, PPR::CIRCLE); queuecurve(ringcolor, 0, PPR::CIRCLE);
ld& tz = models::top_z; ld& tz = pconf.top_z;
ld z = acosh(tz); ld z = acosh(tz);
hyperpoint a = xpush0(z); hyperpoint a = xpush0(z);
@ -1629,12 +1683,12 @@ EX void draw_model_elements() {
a[1] = sb * a[2] / -cb; a[1] = sb * a[2] / -cb;
a[0] = sqrt(-1 + a[2] * a[2] - a[1] * a[1]); a[0] = sqrt(-1 + a[2] * a[2] - a[1] * a[1]);
curvepoint(point3(0,0,-vid.alpha)); curvepoint(point3(0,0,-pconf.alpha));
curvepoint(a); curvepoint(a);
curvepoint(point3(0,0,0)); curvepoint(point3(0,0,0));
a[0] = -a[0]; a[0] = -a[0];
curvepoint(a); curvepoint(a);
curvepoint(point3(0,0,-vid.alpha)); curvepoint(point3(0,0,-pconf.alpha));
queuecurve(ringcolor, 0, PPR::CIRCLE); queuecurve(ringcolor, 0, PPR::CIRCLE);
curvepoint(point3(-1,0,0)); curvepoint(point3(-1,0,0));
@ -1643,10 +1697,10 @@ EX void draw_model_elements() {
a[1] = sb * tz / -cb; a[1] = sb * tz / -cb;
a[0] = sqrt(tz * tz - a[1] * a[1]); a[0] = sqrt(tz * tz - a[1] * a[1]);
a[2] = tz - vid.alpha; a[2] = tz - pconf.alpha;
curvepoint(a); curvepoint(a);
curvepoint(point3(0,0,-vid.alpha)); curvepoint(point3(0,0,-pconf.alpha));
a[0] = -a[0]; a[0] = -a[0];
curvepoint(a); curvepoint(a);
queuecurve(ringcolor, 0, PPR::CIRCLE); queuecurve(ringcolor, 0, PPR::CIRCLE);
@ -1727,7 +1781,7 @@ EX void draw_boundary(int w) {
for(int b=-1; b<=1; b+=2) for(int b=-1; b<=1; b+=2)
for(ld a=-90; a<=90+1e-6; a+=pow(.5, vid.linequality)) { for(ld a=-90; a<=90+1e-6; a+=pow(.5, vid.linequality)) {
ld x = sin(a * vid.twopoint_param * b / 90); ld x = sin(a * pconf.twopoint_param * b / 90);
ld y = 0; ld y = 0;
ld z = -sqrt(1 - x*x); ld z = -sqrt(1 - x*x);
models::apply_orientation(y, x); models::apply_orientation(y, x);
@ -1747,9 +1801,9 @@ EX void draw_boundary(int w) {
case mdBand: case mdBandEquidistant: case mdBandEquiarea: case mdSinusoidal: case mdMollweide: case mdCentralCyl: case mdCollignon: { case mdBand: case mdBandEquidistant: case mdBandEquiarea: case mdSinusoidal: case mdMollweide: case mdCentralCyl: case mdCollignon: {
if(GDIM == 3) return; if(GDIM == 3) return;
if(pmodel == mdBand && models::model_transition != 1) return; if(pmodel == mdBand && pconf.model_transition != 1) return;
bool bndband = (among(pmodel, mdBand, mdCentralCyl) ? hyperbolic : sphere); bool bndband = (among(pmodel, mdBand, mdCentralCyl) ? hyperbolic : sphere);
transmatrix T = spin(-models::model_orientation * degree); transmatrix T = spin(-pconf.model_orientation * degree);
ld right = M_PI/2 - 1e-5; ld right = M_PI/2 - 1e-5;
if(bndband) if(bndband)
queuestraight(T * ypush0(hyperbolic ? 10 : right), 2, lc, fc, p); queuestraight(T * ypush0(hyperbolic ? 10 : right), 2, lc, fc, p);
@ -1773,7 +1827,7 @@ EX void draw_boundary(int w) {
case mdHalfplane: case mdHalfplane:
if(hyperbolic && GDIM == 2) { if(hyperbolic && GDIM == 2) {
queuestraight(xspinpush0(-models::model_orientation * degree - M_PI/2, fakeinf), 1, lc, fc, p); queuestraight(xspinpush0(-pconf.model_orientation * degree - M_PI/2, fakeinf), 1, lc, fc, p);
return; return;
} }
break; break;
@ -1810,7 +1864,7 @@ EX void draw_boundary(int w) {
case mdHyperboloid: { case mdHyperboloid: {
if(hyperbolic) { if(hyperbolic) {
ld& tz = models::top_z; ld& tz = pconf.top_z;
ld mz = acosh(tz); ld mz = acosh(tz);
ld cb = models::cos_ball; ld cb = models::cos_ball;
ld sb = models::sin_ball; ld sb = models::sin_ball;
@ -1871,7 +1925,7 @@ EX void draw_boundary(int w) {
for(ld a=-10; a<=10; a+=0.01 / (1 << vid.linequality) / u) { for(ld a=-10; a<=10; a+=0.01 / (1 << vid.linequality) / u) {
cld z = exp(cld(a, a * imag(sm) / real(sm) + M_PI)); cld z = exp(cld(a, a * imag(sm) / real(sm) + M_PI));
hyperpoint ret = point2(real(z), imag(z)); hyperpoint ret = point2(real(z), imag(z));
ret = mobius(ret, vid.skiprope, 1); ret = mobius(ret, pconf.skiprope, 1);
ret *= current_display->radius; ret *= current_display->radius;
curvepoint(ret); curvepoint(ret);
} }
@ -1884,8 +1938,8 @@ EX void draw_boundary(int w) {
default: break; default: break;
} }
if(sphere && pmodel == mdDisk && vid.alpha > 1) { if(sphere && pmodel == mdDisk && pconf.alpha > 1) {
double rad = current_display->radius / sqrt(vid.alpha*vid.alpha - 1); double rad = current_display->radius / sqrt(pconf.alpha*pconf.alpha - 1);
queuecircle(current_display->xcenter, current_display->ycenter, rad, lc, p, fc); queuecircle(current_display->xcenter, current_display->ycenter, rad, lc, p, fc);
return; return;
} }
@ -1898,7 +1952,7 @@ EX void draw_boundary(int w) {
EX ld band_shift = 0; EX ld band_shift = 0;
EX void fix_the_band(transmatrix& T) { EX void fix_the_band(transmatrix& T) {
if(((mdinf[pmodel].flags & mf::uses_bandshift) && T[LDIM][LDIM] > 1e6) || (sphere && pmodel == mdSpiral)) { if(((mdinf[pmodel].flags & mf::uses_bandshift) && T[LDIM][LDIM] > 1e6) || (sphere && pmodel == mdSpiral)) {
T = spin(models::model_orientation * degree) * T; T = spin(pconf.model_orientation * degree) * T;
hyperpoint H = tC0(T); hyperpoint H = tC0(T);
find_zlev(H); find_zlev(H);
@ -1911,7 +1965,7 @@ EX void fix_the_band(transmatrix& T) {
band_shift += x; band_shift += x;
T = xpush(-x) * T; T = xpush(-x) * T;
fixmatrix(T); fixmatrix(T);
T = spin(-models::model_orientation * degree) * T; T = spin(-pconf.model_orientation * degree) * T;
} }
} }
@ -1995,7 +2049,7 @@ EX bool do_draw(cell *c, const transmatrix& T) {
if(cells_drawn > vid.cells_drawn_limit) return false; if(cells_drawn > vid.cells_drawn_limit) return false;
if(cells_drawn < 50) { limited_generation(c); return true; } if(cells_drawn < 50) { limited_generation(c); return true; }
if(nil && pmodel == mdGeodesic) { if(nil && pmodel == mdGeodesic) {
ld dist = hypot_d(3, inverse_exp(tC0(T), iLazy)); ld dist = hypot_d(3, inverse_exp(tC0(T), pQUICK));
if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : 0.9)) return false; if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : 0.9)) return false;
if(dist <= extra_generation_distance && !limited_generation(c)) return false; if(dist <= extra_generation_distance && !limited_generation(c)) return false;
} }
@ -2004,7 +2058,7 @@ EX bool do_draw(cell *c, const transmatrix& T) {
if(!limited_generation(c)) return false; if(!limited_generation(c)) return false;
} }
else if(pmodel == mdGeodesic && nih) { else if(pmodel == mdGeodesic && nih) {
hyperpoint h = inverse_exp(tC0(T), iLazy, false); hyperpoint h = inverse_exp(tC0(T), pQUICK);
ld dist = hypot_d(3, h); ld dist = hypot_d(3, h);
if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : cgi.corner_bonus)) return false; if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : cgi.corner_bonus)) return false;
if(dist <= extra_generation_distance && !limited_generation(c)) return false; if(dist <= extra_generation_distance && !limited_generation(c)) return false;
@ -2056,7 +2110,7 @@ EX int cone_side(const hyperpoint H) {
cld z = cld(ret[0], ret[1]) * models::spiral_multiplier; cld z = cld(ret[0], ret[1]) * models::spiral_multiplier;
auto zth = [&] (cld z) { auto zth = [&] (cld z) {
ld alpha = imag(z) * 360 / models::spiral_cone; ld alpha = imag(z) * 360 / pconf.spiral_cone;
ld r = real(z); ld r = real(z);
r = exp(r); r = exp(r);
@ -2064,7 +2118,7 @@ EX int cone_side(const hyperpoint H) {
ret[0] = -sin(alpha) * r; ret[0] = -sin(alpha) * r;
ret[1] = cos(alpha) * r; ret[1] = cos(alpha) * r;
ret[2] = (r-1) * sqrt( pow(360/models::spiral_cone, 2) - 1); ret[2] = (r-1) * sqrt( pow(360/pconf.spiral_cone, 2) - 1);
models::apply_ball(ret[2], ret[1]); models::apply_ball(ret[2], ret[1]);
return ret; return ret;
@ -2082,21 +2136,21 @@ EX transmatrix& get_view_orientation() {
return prod ? NLP : View; return prod ? NLP : View;
} }
EX hookset<bool(const transmatrix&)> *hooks_rotate_view; EX hookset<bool(const transmatrix&)> hooks_rotate_view;
EX hookset<bool(const hyperpoint&)> *hooks_shift_view; EX hookset<bool(const hyperpoint&)> hooks_shift_view;
/** rotate the view using the given rotation matrix */ /** rotate the view using the given rotation matrix */
EX void rotate_view(transmatrix T) { EX void rotate_view(transmatrix T) {
if(callhandlers(false, hooks_rotate_view, T)) return; if(callhandlers(false, hooks_rotate_view, T)) return;
transmatrix& which = get_view_orientation(); transmatrix& which = get_view_orientation();
which = T * which; which = T * which;
if(!prod && !nonisotropic) current_display->which_copy = T * current_display->which_copy; if(!prod && !nonisotropic && !rug::rugged) current_display->which_copy = T * current_display->which_copy;
} }
/** shift the view according to the given tangent vector */ /** shift the view according to the given tangent vector */
EX transmatrix get_shift_view_of(const hyperpoint H, const transmatrix V) { EX transmatrix get_shift_view_of(const hyperpoint H, const transmatrix V) {
if(!nonisotropic) { if(!nonisotropic && !stretch::in()) {
return rgpushxto0(direct_exp(lp_iapply(H), 100)) * V; return rgpushxto0(direct_exp(lp_iapply(H))) * V;
} }
else if(!nisot::geodesic_movement) { else if(!nisot::geodesic_movement) {
transmatrix IV = inverse(V); transmatrix IV = inverse(V);
@ -2115,7 +2169,7 @@ EX void shift_view(hyperpoint H) {
auto oView = View; auto oView = View;
View = get_shift_view_of(H, View); View = get_shift_view_of(H, View);
auto& wc = current_display->which_copy; auto& wc = current_display->which_copy;
if(nonisotropic) { if(nonisotropic || stretch::in()) {
transmatrix ioldv = eupush(tC0(inverse(oView))); transmatrix ioldv = eupush(tC0(inverse(oView)));
transmatrix newv = inverse(eupush(tC0(inverse(View)))); transmatrix newv = inverse(eupush(tC0(inverse(View))));
wc = newv * ioldv * wc; wc = newv * ioldv * wc;
@ -2132,7 +2186,7 @@ void multiply_view(transmatrix T) {
EX void shift_view_to(hyperpoint H) { EX void shift_view_to(hyperpoint H) {
if(!nonisotropic) multiply_view(gpushxto0(H)); if(!nonisotropic) multiply_view(gpushxto0(H));
else shift_view(-inverse_exp(H, iTable, false)); else shift_view(-inverse_exp(H));
} }
EX void shift_view_towards(hyperpoint H, ld l) { EX void shift_view_towards(hyperpoint H, ld l) {
@ -2141,7 +2195,7 @@ EX void shift_view_towards(hyperpoint H, ld l) {
else if(nonisotropic && !nisot::geodesic_movement) else if(nonisotropic && !nisot::geodesic_movement)
shift_view(tangent_length(H-C0, -l)); shift_view(tangent_length(H-C0, -l));
else { else {
hyperpoint ie = inverse_exp(H, iTable, true); hyperpoint ie = inverse_exp(H, pNORMAL | pfNO_DISTANCE);
if(prod) ie = lp_apply(ie); if(prod) ie = lp_apply(ie);
shift_view(tangent_length(ie, -l)); shift_view(tangent_length(ie, -l));
} }

View File

@ -513,14 +513,7 @@ EX namespace inv {
int j = 0, oc = 6; int j = 0, oc = 6;
if(1) { if(1) {
dynamicval<eModel> pm(pmodel, flat_model()); flat_model_enabler fme;
glClear(GL_DEPTH_BUFFER_BIT);
// dynamicval<videopar> v(vid, vid);
// vid.alpha = vid.scale = 1;
dynamicval<ld> va(vid.alpha, 1);
dynamicval<ld> vs(vid.scale, 1);
dynamicval<ld> vc(vid.camera_angle, 0);
calcparam();
for(int i=0; i<ittypes; i++) { for(int i=0; i<ittypes; i++) {
eItem o = eItem(i); eItem o = eItem(i);
@ -595,7 +588,7 @@ EX namespace inv {
displaystr(vid.xres/2, vid.yres - vid.fsize*6, 2, vid.fsize, osminfo(which), icol, 8); displaystr(vid.xres/2, vid.yres - vid.fsize*6, 2, vid.fsize, osminfo(which), icol, 8);
} }
#if ISMOBILE==0 #if !ISMOBILE
string hot = XLAT1("Hotkey: "); hot += getcstat; string hot = XLAT1("Hotkey: "); hot += getcstat;
displaystr(vid.xres/2, vid.yres - vid.fsize*5, 2, vid.fsize, hot, icol, 8); displaystr(vid.xres/2, vid.yres - vid.fsize*5, 2, vid.fsize, hot, icol, 8);
#endif #endif
@ -689,7 +682,7 @@ EX namespace inv {
#if CAP_SAVE #if CAP_SAVE
EX void applyBox(eItem it) { EX void applyBox(eItem it) {
scores::applyBoxNum(inv::usedup[it]); scores::applyBoxNum(inv::usedup[it], "@inv-" + dnameof(it));
} }
#endif #endif

View File

@ -467,7 +467,7 @@ EX void gainItem(eItem it) {
if(chaosmode && gold() >= 300) if(chaosmode && gold() >= 300)
achievement_gain_once("CHAOS", rg::chaos); achievement_gain_once("CHAOS", rg::chaos);
#if ISMOBILE==1 #if ISMOBILE
if(g < lastsafety + R30*3/2 && g2 >= lastsafety + R30*3/2) if(g < lastsafety + R30*3/2 && g2 >= lastsafety + R30*3/2)
addMessage(XLAT("The Orb of Safety from the Land of Eternal Motion might save you.")); addMessage(XLAT("The Orb of Safety from the Land of Eternal Motion might save you."));
#endif #endif

View File

@ -1054,8 +1054,11 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
break; break;
case laCA: case laCA:
if(fargen) if(fargen) {
c->wall = (hrand(1000000) < ca::prob * 1000000) ? ca::wlive : waNone; c->wall = (hrand(1000000) < ca::prob * 1000000) ? ca::wlive : waNone;
if(c->wall == ca::wlive)
ca::list_adj(c);
}
break; break;
case laLivefjord: case laLivefjord:
@ -2752,6 +2755,8 @@ EX void set_land_for_geometry(cell *c) {
} }
EX void setdist(cell *c, int d, cell *from) { 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) return;
if(c->mpdist > d+1 && d < BARLEV) setdist(c, d+1, from); if(c->mpdist > d+1 && d < BARLEV) setdist(c, d+1, from);
@ -2911,7 +2916,7 @@ EX void setdist(cell *c, int d, cell *from) {
// the number of tiles in the standard geometry has about 7553 digits! // the number of tiles in the standard geometry has about 7553 digits!
int gdist = abs(c->master->distance); int gdist = abs(c->master->distance);
if(gdist > global_distance_limit) { if(gdist > global_distance_limit && hyperbolic) {
gdist -= global_distance_limit; gdist -= global_distance_limit;
if(d == 8 && hrand(100) < gdist) { if(d == 8 && hrand(100) < gdist) {
if(!isMultitile(c)) c->monst = moNone; if(!isMultitile(c)) c->monst = moNone;

View File

@ -276,7 +276,7 @@ EX bool createOnSea(eLand old) {
(old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant)); (old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant));
} }
EX hookset<eLand(eLand)> *hooks_nextland; EX hookset<eLand(eLand)> hooks_nextland;
EX eLand getNewLand(eLand old) { EX eLand getNewLand(eLand old) {

View File

@ -31,8 +31,13 @@ const char *escape(std::string s, const std::string& dft);
template<class T> struct dictionary { template<class T> struct dictionary {
std::map<std::string, T> m; std::map<std::string, T> m;
void add(const std::string& s, T val) { void add(const std::string& s, T val) {
if(m.count(s)) add(s + " [repeat]", std::move(val)); auto it = m.find(s);
else m[s] = std::move(val); if (it == m.end()) {
m.emplace(s, std::move(val));
}
else if (val != it->second) {
printf("// #warning Two translations for %s\n", escape(s, s));
}
} }
T& operator [] (const std::string& s) { return m[s]; } T& operator [] (const std::string& s) { return m[s]; }
int count(const std::string& s) const { return m.count(s); } int count(const std::string& s) const { return m.count(s); }
@ -53,6 +58,12 @@ struct noun {
std::string nom, nomp, acc, abl; std::string nom, nomp, acc, abl;
noun() = default; noun() = default;
noun(const noun2& n) : genus(n.genus), nom(n.nom), nomp(n.nomp), acc(n.acc), abl(n.abl) {} noun(const noun2& n) : genus(n.genus), nom(n.nom), nomp(n.nomp), acc(n.acc), abl(n.abl) {}
friend bool operator==(const noun& a, const noun& b) {
return std::tie(a.genus, a.nom, a.nomp, a.acc, a.abl) == std::tie(b.genus, b.nom, b.nomp, b.acc, b.abl);
}
friend bool operator!=(const noun& a, const noun& b) {
return std::tie(a.genus, a.nom, a.nomp, a.acc, a.abl) != std::tie(b.genus, b.nom, b.nomp, b.acc, b.abl);
}
}; };
dictionary<noun> nouns[NUMLAN]; dictionary<noun> nouns[NUMLAN];
@ -93,14 +104,7 @@ typedef unsigned hashcode;
hashcode hashval; hashcode hashval;
bool isrepeat(const std::string& s) {
return s.find(" [repeat]") != std::string::npos;
}
hashcode langhash(const std::string& s) { hashcode langhash(const std::string& s) {
if(isrepeat(s)) {
return langhash(s.substr(0, s.size() - 9)) + 1;
}
hashcode r = 0; hashcode r = 0;
for (char ch : s) r = hashval * r + ch; for (char ch : s) r = hashval * r + ch;
return r; return r;
@ -265,13 +269,11 @@ void compute_completeness(const T& dict)
else else
mis1 += which; mis1 += which;
} }
if(mis != "" && !isrepeat(elt)) if(mis != "")
printf("// #warning Missing [%s/%s]: %s\n", mis.c_str(), mis1.c_str(), escape(elt, "?")); printf("// #warning Missing [%s/%s]: %s\n", mis.c_str(), mis1.c_str(), escape(elt, "?"));
if(!isrepeat(elt)) { completeness[0]++;
completeness[0]++; for(int i=1; i<NUMLAN; i++) if(dict[i].count(elt)) completeness[i]++;
for(int i=1; i<NUMLAN; i++) if(dict[i].count(elt)) completeness[i]++;
}
} }
} }
@ -389,11 +391,9 @@ int main() {
for(auto&& elt : ms) { for(auto&& elt : ms) {
const std::string& s = elt.second; const std::string& s = elt.second;
if(isrepeat(s)) printf("#if REPEATED\n");
printf(" {0x%x, { // %s\n", elt.first, escape(s, s)); printf(" {0x%x, { // %s\n", elt.first, escape(s, s));
for(int i=1; i<NUMLAN; i++) printf(" %s,\n", escape(d[i][s], s)); for(int i=1; i<NUMLAN; i++) printf(" %s,\n", escape(d[i][s], s));
printf(" }},\n"); printf(" }},\n");
if(isrepeat(s)) printf("#endif\n");
} }
printf(" };\n\n"); printf(" };\n\n");
@ -401,7 +401,6 @@ int main() {
for(auto&& elt : mn) { for(auto&& elt : mn) {
const std::string& s = elt.second; const std::string& s = elt.second;
if(isrepeat(s)) printf("#if REPEATED\n");
printf(" {0x%x, %d, { // \"%s\"\n", elt.first, printf(" {0x%x, %d, { // \"%s\"\n", elt.first,
(nothe.count(s) ? 1:0) + (plural.count(s) ? 2:0), (nothe.count(s) ? 1:0) + (plural.count(s) ? 2:0),
escape(s, s)); escape(s, s));
@ -415,7 +414,6 @@ int main() {
} }
printf(" }},\n"); printf(" }},\n");
if(isrepeat(s)) printf("#endif\n");
} }
printf(" };\n"); printf(" };\n");

View File

@ -510,10 +510,10 @@ S(
"nebo získat informace o objektech na mapě). Také se můžeš dotknout zobrazených čísel, " "nebo získat informace o objektech na mapě). Také se můžeš dotknout zobrazených čísel, "
"abys zjistil jejich význam.\n") "abys zjistil jejich význam.\n")
S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n", "To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n",
"Pohybuješ se pomocí myši, numerické klávesnice, kláves qweadzxc nebo kláves hjklyubn. Klávesy 's' nebo '.' ti umožňují čekat na místě. " "Pohybuješ se pomocí myši, numerické klávesnice, kláves qweadzxc nebo kláves hjklyubn. Klávesy 's' nebo '.' ti umožňují čekat na místě. "
"Šipky, klávesy PgUp/PgDn a Home nebo mezerník ti umožňují otáčet svět. Nastavení můžeš otevřít klávesou 'v', úkoly a menu klávesou ESC.\n\n") "Šipky, klávesy PgUp/PgDn a mezerník ti umožňují otáčet svět. Nastavení můžeš otevřít klávesou 'v', úkoly a menu klávesou ESC.\n\n")
S("See more on the website: ", "Více informací najdeš na webové stránce: ") S("See more on the website: ", "Více informací najdeš na webové stránce: ")
@ -4587,11 +4587,11 @@ S(
"are not about points on the ground level, but " "are not about points on the ground level, but "
"about the matching points on the plane P -- " "about the matching points on the plane P -- "
"divide them by the factor above to get actual " "divide them by the factor above to get actual "
"distances.", "distances.)",
"(Vzdálenosti v editoru vektorové " "(Vzdálenosti v editoru vektorové "
"grafiky nejsou vzdálenosti bodů na úrovni země, ale odpovídajících bodů v rovině " "grafiky nejsou vzdálenosti bodů na úrovni země, ale odpovídajících bodů v rovině "
"P -- skutečné vzdálenosti získáte jejich vydělením výše zmíněným faktorem.") "P -- skutečné vzdálenosti získáte jejich vydělením výše zmíněným faktorem.)")
S( "If we are viewing an equidistant g absolute units below a plane, " S( "If we are viewing an equidistant g absolute units below a plane, "
"from a point c absolute units above the plane, this corresponds " "from a point c absolute units above the plane, this corresponds "
@ -6146,7 +6146,7 @@ S("would be destroyed in %the1", "by%l1by zničen%1 %abl1")
S(" to go cold", " vychladnout") S(" to go cold", " vychladnout")
S("%The1 is destroyed by lava!", "Láva zničila %a1!") S("%The1 is destroyed by lava!", "Láva zničila %a1!")
S("%The1 is killed by lava!", "Láva zabila %a1!") S("%The1 is killed by lava!", "Láva zabila %a1!")
S("Run away from the lava!", "Utíkej od lávy!") S("Run away from the magma!", "Utíkej od lávy!")
// Terracotta Army // Terracotta Army
//----------------- //-----------------
@ -6693,7 +6693,7 @@ N("Chrysoberyl", GEN_O, "Chryzoberyl", "Chryzoberyly", "Chryzoberyl", "Chryzober
S("Fragment of the past glory.", "Fragment minulé slávy.") S("Fragment of the past glory.", "Fragment minulé slávy.")
N("Red Raider", GEN_M, "Èervený nájezdník", "Èervení nájezdníci", "Èerveného nájezdníka", "Èerveným nájezdníkem") N("Red Raider", GEN_M, "Èervený nájezdník", "Èervení nájezdníci", "Èerveného nájezdníka", "Èerveným nájezdníkem")
S("Red Raiders travel in pairs. They have promised to always watch another one's back. They are able to destroy walls on their way", S("Red Raiders travel in pairs. They have promised to always watch each other's backs. They are able to destroy walls on their way.",
"Èervení nájezdníci cestují ve dvojicích. Slíbili si, že si vždycky budou navzájem " "Èervení nájezdníci cestují ve dvojicích. Slíbili si, že si vždycky budou navzájem "
"hlídat záda. Po cestì mohou nièit zdi.") "hlídat záda. Po cestì mohou nièit zdi.")
@ -7682,7 +7682,7 @@ S("A large bug native to the Brown Islands. Cannot be killed easily due to their
N("Acid Gull", GEN_M, "Kyselinoracek", "Kyselinorackové", "Kyselinoracka", "Kyselinorackem") N("Acid Gull", GEN_M, "Kyselinoracek", "Kyselinorackové", "Kyselinoracka", "Kyselinorackem")
S("Where did this strange bird come from?...\n\n Acid Gulls dissolve the land on which they fall when they die. ", S("Where did this strange bird come from?...\n\nAcid Gulls dissolve the land on which they fall when they die. ",
"Kde se tu vzal tenhle podivný pták...?\n\nKdyž Kyselinoracek zemře, rozpustí terén, na který dopadne.") "Kde se tu vzal tenhle podivný pták...?\n\nKdyž Kyselinoracek zemře, rozpustí terén, na který dopadne.")
@ -7767,7 +7767,7 @@ S("These guys look a bit strange, but they have no special properties.",
N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem") N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem")
S("Crystals emiting magical radiation.", "Drahokamy vyzařující prospěšnou magickou radiaci.") S("Crystals emitting magical radiation.", "Drahokamy vyzařující prospěšnou magickou radiaci.")
N("fire trap", GEN_F, "ohnivá past", "ohnivé pasti", "ohnivou past", "ohnivou pastí") N("fire trap", GEN_F, "ohnivá past", "ohnivé pasti", "ohnivou past", "ohnivou pastí")
@ -7845,7 +7845,7 @@ S(
N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem") N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem")
S("Crystals emiting magical radiation.", "Krystaly vyzařující magickou radiaci.") S("Crystals emitting magical radiation.", "Krystaly vyzařující magickou radiaci.")
// other things: // other things:
@ -7936,7 +7936,7 @@ S("Crystal", "Krystal")
#define Cell(x) \ #define Cell(x) \
S(x "-cell", x "-nadstěn") \ S(x "-cell", x "-nadstěn") \
S(x "-cell (elliptic space", x "-nadstěn (eliptický prostor)") S(x "-cell (elliptic space)", x "-nadstěn (eliptický prostor)")
Cell("{3,3,3} 5") Cell("{4,3,3} 8") Cell("{3,3,4} 16") Cell("{3,4,3} 24") Cell("{5,3,3} 120") Cell("{3,3,5} 600") Cell("{3,3,3} 5") Cell("{4,3,3} 8") Cell("{3,3,4} 16") Cell("{3,4,3} 24") Cell("{5,3,3} 120") Cell("{3,3,5} 600")
#undef Cell #undef Cell
@ -8408,7 +8408,7 @@ S("Hint: this is more playable with pure {7,3} or pure {5,4}", "Nápověda: toto
S("view the underlying geometry", "zobraz základní geometrii") S("view the underlying geometry", "zobraz základní geometrii")
S("The space you are currently in the space of rotations of the underlying hyperbolic or spherical geometry. ", S("The space you are currently in is the space of rotations of the underlying hyperbolic or spherical geometry. ",
"Prostor, ve kterém se právě nacházíš, je prostorem rotací základní hyperbolické nebo sférické geometrie. ") "Prostor, ve kterém se právě nacházíš, je prostorem rotací základní hyperbolické nebo sférické geometrie. ")
S("This option lets you see the underlying space. Lands and some walls (e.g. in the Graveyard) are based on " S("This option lets you see the underlying space. Lands and some walls (e.g. in the Graveyard) are based on "

View File

@ -488,8 +488,8 @@ S("Usually, you move by touching somewhere on the map; you can also touch one "
"vier Knöpfe in den Ecken antippen um dies zu ändern (um die Karte zu scrollen oder Infos " "vier Knöpfe in den Ecken antippen um dies zu ändern (um die Karte zu scrollen oder Infos "
"über Objekte zu bekommen). Du kannst auch die Zahlen antippen um ihre Bedeutung zu erfahren.\n") "über Objekte zu bekommen). Du kannst auch die Zahlen antippen um ihre Bedeutung zu erfahren.\n")
S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n", S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n",
"Bewege dich mit der Maus, dem Numpad, qweadzxc, oder hjklyubn. Warte mit 's' oder '.'. Drehe die Welt mit den Pfeiltasten, Bild auf/ab und Pos1/Space. " // FIXME: Leertaste? "Bewege dich mit der Maus, dem Numpad, qweadzxc, oder hjklyubn. Warte mit 's' oder '.'. Drehe die Welt mit den Pfeiltasten, Bild auf/ab und Space. " // FIXME: Leertaste?
"Um zu speichern, benötigst du einen Orb der Geborgenheit. Drücke V für Einstellungen, ESC für den Quest-Status und das Menü.\n\n") "Um zu speichern, benötigst du einen Orb der Geborgenheit. Drücke V für Einstellungen, ESC für den Quest-Status und das Menü.\n\n")
S("See more on the website: ", "Mehr auf der Website: ") S("See more on the website: ", "Mehr auf der Website: ")

View File

@ -482,10 +482,10 @@ S(
"zdobyć informacje). Możesz też dotknąć liczb na ekranie, by poznać ich " "zdobyć informacje). Możesz też dotknąć liczb na ekranie, by poznać ich "
"znaczenie.\n") "znaczenie.\n")
S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n", "To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n",
"Ruszasz się myszą, klawiaturą numeryczną, qweadzxc, lub hjklyubn. Czekasz naciskając 's' lub '.'. " "Ruszasz się myszą, klawiaturą numeryczną, qweadzxc, lub hjklyubn. Czekasz naciskając 's' lub '.'. "
"Obracasz świat strzałkami, PgUp/Dn, lub Home/Space. Naciśnij 'v' by przejść do menu (konfiguracja, tryby specjalne itd.), ESC " "Obracasz świat strzałkami, PgUp/Dn, lub Space. Naciśnij 'v' by przejść do menu (konfiguracja, tryby specjalne itd.), ESC "
"by zobaczyć stan misji.\n\n") "by zobaczyć stan misji.\n\n")
S("See more on the website: ", "Więcej na stronie: ") S("See more on the website: ", "Więcej na stronie: ")
@ -4501,10 +4501,10 @@ S("(Distances reported by the vector graphics editor "
"are not about points on the ground level, but " "are not about points on the ground level, but "
"about the matching points on the plane P -- " "about the matching points on the plane P -- "
"divide them by the factor above to get actual " "divide them by the factor above to get actual "
"distances.", "distances.)",
"(Odległości wyświetlane przez edytor grafiki " "(Odległości wyświetlane przez edytor grafiki "
"dotyczą odległości między odpowiednimi punktami na płaszczyźnie P.") "dotyczą odległości między odpowiednimi punktami na płaszczyźnie P.)")
S( "If we are viewing an equidistant g absolute units below a plane, " S( "If we are viewing an equidistant g absolute units below a plane, "
"from a point c absolute units above the plane, this corresponds " "from a point c absolute units above the plane, this corresponds "
@ -5057,7 +5057,7 @@ S("firewall lines: Palace", "linie ścian ognia: Pałac")
S("firewall lines: Power", "linie ścian ognia: Moc") S("firewall lines: Power", "linie ścian ognia: Moc")
S("(ESC) tour menu", "(ESC) menu ucznia") S("(ESC) tour menu", "(ESC) menu ucznia")
S("Try the Guided tour to help with understanding the " S("Try the Guided Tour to help with understanding the "
"geometry of HyperRogue (menu -> special modes).\n\n", "geometry of HyperRogue (menu -> special modes).\n\n",
"Uruchom Wycieczkę, by zrozumieć geometrię HyperRogue (menu -> tryby specjalne).\n\n") "Uruchom Wycieczkę, by zrozumieć geometrię HyperRogue (menu -> tryby specjalne).\n\n")
@ -6037,7 +6037,7 @@ S("would be destroyed in %the1", "by%ł1by zniszczon%y1 %abl1")
S(" to go cold", " by ostygnąć") S(" to go cold", " by ostygnąć")
S("%The1 is destroyed by lava!", "%The1 jest zniszczon%y1 przez lawę!") S("%The1 is destroyed by lava!", "%The1 jest zniszczon%y1 przez lawę!")
S("%The1 is killed by lava!", "%The1 jest zabit%y1 przez lawę!") S("%The1 is killed by lava!", "%The1 jest zabit%y1 przez lawę!")
S("Run away from the lava!", "Uciekaj od lawy!") S("Run away from the magma!", "Uciekaj od lawy!")
// Terracotta Army // Terracotta Army
//----------------- //-----------------
@ -6535,7 +6535,7 @@ N("Chrysoberyl", GEN_O, "Chryzoberyl", "Chryzoberyle", "Chryzoberyl", "Chryzober
S("Fragment of the past glory.", "Fragment dawnej chwały.") S("Fragment of the past glory.", "Fragment dawnej chwały.")
N("Red Raider", GEN_M, "Czerwony Najeźdźca", "Czerwoni Najeźdźcy", "Czerwonego Najeźdźcę", "Czerwonym Najeźdźcą") N("Red Raider", GEN_M, "Czerwony Najeźdźca", "Czerwoni Najeźdźcy", "Czerwonego Najeźdźcę", "Czerwonym Najeźdźcą")
S("Red Raiders travel in pairs. They have promised to always watch another one's back. They are able to destroy walls on their way", S("Red Raiders travel in pairs. They have promised to always watch each other's backs. They are able to destroy walls on their way.",
"Czerwoni Najeźdźcy podróżują parami. Obiecali sobie nawzajem, że zawsze będą chronić swoje tyły. Mogą niszczyć ściany na swojej drodze.") "Czerwoni Najeźdźcy podróżują parami. Obiecali sobie nawzajem, że zawsze będą chronić swoje tyły. Mogą niszczyć ściany na swojej drodze.")
N("Gray Raider", GEN_M, "Szary Najeźdźca", "Szarzy Najeźdźcy", "Szarego Najeźdźcę", "Szarym Najeźdźcą") N("Gray Raider", GEN_M, "Szary Najeźdźca", "Szarzy Najeźdźcy", "Szarego Najeźdźcę", "Szarym Najeźdźcą")
@ -7429,7 +7429,7 @@ S("A large bug native to the Brown Islands. Cannot be killed easily due to their
N("Acid Gull", GEN_F, "Kwaśna Mewa", "Kwaśne Mewy", "Kwaśną Mewę", "Kwaśną Mewą") N("Acid Gull", GEN_F, "Kwaśna Mewa", "Kwaśne Mewy", "Kwaśną Mewę", "Kwaśną Mewą")
S("Where did this strange bird come from?...\n\n Acid Gulls dissolve the land on which they fall when they die. ", S("Where did this strange bird come from?...\n\nAcid Gulls dissolve the land on which they fall when they die. ",
"Skąd się wziął ten dziwny ptak?...\n\nKwaśne Mewy rozpuszczają ląd, na który spadają, gdy zostaną zabite. ") "Skąd się wziął ten dziwny ptak?...\n\nKwaśne Mewy rozpuszczają ląd, na który spadają, gdy zostaną zabite. ")
@ -7572,7 +7572,7 @@ S(
N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem") N("Torbernite", GEN_O, "Torbernit", "Torbernity", "Torbernit", "Torbernitem")
S("Crystals emiting magical radiation.", "Te kryształy emitują magiczne promieniowanie.") S("Crystals emitting magical radiation.", "Te kryształy emitują magiczne promieniowanie.")
// other things: // other things:
@ -7661,7 +7661,7 @@ S("Crystal", "Kryształ")
#define Cell(x) \ #define Cell(x) \
S(x "-cell", x "-komórka") \ S(x "-cell", x "-komórka") \
S(x "-cell (elliptic space", x "-komórka (przestrzeń eliptyczna)") S(x "-cell (elliptic space)", x "-komórka (przestrzeń eliptyczna)")
Cell("{3,3,3} 5") Cell("{4,3,3} 8") Cell("{3,3,4} 16") Cell("{3,4,3} 24") Cell("{5,3,3} 120") Cell("{3,3,5} 600") Cell("{3,3,3} 5") Cell("{4,3,3} 8") Cell("{3,3,4} 16") Cell("{3,4,3} 24") Cell("{5,3,3} 120") Cell("{3,3,5} 600")
#undef Cell #undef Cell

View File

@ -4579,7 +4579,7 @@ S("Ground level is actually an equidistant surface, "
"are not about points on the ground level, but " "are not about points on the ground level, but "
"about the matching points on the plane P -- " "about the matching points on the plane P -- "
"divide them by the factor above to get actual " "divide them by the factor above to get actual "
"distances.", "distances.)",
"Poziom podłoża jest w rzeczywistości ekwidystantną powierzchnią " "Poziom podłoża jest w rzeczywistości ekwidystantną powierzchnią "
"%1 jednostek pod płaszczyzną P. Teoretycznie, ta wartość " "%1 jednostek pod płaszczyzną P. Teoretycznie, ta wartość "
@ -4587,7 +4587,7 @@ S("Ground level is actually an equidistant surface, "
"szybciej latając powyżej poziomu podłoża, na wysokości płaszczyzny " "szybciej latając powyżej poziomu podłoża, na wysokości płaszczyzny "
"P -- ale nie wpływa to na mechanikę gry w żaden sposób. " "P -- ale nie wpływa to na mechanikę gry w żaden sposób. "
"(Odległości wyświetlane przez edytor grafiki " "(Odległości wyświetlane przez edytor grafiki "
"dotyczą odległości między odpowiednimi punktami na płaszczyźnie P.") "dotyczą odległości między odpowiednimi punktami na płaszczyźnie P.)")
S( "If we are viewing an equidistant g absolute units below a plane, " S( "If we are viewing an equidistant g absolute units below a plane, "
"from a point c absolute units above the plane, this corresponds " "from a point c absolute units above the plane, this corresponds "

View File

@ -484,10 +484,10 @@ S(
"информацию про объекты. Кликая по изображаемым числам, можно смотреть, " "информацию про объекты. Кликая по изображаемым числам, можно смотреть, "
"что они означают.\n") "что они означают.\n")
S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n", "To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n",
"Двигайтесь с помощью мышки, нумпада, qweadzxc или hjklyubn. Ждите, нажимая 's' или '.'. " "Двигайтесь с помощью мышки, нумпада, qweadzxc или hjklyubn. Ждите, нажимая 's' или '.'. "
"Поворачивайте карту стрелками, PageUp/Down или Home/Space. " "Поворачивайте карту стрелками, PageUp/Down или Space. "
"Чтобы сохраниться, Вам нужна сфера безопасности. Нажмите 'v' для настроек, Esc для статуса квеста или меню.") "Чтобы сохраниться, Вам нужна сфера безопасности. Нажмите 'v' для настроек, Esc для статуса квеста или меню.")
S("See more on the website: ", "Смотрите далее на сайте: ") S("See more on the website: ", "Смотрите далее на сайте: ")
@ -4676,7 +4676,7 @@ S("(Distances reported by the vector graphics editor "
"are not about points on the ground level, but " "are not about points on the ground level, but "
"about the matching points on the plane P -- " "about the matching points on the plane P -- "
"divide them by the factor above to get actual " "divide them by the factor above to get actual "
"distances.", "distances.)",
"(Расстояния в редакторе векторной графики относятся" "(Расстояния в редакторе векторной графики относятся"
"не к точкам на поверхности, а к их проекциям на плоскость P.)") "не к точкам на поверхности, а к их проекциям на плоскость P.)")
@ -6220,7 +6220,7 @@ S("would be destroyed in %the1", "будет уничтожено %abl1")
S(" to go cold", " чтобы остыть") S(" to go cold", " чтобы остыть")
S("%The1 is destroyed by lava!", "%1 уничтожен%E1 лавой!") S("%The1 is destroyed by lava!", "%1 уничтожен%E1 лавой!")
S("%The1 is killed by lava!", "%1 убит%E1 лавой!") S("%The1 is killed by lava!", "%1 убит%E1 лавой!")
S("Run away from the lava!", "Беги из лавы!") S("Run away from the magma!", "Беги из лавы!")
// Terracotta Army // Terracotta Army
//----------------- //-----------------
@ -6760,7 +6760,7 @@ N("Chrysoberyl", GEN_O, "Хризоберилл", "Хризобериллы", "
S("Fragment of the past glory.", "Фрагмент былой славы.") S("Fragment of the past glory.", "Фрагмент былой славы.")
N("Red Raider", GEN_M, "Красный Рейдер", "Красные Рейдеры", "Красного Рейдера", "Красным Рейдером") N("Red Raider", GEN_M, "Красный Рейдер", "Красные Рейдеры", "Красного Рейдера", "Красным Рейдером")
S("Red Raiders travel in pairs. They have promised to always watch another one's back. They are able to destroy walls on their way", S("Red Raiders travel in pairs. They have promised to always watch each other's backs. They are able to destroy walls on their way.",
"Красные рейдеры путешествуют парами. Они обещали, что будут защищать спины друг друга. Они могут разрушать стены на своем пути.") "Красные рейдеры путешествуют парами. Они обещали, что будут защищать спины друг друга. Они могут разрушать стены на своем пути.")
N("Gray Raider", GEN_M, "Серый Рейдер", "Серые Рейдеры", "Серого Рейдера", "Серым Рейдером") N("Gray Raider", GEN_M, "Серый Рейдер", "Серые Рейдеры", "Серого Рейдера", "Серым Рейдером")

View File

@ -459,10 +459,10 @@ S(
"Normalde haritada bir yere dokunarak hareket edersin, haritanın dört köşesindeki tuşlara dokunarak bunu değiştirebilirsin." "Normalde haritada bir yere dokunarak hareket edersin, haritanın dört köşesindeki tuşlara dokunarak bunu değiştirebilirsin."
" (haritayı çevirmek ya da haritadaki nesneler hakkında bilgi almak için). Gösterilen numaralara dokunarak da anlamlarını görebilirsin. \n") " (haritayı çevirmek ya da haritadaki nesneler hakkında bilgi almak için). Gösterilen numaralara dokunarak da anlamlarını görebilirsin. \n")
S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " S("Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n", "To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n",
"Fareyle, sayı tuşlarıyla, qweadzxc ile, veya hjklyubn ile hareket et. 's' ya da '.' ile bekle. " "Fareyle, sayı tuşlarıyla, qweadzxc ile, veya hjklyubn ile hareket et. 's' ya da '.' ile bekle. "
"Oklarla, PageUp/Down ile veya Home/Space ile dünyayı çevirebilirsin. Oyunu kaydetmek için Güvenlik Küresine ihtiyacın var. Ayarlar için 'v'ye görev durumu ve menü için 'ESC'ye bas.\n\n") "Oklarla, PageUp/Down ile veya Space ile dünyayı çevirebilirsin. Oyunu kaydetmek için Güvenlik Küresine ihtiyacın var. Ayarlar için 'v'ye görev durumu ve menü için 'ESC'ye bas.\n\n")
S("See more on the website: ", "Daha fazlasını websitesinde görüntüleyin: ") S("See more on the website: ", "Daha fazlasını websitesinde görüntüleyin: ")

View File

@ -49,7 +49,7 @@ void loadOldConfig(FILE *f) {
float a, b, c, d; float a, b, c, d;
err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d); err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d);
if(err == 4) { if(err == 4) {
vid.scale = a; vid.alpha = c; vid.sspeed = d; vid.scale = a; pconf.alpha = c; vid.sspeed = d;
} }
err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &vid.antialias); err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &vid.antialias);
vid.usingGL = gl; vid.usingGL = gl;

View File

@ -10,12 +10,288 @@ namespace hr {
EX namespace mapeditor { EX namespace mapeditor {
EX bool drawing_tool;
#if HDR #if HDR
enum eShapegroup { sgPlayer, sgMonster, sgItem, sgFloor, sgWall }; enum eShapegroup { sgPlayer, sgMonster, sgItem, sgFloor, sgWall };
static const int USERSHAPEGROUPS = 5; static const int USERSHAPEGROUPS = 5;
#endif #endif
EX color_t dtfill = 0;
EX color_t dtcolor = 0x000000FF;
EX ld dtwidth = .02;
hyperpoint lstart; /* drawing_tool shapes */
struct dtshape {
cell *where;
color_t col, fill;
ld lw;
virtual void rotate(const transmatrix& T) = 0;
virtual void save(hstream& hs) = 0;
virtual dtshape* load(hstream& hs) = 0;
virtual void draw(const transmatrix& V) = 0;
virtual ld distance(hyperpoint h) = 0;
virtual ~dtshape() {}
};
struct dtline : dtshape {
hyperpoint s, e;
void rotate(const transmatrix& T) override {
s = T * s;
e = T * e;
}
void save(hstream& hs) override {
hs.write<char>(1);
hs.write(s);
hs.write(e);
}
dtshape *load(hstream& hs) override {
hs.read(s);
hs.read(e);
return this;
}
void draw(const transmatrix& V) override {
queueline(V*s, V*e, col, 2 + vid.linequality);
}
ld distance(hyperpoint h) override {
return hdist(s, h) + hdist(e, h) - hdist(s, e);
}
};
struct dtcircle : dtshape {
hyperpoint s; ld radius;
void rotate(const transmatrix& T) override {
s = T * s;
}
void save(hstream& hs) override {
hs.write<char>(2);
hs.write(s);
hs.write(radius);
}
dtshape *load(hstream& hs) override {
hs.read(s);
hs.read(radius);
return this;
}
void draw(const transmatrix& V) override {
ld len = sin_auto(radius);
int ll = ceil(360 * len);
transmatrix W = V * rgpushxto0(s);
for(int i=0; i<=ll; i++)
curvepoint(W * xspinpush0(360*degree*i/ll, radius));
queuecurve(col, fill, PPR::LINE);
}
ld distance(hyperpoint h) override {
return abs(hdist(s, h) - radius);
}
};
struct dttext : dtshape {
hyperpoint where;
ld size;
string caption;
void rotate(const transmatrix& T) override {
where = T * where;
}
void save(hstream& hs) override {
hs.write<char>(4);
hs.write(where);
hs.write(size);
hs.write(caption);
}
dtshape *load(hstream& hs) override {
hs.read(where);
hs.read(size);
hs.read(caption);
return this;
}
void draw(const transmatrix& V) override {
queuestr(V * rgpushxto0(where), size, caption, col);
}
ld distance(hyperpoint h) override {
return hdist(h, where);
}
};
struct dtfree : dtshape {
vector<hyperpoint> lh;
void rotate(const transmatrix& T) override {
for(auto& h: lh) h = T * h;
}
void save(hstream& hs) override {
hs.write<char>(3);
hs.write(lh);
}
dtshape *load(hstream& hs) override {
hs.read(lh);
return this;
}
void draw(const transmatrix& V) override {
for(auto& p: lh) curvepoint(V*p);
queuecurve(col, fill, PPR::LINE);
}
ld distance(hyperpoint h) override {
ld mind = 99;
for(auto h1: lh) mind = min(mind, hdist(h, h1));
return mind;
}
};
vector<unique_ptr<dtshape>> dtshapes;
EX void clear_dtshapes() { dtshapes.clear(); }
EX void draw_dtshapes() {
for(auto& shp: dtshapes) {
if(shp == nullptr) continue;
auto& sh = *shp;
cell*& c = sh.where;
for(const transmatrix& V: current_display->all_drawn_copies[c]) {
dynamicval<ld> lw(vid.linewidth, vid.linewidth * sh.lw);
sh.draw(V);
}
}
if(drawing_tool && (cmode & sm::DRAW)) {
dynamicval<ld> lw(vid.linewidth, vid.linewidth * dtwidth * 100);
if(holdmouse && mousekey == 'c')
queue_hcircle(rgpushxto0(lstart), hdist(lstart, mouseh));
else if(holdmouse && mousekey == 'l')
queueline(lstart, mouseh, dtcolor, 4 + vid.linequality, PPR::LINE);
else if(!holdmouse) {
transmatrix T = rgpushxto0(mouseh);
queueline(T * xpush0(-.1), T * xpush0(.1), dtcolor);
queueline(T * ypush0(-.1), T * ypush0(.1), dtcolor);
}
}
}
/** dtshapes takes ownership of sh */
void dt_add(cell *where, dtshape *sh) {
sh->where = where;
sh->col = dtcolor;
sh->fill = dtfill;
sh->lw = dtwidth * 100;
dtshapes.push_back(unique_ptr<dtshape>(sh));
}
EX void dt_add_line(hyperpoint h1, hyperpoint h2, int maxl) {
if(hdist(h1, h2) > 1 && maxl > 0) {
hyperpoint h3 = mid(h1, h2);
dt_add_line(h1, h3, maxl-1);
dt_add_line(h3, h2, maxl-1);
return;
}
cell *b = centerover;
transmatrix T = rgpushxto0(h1);
auto T1 = inverse(ggmatrix(b)) * T;
virtualRebase(b, T1);
auto l = new dtline;
l->s = tC0(T1);
l->e = T1 * gpushxto0(h1) * h2;
dt_add(b, l);
}
EX void dt_add_circle(hyperpoint h1, hyperpoint h2) {
cell *b = centerover;
auto d = hdist(h1, h2);
h1 = inverse(ggmatrix(b)) * h1;
virtualRebase(b, h1);
auto l = new dtcircle;
l->s = h1;
l->radius = d;
dt_add(b, l);
}
EX void dt_add_text(hyperpoint h, ld size, string cap) {
cell *b = centerover;
h = inverse(ggmatrix(b)) * h;
virtualRebase(b, h);
auto l = new dttext;
l->where = h;
l->size = size;
l->caption = cap;
dt_add(b, l);
}
dtshape *load_shape(hstream& hs) {
char type = hs.get<char>();
switch(type) {
case 1:
return (new dtline)->load(hs);
case 2:
return (new dtcircle)->load(hs);
case 3:
return (new dtfree)->load(hs);
default:
return nullptr;
}
}
dtfree *cfree;
cell *cfree_at;
transmatrix cfree_T;
EX void dt_add_free(hyperpoint h) {
cell *b = centerover;
transmatrix T = rgpushxto0(h);
auto T1 = inverse(ggmatrix(b)) * T;
virtualRebase(b, T1);
if(cfree)
cfree->lh.push_back(cfree_T * h);
if(b != cfree_at && !(dtfill && cfree_at)) {
cfree = new dtfree;
dt_add(b, cfree);
cfree_T = T1 * gpushxto0(h);
cfree->lh.push_back(cfree_T * h);
cfree_at = b;
}
}
EX void dt_erase(hyperpoint h) {
ld nearest = 1;
int nearest_id = -1;
int id = -1;
for(auto& shp: dtshapes) {
id++;
if(shp == nullptr) continue;
auto& sh = *shp;
cell*& c = sh.where;
for(const transmatrix& V: current_display->all_drawn_copies[c]) {
ld dist = sh.distance(inverse(V) * h);
if(dist < nearest) nearest = dist, nearest_id = id;
}
}
if(nearest_id >= 0)
dtshapes.erase(dtshapes.begin() + nearest_id);
}
EX hyperpoint lstart;
cell *lstartcell; cell *lstartcell;
ld front_edit = 0.5; ld front_edit = 0.5;
enum class eFront { sphere_camera, sphere_center, equidistants, const_x, const_y }; enum class eFront { sphere_camera, sphere_center, equidistants, const_x, const_y };
@ -33,26 +309,20 @@ EX namespace mapeditor {
EX editwhat ew, ewsearch; EX editwhat ew, ewsearch;
EX bool autochoose = ISMOBILE; EX bool autochoose = ISMOBILE;
EX void scaleall(ld z) { EX void scaleall(ld z, bool keep_mouse) {
// (mx,my) = (xcb,ycb) + ss * (xpos,ypos) + (mrx,mry) * scale if(keep_mouse) {
ld mrx = (.0 + mousex - current_display->xcenter) / vpconf.scale;
// (mrx,mry) * (scale-scale') = ld mry = (.0 + mousey - current_display->ycenter) / vpconf.scale;
// ss * ((xpos',ypos')-(xpos,ypos))
if(vid.xres > vid.yres) {
// mx = xb + ssiz*xpos + mrx * scale vpconf.xposition += (vpconf.scale - vpconf.scale*z) * mrx / current_display->scrsize;
// mx = xb + ssiz*xpos' + mrx * scale' vpconf.yposition += (vpconf.scale - vpconf.scale*z) * mry / current_display->scrsize;
}
ld mrx = (.0 + mousex - current_display->xcenter) / vid.scale;
ld mry = (.0 + mousey - current_display->ycenter) / vid.scale;
if(vid.xres > vid.yres) {
vid.xposition += (vid.scale - vid.scale*z) * mrx / current_display->scrsize;
vid.yposition += (vid.scale - vid.scale*z) * mry / current_display->scrsize;
} }
vid.scale *= z; vpconf.scale *= z;
// printf("scale = " LDF "\n", vid.scale); // printf("scale = " LDF "\n", vpconf.scale);
#if CAP_TEXTURE #if CAP_TEXTURE
// display(texture::itt); // display(texture::itt);
texture::config.itt = xyscale(texture::config.itt, 1/z); texture::config.itt = xyscale(texture::config.itt, 1/z);
@ -101,6 +371,39 @@ namespace mapstream {
vector<cell*> cellbyid; vector<cell*> cellbyid;
vector<char> relspin; vector<char> relspin;
void load_drawing_tool(fhstream& hs) {
using namespace mapeditor;
if(hs.vernum < 0xA82A) return;
int i = hs.get<int>();
while(i--) {
auto sh = load_shape(hs);
if(!sh) continue;
hs.read(sh->col);
hs.read(sh->fill);
hs.read(sh->lw);
int id = hs.get<int>();
sh->where = cellbyid[id];
sh->rotate(spin(currentmap->spin_angle(sh->where, relspin[id]) - currentmap->spin_angle(sh->where, 0)));
dtshapes.push_back(unique_ptr<dtshape>(sh));
}
}
void save_drawing_tool(hstream& hs) {
using namespace mapeditor;
hs.write<int>(isize(dtshapes));
for(auto& shp: dtshapes) {
if(shp == nullptr) { hs.write<char>(0); }
else {
auto& sh = *shp;
sh.save(hs);
hs.write(sh.col);
hs.write(sh.fill);
hs.write(sh.lw);
hs.write(cellids[sh.where]);
}
}
}
void addToQueue(cell* c) { void addToQueue(cell* c) {
if(cellids.count(c)) return; if(cellids.count(c)) return;
@ -325,6 +628,8 @@ namespace mapstream {
int32_t n = -1; f.write(n); int32_t n = -1; f.write(n);
int32_t id = cellids.count(cwt.at) ? cellids[cwt.at] : -1; int32_t id = cellids.count(cwt.at) ? cellids[cwt.at] : -1;
f.write(id); f.write(id);
save_drawing_tool(f);
f.write(vid.always3); f.write(vid.always3);
f.write(mutantphase); f.write(mutantphase);
@ -507,6 +812,8 @@ namespace mapstream {
savecount = 0; savetime = 0; savecount = 0; savetime = 0;
cheater = 1; cheater = 1;
load_drawing_tool(f);
dynamicval<bool> a3(vid.always3, vid.always3); dynamicval<bool> a3(vid.always3, vid.always3);
if(f.vernum >= 0xA616) { f.read(vid.always3); geom3::apply_always3(); } if(f.vernum >= 0xA616) { f.read(vid.always3); geom3::apply_always3(); }
@ -730,7 +1037,7 @@ namespace mapeditor {
displayButton(8, vid.yres-8-fs*11, XLAT("F1 = help"), SDLK_F1, 0); displayButton(8, vid.yres-8-fs*11, XLAT("F1 = help"), SDLK_F1, 0);
displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0); displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0);
displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0); displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*7, XLAT("F5 = restart"), SDLK_F5, 0); displayButton(8, vid.yres-8-fs*7, drawing_tool ? XLAT("F5 = clear") : XLAT("F5 = restart"), SDLK_F5, 0);
#if CAP_SHOT #if CAP_SHOT
displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0); displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0);
#endif #endif
@ -983,6 +1290,32 @@ namespace mapeditor {
if(d == -1 && fix) d = hrand(mouseover->type); if(d == -1 && fix) d = hrand(mouseover->type);
return cellwalker(mouseover, d); return cellwalker(mouseover, d);
} }
void save_level() {
dialog::openFileDialog(levelfile, XLAT("level to save:"), ".lev", [] () {
if(mapstream::saveMap(levelfile.c_str())) {
addMessage(XLAT("Map saved to %1", levelfile));
return true;
}
else {
addMessage(XLAT("Failed to save map to %1", levelfile));
return false;
}
});
}
void load_level() {
dialog::openFileDialog(levelfile, XLAT("level to load:"), ".lev", [] () {
if(mapstream::loadMap(levelfile.c_str())) {
addMessage(XLAT("Map loaded from %1", levelfile));
return true;
}
else {
addMessage(XLAT("Failed to load map from %1", levelfile));
return false;
}
});
}
void showList() { void showList() {
dialog::v.clear(); dialog::v.clear();
@ -1083,31 +1416,10 @@ namespace mapeditor {
else if(uni == 'G') else if(uni == 'G')
push_debug_screen(); push_debug_screen();
else if(sym == SDLK_F5) { else if(sym == SDLK_F5) {
restart_game(); dialog::push_confirm_dialog([] { restart_game(); }, XLAT("Are you sure you want to clear the map?"));
} }
else if(sym == SDLK_F2) { else if(sym == SDLK_F2) save_level();
dialog::openFileDialog(levelfile, XLAT("level to save:"), ".lev", [] () { else if(sym == SDLK_F3) load_level();
if(mapstream::saveMap(levelfile.c_str())) {
addMessage(XLAT("Map saved to %1", levelfile));
return true;
}
else {
addMessage(XLAT("Failed to save map to %1", levelfile));
return false;
}
});
}
else if(sym == SDLK_F3)
dialog::openFileDialog(levelfile, XLAT("level to load:"), ".lev", [] () {
if(mapstream::loadMap(levelfile.c_str())) {
addMessage(XLAT("Map loaded from %1", levelfile));
return true;
}
else {
addMessage(XLAT("Failed to load map from %1", levelfile));
return false;
}
});
#if CAP_SHOT #if CAP_SHOT
else if(sym == SDLK_F6) { else if(sym == SDLK_F6) {
pushScreen(shot::menu); pushScreen(shot::menu);
@ -1188,7 +1500,7 @@ namespace mapeditor {
unsigned gridcolor = 0xC0C0C040; unsigned gridcolor = 0xC0C0C040;
hyperpoint in_front_dist(ld d) { hyperpoint in_front_dist(ld d) {
return direct_exp(lp_iapply(ztangent(d)), 100); return direct_exp(lp_iapply(ztangent(d)));
} }
hyperpoint find_mouseh3() { hyperpoint find_mouseh3() {
@ -1203,7 +1515,7 @@ namespace mapeditor {
ld d1 = front_edit; ld d1 = front_edit;
hyperpoint h1 = in_front_dist(d); hyperpoint h1 = in_front_dist(d);
if(front_config == eFront::sphere_center) if(front_config == eFront::sphere_center)
d1 = geo_dist(drawtrans * C0, h1, iTable); d1 = geo_dist(drawtrans * C0, h1);
if(front_config == eFront::equidistants) { if(front_config == eFront::equidistants) {
hyperpoint h = idt * in_front_dist(d); hyperpoint h = idt * in_front_dist(d);
d1 = asin_auto(h[2]); d1 = asin_auto(h[2]);
@ -1232,6 +1544,7 @@ namespace mapeditor {
ld equi_range = 1; ld equi_range = 1;
EX void drawGrid() { EX void drawGrid() {
if(!drawcell) drawcell = cwt.at;
color_t lightgrid = gridcolor; color_t lightgrid = gridcolor;
lightgrid -= (lightgrid & 0xFF) / 2; lightgrid -= (lightgrid & 0xFF) / 2;
transmatrix d2 = drawtrans * rgpushxto0(ccenter) * rspintox(gpushxto0(ccenter) * coldcenter); transmatrix d2 = drawtrans * rgpushxto0(ccenter) * rspintox(gpushxto0(ccenter) * coldcenter);
@ -1246,7 +1559,7 @@ namespace mapeditor {
} }
if(front_config == eFront::sphere_center) for(int i=0; i<4; i+=2) { if(front_config == eFront::sphere_center) for(int i=0; i<4; i+=2) {
auto pt = [&] (ld a, ld b) { auto pt = [&] (ld a, ld b) {
return d2 * direct_exp(spin(a*degree) * cspin(0, 2, b*degree) * xtangent(front_edit), 100); return d2 * direct_exp(spin(a*degree) * cspin(0, 2, b*degree) * xtangent(front_edit));
}; };
for(int ai=0; ai<parallels; ai++) { for(int ai=0; ai<parallels; ai++) {
ld a = ai * 360 / parallels; ld a = ai * 360 / parallels;
@ -1363,6 +1676,9 @@ namespace mapeditor {
usershape *us = NULL; usershape *us = NULL;
bool intexture = false; bool intexture = false;
(void) intexture;
bool freedraw = drawing_tool;
#if CAP_TEXTURE #if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive) { if(texture::config.tstate == texture::tsActive) {
@ -1371,12 +1687,12 @@ namespace mapeditor {
line2 = ""; line2 = "";
texture::config.data.update(); texture::config.data.update();
intexture = true; intexture = true;
freedraw = true;
drawing_tool = false;
} }
#else
if(0);
#endif #endif
else { if(!freedraw) {
sg = drawcellShapeGroup(); sg = drawcellShapeGroup();
@ -1419,7 +1735,7 @@ namespace mapeditor {
// displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0); // displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
displayfr(8, 8+fs, 2, vid.fsize, line1, 0xC0C0C0, 0); displayfr(8, 8+fs, 2, vid.fsize, line1, 0xC0C0C0, 0);
if(!intexture) { if(!freedraw) {
if(sg == sgFloor) if(sg == sgFloor)
displayButton(8, 8+fs*2, line2 + XLAT(" (r = complex tesselations)"), 'r', 0); displayButton(8, 8+fs*2, line2 + XLAT(" (r = complex tesselations)"), 'r', 0);
else else
@ -1466,17 +1782,21 @@ namespace mapeditor {
} }
#if CAP_TEXTURE #if CAP_TEXTURE
else if(texture::config.tstate == texture::tsActive) { else if(freedraw) {
displayButton(8, 8+fs*2, XLAT(texture::texturesym ? "0 = symmetry" : "0 = asymmetry"), '0', 0); displayButton(8, 8+fs*2, XLAT(texture::texturesym ? "0 = symmetry" : "0 = asymmetry"), '0', 0);
if(mousekey == 'g') if(mousekey == 'g')
displayButton(8, 8+fs*16, XLAT("p = grid color"), 'p', 0); displayButton(8, 8+fs*16, XLAT("p = grid color"), 'p', 0);
else else
displayButton(8, 8+fs*16, XLAT("p = color"), 'p', 0); displayButton(8, 8+fs*16, XLAT("p = color"), 'p', 0);
displayButton(8, 8+fs*4, XLAT("b = brush size: %1", fts(texture::penwidth)), 'b', 0); if(drawing_tool)
displayButton(8, 8+fs*17, XLAT("f = fill") + (dtfill ? " (on)" : " (off)"), 'f', 0);
displayButton(8, 8+fs*4, XLAT("b = brush size: %1", fts(dtwidth)), 'b', 0);
displayButton(8, 8+fs*5, XLAT("u = undo"), 'u', 0); displayButton(8, 8+fs*5, XLAT("u = undo"), 'u', 0);
displaymm('d', 8, 8+fs*7, 2, vid.fsize, XLAT("d = draw"), 0); displaymm('d', 8, 8+fs*7, 2, vid.fsize, XLAT("d = draw"), 0);
displaymm('l', 8, 8+fs*8, 2, vid.fsize, XLAT("l = line"), 0); displaymm('l', 8, 8+fs*8, 2, vid.fsize, XLAT("l = line"), 0);
displaymm('c', 8, 8+fs*9, 2, vid.fsize, XLAT("c = circle"), 0); displaymm('c', 8, 8+fs*9, 2, vid.fsize, XLAT("c = circle"), 0);
if(drawing_tool)
displaymm('e', 8, 8+fs*10, 2, vid.fsize, XLAT("e = erase"), 0);
int s = isize(texture::config.data.pixels_to_draw); int s = isize(texture::config.data.pixels_to_draw);
if(s) displaymm(0, 8, 8+fs*11, 2, vid.fsize, its(s), 0); if(s) displaymm(0, 8, 8+fs*11, 2, vid.fsize, its(s), 0);
} }
@ -1498,15 +1818,15 @@ namespace mapeditor {
displaymm('g', vid.xres-8, 8+fs*4, 2, vid.fsize, XLAT("g = grid"), 16); displaymm('g', vid.xres-8, 8+fs*4, 2, vid.fsize, XLAT("g = grid"), 16);
#if CAP_TEXTURE #if CAP_TEXTURE
if(intexture) for(int i=0; i<10; i++) { if(freedraw) for(int i=0; i<10; i++) {
if(8 + fs * (6+i) < vid.yres - 8 - fs * 7) if(8 + fs * (6+i) < vid.yres - 8 - fs * 7)
displayColorButton(vid.xres-8, 8+fs*(6+i), "###", 1000 + i, 16, 1, dialog::displaycolor(texture_colors[i+1])); displayColorButton(vid.xres-8, 8+fs*(6+i), "###", 1000 + i, 16, 1, dialog::displaycolor(texture_colors[i+1]));
if(displayfr(vid.xres-8 - fs * 3, 8+fs*(6+i), 0, vid.fsize, its(i+1), texture::penwidth == brush_sizes[i] ? 0xFF8000 : 0xC0C0C0, 16)) if(displayfr(vid.xres-8 - fs * 3, 8+fs*(6+i), 0, vid.fsize, its(i+1), dtwidth == brush_sizes[i] ? 0xFF8000 : 0xC0C0C0, 16))
getcstat = 2000+i; getcstat = 2000+i;
} }
if(texture::config.tstate != texture::tsActive) if(!freedraw)
displaymm('e', vid.xres-8, 8+fs, 2, vid.fsize, XLAT("e = edit this"), 16); displaymm('e', vid.xres-8, 8+fs, 2, vid.fsize, XLAT("e = edit this"), 16);
#endif #endif
@ -1524,7 +1844,7 @@ namespace mapeditor {
displayfr(vid.xres-8, vid.yres-8-fs*5, 2, vid.fsize, XLAT("z: %1", fts(mh[2],4)), 0xC0C0C0, 16); displayfr(vid.xres-8, vid.yres-8-fs*5, 2, vid.fsize, XLAT("z: %1", fts(mh[2],4)), 0xC0C0C0, 16);
if(MDIM == 4) if(MDIM == 4)
displayfr(vid.xres-8, vid.yres-8-fs*4, 2, vid.fsize, XLAT("w: %1", fts(mh[3],4)), 0xC0C0C0, 16); displayfr(vid.xres-8, vid.yres-8-fs*4, 2, vid.fsize, XLAT("w: %1", fts(mh[3],4)), 0xC0C0C0, 16);
mh = inverse_exp(mh, iTable, false); mh = inverse_exp(mh);
displayfr(vid.xres-8, vid.yres-8-fs*3, 2, vid.fsize, XLAT("r: %1", fts(hypot_d(3, mh),4)), 0xC0C0C0, 16); displayfr(vid.xres-8, vid.yres-8-fs*3, 2, vid.fsize, XLAT("r: %1", fts(hypot_d(3, mh),4)), 0xC0C0C0, 16);
if(GDIM == 3) { if(GDIM == 3) {
displayfr(vid.xres-8, vid.yres-8-fs, 2, vid.fsize, XLAT("ϕ: %1°", fts(-atan2(mh[2], hypot_d(2, mh)) / degree,4)), 0xC0C0C0, 16); displayfr(vid.xres-8, vid.yres-8-fs, 2, vid.fsize, XLAT("ϕ: %1°", fts(-atan2(mh[2], hypot_d(2, mh)) / degree,4)), 0xC0C0C0, 16);
@ -1933,12 +2253,35 @@ namespace mapeditor {
if(mkuni == 'g') if(mkuni == 'g')
coldcenter = ccenter, ccenter = mh, clickused = true; coldcenter = ccenter, ccenter = mh, clickused = true;
if(uni == 'd' || uni == 'l' || uni == 'c') if(uni == 'd' || uni == 'l' || uni == 'c' || uni == 'e')
mousekey = uni; mousekey = uni;
if(drawing_tool) {
if(sym == SDLK_F2) save_level();
if(sym == SDLK_F3) load_level();
if(sym == SDLK_F5) {
dialog::push_confirm_dialog([] {
stop_game();
firstland = specialland = laCanvas;
canvas_default_wall = waInvisibleFloor;
patterns::whichCanvas = 'g';
patterns::canvasback = 0xFFFFFF;
dtcolor = (forecolor << 8) | 255;
drawplayer = false;
vid.use_smart_range = 2;
start_game();
},
XLAT("Are you sure you want to restart? This will let you draw on a blank screen.")
);
}
}
if(uni == ' ' && (cheater || autocheat)) { if(uni == ' ' && (cheater || autocheat)) {
popScreen(); drawing_tool = !drawing_tool;
pushScreen(showMapEditor); if(!drawing_tool) {
popScreen();
pushScreen(showMapEditor);
}
} }
if(uni == 'z' && GDIM == 3) { if(uni == 'z' && GDIM == 3) {
@ -2001,35 +2344,57 @@ namespace mapeditor {
if(sym == SDLK_F10) popScreen(); if(sym == SDLK_F10) popScreen();
#if CAP_TEXTURE (void)clickused;
if(texture::config.tstate == texture::tsActive) {
int tcolor = (texture::config.paint_color >> 8) | ((texture::config.paint_color & 0xFF) << 24);
bool freedraw = drawing_tool;
#if CAP_TEXTURE
bool intexture = texture::config.tstate == texture::tsActive;
freedraw |= intexture;
#else
always_false intexture;
#endif
if(freedraw) {
int tcolor = (dtcolor >> 8) | ((dtcolor & 0xFF) << 24);
if(uni == '-' && !clickused) { if(uni == '-' && !clickused) {
if(mousekey == 'l' || mousekey == 'c') { if(mousekey == 'e') {
dt_erase(mouseh);
clickused = true;
}
else if(mousekey == 'l' || mousekey == 'c') {
if(!holdmouse) lstart = mouseh, lstartcell = mouseover, holdmouse = true; if(!holdmouse) lstart = mouseh, lstartcell = mouseover, holdmouse = true;
} }
else { else if(intexture) {
if(!holdmouse) texture::config.data.undoLock(); if(!holdmouse) texture::config.data.undoLock();
texture::drawPixel(mouseover, mouseh, tcolor); texture::drawPixel(mouseover, mouseh, tcolor);
holdmouse = true; lstartcell = NULL; holdmouse = true; lstartcell = NULL;
} }
else {
dt_add_free(mouseh);
holdmouse = true;
}
} }
if(sym == PSEUDOKEY_RELEASE) { if(sym == PSEUDOKEY_RELEASE) {
printf("release\n"); printf("release\n");
if(mousekey == 'l') { if(mousekey == 'l' && intexture) {
texture::config.data.undoLock(); texture::config.data.undoLock();
texture::where = mouseover; texture::where = mouseover;
texture::drawPixel(mouseover, mouseh, tcolor); texture::drawPixel(mouseover, mouseh, tcolor);
texture::drawLine(mouseh, lstart, tcolor); texture::drawLine(mouseh, lstart, tcolor);
lstartcell = NULL; lstartcell = NULL;
} }
if(mousekey == 'c') { else if(mousekey == 'l') {
dt_add_line(mouseh, lstart, 10);
lstartcell = NULL;
}
else if(mousekey == 'c' && intexture) {
texture::config.data.undoLock(); texture::config.data.undoLock();
ld rad = hdist(lstart, mouseh); ld rad = hdist(lstart, mouseh);
int circp = int(1 + 3 * (circlelength(rad) / texture::penwidth)); int circp = int(1 + 3 * (circlelength(rad) / dtwidth));
if(circp > 1000) circp = 1000; if(circp > 1000) circp = 1000;
transmatrix T = rgpushxto0(lstart); transmatrix T = rgpushxto0(lstart);
texture::where = lstartcell; texture::where = lstartcell;
@ -2037,13 +2402,21 @@ namespace mapeditor {
texture::drawPixel(T * xspinpush0(2 * M_PI * i / circp, rad), tcolor); texture::drawPixel(T * xspinpush0(2 * M_PI * i / circp, rad), tcolor);
lstartcell = NULL; lstartcell = NULL;
} }
else if(mousekey == 'c') {
dt_add_circle(lstart, mouseh);
lstartcell = NULL;
}
else {
cfree = nullptr;
cfree_at = nullptr;
}
} }
if(uni >= 1000 && uni < 1010) if(uni >= 1000 && uni < 1010)
texture::config.paint_color = texture_colors[uni - 1000 + 1]; dtcolor = texture_colors[uni - 1000 + 1];
if(uni >= 2000 && uni < 2010) if(uni >= 2000 && uni < 2010)
texture::penwidth = brush_sizes[uni - 2000]; dtwidth = brush_sizes[uni - 2000];
if(uni == '0') if(uni == '0')
texture::texturesym = !texture::texturesym; texture::texturesym = !texture::texturesym;
@ -2054,16 +2427,19 @@ namespace mapeditor {
if(uni == 'p') { if(uni == 'p') {
if(!clickused) if(!clickused)
dialog::openColorDialog(texture::config.paint_color, texture_colors); dialog::openColorDialog(dtcolor, texture_colors);
}
if(uni == 'f') {
if(dtfill == dtcolor)
dtfill = 0;
else
dtfill = dtcolor;
} }
if(uni == 'b') if(uni == 'b')
dialog::editNumber(texture::penwidth, 0, 0.1, 0.005, 0.02, XLAT("brush size"), XLAT("brush size")); dialog::editNumber(dtwidth, 0, 0.1, 0.005, 0.02, XLAT("brush size"), XLAT("brush size"));
} }
#else
(void)clickused;
if(0);
#endif
else { else {
dslayer %= USERLAYERS; dslayer %= USERLAYERS;
@ -2128,7 +2504,7 @@ namespace mapeditor {
} }
#endif #endif
auto hooks = addHook(clearmemory, 0, [] () { auto hooks = addHook(hooks_clearmemory, 0, [] () {
if(mapeditor::painttype == 4) if(mapeditor::painttype == 4)
mapeditor::painttype = 0, mapeditor::paintwhat = 0, mapeditor::painttype = 0, mapeditor::paintwhat = 0,
mapeditor::paintwhat_str = "clear monster"; mapeditor::paintwhat_str = "clear monster";
@ -2137,6 +2513,10 @@ namespace mapeditor {
if(!cheater) patterns::displaycodes = false; if(!cheater) patterns::displaycodes = false;
if(!cheater) patterns::whichShape = 0; if(!cheater) patterns::whichShape = 0;
modelcell.clear(); modelcell.clear();
mapeditor::dtshapes.clear();
mapeditor::cfree = nullptr;
mapeditor::cfree_at = nullptr;
drawcell = nullptr;
}) + }) +
addHook(hooks_removecells, 0, [] () { addHook(hooks_removecells, 0, [] () {
modelcell.clear(); modelcell.clear();
@ -2159,8 +2539,7 @@ namespace mapeditor {
transmatrix textrans; transmatrix textrans;
#if CAP_TEXTURE EX void queue_hcircle(transmatrix Ctr, ld radius) {
void queue_hcircle(transmatrix Ctr, ld radius) {
vector<hyperpoint> pts; vector<hyperpoint> pts;
int circp = int(6 * pow(2, vid.linequality)); int circp = int(6 * pow(2, vid.linequality));
if(radius > 0.04) circp *= 2; if(radius > 0.04) circp *= 2;
@ -2170,9 +2549,8 @@ namespace mapeditor {
pts.push_back(Ctr * xspinpush0(M_PI*j*2/circp, radius)); pts.push_back(Ctr * xspinpush0(M_PI*j*2/circp, radius));
for(int j=0; j<circp; j++) curvepoint(pts[j]); for(int j=0; j<circp; j++) curvepoint(pts[j]);
curvepoint(pts[0]); curvepoint(pts[0]);
queuecurve(texture::config.paint_color, 0, PPR::LINE); queuecurve(dtcolor, 0, PPR::LINE);
} }
#endif
#if CAP_POLY #if CAP_POLY
EX bool haveUserShape(eShapegroup group, int id) { EX bool haveUserShape(eShapegroup group, int id) {
@ -2210,10 +2588,10 @@ namespace mapeditor {
queue_hcircle(M2 * ml, hdist(lstart, mouseh)); queue_hcircle(M2 * ml, hdist(lstart, mouseh));
break; break;
case 'l': case 'l':
queueline(M2 * mh * C0, M2 * ml * C0, texture::config.paint_color, 4 + vid.linequality, PPR::LINE); queueline(M2 * mh * C0, M2 * ml * C0, dtcolor, 4 + vid.linequality, PPR::LINE);
break; break;
default: default:
queue_hcircle(M2 * mh, texture::penwidth); queue_hcircle(M2 * mh, dtwidth);
} }
} }
} }
@ -2408,6 +2786,14 @@ int read_editor_args() {
if(argis("-lev")) { shift(); levelfile = args(); } if(argis("-lev")) { shift(); levelfile = args(); }
else if(argis("-pic")) { shift(); picfile = args(); } else if(argis("-pic")) { shift(); picfile = args(); }
else if(argis("-load")) { PHASE(3); shift(); mapstream::loadMap(args()); } else if(argis("-load")) { PHASE(3); shift(); mapstream::loadMap(args()); }
else if(argis("-d:draw")) { PHASE(3);
#if CAP_EDIT
start_game();
mapeditor::drawing_tool = true;
mapeditor::initdraw(cwt.at);
launch_dialog(mapeditor::showDrawEditor);
#endif
}
#if CAP_POLY #if CAP_POLY
else if(argis("-picload")) { PHASE(3); shift(); mapeditor::loadPicFile(args()); } else if(argis("-picload")) { PHASE(3); shift(); mapeditor::loadPicFile(args()); }
#endif #endif

View File

@ -230,7 +230,7 @@ EX void showMainMenu() {
dialog::addItem(XLAT(inSpecialMode() ? "reset special modes" : "back to the start menu"), 'R'); dialog::addItem(XLAT(inSpecialMode() ? "reset special modes" : "back to the start menu"), 'R');
string q; string q;
#if ISMOBILE==1 #if ISMOBILE
dialog::addItem(XLAT("visit the website"), 'q'); dialog::addItem(XLAT("visit the website"), 'q');
#else #else
q = quitsaves() ? "save" : "quit"; q = quitsaves() ? "save" : "quit";
@ -248,7 +248,7 @@ EX void showMainMenu() {
if(inv::on) if(inv::on)
dialog::addItem(XLAT("inventory"), 'i'); dialog::addItem(XLAT("inventory"), 'i');
#if ISMOBILE==1 #if ISMOBILE
#if CAP_ACHIEVE #if CAP_ACHIEVE
dialog::addItem(XLAT("leaderboards/achievements"), '3'); dialog::addItem(XLAT("leaderboards/achievements"), '3');
#endif #endif
@ -305,7 +305,7 @@ EX void showMainMenu() {
#endif #endif
else if(sym == SDLK_ESCAPE) else if(sym == SDLK_ESCAPE)
showMissionScreen(); showMissionScreen();
#if ISMOBILE==1 #if ISMOBILE
#ifdef HAVE_ACHIEVEMENTS #ifdef HAVE_ACHIEVEMENTS
else if(NUMBERKEY == '3') { else if(NUMBERKEY == '3') {
achievement_final(false); achievement_final(false);
@ -323,7 +323,7 @@ EX void showMainMenu() {
// -- display modes -- // -- display modes --
EX void editScale() { EX void editScale() {
dialog::editNumber(vid.scale, .001, 1000, .1, 1, XLAT("scale factor"), dialog::editNumber(vpconf.scale, .001, 1000, .1, 1, XLAT("scale factor"),
XLAT("Scale the displayed model.")); XLAT("Scale the displayed model."));
dialog::scaleSinh(); dialog::scaleSinh();
} }
@ -335,10 +335,10 @@ EX void showGraphQuickKeys() {
dialog::init(XLAT("quick options")); dialog::init(XLAT("quick options"));
if(GDIM == 2) { if(GDIM == 2) {
dialog::addBoolItem(XLAT("orthogonal projection"), vid.alpha >= 500, '1'); dialog::addBoolItem(XLAT("orthogonal projection"), vpconf.alpha >= 500, '1');
dialog::addBoolItem(XLAT(sphere ? "stereographic projection" : euclid ? "zoomed out" : "small Poincaré model"), vid.alpha == 1 && vid.scale < 1, '2'); dialog::addBoolItem(XLAT(sphere ? "stereographic projection" : euclid ? "zoomed out" : "small Poincaré model"), vpconf.alpha == 1 && vpconf.scale < 1, '2');
dialog::addBoolItem(XLAT(sphere ? "zoomed stereographic projection" : euclid ? "zoomed in" : "big Poincaré model"), vid.alpha == 1 && vid.scale >= 1, '3'); dialog::addBoolItem(XLAT(sphere ? "zoomed stereographic projection" : euclid ? "zoomed in" : "big Poincaré model"), vpconf.alpha == 1 && vpconf.scale >= 1, '3');
dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vid.alpha == 0, '4'); dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vpconf.alpha == 0, '4');
} }
else { else {
dialog::addBoolItem(XLAT("first person perspective"), vid.yshift == 0 && vid.sspeed > -5, '1'); dialog::addBoolItem(XLAT("first person perspective"), vid.yshift == 0 && vid.sspeed > -5, '1');
@ -382,7 +382,7 @@ EX void enable_cheat() {
else if(!cheater) dialog::cheat_if_confirmed([] { else if(!cheater) dialog::cheat_if_confirmed([] {
cheater++; cheater++;
addMessage(XLAT("You activate your demonic powers!")); addMessage(XLAT("You activate your demonic powers!"));
#if ISMOBILE==0 #if !ISMOBILE
addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc.")); addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc."));
#endif #endif
popScreen(); popScreen();
@ -433,13 +433,25 @@ EX void showCreative() {
#endif #endif
#if CAP_EDIT #if CAP_EDIT
dialog::addItem(XLAT("vector graphics editor"), 'g'); dialog::addItem(XLAT("shape editor"), 'g');
dialog::add_action([] { dialog::add_action([] {
mapeditor::drawing_tool = false;
pushScreen(mapeditor::showDrawEditor); pushScreen(mapeditor::showDrawEditor);
mapeditor::initdraw(cwt.at); mapeditor::initdraw(cwt.at);
}); });
#endif #endif
#if CAP_EDIT
dialog::addItem(XLAT("drawing tool"), 'd');
dialog::add_action([] {
dialog::cheat_if_confirmed([] {
mapeditor::drawing_tool = true;
pushScreen(mapeditor::showDrawEditor);
mapeditor::initdraw(cwt.at);
});
});
#endif
// display modes // display modes
#if CAP_MODEL #if CAP_MODEL
if(GDIM == 2) { if(GDIM == 2) {
@ -875,11 +887,11 @@ EX void showStartMenu() {
specialland = laHalloween; specialland = laHalloween;
set_geometry(gSphere); set_geometry(gSphere);
start_game(); start_game();
vid.alpha = 999; pconf.alpha = 999;
vid.scale = 998; pconf.scale = 998;
} }
} }
#if CAP_RACING #if CAP_RACING && MAXMDIM >= 4
else if(uni == 'r' - 96) { else if(uni == 'r' - 96) {
popScreenAll(); popScreenAll();
resetModes(); resetModes();
@ -889,13 +901,13 @@ EX void showStartMenu() {
specialland = racing::race_lands[rand() % isize(racing::race_lands)]; specialland = racing::race_lands[rand() % isize(racing::race_lands)];
start_game(); start_game();
pmodel = mdBand; pmodel = mdBand;
models::model_orientation = racing::race_angle; pconf.model_orientation = racing::race_angle;
racing::race_advance = 1; racing::race_advance = 1;
vid.yshift = 0; vid.yshift = 0;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = 0; pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
vid.use_smart_range = 1; vid.use_smart_range = 1;
vid.smart_range_detail = 3; vid.smart_range_detail = 3;
} }
@ -947,18 +959,22 @@ EX void showStartMenu() {
// -- overview -- // -- overview --
#if HDR #if HDR
typedef pair<string, reaction_t> named_functionality; struct named_functionality {
std::string first;
reaction_t second;
explicit named_functionality() = default;
explicit named_functionality(std::string s, reaction_t r) : first(std::move(s)), second(std::move(r)) {}
friend bool operator==(const named_functionality& a, const named_functionality& b) { return a.first == b.first; }
friend bool operator!=(const named_functionality& a, const named_functionality& b) { return a.first != b.first; }
};
inline named_functionality named_dialog(string x, reaction_t dialog) { return named_functionality(x, [dialog] () { pushScreen(dialog); }); } inline named_functionality named_dialog(string x, reaction_t dialog) { return named_functionality(x, [dialog] () { pushScreen(dialog); }); }
#endif #endif
EX hookset<named_functionality()> *hooks_o_key; EX hookset<named_functionality()> hooks_o_key;
EX named_functionality get_o_key() { EX named_functionality get_o_key() {
auto res = callhandlers(named_functionality(), hooks_o_key);
if(hooks_o_key) for(auto& h: *hooks_o_key) { if (res != named_functionality()) return res;
auto res = h.second();
if(res.first != "") return res;
}
#if CAP_DAILY #if CAP_DAILY
if(daily::on) if(daily::on)

View File

@ -44,7 +44,7 @@ string buildScoreDescription() {
} }
#endif #endif
#if ISMOBILE==1 #if ISMOBILE
int andmode; int andmode;

View File

@ -115,20 +115,13 @@ inline bool mdPseudocylindrical() { return mdBandAny() && !(mdinf[pmodel].flags
EX namespace models { EX namespace models {
EX string formula = "z^2";
EX eModel basic_model;
EX ld rotation = 0; EX ld rotation = 0;
EX ld rotation_xz = 90; EX ld rotation_xz = 90;
EX ld rotation_xy2 = 90; EX ld rotation_xy2 = 90;
EX int do_rotate = 1; EX int do_rotate = 1;
EX ld model_orientation, halfplane_scale, model_orientation_yz;
EX ld clip_min, clip_max;
EX ld ocos, osin, ocos_yz, osin_yz; EX ld ocos, osin, ocos_yz, osin_yz;
EX ld cos_ball, sin_ball; EX ld cos_ball, sin_ball;
EX bool model_straight, model_straight_yz; EX bool model_straight, model_straight_yz;
EX ld top_z = 5;
EX ld model_transition = 1;
#if HDR #if HDR
// screen coordinates to logical coordinates: apply_orientation(x,y) // screen coordinates to logical coordinates: apply_orientation(x,y)
@ -146,53 +139,43 @@ EX namespace models {
return spin(rotation_xy2 * degree) * cspin(0, 2, -rotation_xz * degree) * spin(rotation * degree); return spin(rotation_xy2 * degree) * cspin(0, 2, -rotation_xz * degree) * spin(rotation * degree);
} }
EX ld spiral_angle = 70;
EX ld spiral_x = 10;
EX ld spiral_y = 7;
int spiral_id = 7; int spiral_id = 7;
EX bool use_atan = false;
EX cld spiral_multiplier; EX cld spiral_multiplier;
EX ld right_spiral_multiplier = 1;
EX ld any_spiral_multiplier = 1;
EX ld sphere_spiral_multiplier = 2;
EX ld spiral_cone = 360;
EX ld spiral_cone_rad; EX ld spiral_cone_rad;
EX bool ring_not_spiral; EX bool ring_not_spiral;
/** the matrix to rotate the Euclidean view from the standard coordinates to the screen coordinates */ /** the matrix to rotate the Euclidean view from the standard coordinates to the screen coordinates */
EX transmatrix euclidean_spin; EX transmatrix euclidean_spin;
EX ld product_z_scale = 1;
EX void configure() { EX void configure() {
ld ball = -vid.ballangle * degree; ld ball = -pconf.ballangle * degree;
cos_ball = cos(ball), sin_ball = sin(ball); cos_ball = cos(ball), sin_ball = sin(ball);
ocos = cos(model_orientation * degree); ocos = cos(pconf.model_orientation * degree);
osin = sin(model_orientation * degree); osin = sin(pconf.model_orientation * degree);
ocos_yz = cos(model_orientation_yz * degree); ocos_yz = cos(pconf.model_orientation_yz * degree);
osin_yz = sin(model_orientation_yz * degree); osin_yz = sin(pconf.model_orientation_yz * degree);
model_straight = (ocos > 1 - 1e-9); model_straight = (ocos > 1 - 1e-9);
model_straight_yz = GDIM == 2 || (ocos_yz > 1-1e-9); model_straight_yz = GDIM == 2 || (ocos_yz > 1-1e-9);
if(history::on) history::apply(); if(history::on) history::apply();
if(!euclid) { if(!euclid) {
ld b = spiral_angle * degree; ld b = pconf.spiral_angle * degree;
ld cos_spiral = cos(b); ld cos_spiral = cos(b);
ld sin_spiral = sin(b); ld sin_spiral = sin(b);
spiral_cone_rad = spiral_cone * degree; spiral_cone_rad = pconf.spiral_cone * degree;
ring_not_spiral = abs(cos_spiral) < 1e-3; ring_not_spiral = abs(cos_spiral) < 1e-3;
ld mul = 1; ld mul = 1;
if(sphere) mul = .5 * sphere_spiral_multiplier; if(sphere) mul = .5 * pconf.sphere_spiral_multiplier;
else if(ring_not_spiral) mul = right_spiral_multiplier; else if(ring_not_spiral) mul = pconf.right_spiral_multiplier;
else mul = any_spiral_multiplier * cos_spiral; else mul = pconf.any_spiral_multiplier * cos_spiral;
spiral_multiplier = cld(cos_spiral, sin_spiral) * cld(spiral_cone_rad * mul / 2., 0); spiral_multiplier = cld(cos_spiral, sin_spiral) * cld(spiral_cone_rad * mul / 2., 0);
} }
if(euclid) { if(euclid) {
euclidean_spin = pispin * inverse(cview() * master_relative(centerover, true)); euclidean_spin = pispin * inverse(cview() * master_relative(centerover, true));
euclidean_spin = gpushxto0(euclidean_spin * C0) * euclidean_spin; euclidean_spin = gpushxto0(euclidean_spin * C0) * euclidean_spin;
hyperpoint h = inverse(euclidean_spin) * (C0 + (euc::eumove(gp::loc{1,0})*C0 - C0) * spiral_x + (euc::eumove(gp::loc{0,1})*C0 - C0) * spiral_y); hyperpoint h = inverse(euclidean_spin) * (C0 + (euc::eumove(gp::loc{1,0})*C0 - C0) * vpconf.spiral_x + (euc::eumove(gp::loc{0,1})*C0 - C0) * vpconf.spiral_y);
spiral_multiplier = cld(0, 2 * M_PI) / cld(h[0], h[1]); spiral_multiplier = cld(0, 2 * M_PI) / cld(h[0], h[1]);
} }
@ -222,18 +205,27 @@ EX namespace models {
return true; return true;
} }
EX bool model_has_orientation() { EX bool has_orientation(eModel m) {
return return
among(pmodel, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic) || mdBandAny(); among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic) || mdBandAny();
} }
EX bool model_has_transition() { EX bool is_perspective(eModel m) {
return among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdBand) && GDIM == 2; return among(m, mdPerspective, mdGeodesic);
}
EX bool is_3d(const projection_configuration& p) {
if(GDIM == 3) return true;
return among(p.model, mdBall, mdHyperboloid, mdHemisphere) || (p.model == mdSpiral && p.spiral_cone != 360);
} }
EX bool product_model() { EX bool has_transition(eModel m) {
return among(m, mdJoukowsky, mdJoukowskyInverted, mdBand) && GDIM == 2;
}
EX bool product_model(eModel m) {
if(!prod) return false; if(!prod) return false;
if(among(pmodel, mdPerspective, mdHyperboloid, mdEquidistant)) return false; if(among(m, mdPerspective, mdHyperboloid, mdEquidistant)) return false;
return true; return true;
} }
@ -286,16 +278,16 @@ EX namespace models {
dialog::editNumber(spiral_id, 0, isize(torus_zeros)-1, 1, 10, XLAT("match the period of the torus"), ""); dialog::editNumber(spiral_id, 0, isize(torus_zeros)-1, 1, 10, XLAT("match the period of the torus"), "");
dialog::reaction = [] () { dialog::reaction = [] () {
auto& co = torus_zeros[spiral_id]; auto& co = torus_zeros[spiral_id];
spiral_x = co.first; vpconf.spiral_x = co.first;
spiral_y = co.second; vpconf.spiral_y = co.second;
}; };
dialog::bound_low(0); dialog::bound_low(0);
dialog::bound_up(isize(torus_zeros)-1); dialog::bound_up(isize(torus_zeros)-1);
} }
EX void edit_formula() { EX void edit_formula() {
if(pmodel != mdFormula) basic_model = pmodel; if(vpconf.model != mdFormula) vpconf.basic_model = vpconf.model;
dialog::edit_string(formula, "formula", dialog::edit_string(vpconf.formula, "formula",
XLAT( XLAT(
"This lets you specify the projection as a formula f. " "This lets you specify the projection as a formula f. "
"The formula has access to the value 'z', which is a complex number corresponding to the (x,y) coordinates in the currently selected model; " "The formula has access to the value 'z', which is a complex number corresponding to the (x,y) coordinates in the currently selected model; "
@ -314,12 +306,12 @@ EX namespace models {
curvepoint(point2(a*current_display->radius, +M_PI/2*current_display->radius)); curvepoint(point2(a*current_display->radius, +M_PI/2*current_display->radius));
queuecurve(forecolor, 0, PPR::LINE); queuecurve(forecolor, 0, PPR::LINE);
} }
queuereset(pmodel, PPR::LINE); queuereset(vpconf.model, PPR::LINE);
quickqueue(); quickqueue();
}; };
#endif #endif
dialog::reaction_final = [] () { dialog::reaction_final = [] () {
pmodel = mdFormula; vpconf.model = mdFormula;
}; };
} }
@ -351,24 +343,25 @@ EX namespace models {
cmode = sm::SIDE | sm::MAYDARK | sm::CENTER; cmode = sm::SIDE | sm::MAYDARK | sm::CENTER;
gamescreen(0); gamescreen(0);
dialog::init(XLAT("models & projections")); dialog::init(XLAT("models & projections"));
USING_NATIVE_GEOMETRY_IN_RUG;
for(int i=0; i<mdGUARD; i++) { for(int i=0; i<mdGUARD; i++) {
eModel m = eModel(i); eModel m = eModel(i);
if(m == mdFormula && ISMOBILE) continue; if(m == mdFormula && ISMOBILE) continue;
if(model_available(m)) { if(model_available(m)) {
dialog::addBoolItem(get_model_name(m), pmodel == m, (i < 26 ? 'a'+i : 'A'+i-26)); dialog::addBoolItem(get_model_name(m), vpconf.model == m, (i < 26 ? 'a'+i : 'A'+i-26));
dialog::add_action([m] () { dialog::add_action([m] () {
if(m == mdFormula) { if(m == mdFormula) {
edit_formula(); edit_formula();
return; return;
} }
pmodel = m; vpconf.model = m;
polygonal::solve(); polygonal::solve();
vid.alpha = 1; vid.scale = 1; vpconf.alpha = 1; vpconf.scale = 1;
if(pmodel == mdBand && sphere) if(pmodel == mdBand && sphere)
vid.scale = .3; vpconf.scale = .3;
if(pmodel == mdDisk && sphere) if(pmodel == mdDisk && sphere)
vid.scale = .4; vpconf.scale = .4;
popScreen(); popScreen();
}); });
} }
@ -378,24 +371,24 @@ EX namespace models {
} }
void edit_stretch() { void edit_stretch() {
dialog::editNumber(vid.stretch, 0, 10, .1, 1, XLAT("vertical stretch"), dialog::editNumber(vpconf.stretch, 0, 10, .1, 1, XLAT("vertical stretch"),
"Vertical stretch factor." "Vertical stretch factor."
); );
dialog::extra_options = [] () { dialog::extra_options = [] () {
dialog::addBreak(100); dialog::addBreak(100);
if(sphere && pmodel == mdBandEquiarea) { if(sphere && pmodel == mdBandEquiarea) {
dialog::addBoolItem("Gall-Peters", vid.stretch == 2, 'O'); dialog::addBoolItem("Gall-Peters", vpconf.stretch == 2, 'O');
dialog::add_action([] { vid.stretch = 2; dialog::ne.s = "2"; }); dialog::add_action([] { vpconf.stretch = 2; dialog::ne.s = "2"; });
} }
if(pmodel == mdBandEquiarea) { if(pmodel == mdBandEquiarea) {
// y = K * sin(phi) // y = K * sin(phi)
// cos(phi) * cos(phi) = 1/K // cos(phi) * cos(phi) = 1/K
if(sphere && vid.stretch >= 1) { if(sphere && vpconf.stretch >= 1) {
ld phi = acos(sqrt(1/vid.stretch)); ld phi = acos(sqrt(1/vpconf.stretch));
dialog::addInfo(XLAT("The current value makes the map conformal at the latitude of %1 (%2°).", fts(phi), fts(phi / degree))); dialog::addInfo(XLAT("The current value makes the map conformal at the latitude of %1 (%2°).", fts(phi), fts(phi / degree)));
} }
else if(hyperbolic && abs(vid.stretch) <= 1 && abs(vid.stretch) >= 1e-9) { else if(hyperbolic && abs(vpconf.stretch) <= 1 && abs(vpconf.stretch) >= 1e-9) {
ld phi = acosh(abs(sqrt(1/vid.stretch))); ld phi = acosh(abs(sqrt(1/vpconf.stretch)));
dialog::addInfo(XLAT("The current value makes the map conformal %1 units from the main line.", fts(phi))); dialog::addInfo(XLAT("The current value makes the map conformal %1 units from the main line.", fts(phi)));
} }
else dialog::addInfo(""); else dialog::addInfo("");
@ -406,9 +399,12 @@ EX namespace models {
EX void model_menu() { EX void model_menu() {
cmode = sm::SIDE | sm::MAYDARK | sm::CENTER; cmode = sm::SIDE | sm::MAYDARK | sm::CENTER;
gamescreen(0); gamescreen(0);
USING_NATIVE_GEOMETRY_IN_RUG;
dialog::init(XLAT("models & projections")); dialog::init(XLAT("models & projections"));
dialog::addSelItem(XLAT("projection type"), get_model_name(pmodel), 'm'); auto vpmodel = vpconf.model;
dialog::addSelItem(XLAT("projection type"), get_model_name(vpmodel), 'm');
dialog::add_action_push(model_list); dialog::add_action_push(model_list);
if(nonisotropic && !sl2) if(nonisotropic && !sl2)
@ -420,61 +416,61 @@ EX namespace models {
dialog::lastItem().value += " " + its(rotation) + "°"; dialog::lastItem().value += " " + its(rotation) + "°";
else else
dialog::lastItem().value += " " + its(rotation) + "°" + its(rotation_xz) + "°" + its(rotation_xy2) + "°"; dialog::lastItem().value += " " + its(rotation) + "°" + its(rotation_xz) + "°" + its(rotation_xy2) + "°";
dialog::add_action([] { edit_rotation(models::rotation); }); dialog::add_action([] { edit_rotation(rotation); });
// if(pmodel == mdBand && sphere) // if(vpmodel == mdBand && sphere)
if(!in_perspective()) { if(!in_perspective()) {
dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z'); dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z');
dialog::add_action(editScale); dialog::add_action(editScale);
} }
if(abs(vid.alpha-1) > 1e-3 && pmodel != mdBall && pmodel != mdHyperboloid && pmodel != mdHemisphere && pmodel != mdDisk) { if(abs(pconf.alpha-1) > 1e-3 && vpmodel != mdBall && vpmodel != mdHyperboloid && vpmodel != mdHemisphere && vpmodel != mdDisk) {
dialog::addBreak(50); dialog::addBreak(50);
dialog::addInfo("NOTE: this works 'correctly' only if the Poincaré model/stereographic projection is used."); dialog::addInfo("NOTE: this works 'correctly' only if the Poincaré model/stereographic projection is used.");
dialog::addBreak(50); dialog::addBreak(50);
} }
if(among(pmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) { if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) {
dialog::addSelItem(XLAT("projection distance"), fts(vid.alpha) + " (" + current_proj_name() + ")", 'p'); dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", 'p');
dialog::add_action(projectionDialog); dialog::add_action(projectionDialog);
} }
if(model_has_orientation()) { if(has_orientation(vpmodel)) {
dialog::addSelItem(XLAT("model orientation"), fts(model_orientation) + "°", 'l'); dialog::addSelItem(XLAT("model orientation"), fts(vpconf.model_orientation) + "°", 'l');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(model_orientation, 0, 360, 90, 0, XLAT("model orientation"), ""); dialog::editNumber(vpconf.model_orientation, 0, 360, 90, 0, XLAT("model orientation"), "");
}); });
if(GDIM == 3) { if(GDIM == 3) {
dialog::addSelItem(XLAT("model orientation (y/z plane)"), fts(model_orientation_yz) + "°", 'L'); dialog::addSelItem(XLAT("model orientation (y/z plane)"), fts(vpconf.model_orientation_yz) + "°", 'L');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(model_orientation_yz, 0, 360, 90, 0, XLAT("model orientation (y/z plane)"), ""); dialog::editNumber(vpconf.model_orientation_yz, 0, 360, 90, 0, XLAT("model orientation (y/z plane)"), "");
}); });
} }
} }
if(GDIM == 3 && pmodel != mdPerspective) { if(GDIM == 3 && vpmodel != mdPerspective) {
const string cliphelp = XLAT( const string cliphelp = XLAT(
"Your view of the 3D model is naturally bounded from four directions by your window. " "Your view of the 3D model is naturally bounded from four directions by your window. "
"Here, you can also set up similar bounds in the Z direction. Radius of the ball/band " "Here, you can also set up similar bounds in the Z direction. Radius of the ball/band "
"models, and the distance from the center to the plane in the halfspace model, are 1.\n\n"); "models, and the distance from the center to the plane in the halfspace model, are 1.\n\n");
dialog::addSelItem(XLAT("near clipping plane"), fts(clip_max), 'c'); dialog::addSelItem(XLAT("near clipping plane"), fts(vpconf.clip_max), 'c');
dialog::add_action([cliphelp] () { dialog::add_action([cliphelp] () {
dialog::editNumber(clip_max, -10, 10, 0.2, 1, XLAT("near clipping plane"), dialog::editNumber(vpconf.clip_max, -10, 10, 0.2, 1, XLAT("near clipping plane"),
cliphelp + XLAT("Objects with Z coordinate " cliphelp + XLAT("Objects with Z coordinate "
"bigger than this parameter are not shown. This is useful with the models which " "bigger than this parameter are not shown. This is useful with the models which "
"extend infinitely in the Z direction, or if you want things close to your character " "extend infinitely in the Z direction, or if you want things close to your character "
"to be not obscured by things closer to the camera.")); "to be not obscured by things closer to the camera."));
}); });
dialog::addSelItem(XLAT("far clipping plane"), fts(clip_min), 'C'); dialog::addSelItem(XLAT("far clipping plane"), fts(vpconf.clip_min), 'C');
dialog::add_action([cliphelp] () { dialog::add_action([cliphelp] () {
dialog::editNumber(clip_min, -10, 10, 0.2, -1, XLAT("far clipping plane"), dialog::editNumber(vpconf.clip_min, -10, 10, 0.2, -1, XLAT("far clipping plane"),
cliphelp + XLAT("Objects with Z coordinate " cliphelp + XLAT("Objects with Z coordinate "
"smaller than this parameter are not shown; it also affects the fog effect" "smaller than this parameter are not shown; it also affects the fog effect"
" (near clipping plane = 0% fog, far clipping plane = 100% fog).")); " (near clipping plane = 0% fog, far clipping plane = 100% fog)."));
}); });
} }
if(pmodel == mdPolynomial) { if(vpmodel == mdPolynomial) {
dialog::addSelItem(XLAT("coefficient"), dialog::addSelItem(XLAT("coefficient"),
fts(polygonal::coefr[polygonal::coefid]), 'x'); fts(polygonal::coefr[polygonal::coefid]), 'x');
dialog::add_action([] () { dialog::add_action([] () {
@ -496,26 +492,26 @@ EX namespace models {
}); });
} }
if(pmodel == mdHalfplane) { if(vpmodel == mdHalfplane) {
dialog::addSelItem(XLAT("half-plane scale"), fts(halfplane_scale), 'b'); dialog::addSelItem(XLAT("half-plane scale"), fts(vpconf.halfplane_scale), 'b');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(halfplane_scale, 0, 2, 0.25, 1, XLAT("half-plane scale"), ""); dialog::editNumber(vpconf.halfplane_scale, 0, 2, 0.25, 1, XLAT("half-plane scale"), "");
}); });
} }
if(pmodel == mdRotatedHyperboles) { if(vpmodel == mdRotatedHyperboles) {
dialog::addBoolItem_action(XLAT("use atan to make it finite"), use_atan, 'x'); dialog::addBoolItem_action(XLAT("use atan to make it finite"), vpconf.use_atan, 'x');
} }
if(pmodel == mdBall) { if(vpmodel == mdBall) {
dialog::addSelItem(XLAT("projection in ball model"), fts(vid.ballproj), 'x'); dialog::addSelItem(XLAT("projection in ball model"), fts(vpconf.ballproj), 'x');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(vid.ballproj, 0, 100, .1, 0, XLAT("projection in ball model"), dialog::editNumber(vpconf.ballproj, 0, 100, .1, 0, XLAT("projection in ball model"),
"This parameter affects the ball model the same way as the projection parameter affects the disk model."); "This parameter affects the ball model the same way as the projection parameter affects the disk model.");
}); });
} }
if(pmodel == mdPolygonal) { if(vpmodel == mdPolygonal) {
dialog::addSelItem(XLAT("polygon sides"), its(polygonal::SI), 'x'); dialog::addSelItem(XLAT("polygon sides"), its(polygonal::SI), 'x');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(polygonal::SI, 3, 10, 1, 4, XLAT("polygon sides"), ""); dialog::editNumber(polygonal::SI, 3, 10, 1, 4, XLAT("polygon sides"), "");
@ -534,90 +530,91 @@ EX namespace models {
}); });
} }
if(pmodel == mdBall || pmodel == mdHyperboloid || pmodel == mdHemisphere || (pmodel == mdSpiral && spiral_cone != 360)) { if(is_3d(vpconf) && GDIM == 2) {
dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vid.ballangle) + "°", 'b'); dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vpconf.ballangle) + "°", 'b');
dialog::add_action(config_camera_rotation); dialog::add_action(config_camera_rotation);
} }
if(pmodel == mdHyperboloid) { if(vpmodel == mdHyperboloid) {
dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(top_z), 'l'); dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(vpconf.top_z), 'l');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(top_z, 1, 20, 0.25, 4, XLAT("maximum z coordinate to show"), ""); dialog::editNumber(vpconf.top_z, 1, 20, 0.25, 4, XLAT("maximum z coordinate to show"), "");
}); });
} }
if(model_has_transition()) { if(has_transition(vpmodel)) {
dialog::addSelItem(XLAT("model transition"), fts(model_transition), 't'); dialog::addSelItem(XLAT("model transition"), fts(vpconf.model_transition), 't');
dialog::add_action([]() { dialog::add_action([]() {
dialog::editNumber(model_transition, 0, 1, 0.1, 1, XLAT("model transition"), dialog::editNumber(vpconf.model_transition, 0, 1, 0.1, 1, XLAT("model transition"),
"You can change this parameter for a transition from another model to this one." "You can change this parameter for a transition from another model to this one."
); );
}); });
} }
if(among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) { if(among(vpmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) {
dialog::addSelItem(XLAT("Möbius transformations"), fts(vid.skiprope) + "°", 'S'); dialog::addSelItem(XLAT("Möbius transformations"), fts(vpconf.skiprope) + "°", 'S');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(vid.skiprope, 0, 360, 15, 0, XLAT("Möbius transformations"), ""); dialog::editNumber(vpconf.skiprope, 0, 360, 15, 0, XLAT("Möbius transformations"), "");
}); });
} }
if(pmodel == mdHemisphere && euclid) { if(vpmodel == mdHemisphere && euclid) {
dialog::addSelItem(XLAT("parameter"), fts(vid.euclid_to_sphere), 'l'); dialog::addSelItem(XLAT("parameter"), fts(vpconf.euclid_to_sphere), 'l');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(vid.euclid_to_sphere, 0, 10, .1, 1, XLAT("parameter"), dialog::editNumber(vpconf.euclid_to_sphere, 0, 10, .1, 1, XLAT("parameter"),
"Stereographic projection to a sphere. Choose the radius of the sphere." "Stereographic projection to a sphere. Choose the radius of the sphere."
); );
dialog::scaleLog(); dialog::scaleLog();
}); });
} }
if(among(pmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) { if(among(vpmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) {
dialog::addSelItem(XLAT("parameter"), fts(vid.twopoint_param), 'b'); dialog::addSelItem(XLAT("parameter"), fts(vpconf.twopoint_param), 'b');
dialog::add_action([](){ dialog::add_action([vpmodel](){
dialog::editNumber(vid.twopoint_param, 1e-3, 10, .1, 1, XLAT("parameter"), dialog::editNumber(vpconf.twopoint_param, 1e-3, 10, .1, 1, XLAT("parameter"),
s0 + (vpmodel == mdTwoPoint ?
"This model maps the world so that the distances from two points " "This model maps the world so that the distances from two points "
"are kept. This parameter gives the distance from the two points to " "are kept. " : "") + "This parameter gives the distance from the two points to "
"the center." "the center."
); );
dialog::scaleLog(); dialog::scaleLog();
}); });
} }
if(pmodel == mdFisheye) { if(vpmodel == mdFisheye) {
dialog::addSelItem(XLAT("parameter"), fts(vid.fisheye_param), 'b'); dialog::addSelItem(XLAT("parameter"), fts(vpconf.fisheye_param), 'b');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(vid.fisheye_param, 1e-3, 10, .1, 1, XLAT("parameter"), dialog::editNumber(vpconf.fisheye_param, 1e-3, 10, .1, 1, XLAT("parameter"),
"Size of the fish eye." "Size of the fish eye."
); );
dialog::scaleLog(); dialog::scaleLog();
}); });
} }
if(pmodel == mdCollignon) { if(vpmodel == mdCollignon) {
dialog::addSelItem(XLAT("parameter"), fts(vid.collignon_parameter) + (vid.collignon_reflected ? " (r)" : ""), 'b'); dialog::addSelItem(XLAT("parameter"), fts(vpconf.collignon_parameter) + (vpconf.collignon_reflected ? " (r)" : ""), 'b');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(vid.collignon_parameter, -1, 1, .1, 1, XLAT("parameter"), dialog::editNumber(vpconf.collignon_parameter, -1, 1, .1, 1, XLAT("parameter"),
"" ""
); );
dialog::extra_options = [] { dialog::extra_options = [] {
dialog::addBoolItem_action(XLAT("reflect"), vid.collignon_reflected, 'R'); dialog::addBoolItem_action(XLAT("reflect"), vpconf.collignon_reflected, 'R');
}; };
}); });
} }
if(pmodel == mdSpiral && !euclid) { if(vpmodel == mdSpiral && !euclid) {
dialog::addSelItem(XLAT("spiral angle"), fts(spiral_angle) + "°", 'x'); dialog::addSelItem(XLAT("spiral angle"), fts(vpconf.spiral_angle) + "°", 'x');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(spiral_angle, 0, 360, 15, 0, XLAT("spiral angle"), dialog::editNumber(vpconf.spiral_angle, 0, 360, 15, 0, XLAT("spiral angle"),
XLAT("set to 90° for the ring projection") XLAT("set to 90° for the ring projection")
); );
}); });
ld& which = ld& which =
sphere ? sphere_spiral_multiplier : sphere ? vpconf.sphere_spiral_multiplier :
ring_not_spiral ? right_spiral_multiplier : ring_not_spiral ? vpconf.right_spiral_multiplier :
any_spiral_multiplier; vpconf.any_spiral_multiplier;
dialog::addSelItem(XLAT("spiral multiplier"), fts(which) + "°", 'M'); dialog::addSelItem(XLAT("spiral multiplier"), fts(which) + "°", 'M');
dialog::add_action([&which](){ dialog::add_action([&which](){
@ -631,20 +628,20 @@ EX namespace models {
); );
}); });
dialog::addSelItem(XLAT("spiral cone"), fts(spiral_cone) + "°", 'C'); dialog::addSelItem(XLAT("spiral cone"), fts(vpconf.spiral_cone) + "°", 'C');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(spiral_cone, 0, 360, -45, 360, XLAT("spiral cone"), ""); dialog::editNumber(vpconf.spiral_cone, 0, 360, -45, 360, XLAT("spiral cone"), "");
}); });
} }
if(pmodel == mdSpiral && euclid) { if(vpmodel == mdSpiral && euclid) {
dialog::addSelItem(XLAT("spiral period: x"), fts(spiral_x), 'x'); dialog::addSelItem(XLAT("spiral period: x"), fts(vpconf.spiral_x), 'x');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(spiral_x, -20, 20, 1, 10, XLAT("spiral period: x"), ""); dialog::editNumber(vpconf.spiral_x, -20, 20, 1, 10, XLAT("spiral period: x"), "");
}); });
dialog::addSelItem(XLAT("spiral period: y"), fts(spiral_y), 'y'); dialog::addSelItem(XLAT("spiral period: y"), fts(vpconf.spiral_y), 'y');
dialog::add_action([](){ dialog::add_action([](){
dialog::editNumber(spiral_y, -20, 20, 1, 10, XLAT("spiral period: y"), ""); dialog::editNumber(vpconf.spiral_y, -20, 20, 1, 10, XLAT("spiral period: y"), "");
}); });
if(euclid && quotient) { if(euclid && quotient) {
dialog::addSelItem(XLAT("match the period"), its(spiral_id), 'n'); dialog::addSelItem(XLAT("match the period"), its(spiral_id), 'n');
@ -652,13 +649,13 @@ EX namespace models {
} }
} }
dialog::addSelItem(XLAT("vertical stretch"), fts(vid.stretch), 's'); dialog::addSelItem(XLAT("vertical stretch"), fts(vpconf.stretch), 's');
dialog::add_action(edit_stretch); dialog::add_action(edit_stretch);
if(product_model()) { if(product_model(vpmodel)) {
dialog::addSelItem(XLAT("product Z stretch"), fts(product_z_scale), 'Z'); dialog::addSelItem(XLAT("product Z stretch"), fts(vpconf.product_z_scale), 'Z');
dialog::add_action([] { dialog::add_action([] {
dialog::editNumber(product_z_scale, 0.1, 10, 0.1, 1, XLAT("product Z stretch"), ""); dialog::editNumber(vpconf.product_z_scale, 0.1, 10, 0.1, 1, XLAT("product Z stretch"), "");
dialog::scaleLog(); dialog::scaleLog();
}); });
} }
@ -667,7 +664,7 @@ EX namespace models {
bool shaderside_projection = get_shader_flags() & SF_DIRECT; bool shaderside_projection = get_shader_flags() & SF_DIRECT;
if(vid.consider_shader_projection && !shaderside_projection) if(vid.consider_shader_projection && !shaderside_projection)
dialog::lastItem().value = XLAT("N/A"); dialog::lastItem().value = XLAT("N/A");
if(vid.consider_shader_projection && shaderside_projection && pmodel) if(vid.consider_shader_projection && shaderside_projection && vpmodel)
dialog::lastItem().value += XLAT(" (2D only)"); dialog::lastItem().value += XLAT(" (2D only)");
dialog::add_action([] { vid.consider_shader_projection = !vid.consider_shader_projection; }); dialog::add_action([] { vid.consider_shader_projection = !vid.consider_shader_projection; });
@ -677,7 +674,7 @@ EX namespace models {
dialog::addItem(XLAT("history mode"), 'a'); dialog::addItem(XLAT("history mode"), 'a');
dialog::add_action_push(history::history_menu); dialog::add_action_push(history::history_menu);
#if CAP_RUG #if CAP_RUG
if(GDIM == 2) { if(GDIM == 2 || rug::rugged) {
dialog::addItem(XLAT("hypersian rug mode"), 'u'); dialog::addItem(XLAT("hypersian rug mode"), 'u');
dialog::add_action_push(rug::show); dialog::add_action_push(rug::show);
} }
@ -707,39 +704,39 @@ EX namespace models {
shift_arg_formula(history::extra_line_steps); shift_arg_formula(history::extra_line_steps);
} }
else if(argis("-stretch")) { else if(argis("-stretch")) {
PHASEFROM(2); shift_arg_formula(vid.stretch); PHASEFROM(2); shift_arg_formula(vpconf.stretch);
} }
else if(argis("-PM")) { else if(argis("-PM")) {
PHASEFROM(2); shift(); pmodel = read_model(args()); PHASEFROM(2); shift(); vpconf.model = read_model(args());
if(pmodel == mdFormula) { if(vpconf.model == mdFormula) {
shift(); basic_model = eModel(argi()); shift(); vpconf.basic_model = eModel(argi());
shift(); formula = args(); shift(); vpconf.formula = args();
} }
} }
else if(argis("-ballangle")) { else if(argis("-ballangle")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(vid.ballangle); shift_arg_formula(vpconf.ballangle);
} }
else if(argis("-topz")) { else if(argis("-topz")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::top_z); shift_arg_formula(vpconf.top_z);
} }
else if(argis("-twopoint")) { else if(argis("-twopoint")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(vid.twopoint_param); shift_arg_formula(vpconf.twopoint_param);
} }
else if(argis("-hp")) { else if(argis("-hp")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::halfplane_scale); shift_arg_formula(vpconf.halfplane_scale);
} }
else if(argis("-mori")) { else if(argis("-mori")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::model_orientation); shift_arg_formula(vpconf.model_orientation);
} }
else if(argis("-mori2")) { else if(argis("-mori2")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::model_orientation); shift_arg_formula(vpconf.model_orientation);
shift_arg_formula(models::model_orientation_yz); shift_arg_formula(vpconf.model_orientation_yz);
} }
else if(argis("-crot")) { else if(argis("-crot")) {
PHASEFROM(2); PHASEFROM(2);
@ -749,43 +746,43 @@ EX namespace models {
} }
else if(argis("-clip")) { else if(argis("-clip")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::clip_min); shift_arg_formula(vpconf.clip_min);
shift_arg_formula(models::clip_max); shift_arg_formula(vpconf.clip_max);
} }
else if(argis("-mtrans")) { else if(argis("-mtrans")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::model_transition); shift_arg_formula(vpconf.model_transition);
} }
else if(argis("-sang")) { else if(argis("-sang")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::spiral_angle); shift_arg_formula(vpconf.spiral_angle);
if(sphere) if(sphere)
shift_arg_formula(models::sphere_spiral_multiplier); shift_arg_formula(vpconf.sphere_spiral_multiplier);
else if(models::spiral_angle == 90) else if(vpconf.spiral_angle == 90)
shift_arg_formula(models::right_spiral_multiplier); shift_arg_formula(vpconf.right_spiral_multiplier);
} }
else if(argis("-ssm")) { else if(argis("-ssm")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::any_spiral_multiplier); shift_arg_formula(vpconf.any_spiral_multiplier);
} }
else if(argis("-scone")) { else if(argis("-scone")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::spiral_cone); shift_arg_formula(vpconf.spiral_cone);
} }
else if(argis("-sxy")) { else if(argis("-sxy")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(models::spiral_x); shift_arg_formula(vpconf.spiral_x);
shift_arg_formula(models::spiral_y); shift_arg_formula(vpconf.spiral_y);
} }
else if(argis("-mob")) { else if(argis("-mob")) {
PHASEFROM(2); PHASEFROM(2);
shift_arg_formula(vid.skiprope); shift_arg_formula(vpconf.skiprope);
} }
else if(argis("-zoom")) { else if(argis("-zoom")) {
PHASEFROM(2); shift_arg_formula(vid.scale); PHASEFROM(2); shift_arg_formula(vpconf.scale);
} }
else if(argis("-alpha")) { else if(argis("-alpha")) {
PHASEFROM(2); shift_arg_formula(vid.alpha); PHASEFROM(2); shift_arg_formula(vpconf.alpha);
} }
else if(argis("-d:model")) else if(argis("-d:model"))
launch_dialog(model_menu); launch_dialog(model_menu);

View File

@ -354,8 +354,9 @@ EX int angledist(int t, int d1, int d2) {
return dd; return dd;
} }
EX int angledistButterfly(int t, int d1, int d2) { EX int angledistButterfly(int t, int d1, int d2, bool mirrored) {
int dd = d1 - d2; int dd = d1 - d2;
if(mirrored) dd = -dd;
while(dd<0) dd += t; while(dd<0) dd += t;
return dd; return dd;
} }
@ -485,7 +486,7 @@ EX int moveval(cell *c1, cell *c2, int d, flagtype mf) {
if(m == moBat && batsAfraid(c2)) return 790; if(m == moBat && batsAfraid(c2)) return 790;
if(m == moButterfly) if(m == moButterfly)
return 1500 + angledistButterfly(c1->type, c1->mondir, d); return 1500 + angledistButterfly(c1->type, c1->mondir, d, c1->monmirror);
if(m == moRagingBull && c1->mondir != NODIR) if(m == moRagingBull && c1->mondir != NODIR)
return 1500 - bulldist(c2); return 1500 - bulldist(c2);
@ -1410,7 +1411,7 @@ EX cell *lastmountpos[MAXPLAYER];
EX void clearshadow() { EX void clearshadow() {
shpos.resize(SHSIZE); shpos.resize(SHSIZE);
for(int i=0; i<SHSIZE; i++) for(int p=0; p<MAXPLAYER; p++) for(int i=0; i<SHSIZE; i++) for(int p=0; p<MAXPLAYER; p++)
shpos[p][i] = NULL; shpos[i][p] = NULL;
} }
EX void moveshadow() { EX void moveshadow() {

View File

@ -549,7 +549,7 @@ EX void initConfig() {
t[(int)'s'] = 16 + 6; t[(int)'s'] = 16 + 6;
t[(int)'a'] = 16 + 7; t[(int)'a'] = 16 + 7;
#if ISMOBILE==0 #if !ISMOBILE
t[SDLK_KP8] = 16 + 4; t[SDLK_KP8] = 16 + 4;
t[SDLK_KP6] = 16 + 5; t[SDLK_KP6] = 16 + 5;
t[SDLK_KP2] = 16 + 6; t[SDLK_KP2] = 16 + 6;
@ -572,7 +572,7 @@ EX void initConfig() {
t[(int)'p'] = 32 + 10; t[(int)'p'] = 32 + 10;
t[(int)'['] = 32 + pcCenter; t[(int)'['] = 32 + pcCenter;
#if ISMOBILE==0 #if !ISMOBILE
t[SDLK_UP] = 48 ; t[SDLK_UP] = 48 ;
t[SDLK_RIGHT] = 48 + 1; t[SDLK_RIGHT] = 48 + 1;
t[SDLK_DOWN] = 48 + 2; t[SDLK_DOWN] = 48 + 2;
@ -973,4 +973,4 @@ EX void handleInput(int delta) {
EX } EX }
} }

View File

@ -82,7 +82,7 @@ void gamedata::restoregame() {
gamedata_all(*this); gamedata_all(*this);
} }
EX hookset<void(gamedata*)> *hooks_gamedata; EX hookset<void(gamedata*)> hooks_gamedata;
EX namespace gamestack { EX namespace gamestack {

View File

@ -1,223 +1,232 @@
// HyperRogue: alternative build system // HyperRogue: alternative build system
// This reads the file 'hyper.cpp' and compiles the cpp files it includes into separate object files, and then links them. // This reads the file 'hyper.cpp' and compiles the cpp files it includes into separate object files, and then links them.
// Tested in Linux, should work in other systems with some changes. // Tested in Linux, should work in other systems with some changes.
// Options: // Options:
// -O2 -- optimize // -O2 -- optimize
// -O3 -- optimize // -O3 -- optimize
// -D... -- change compilation flags // -D... -- change compilation flags
// [file.cpp] -- add a module to the build (e.g. ./mymake rogueviz) // [file.cpp] -- add a module to the build (e.g. ./mymake rogueviz)
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
using namespace std; using namespace std;
string opts; string opts;
string default_standard = " -std=c++11"; string default_standard = " -std=c++11";
string standard = default_standard; string standard = default_standard;
string preprocessor; string preprocessor;
string compiler; string compiler;
string linker; string linker;
string libs; string libs;
void set_linux() { void set_linux() {
preprocessor = "g++ -E"; preprocessor = "g++ -E";
compiler = "g++ -Wall -Wextra -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-implicit-fallthrough -rdynamic -fdiagnostics-color=always -c"; compiler = "g++ -Wall -Wextra -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-implicit-fallthrough -rdynamic -fdiagnostics-color=always -c";
linker = "g++ -rdynamic -o hyper"; linker = "g++ -rdynamic -o hyper";
opts = "-DFHS -DLINUX -I/usr/include/SDL"; opts = "-DFHS -DLINUX -I/usr/include/SDL";
libs = " savepng.o -lSDL -lSDL_ttf -lSDL_mixer -lSDL_gfx -lGLEW -lGL -lpng -rdynamic -lpthread -lz"; libs = " savepng.o -lSDL -lSDL_ttf -lSDL_mixer -lSDL_gfx -lGLEW -lGL -lpng -rdynamic -lpthread -lz";
} }
void set_mac() { void set_mac() {
preprocessor = "g++ -E"; preprocessor = "g++ -E";
compiler = "g++ -march=native -W -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-implicit-fallthrough -c"; compiler = "g++ -march=native -W -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-implicit-fallthrough -c";
linker = "g++ -o hyper"; linker = "g++ -o hyper";
opts = "-DMAC -I/usr/local/include"; opts = "-DMAC -I/usr/local/include";
libs = " savepng.o -L/usr/local/lib -framework AppKit -framework OpenGL -lSDL -lSDLMain -lSDL_gfx -lSDL_mixer -lSDL_ttf -lpng -lpthread -lz"; libs = " savepng.o -L/usr/local/lib -framework AppKit -framework OpenGL -lSDL -lSDLMain -lSDL_gfx -lSDL_mixer -lSDL_ttf -lpng -lpthread -lz";
} }
void set_win() { void set_win() {
preprocessor = "g++ -E"; preprocessor = "g++ -E";
compiler = "runbat bwin-g.bat -c"; compiler = "runbat bwin-g.bat -c";
linker = "runbat bwin-linker.bat"; linker = "runbat bwin-linker.bat";
opts = "-DFHS -DLINUX -I/usr/include/SDL"; opts = "-DFHS -DLINUX -I/usr/include/SDL";
libs = ""; libs = "";
standard = ""; standard = "";
} }
vector<string> modules; vector<string> modules;
time_t get_file_time(const string s) { time_t get_file_time(const string s) {
struct stat attr; struct stat attr;
if(stat(s.c_str(), &attr)) return 0; if(stat(s.c_str(), &attr)) return 0;
return attr.st_mtime; return attr.st_mtime;
} }
int optimized = 0; int optimized = 0;
string obj_dir = "mymake_files"; string obj_dir = "mymake_files";
string setdir = "../"; string setdir = "../";
int system(string cmdline) { int system(string cmdline) {
return system(cmdline.c_str()); return system(cmdline.c_str());
} }
bool file_exists(string fname) { bool file_exists(string fname) {
return access(fname.c_str(), F_OK) != -1; return access(fname.c_str(), F_OK) != -1;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
#if defined(MAC) #if defined(MAC)
set_mac(); set_mac();
#elif defined(WINDOWS) #elif defined(WINDOWS)
set_win(); set_win();
#else #else
set_linux(); set_linux();
#endif #endif
for(string fname: {"Makefile.loc", "Makefile.simple", "Makefile"}) for(string fname: {"Makefile.loc", "Makefile.simple", "Makefile"})
if(file_exists(fname)) if(file_exists(fname))
system("make -f " + fname + " language-data.cpp autohdr.h savepng.o"); system("make -f " + fname + " language-data.cpp autohdr.h savepng.o");
for(int i=1; i<argc; i++) { for(int i=1; i<argc; i++) {
string s = argv[i]; string s = argv[i];
if(s.substr(0, 2) == "-D") { if(s.substr(0, 2) == "-D") {
opts += " " + s; opts += " " + s;
obj_dir += "/"; obj_dir += "/";
setdir += "../"; setdir += "../";
for(char c: s) for(char c: s)
if(c == '=' || c == '-' || c == '/') obj_dir += "_"; if(c == '=' || c == '-' || c == '/') obj_dir += "_";
else obj_dir += c; else obj_dir += c;
} }
else if(s == "-win") { else if(s == "-win") {
set_win(); set_win();
obj_dir += "/win"; obj_dir += "/win";
setdir += "../"; setdir += "../";
} }
else if(s == "-mac") { else if(s == "-mac") {
set_mac(); set_mac();
obj_dir += "/mac"; obj_dir += "/mac";
setdir += "../"; setdir += "../";
} }
else if(s == "-linux") { else if(s == "-linux") {
set_linux(); set_linux();
obj_dir += "/linux"; obj_dir += "/linux";
setdir += "../"; setdir += "../";
} }
else if(s == "-O2") else if(s.substr(0, 2) == "-f") {
optimized = 2, compiler += " -O2", obj_dir += "/O2", setdir += "../"; opts += " " + s;
else if(s == "-O3") obj_dir += "/";
optimized = 3, compiler += " -O3", obj_dir += "/O3", setdir += "../"; setdir += "../";
else if(s.substr(0, 4) == "-std") for(char c: s)
standard = s; if(c == '=' || c == '-' || c == '/') obj_dir += "_";
else if(s.substr(0, 2) == "-l") else obj_dir += c;
linker += " " + s; linker += " " + s;
else if(s == "-I") { }
opts += " " + s + " " + argv[i+1]; else if(s == "-O2")
i++; optimized = 2, compiler += " -O2", obj_dir += "/O2", setdir += "../";
} else if(s == "-O3")
else if(s.substr(0, 2) == "-I") optimized = 3, compiler += " -O3", obj_dir += "/O3", setdir += "../";
opts += " " + s; else if(s.substr(0, 4) == "-std")
else if(s == "-rv") { standard = s;
else if(s.substr(0, 2) == "-l")
if(standard == default_standard) { linker += " " + s;
standard = "-std=c++17"; else if(s == "-I") {
} opts += " " + s + " " + argv[i+1];
ifstream ifs("rogueviz/rogueviz-all.cpp"); i++;
string s; }
while(getline(ifs, s)) { else if(s.substr(0, 2) == "-I")
if(s.substr(0, 10) == "#include \"") { opts += " " + s;
string t = s.substr(10); else if(s == "-rv") {
t = t.substr(0, t.find(".cpp\""));
modules.push_back("rogueviz/" + t); if(standard == default_standard) {
} standard = "-std=c++17";
} }
} ifstream ifs("rogueviz/rogueviz-all.cpp");
else { string s;
if(s.size() >= 5 && s.substr(s.size() - 4) == ".cpp") while(getline(ifs, s)) {
s = s.substr(0, s.size() - 4); if(s.substr(0, 10) == "#include \"") {
modules.push_back(s); string t = s.substr(10);
} t = t.substr(0, t.find(".cpp\""));
} modules.push_back("rogueviz/" + t);
if(!optimized) }
compiler += " -g3"; }
preprocessor += " " + standard; }
compiler += " " + standard; else {
ifstream fs("hyper.cpp"); if(s.size() >= 5 && s.substr(s.size() - 4) == ".cpp")
s = s.substr(0, s.size() - 4);
system("mkdir -p " + obj_dir); modules.push_back(s);
}
ofstream fsm(obj_dir + "/hyper.cpp"); }
fsm << "#if REM\n#define INCLUDE(x)\n#endif\n"; if(!optimized)
string s; compiler += " -g3";
while(getline(fs, s)) { preprocessor += " " + standard;
if(s.substr(0, 8) == "#include") { compiler += " " + standard;
string t; ifstream fs("hyper.cpp");
bool in = false;
bool ext = false; system("mkdir -p " + obj_dir);
string iext = "";
for(char c: s) if(c == '"') in = !in; else if(!in) ; else if(c == '.') ext = !ext; else if(!ext) t += c; else iext += c; ofstream fsm(obj_dir + "/hyper.cpp");
if(iext == "h") { fsm << "#include \"" + setdir + "hyper.h\"\n"; continue; } fsm << "#if REM\n#define INCLUDE(x)\n#endif\n";
if(iext != "cpp") printf("unknown extension: %s\n", iext.c_str()); string s;
fsm << "INCLUDE(\"" << t << "\")\n"; while(getline(fs, s)) {
continue; if(s.substr(0, 8) == "#include") {
} string t;
fsm << s << "\n"; bool in = false;
} bool ext = false;
fsm.close(); string iext = "";
for(char c: s) if(c == '"') in = !in; else if(!in) ; else if(c == '.') ext = !ext; else if(!ext) t += c; else iext += c;
printf("preprocessing...\n"); if(iext == "h") { fsm << "#include \"" + setdir + "hyper.h\"\n"; continue; }
if(system(preprocessor + " " + opts + " "+obj_dir+"/hyper.cpp -o "+obj_dir+"/hyper.E")) { printf("preprocessing error\n"); exit(1); } if(iext != "cpp") printf("unknown extension: %s\n", iext.c_str());
fsm << "INCLUDE(\"" << t << "\")\n";
if(true) { continue;
ifstream fs2(obj_dir+"/hyper.E"); }
while(getline(fs2, s)) { fsm << s << "\n";
if(s.substr(0, 7) == "INCLUDE") { }
s = s.substr(9); fsm.close();
s = s.substr(0,s.size() - 2);
modules.push_back(s); printf("preprocessing...\n");
} if(system(preprocessor + " " + opts + " "+obj_dir+"/hyper.cpp -o "+obj_dir+"/hyper.E")) { printf("preprocessing error\n"); exit(1); }
}
} if(true) {
ifstream fs2(obj_dir+"/hyper.E");
if(get_file_time(obj_dir + "/hyper.o") < get_file_time("hyper.cpp")) { while(getline(fs2, s)) {
printf("compiling hyper...\n"); if(s.substr(0, 7) == "INCLUDE") {
if(system(compiler + " -DREM " + opts + " " + obj_dir + "/hyper.cpp -c -o " + obj_dir + "/hyper.o")) { printf("error\n"); exit(1); } s = s.substr(9);
} s = s.substr(0,s.size() - 2);
modules.push_back(s);
string allobj = " " + obj_dir + "/hyper.o"; }
}
int id = 0; }
for(string m: modules) {
id++; if(get_file_time(obj_dir + "/hyper.o") < get_file_time("hyper.cpp")) {
string src = m + ".cpp"; printf("compiling hyper...\n");
string m2 = m; if(system(compiler + " -DREM " + opts + " " + obj_dir + "/hyper.cpp -c -o " + obj_dir + "/hyper.o")) { printf("error\n"); exit(1); }
for(char& c: m2) if(c == '/') c = '_'; }
string obj = obj_dir + "/" + m2 + ".o";
time_t src_time = get_file_time(src); string allobj = " " + obj_dir + "/hyper.o";
if(!src_time) {
printf("file not found: %s\n", src.c_str()); int id = 0;
exit(1); for(string m: modules) {
} id++;
time_t obj_time = get_file_time(obj); string src = m + ".cpp";
if(src_time > obj_time) { string m2 = m;
printf("compiling %s... [%d/%d]\n", m.c_str(), id, int(modules.size())); for(char& c: m2) if(c == '/') c = '_';
if(system(compiler + " " + opts + " " + src + " -o " + obj)) { printf("error\n"); exit(1); } string obj = obj_dir + "/" + m2 + ".o";
} time_t src_time = get_file_time(src);
else { if(!src_time) {
printf("ok: %s\n", m.c_str()); printf("file not found: %s\n", src.c_str());
} exit(1);
allobj += " "; }
allobj += obj; time_t obj_time = get_file_time(obj);
} if(src_time > obj_time) {
printf("compiling %s... [%d/%d]\n", m.c_str(), id, int(modules.size()));
printf("linking...\n"); if(system(compiler + " " + opts + " " + src + " -o " + obj)) { printf("error\n"); exit(1); }
system(linker + allobj + libs); }
return 0; else {
} printf("ok: %s\n", m.c_str());
}
allobj += " ";
allobj += obj;
}
printf("linking...\n");
system(linker + allobj + libs);
return 0;
}

View File

@ -307,7 +307,7 @@ EX namespace netgen {
renderbuffer rbuf(2000, 2000, vid.usingGL); renderbuffer rbuf(2000, 2000, vid.usingGL);
dynamicval<videopar> dv(vid, vid); dynamicval<videopar> dv(vid, vid);
vid.xres = vid.yres = 2000; vid.scale = 0.99; vid.xres = vid.yres = 2000; pconf.scale = 0.99;
if(1) { if(1) {
resetbuffer rb; resetbuffer rb;

View File

@ -104,7 +104,7 @@ EX namespace sn {
hyperpoint res; hyperpoint res;
if(lazy) { if(lazy) {
return decompress(get_int(int(ix), int(iy), int(iz))); return decompress(get_int(int(ix+.5), int(iy+.5), int(iz+.5)));
} }
else { else {
@ -552,7 +552,7 @@ EX namespace sn {
} }
} }
EX hyperpoint get_inverse_exp_symsol(hyperpoint h, bool lazy, bool just_direction) { EX hyperpoint get_inverse_exp_symsol(hyperpoint h, flagtype flags) {
auto& s = get_tabled(); auto& s = get_tabled();
s.load(); s.load();
@ -562,18 +562,17 @@ EX namespace sn {
if(h[2] < 0.) { iz = -iz; swap(ix, iy); } if(h[2] < 0.) { iz = -iz; swap(ix, iy); }
hyperpoint res = s.get(ix, iy, iz, lazy); hyperpoint res = s.get(ix, iy, iz, flags & pfNO_INTERPOLATION);
if(h[2] < 0.) { swap(res[0], res[1]); res[2] = -res[2]; } if(h[2] < 0.) { swap(res[0], res[1]); res[2] = -res[2]; }
if(h[0] < 0.) res[0] = -res[0]; if(h[0] < 0.) res[0] = -res[0];
if(h[1] < 0.) res[1] = -res[1]; if(h[1] < 0.) res[1] = -res[1];
if(!just_direction) return table_to_azeq(res); if(flags & pfNO_DISTANCE) return res;
return table_to_azeq(res);
return res;
} }
EX hyperpoint get_inverse_exp_nsym(hyperpoint h, bool lazy, bool just_direction) { EX hyperpoint get_inverse_exp_nsym(hyperpoint h, flagtype flags) {
auto& s = get_tabled(); auto& s = get_tabled();
s.load(); s.load();
@ -581,14 +580,13 @@ EX namespace sn {
ld iy = h[1] >= 0. ? sn::x_to_ix(h[1]) : sn::x_to_ix(-h[1]); ld iy = h[1] >= 0. ? sn::x_to_ix(h[1]) : sn::x_to_ix(-h[1]);
ld iz = sn::z_to_iz(h[2]); ld iz = sn::z_to_iz(h[2]);
hyperpoint res = s.get(ix, iy, iz, lazy); hyperpoint res = s.get(ix, iy, iz, flags & pfNO_INTERPOLATION);
if(h[0] < 0.) res[0] = -res[0]; if(h[0] < 0.) res[0] = -res[0];
if(h[1] < 0.) res[1] = -res[1]; if(h[1] < 0.) res[1] = -res[1];
if(!just_direction) return table_to_azeq(res); if(flags & pfNO_DISTANCE) return res;
return table_to_azeq(res);
return res;
} }
EX string shader_symsol = sn::common + EX string shader_symsol = sn::common +
@ -609,9 +607,25 @@ EX namespace sn {
"float cz = iz*(1.-1./PRECZ) + .5/PRECZ;" "float cz = iz*(1.-1./PRECZ) + .5/PRECZ;"
// "if(ix > .5 && iy > .6 && ix < iy + .05 && iz < .2 && iz < (iy - 0.5) * 0.6)" // "if(ix > .5 && iy > .6 && ix < iy + .05 && iz < .2 && iz < (iy - 0.5) * 0.6)"
"if(ix > .65 + iz * .25 && iy > .55) res = vec4(0.,0.,0.,1.); " "\n#ifndef SOLV_ALL\n"
"else " "bool ok = true;"
// hard to tell which triangles fall on the other sides
"if(iz < .03 && ix > .65 && iy > .65) ok = false;"
"if(iz < .013 && ix > .55 && iy > .55) ok = false;"
"if(iz < .0075 && ix > .45 && iy > .45) ok = false;"
"if(iz > 0.004 && ix > 0.4 && iy > 0.4 && ix < .6 && iy < .6) ok = true;"
"if(iz > 0.000004 && ix > 0.4 && ix < 0.7 && iy > 0.4 && iy < 0.7) ok = true;"
"if(iz < 0.04 && ix > 0.70 && ix < 0.8 && iy > 0.5 && iy < 0.7) ok = false;"
"if(iz < 0.05 && ix > .45 && iy > .75 && ix < .55 && iy < .95) ok = false;"
"if(iz < 0.05 && ix > .85 && iy > .45 && iy < .75) ok = false;"
"if(iz < 0.025 && ix > .65 && iy > .65 && ix < .8 && iy < .8) ok = false;"
"if(!ok) res = vec4(0,0,0,1);"
"else "
"\n#endif\n"
"res = texture3D(tInvExpTable, vec3(cx, cy, cz));" "res = texture3D(tInvExpTable, vec3(cx, cy, cz));"
@ -745,7 +759,7 @@ EX namespace nilv {
); );
} }
EX hyperpoint get_inverse_exp(hyperpoint h, int iterations) { EX hyperpoint get_inverse_exp(hyperpoint h, flagtype prec IS(pNORMAL)) {
ld wmin, wmax; ld wmin, wmax;
ld side = h[2] - h[0] * h[1] / 2; ld side = h[2] - h[0] * h[1] / 2;
@ -769,11 +783,13 @@ EX namespace nilv {
ld s = sin(2 * alpha_total); ld s = sin(2 * alpha_total);
int max_iter = (prec & pfLOW_BS_ITER) ? 5 : 20;
for(int it=0;; it++) { for(int it=0;; it++) {
ld w = (wmin + wmax) / 2; ld w = (wmin + wmax) / 2;
ld z = b * b * (s + (sin(w) - w)/(cos(w) - 1)) + w; ld z = b * b * (s + (sin(w) - w)/(cos(w) - 1)) + w;
if(it == iterations) { if(it == max_iter) {
ld alpha = alpha_total - w/2; ld alpha = alpha_total - w/2;
ld c = b / sin(w/2); ld c = b / sin(w/2);
return point3(c * w * cos(alpha), c * w * sin(alpha), w); return point3(c * w * cos(alpha), c * w * sin(alpha), w);
@ -967,7 +983,7 @@ EX namespace nilv {
EX hyperpoint on_geodesic(hyperpoint s0, hyperpoint s1, ld x) { EX hyperpoint on_geodesic(hyperpoint s0, hyperpoint s1, ld x) {
hyperpoint local = inverse(nisot::translate(s0)) * s1; hyperpoint local = inverse(nisot::translate(s0)) * s1;
hyperpoint h = get_inverse_exp(local, 100); hyperpoint h = get_inverse_exp(local);
return nisot::translate(s0) * formula_exp(h * x); return nisot::translate(s0) * formula_exp(h * x);
} }
@ -1048,6 +1064,26 @@ EX namespace hybrid {
EX geometry_information *underlying_cgip; EX geometry_information *underlying_cgip;
EX eGeometryClass under_class() { return ginf[hybrid::underlying].cclass; } EX eGeometryClass under_class() { return ginf[hybrid::underlying].cclass; }
EX transmatrix ray_iadj(cell *c, int i) {
if(prod && i == c->type-2) return (mscale(Id, +cgi.plevel));
if(prod && i == c->type-1) return (mscale(Id, -cgi.plevel));
if(prod) {
transmatrix T;
cell *cw = hybrid::get_where(c).first;
hybrid::in_underlying_geometry([&] {
hyperpoint h0 = get_corner_position(cw, i);
hyperpoint h1 = get_corner_position(cw, (i+1));
hyperpoint hm = mid(h0, h1);
ld d = hdist0(hm);
d *= 2;
T = xpush(-d) * spintox(hm);
});
return T;
}
if(rotspace) return inverse(rots::ray_adj(c, i));
return currentmap->iadj(c, i);
}
EX void configure(eGeometry g) { EX void configure(eGeometry g) {
if(WDIM == 3) return; if(WDIM == 3) return;
@ -1243,8 +1279,10 @@ EX namespace hybrid {
EX int wall_offset(cell *c) { EX int wall_offset(cell *c) {
int id = hybrid::underlying == gArchimedean ? arcm::id_of(c->master) + 20 * arcm::parent_index_of(c->master) : shvid(c); int id = hybrid::underlying == gArchimedean ? arcm::id_of(c->master) + 20 * arcm::parent_index_of(c->master) : shvid(c);
if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, -1); if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, {-1, nullptr});
int &wo = cgi.walloffsets[id]; auto &wop = cgi.walloffsets[id];
int &wo = wop.first;
if(!wop.second) wop.second = c;
if(wo == -1) { if(wo == -1) {
cell *c1 = hybrid::get_where(c).first; cell *c1 = hybrid::get_where(c).first;
wo = isize(cgi.shWall3D); wo = isize(cgi.shWall3D);
@ -1296,6 +1334,20 @@ EX namespace hybrid {
return wo; return wo;
} }
auto clear_samples = addHook(hooks_clearmemory, 40, [] () {
for(auto& c: cgis) for(auto& v: c.second.walloffsets)
v.second = nullptr;
});
EX vector<pair<int, cell*>> gen_sample_list() {
if(!hybri) return {make_pair(0, centerover), make_pair(centerover->type, nullptr)};
vector<pair<int, cell*>> result;
for(auto& v: cgi.walloffsets) if(v.first >= 0) result.push_back(v);
sort(result.begin(), result.end());
result.emplace_back(isize(cgi.wallstart)-1, nullptr);
return result;
}
vector<cell*> to_link; vector<cell*> to_link;
EX void will_link(cell *c) { if(pmap && ((hrmap_hybrid*) pmap)->twisted) to_link.push_back(c); } EX void will_link(cell *c) { if(pmap && ((hrmap_hybrid*) pmap)->twisted) to_link.push_back(c); }
@ -1822,7 +1874,7 @@ EX namespace rots {
hybrid::in_underlying_geometry([&] { hybrid::in_underlying_geometry([&] {
hyperpoint h = tC0(T); hyperpoint h = tC0(T);
Spin = inverse(gpushxto0(h) * T); Spin = inverse(gpushxto0(h) * T);
d = hr::inverse_exp(h, iTable); d = hr::inverse_exp(h);
alpha = atan2(Spin[0][1], Spin[0][0]); alpha = atan2(Spin[0][1], Spin[0][0]);
distance = hdist0(h); distance = hdist0(h);
beta = atan2(h[1], h[0]); beta = atan2(h[1], h[0]);
@ -1831,6 +1883,34 @@ EX namespace rots {
return spin(beta) * uxpush(distance/2) * spin(-beta+alpha); return spin(beta) * uxpush(distance/2) * spin(-beta+alpha);
} }
std::unordered_map<int, transmatrix> saved_matrices_ray;
EX transmatrix ray_adj(cell *c1, int i) {
if(i == c1->type-2) return uzpush(-cgi.plevel) * spin(-2*cgi.plevel);
if(i == c1->type-1) return uzpush(+cgi.plevel) * spin(+2*cgi.plevel);
cell *c2 = c1->cmove(i);
int id1 = hybrid::underlying == gArchimedean ? arcm::id_of(c1->master) + 20 * arcm::parent_index_of(c1->master) : shvid(c1);
int id2 = hybrid::underlying == gArchimedean ? arcm::id_of(c2->master) + 20 * arcm::parent_index_of(c2->master) : shvid(c2);
int j = c1->c.spin(i);
int id = id1 + (id2 << 10) + (i << 20) + (j << 26);
auto &M = saved_matrices_ray[id];
if(M[3][3]) return M;
cell *cw = hybrid::get_where(c1).first;
transmatrix T;
hybrid::in_underlying_geometry([&] {
hyperpoint h0 = get_corner_position(cw, i);
hyperpoint h1 = get_corner_position(cw, (i+1));
hyperpoint hm = mid(h0, h1);
ld d = hdist0(hm);
d *= 2;
T = rspintox(hm) * xpush(d);
});
return M = lift_matrix(T);
}
struct hrmap_rotation_space : hybrid::hrmap_hybrid { struct hrmap_rotation_space : hybrid::hrmap_hybrid {
std::unordered_map<int, transmatrix> saved_matrices; std::unordered_map<int, transmatrix> saved_matrices;
@ -1862,6 +1942,7 @@ EX namespace rots {
/** reinterpret the given point of rotspace as a rotation matrix in the underlying geometry */ /** reinterpret the given point of rotspace as a rotation matrix in the underlying geometry */
EX transmatrix qtm(hyperpoint h) { EX transmatrix qtm(hyperpoint h) {
ld& x = h[0]; ld& x = h[0];
ld& y = h[1]; ld& y = h[1];
ld& z = h[2]; ld& z = h[2];
@ -1894,6 +1975,16 @@ EX namespace rots {
M[1][2] = -2 * (yz + xw); M[1][2] = -2 * (yz + xw);
M[2][1] = -2 * (yz - xw); M[2][1] = -2 * (yz - xw);
if(hyperbolic) {
swap(M[0][2], M[1][2]);
swap(M[2][0], M[2][1]);
M[1][2] *= -1;
M[2][0] *= -1;
M[2][2] = xx + yy + zz + ww;
return M;
}
return M; return M;
} }
@ -1930,13 +2021,18 @@ EX namespace rots {
dynamicval<bool> pf(playerfound, true); dynamicval<bool> pf(playerfound, true);
dynamicval<cell*> m5(centerover, co); dynamicval<cell*> m5(centerover, co);
dynamicval<transmatrix> m2(View, inprod ? pView : ypush(0) * qtm(h)); dynamicval<transmatrix> m2(View, inprod ? pView : ypush(0) * qtm(h));
if(PURE) View = View * pispin;
dynamicval<transmatrix> m3(playerV, Id); dynamicval<transmatrix> m3(playerV, Id);
dynamicval<transmatrix> m4(actual_view_transform, Id); dynamicval<transmatrix> m4(actual_view_transform, Id);
dynamicval<eModel> pm(pmodel, mdDisk); dynamicval<eModel> pm(pmodel, mdDisk);
dynamicval<ld> pss(vid.scale, (sphere ? 10 : 1) * underlying_scale); dynamicval<ld> pss(pconf.scale, (sphere ? 10 : 1) * underlying_scale);
dynamicval<ld> psa(vid.alpha, sphere ? 10 : 1); dynamicval<ld> psa(pconf.alpha, sphere ? 10 : 1);
dynamicval<hrmap*> p(hybrid::pmap, NULL); dynamicval<hrmap*> p(hybrid::pmap, NULL);
dynamicval<int> psr(sightrange_bonus, 0); dynamicval<int> psr(sightrange_bonus, 0);
dynamicval<int> psx(vid.use_smart_range, 2);
dynamicval<ld> psy(vid.smart_range_detail, 1);
calcparam(); calcparam();
reset_projection(); current_display->set_all(0); reset_projection(); current_display->set_all(0);
ptds.clear(); ptds.clear();
@ -1953,6 +2049,86 @@ EX namespace rots {
EX } EX }
/** stretched rotation space (S3 or SLR) */
EX namespace stretch {
EX ld factor;
EX bool applicable() {
return rotspace || among(geometry, gCell120, gECell120, gCell24, gECell24, gCell8, gECell8);
}
EX bool in() {
return factor && applicable();
}
EX transmatrix translate(hyperpoint h) {
if(!sphere) return slr::translate(h);
return matrix4(
h[3], -h[2], h[1], h[0],
h[2], h[3], -h[0], h[1],
-h[1], h[0], h[3], h[2],
-h[0], -h[1], -h[2], h[3]
);
}
EX transmatrix itranslate(hyperpoint h) {
h[0] = -h[0];
h[1] = -h[1];
h[2] = -h[2];
if(!sphere) return slr::translate(h);
return translate(h);
}
hyperpoint mulz(const hyperpoint at, const hyperpoint velocity, ld factor) {
auto vel = itranslate(at) * velocity;
vel[2] *= factor;
return translate(at) * vel;
}
EX ld squared() {
return abs(1 + factor);
}
EX ld not_squared() {
return sqrt(squared());
}
hyperpoint isometric_to_actual(const hyperpoint at, const hyperpoint velocity) {
return mulz(at, velocity, 1/not_squared());
}
hyperpoint actual_to_isometric(const hyperpoint at, const hyperpoint velocity) {
return mulz(at, velocity, not_squared());
}
hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) {
auto vel = itranslate(at) * velocity;
auto tra = itranslate(at) * transported;
hyperpoint c;
auto K = factor;
if(!sphere) K = -2 - K;
c[0] = -K * (vel[1] * tra[2] + vel[2] * tra[1]);
c[1] = K * (vel[0] * tra[2] + vel[2] * tra[0]);
c[2] = 0;
c[3] = 0;
return translate(at) * c;
}
EX ld sqnorm(hyperpoint at, hyperpoint h) {
if(sphere)
return sqhypot_d(4, h);
h = itranslate(at) * h;
return h[0] * h[0] + h[1] * h[1] + h[2] * h[2];
}
EX }
EX namespace nisot { EX namespace nisot {
EX hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) { EX hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) {
@ -1960,6 +2136,7 @@ EX namespace nisot {
#if CAP_SOLV #if CAP_SOLV
else if(sn::in()) return sn::christoffel(at, velocity, transported); else if(sn::in()) return sn::christoffel(at, velocity, transported);
#endif #endif
else if(stretch::in()) return stretch::christoffel(at, velocity, transported);
else if(sl2) return slr::christoffel(at, velocity, transported); else if(sl2) return slr::christoffel(at, velocity, transported);
else return point3(0, 0, 0); else return point3(0, 0, 0);
} }
@ -1970,37 +2147,43 @@ EX namespace nisot {
#endif #endif
return true; return true;
} }
EX void geodesic_step(hyperpoint& at, hyperpoint& velocity) { EX hyperpoint get_acceleration(const hyperpoint& at, const hyperpoint& vel) {
auto acc = christoffel(at, velocity, velocity); return christoffel(at, vel, vel);
auto at2 = at + velocity / 2;
auto velocity2 = velocity + acc / 2;
auto acc2 = christoffel(at2, velocity2, velocity2);
at = at + velocity + acc2 / 2;
velocity = velocity + acc;
} }
EX hyperpoint numerical_exp(hyperpoint v, int steps) { EX void geodesic_step(hyperpoint& at, hyperpoint& vel) {
/* RK4 method */
auto acc1 = get_acceleration(at, vel);
auto acc2 = get_acceleration(at + vel/2, vel + acc1/2);
auto acc3 = get_acceleration(at + vel/2 + acc1/4, vel + acc2/2);
auto acc4 = get_acceleration(at + vel + acc2/2, vel + acc3);
at += vel + (acc1+acc2+acc3)/6;
vel += (acc1+2*acc2+2*acc3+acc4)/6;
}
EX int rk_steps = 20;
EX hyperpoint numerical_exp(hyperpoint v) {
hyperpoint at = point31(0, 0, 0); hyperpoint at = point31(0, 0, 0);
v /= steps; v /= rk_steps;
v[3] = 0; v[3] = 0;
for(int i=0; i<steps; i++) geodesic_step(at, v); for(int i=0; i<rk_steps; i++) geodesic_step(at, v);
return at; return at;
} }
EX transmatrix parallel_transport_bare(transmatrix Pos, hyperpoint h) { EX transmatrix parallel_transport_bare(transmatrix Pos, hyperpoint h) {
bool stretch = stretch::in();
h[3] = 0; h[3] = 0;
auto tPos = transpose(Pos); auto tPos = transpose(Pos);
const ld eps = 1e-4; const ld eps = 1e-4;
if(sl2) { if(sl2 && !stretch) {
hyperpoint p = slr::to_phigans(tPos[3]); hyperpoint p = slr::to_phigans(tPos[3]);
for(int i=0; i<3; i++) for(int i=0; i<3; i++)
tPos[i] = (slr::to_phigans(tPos[3] + tPos[i] * eps) - p) / eps; tPos[i] = (slr::to_phigans(tPos[3] + tPos[i] * eps) - p) / eps;
@ -2009,16 +2192,70 @@ EX namespace nisot {
} }
else h = Pos * h; else h = Pos * h;
int steps = 100; int steps = rk_steps;
h /= steps; h /= steps;
for(int i=0; i<steps; i++) { auto& at = tPos[3];
for(int j=0; j<3; j++) auto& vel = h;
tPos[j] += christoffel(tPos[3], h, tPos[j]);
geodesic_step(tPos[3], h); array<ld, 4> ms;
if(stretch) {
for(int i=0; i<3; i++) {
ms[i] = stretch::sqnorm(at, tPos[i]);
tPos[i] = stretch::isometric_to_actual(at, tPos[i]);
}
ms[3] = stretch::sqnorm(at, vel);
if(!ms[3]) return Pos;
vel = stretch::isometric_to_actual(at, vel);
} }
if(sl2) { for(int i=0; i<steps; i++) {
auto acc1 = get_acceleration(at, vel);
auto at1 = at + vel/2; auto vel1 = vel + acc1/2;
auto acc2 = get_acceleration(at1, vel1);
auto at2 = at1 + acc1/4; auto vel2 = vel + acc2/2;
auto acc3 = get_acceleration(at2, vel2);
auto at3 = at + vel + acc2/2; auto vel3 = vel + acc3;
auto acc4 = get_acceleration(at3, vel3);
for(int j=0; j<3; j++) {
auto& tra = tPos[j];
auto tacc1 = christoffel(at, vel, tra);
auto tacc2 = christoffel(at1, vel1, tra + tacc1/2);
auto tacc3 = christoffel(at2, vel2, tra + tacc2/2);
auto tacc4 = christoffel(at3, vel3, tra + tacc3);
tra += (tacc1+tacc2*2+tacc3*2+tacc4) / 6;
}
at += vel + (acc1+acc2+acc3)/6;
vel += (acc1+2*acc2+2*acc3+acc4)/6;
if(stretch) {
at = normalize(at);
auto fix = [&] (hyperpoint& h, ld& m) {
h = stretch::itranslate(at) * h;
h[3] = 0;
ld m1 = h[0] * h[0] + h[1] * h[1] + h[2] * h[2] * stretch::squared();
h /= sqrt(m1/m);
h = stretch::translate(at) * h;
};
for(int i=0; i<3; i++) fix(tPos[i], ms[i]);
fix(vel, ms[3]);
}
}
if(stretch) {
vel = stretch::actual_to_isometric(at, vel);
for(int i=0; i<3; i++) tPos[i] = stretch::actual_to_isometric(at, tPos[i]);
}
else if(sl2) {
hyperpoint p = slr::from_phigans(tPos[3]); hyperpoint p = slr::from_phigans(tPos[3]);
for(int i=0; i<3; i++) for(int i=0; i<3; i++)
tPos[i] = (slr::from_phigans(tPos[3] + tPos[i] * eps) - p) / eps; tPos[i] = (slr::from_phigans(tPos[3] + tPos[i] * eps) - p) / eps;
@ -2029,6 +2266,7 @@ EX namespace nisot {
} }
EX void fixmatrix(transmatrix& T) { EX void fixmatrix(transmatrix& T) {
if(sphere) return hr::fixmatrix(T);
transmatrix push = eupush( tC0(T) ); transmatrix push = eupush( tC0(T) );
transmatrix push_back = inverse(push); transmatrix push_back = inverse(push);
transmatrix gtl = push_back * T; transmatrix gtl = push_back * T;
@ -2043,12 +2281,12 @@ EX namespace nisot {
return parallel_transport_bare(P, direction); return parallel_transport_bare(P, direction);
} }
EX transmatrix spin_towards(const transmatrix Position, const hyperpoint goal) { EX transmatrix spin_towards(const transmatrix Position, const hyperpoint goal, flagtype prec IS(pNORMAL)) {
hyperpoint at = tC0(Position); hyperpoint at = tC0(Position);
transmatrix push_back = inverse(translate(at)); transmatrix push_back = inverse(translate(at));
hyperpoint back_goal = push_back * goal; hyperpoint back_goal = push_back * goal;
back_goal = inverse_exp(back_goal, iTable); back_goal = inverse_exp(back_goal, prec);
transmatrix back_Position = push_back * Position; transmatrix back_Position = push_back * Position;
@ -2131,6 +2369,11 @@ EX namespace nisot {
shift_arg_formula(nilv::nilwidth); shift_arg_formula(nilv::nilwidth);
return 0; return 0;
} }
else if(argis("-rk-steps")) {
PHASEFROM(2);
shift(); rk_steps = argi();
return 0;
}
else if(argis("-nilv")) { else if(argis("-nilv")) {
PHASEFROM(2); PHASEFROM(2);
if(nil) stop_game(); if(nil) stop_game();
@ -2153,6 +2396,11 @@ EX namespace nisot {
hybrid::reconfigure(); hybrid::reconfigure();
return 0; return 0;
} }
else if(argis("-rot-stretch")) {
PHASEFROM(2);
shift_arg_formula(stretch::factor, ray::reset_raycaster);
return 0;
}
else if(argis("-prodturn")) { else if(argis("-prodturn")) {
PHASEFROM(2); PHASEFROM(2);
if(prod) stop_game(); if(prod) stop_game();

View File

@ -58,14 +58,14 @@ int eupattern4(cell *c) {
EX bool ishept(cell *c) { EX bool ishept(cell *c) {
// EUCLIDEAN // 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 if(hybri) { cell *c1 = hybrid::get_where(c).first; return c1 == c1->master->c7; }
else return c == c->master->c7; else return c == c->master->c7;
} }
EX bool ishex1(cell *c) { EX bool ishex1(cell *c) {
// EUCLIDEAN // EUCLIDEAN
if(euclid && PURE) return eupattern(c) == 1; if(euc::in() && PURE) return eupattern(c) == 1;
#if CAP_GP #if CAP_GP
else if(GOLDBERG) return c->master->c7 != c && !pseudohept(c->move(0)); else if(GOLDBERG) return c->master->c7 != c && !pseudohept(c->move(0));
#endif #endif
@ -74,14 +74,14 @@ EX bool ishex1(cell *c) {
bool ishex2(cell *c) { bool ishex2(cell *c) {
// EUCLIDEAN // EUCLIDEAN
if(euclid && PURE) return eupattern(c) == 1; if(euc::in() && PURE) return eupattern(c) == 1;
#if CAP_GP #if CAP_GP
else if(GOLDBERG) return c->master->c7 != c && gp::pseudohept_val(c) == 1; else if(GOLDBERG) return c->master->c7 != c && gp::pseudohept_val(c) == 1;
#endif #endif
else return c->master->c7 != c; else return c->master->c7 != c;
} }
int chessvalue(cell *c) { EX int chessvalue(cell *c) {
#if CAP_ARCM #if CAP_ARCM
if(arcm::in()) if(arcm::in())
return arcm::chessvalue(c); return arcm::chessvalue(c);
@ -372,6 +372,7 @@ EX pair<int, bool> fieldval(cell *c) {
} }
EX int fieldval_uniq(cell *c) { EX int fieldval_uniq(cell *c) {
if(fake::in()) return FPIU(fieldval_uniq(c));
if(experimental) return 0; if(experimental) return 0;
else if(hybri) { else if(hybri) {
auto c1 = hybrid::get_where(c).first; auto c1 = hybrid::get_where(c).first;
@ -388,7 +389,7 @@ EX int fieldval_uniq(cell *c) {
if(ctof(c)) return c->master->fieldval; 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 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); auto p = euc2_coordinates(c);
if(bounded) return p.first + (p.second << 16); if(bounded) return p.first + (p.second << 16);
return gmod(p.first - 22 * p.second, 3*127); return gmod(p.first - 22 * p.second, 3*127);
@ -1405,7 +1406,7 @@ EX bool pseudohept(cell *c) {
#if MAXMDIM == 4 #if MAXMDIM == 4
if(WDIM == 3) { if(WDIM == 3) {
if(geometry == gField435) return false; 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); else return reg3::pseudohept(c);
} }
#endif #endif
@ -1666,7 +1667,11 @@ EX namespace patterns {
} }
} }
EX hookset<int(cell*)> *hooks_generate_canvas; EX hookset<int(cell*)> hooks_generate_canvas;
EX int jhole = 0;
EX int jblock = 0;
EX int rwalls = 50;
EX int generateCanvas(cell *c) { EX int generateCanvas(cell *c) {
@ -1737,7 +1742,8 @@ EX namespace patterns {
case 'g': case 'g':
return canvasback; return canvasback;
case 'r': { case 'r': {
color_t r = hrand(0x1FFFFFF + 1); color_t r = hrand(0xFFFFFF + 1);
if(hrand(100) < rwalls) r |= 0x1000000;
if(c == cwt.at) r &= 0xFFFFFF; if(c == cwt.at) r &= 0xFFFFFF;
return r; return r;
} }
@ -1786,6 +1792,8 @@ EX namespace patterns {
return colortables['x'][zebra3(c)]; return colortables['x'][zebra3(c)];
case 'w': case 'w':
return colortables['w'][randpattern(c, subcanvas) ? 1 : 0]; return colortables['w'][randpattern(c, subcanvas) ? 1 : 0];
case 'H':
return colortables['c'][c->master->c7 == c ? 0 : 1];
case 'l': case 'l':
return random_landscape(c, 3, 1, 17, 0x808080); return random_landscape(c, 3, 1, 17, 0x808080);
case 'd': case 'd':
@ -1801,16 +1809,24 @@ EX namespace patterns {
case 'v': case 'v':
return colortables['v'][sevenval(c)]; return colortables['v'][sevenval(c)];
case 'j': { case 'j': {
if(c == currentmap->gamestart()) return canvasback;
int d = c->master->distance; int d = c->master->distance;
if(geometry == gNil) d = c->master->zebraval; if(geometry == gNil) d = c->master->zebraval;
if(d % 2 == 0 || d < -5 || d > 5) return canvasback; if(euc::in()) d = euc::get_ispacemap()[c->master][0];
return colortables['j'][(d+5)/2]; if(d % 2 == 0 || d < -5 || d > 5) return hrand(100) < jblock ? 0xFFFFFFFF : canvasback;
return hrand(100) < jhole ? canvasback : colortables['j'][(d+5)/2];
} }
case 'J': { case 'J': {
if(c == currentmap->gamestart()) return canvasback;
int d = c->master->distance; int d = c->master->distance;
if(geometry == gNil) d = c->master->zebraval; if(geometry == gNil) d = c->master->zebraval;
if(d % 2 == 0 || d < -5 || d > 5) return hrand(100) < 10 ? 0xFFFFFFFF : canvasback; if((d&3) != 2) return hrand(100) < jblock ? 0xFFFFFFFF : canvasback;
return hrand(100) < 50 ? 0 : colortables['j'][(d+5)/2]; return hrand(100) < jhole ? canvasback : colortables['j'][(d+10)/4];
}
case 'G': {
color_t r = hrand(0xFFFFFF + 1);
if(hrand(100) < rwalls && pseudohept(c) && c != cwt.at) r |= 0x1000000;
return r;
} }
case 'f': { case 'f': {
color_t res; color_t res;
@ -2876,7 +2892,7 @@ EX namespace linepatterns {
dialog::addSelItem("line width", fts(width), 'W'); dialog::addSelItem("line width", fts(width), 'W');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(width, 0, 10, 1, 1, XLAT("line width"), ""); dialog::editNumber(width, 0, 10, 0.1, 1, XLAT("line width"), "");
}); });
dialog::addBoolItem_action("edit widths individually", indiv, 'I'); dialog::addBoolItem_action("edit widths individually", indiv, 'I');
@ -2938,6 +2954,10 @@ int read_pattern_args() {
lp->color = arghex(); lp->color = arghex();
} }
else if(argis("-fat-edges")) {
PHASEFROM(2); shift(); fat_edges = argi();
}
else if(argis("-palw")) { else if(argis("-palw")) {
PHASEFROM(2); PHASEFROM(2);
shift(); string ss = args(); shift(); string ss = args();
@ -2985,6 +3005,13 @@ int read_pattern_args() {
else patterns::canvasback = arghex(); else patterns::canvasback = arghex();
stop_game_and_switch_mode(rg::nothing); stop_game_and_switch_mode(rg::nothing);
} }
else if(argis("-canvas-random")) {
PHASEFROM(2);
stop_game();
firstland = specialland = laCanvas;
patterns::whichCanvas = 'r';
shift(); patterns::rwalls = argi();
}
else if(argis("-cformula")) { else if(argis("-cformula")) {
PHASEFROM(2); PHASEFROM(2);
stop_game(); stop_game();
@ -3013,7 +3040,7 @@ int read_pattern_args() {
return 0; return 0;
} }
auto ah_pattern = addHook(hooks_args, 0, read_pattern_args) + addHook(clearmemory, 100, [] { patterns::computed_nearer_map.clear(); patterns::computed_furthest_map.clear(); }); auto ah_pattern = addHook(hooks_args, 0, read_pattern_args) + addHook(hooks_clearmemory, 100, [] { patterns::computed_nearer_map.clear(); patterns::computed_furthest_map.clear(); });
#endif #endif
} }

View File

@ -1300,8 +1300,8 @@ EX void produceGhost(cell *c, eMonster victim, eMonster who) {
EX bool swordAttack(cell *mt, eMonster who, cell *c, int bb) { EX bool swordAttack(cell *mt, eMonster who, cell *c, int bb) {
eMonster m = c->monst; eMonster m = c->monst;
if(c->wall == waCavewall) markOrb(bb ? itOrbSword2: itOrbSword); if(c->wall == waCavewall) markOrb(bb ? itOrbSword2: itOrbSword);
if(c->wall == waSmallTree || c->wall == waBigTree || c->wall == waRose || c->wall == waCTree || c->wall == waVinePlant || if(among(c->wall, waSmallTree, waBigTree, waRose, waCTree, waVinePlant, waBigBush, waSmallBush, waSolidBranch, waWeakBranch, waShrub)
thruVine(mt, c) || c->wall == waBigBush || c->wall == waSmallBush || c->wall == waSolidBranch || c->wall == waWeakBranch) { || thruVine(mt, c)) {
changes.ccell(c); changes.ccell(c);
playSound(NULL, "hit-axe"+pick123()); playSound(NULL, "hit-axe"+pick123());
markOrb(bb ? itOrbSword2: itOrbSword); markOrb(bb ? itOrbSword2: itOrbSword);

View File

@ -713,7 +713,7 @@ void geometry_information::procedural_shapes() {
bshape(shSwitchDisk, PPR::FLOOR); for(int i=0; i<=S84; i+=S3) hpcpush(ddi(i, .06) * C0); bshape(shSwitchDisk, PPR::FLOOR); for(int i=0; i<=S84; i+=S3) hpcpush(ddi(i, .06) * C0);
} }
vector<ld> equal_weights(20, 1); vector<ld> equal_weights(1000, 1);
#if !(CAP_BT && MAXMDIM >= 4) #if !(CAP_BT && MAXMDIM >= 4)
void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector<ld> weights) { } void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector<ld> weights) { }
@ -756,7 +756,7 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
ld yy = log(2) / 2; ld yy = log(2) / 2;
bshape(shWall3D[id], PPR::WALL); bshape(shWall3D[id], PPR::WALL);
last->flags |= POLY_TRIANGLES; last->flags |= POLY_TRIANGLES | POLY_PRINTABLE;
hyperpoint center = Hypc; hyperpoint center = Hypc;
int n = isize(vertices); int n = isize(vertices);
@ -811,7 +811,7 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
h = zshift(normalize_flat(h), center_altitude * (1-x-y) + altitudes[a] * x + altitudes[b] * y); h = zshift(normalize_flat(h), center_altitude * (1-x-y) + altitudes[a] * x + altitudes[b] * y);
hpcpush(h); return; hpcpush(h); return;
} }
if(sn::in() || !bt::in()) { hpcpush(normalize(h)); return; } if(sn::in() || !bt::in()) { hpcpush(ultra_normalize(h)); return; }
hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]); hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]);
hpcpush(res); hpcpush(res);
}); });
@ -829,7 +829,7 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
} }
if(nil) if(nil)
h = nilv::on_geodesic(vertices[a], vertices[(a+1)%n], y * 1. / STEP); h = nilv::on_geodesic(vertices[a], vertices[(a+1)%n], y * 1. / STEP);
if(sn::in() || !bt::in()) { hpcpush(normalize(h)); continue; } if(sn::in() || !bt::in()) { hpcpush(ultra_normalize(h)); continue; }
hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]); hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]);
hpcpush(res); hpcpush(res);
} }
@ -842,8 +842,13 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
hpcpush(mid(C0, hpc[a])); hpcpush(mid(C0, hpc[a]));
if(shWall3D[id].flags & POLY_TRIANGLES) if(shWall3D[id].flags & POLY_TRIANGLES)
last->flags |= POLY_TRIANGLES; last->flags |= POLY_TRIANGLES;
if(shWall3D[id].flags & POLY_PRINTABLE)
last->flags |= POLY_PRINTABLE;
finishshape(); finishshape();
shWall3D[id].intester = C0;
shMiniWall3D[id].intester = C0;
shPlainWall3D[id] = shWall3D[id]; // force_triangles ? shWall3D[id] : shWireframe3D[id]; shPlainWall3D[id] = shWall3D[id]; // force_triangles ? shWall3D[id] : shWireframe3D[id];
} }
@ -967,66 +972,14 @@ void geometry_information::create_wall3d() {
walloffsets.clear(); walloffsets.clear();
} }
if(GDIM == 3 && euclid && S7 == 6) { if(euc::in() || reg3::in()) {
for(int w=0; w<6; w++) {
vector<hyperpoint> vertices;
for(int a=0; a<4; a++) {
int t[3];
t[0] = (w>=3) ? -1 : 1;
t[1] = among(a, 0, 3) ? -1 : 1;
t[2] = among(a, 2, 3) ? -1 : 1;
int x = w%3;
int y = (x+2)%3;
int z = (y+2)%3;
vertices.push_back(hpxy3(t[x]/2., t[y]/2., t[z]/2.));
}
make_wall(w, vertices);
}
}
if(GDIM == 3 && euclid && S7 == 12) {
auto v = euc::get_shifttable();
for(int w=0; w<12; w++) {
auto co = v[w];
vector<int> valid;
for(int c=0; c<3; c++) if(co[c]) valid.push_back(c);
int third = 3 - valid[1] - valid[0];
hyperpoint v0 = cpush0(valid[0], co[valid[0]] > 0 ? 1 : -1);
hyperpoint v1 = cpush0(valid[1], co[valid[1]] > 0 ? 1 : -1);
make_wall(w, {v0, v0/2 + v1/2 + cpush0(third, .5) - C0, v1, v0/2 + v1/2 + cpush0(third, -.5) - C0});
}
}
if(GDIM == 3 && euclid && S7 == 14) {
auto v = euc::get_shifttable();
for(int w=0; w<14; w++) {
bshape(shWall3D[w], PPR::WALL);
if(w%7 < 3) {
int z = w>=7?-1:1;
make_wall(w, {
cpush0(w%7, z) + cpush0((w%7+1)%3, 1/2.) - C0,
cpush0(w%7, z) + cpush0((w%7+2)%3, 1/2.) - C0,
cpush0(w%7, z) + cpush0((w%7+1)%3,-1/2.) - C0,
cpush0(w%7, z) + cpush0((w%7+2)%3,-1/2.) - C0
});
}
else {
auto t = v[w];
ld x = t[0], y = t[1], z = t[2];
make_wall(w, {
hpxy3(x, y/2, 0), hpxy3(x/2, y, 0), hpxy3(0, y, z/2),
hpxy3(0, y/2, z), hpxy3(x/2, 0, z), hpxy3(x, 0, z/2)
});
}
}
}
if(reg3::in()) {
int facesize = isize(cgi.cellshape) / S7; int facesize = isize(cgi.cellshape) / S7;
int next = 0;
for(int w=0; w<S7; w++) { for(int w=0; w<S7; w++) {
vector<hyperpoint> vertices; vector<hyperpoint> vertices;
if(S7 == 14) facesize = (w%7 < 3 ? 4 : 6);
for(int a=0; a<facesize; a++) for(int a=0; a<facesize; a++)
vertices.push_back(cgi.cellshape[w*facesize+a]); vertices.push_back(cgi.cellshape[next++]);
make_wall(w, vertices); make_wall(w, vertices);
} }
} }
@ -1189,6 +1142,8 @@ void geometry_information::prepare_shapes() {
if(GDIM == 3 && !floor_textures) make_floor_textures(); if(GDIM == 3 && !floor_textures) make_floor_textures();
#endif #endif
if(fake::in()) { FPIU( cgi.require_shapes() ); }
symmetriesAt.clear(); symmetriesAt.clear();
allshapes.clear(); allshapes.clear();
#if CAP_GP #if CAP_GP

View File

@ -122,7 +122,7 @@ EX hint hints[] = {
[]() { return true; }, []() { return true; },
[]() { []() {
dialog::addInfo(XLAT( dialog::addInfo(XLAT(
#if ISMOBILE==1 #if ISMOBILE
"The 'world overview' shows all the lands in HyperRogue." "The 'world overview' shows all the lands in HyperRogue."
#else #else
"Press 'o' to see all the lands in HyperRogue." "Press 'o' to see all the lands in HyperRogue."
@ -274,13 +274,13 @@ EX hint hints[] = {
specialland = laHalloween; specialland = laHalloween;
set_geometry(gSphere); set_geometry(gSphere);
start_game(); start_game();
vid.alpha = 999; pconf.alpha = 999;
vid.scale = 998; pconf.scale = 998;
} }
else { else {
resetModes(); resetModes();
vid.alpha = 1; pconf.alpha = 1;
vid.scale = 1; pconf.scale = 1;
} }
} }
}, },
@ -415,7 +415,7 @@ EX void showMission() {
if(canmove) { if(canmove) {
if(sphere) { if(sphere) {
dialog::addItem(XLAT("return to your game"), '1'); dialog::addItem(XLAT("return to your game"), '1');
dialog::addItem(XLAT(vid.alpha < 2 ? "orthogonal projection" : "stereographic projection"), '3'); dialog::addItem(XLAT(pconf.alpha < 2 ? "orthogonal projection" : "stereographic projection"), '3');
} }
else if(euclid) { else if(euclid) {
dialog::addItem(XLAT("return to your game"), '2'); dialog::addItem(XLAT("return to your game"), '2');
@ -457,12 +457,12 @@ EX void showMission() {
dialog::addItem(XLAT("inventory"), 'i'); dialog::addItem(XLAT("inventory"), 'i');
if(racing::on) if(racing::on)
dialog::addItem(XLAT("racing menu"), 'o'); dialog::addItem(XLAT("racing menu"), 'o');
#if ISMOBILE==0 #if !ISMOBILE
dialog::addItem(XLAT(quitsaves() ? "save" : "quit"), SDLK_F10); dialog::addItem(XLAT(quitsaves() ? "save" : "quit"), SDLK_F10);
#endif #endif
#if CAP_ANDROIDSHARE #if CAP_ANDROIDSHARE
dialog::addItem(XLAT("SHARE"), 's'-96); dialog::addItem(XLAT("SHARE"), 's'-96);
#endif #endif
} }
dialog::addItem(XLAT("message log"), 'l'); dialog::addItem(XLAT("message log"), 'l');

View File

@ -835,7 +835,7 @@ heptspin sview;
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
auto hook = auto hook =
addHook(hooks_args, 100, readArgs) addHook(hooks_args, 100, readArgs)
+ addHook(clearmemory, 0, []() { + addHook(hooks_clearmemory, 0, []() {
track_ready = false; track_ready = false;
track.clear(); track.clear();
rti.clear(); rti.clear();
@ -939,15 +939,15 @@ void race_projection() {
dialog::init(XLAT("racing projections")); dialog::init(XLAT("racing projections"));
dialog::addBoolItem(XLAT("Poincaré disk model"), pmodel == mdDisk && !vid.camera_angle, '1'); dialog::addBoolItem(XLAT("Poincaré disk model"), pmodel == mdDisk && !pconf.camera_angle, '1');
dialog::add_action([] () { dialog::add_action([] () {
pmodel = mdDisk; pmodel = mdDisk;
race_advance = 0; race_advance = 0;
vid.yshift = 0; vid.yshift = 0;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = 0; pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
vid.use_smart_range = 0; vid.use_smart_range = 0;
vid.smart_range_detail = 3; vid.smart_range_detail = 3;
}); });
@ -955,13 +955,13 @@ void race_projection() {
dialog::addBoolItem(XLAT("band"), pmodel == mdBand, '2'); dialog::addBoolItem(XLAT("band"), pmodel == mdBand, '2');
dialog::add_action([] () { dialog::add_action([] () {
pmodel = mdBand; pmodel = mdBand;
models::model_orientation = race_angle; pconf.model_orientation = race_angle;
race_advance = 1; race_advance = 1;
vid.yshift = 0; vid.yshift = 0;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = 0; pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
vid.use_smart_range = 1; vid.use_smart_range = 1;
vid.smart_range_detail = 3; vid.smart_range_detail = 3;
}); });
@ -969,26 +969,26 @@ void race_projection() {
dialog::addBoolItem(XLAT("half-plane"), pmodel == mdHalfplane, '3'); dialog::addBoolItem(XLAT("half-plane"), pmodel == mdHalfplane, '3');
dialog::add_action([] () { dialog::add_action([] () {
pmodel = mdHalfplane; pmodel = mdHalfplane;
models::model_orientation = race_angle + 90; pconf.model_orientation = race_angle + 90;
race_advance = 0.5; race_advance = 0.5;
vid.yshift = 0; vid.yshift = 0;
vid.camera_angle = 0; pconf.camera_angle = 0;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = 0; pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
vid.use_smart_range = 1; vid.use_smart_range = 1;
vid.smart_range_detail = 3; vid.smart_range_detail = 3;
}); });
dialog::addBoolItem(XLAT("third-person perspective"), pmodel == mdDisk && vid.camera_angle, '4'); dialog::addBoolItem(XLAT("third-person perspective"), pmodel == mdDisk && pconf.camera_angle, '4');
dialog::add_action([] () { dialog::add_action([] () {
pmodel = mdDisk; pmodel = mdDisk;
race_advance = 0; race_advance = 0;
vid.yshift = -0.3; vid.yshift = -0.3;
vid.camera_angle = -45; pconf.camera_angle = -45;
vid.scale = 18/16. * vid.xres / vid.yres / multi::players; pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
vid.xposition = 0; pconf.xposition = 0;
vid.yposition = -0.9; pconf.yposition = -0.9;
vid.use_smart_range = 1; vid.use_smart_range = 1;
vid.smart_range_detail = 3; vid.smart_range_detail = 3;
}); });
@ -1011,8 +1011,8 @@ void race_projection() {
dialog::addSelItem(XLAT("race angle"), fts(race_angle), 'a'); dialog::addSelItem(XLAT("race angle"), fts(race_angle), 'a');
dialog::add_action([] () { dialog::add_action([] () {
dialog::editNumber(race_angle, 0, 360, 15, 90, XLAT("race angle"), ""); dialog::editNumber(race_angle, 0, 360, 15, 90, XLAT("race angle"), "");
int q = models::model_orientation - race_angle; int q = pconf.model_orientation - race_angle;
dialog::reaction = [q] () { models::model_orientation = race_angle + q; }; dialog::reaction = [q] () { pconf.model_orientation = race_angle + q; };
}); });
} }
@ -1060,6 +1060,7 @@ void race_projection() {
bool alternate = false; bool alternate = false;
#if MAXMDIM >= 4
EX void thurston_racing() { EX void thurston_racing() {
gamescreen(1); gamescreen(1);
dialog::init(XLAT("racing in Thurston geometries")); dialog::init(XLAT("racing in Thurston geometries"));
@ -1085,16 +1086,20 @@ void race_projection() {
add_thurston_race(XLAT("Euclidean"), [] { stop_game(); euc::clear_torus3(); set_geometry(gBitrunc3); }); add_thurston_race(XLAT("Euclidean"), [] { stop_game(); euc::clear_torus3(); set_geometry(gBitrunc3); });
add_thurston_race(XLAT("hyperbolic"), [] { set_geometry(gBinary3); vid.texture_step = 4; }); add_thurston_race(XLAT("hyperbolic"), [] { set_geometry(gBinary3); vid.texture_step = 4; });
add_thurston_race(XLAT("spherical"), [] { set_geometry(gCell120); }); add_thurston_race(XLAT("spherical"), [] { set_geometry(gCell120); });
#if CAP_SOLV
add_thurston_race(XLAT("Solv geometry"), [] { sn::solrange_xy = 10; sn::solrange_z = 3; set_geometry(gSol); }); add_thurston_race(XLAT("Solv geometry"), [] { sn::solrange_xy = 10; sn::solrange_z = 3; set_geometry(gSol); });
#endif
add_thurston_race(XLAT("S2xE"), [] { set_geometry(gSphere); set_variation(eVariation::bitruncated); set_geometry(gProduct); }); add_thurston_race(XLAT("S2xE"), [] { set_geometry(gSphere); set_variation(eVariation::bitruncated); set_geometry(gProduct); });
add_thurston_race(XLAT("H2xE"), [] { set_geometry(gNormal); set_variation(eVariation::bitruncated); set_geometry(gProduct); }); add_thurston_race(XLAT("H2xE"), [] { set_geometry(gNormal); set_variation(eVariation::bitruncated); set_geometry(gProduct); });
add_thurston_race(XLAT("Nil"), [] { stop_game(); nilv::nilperiod[0] = 0; set_geometry(gNil); }); add_thurston_race(XLAT("Nil"), [] { stop_game(); nilv::nilperiod[0] = 0; set_geometry(gNil); });
add_thurston_race(XLAT("PSL(2,R)"), [] { set_geometry(gNormal); set_variation(eVariation::pure); set_geometry(gRotSpace); }); add_thurston_race(XLAT("PSL(2,R)"), [] { set_geometry(gNormal); set_variation(eVariation::pure); set_geometry(gRotSpace); });
} }
else { else {
#if CAP_SOLV
add_thurston_race(XLAT("stretched hyperbolic"), [] { set_geometry(gNIH); vid.texture_step = 4; }); add_thurston_race(XLAT("stretched hyperbolic"), [] { set_geometry(gNIH); vid.texture_step = 4; });
add_thurston_race(XLAT("stretched Solv"), [] { set_geometry(gSolN); sn::solrange_xy = 10; sn::solrange_z = 3; vid.texture_step = 4; }); add_thurston_race(XLAT("stretched Solv"), [] { set_geometry(gSolN); sn::solrange_xy = 10; sn::solrange_z = 3; vid.texture_step = 4; });
add_thurston_race(XLAT("periodic Solv"), [] { stop_game(); sn::solrange_xy = 5; sn::solrange_z = 2; asonov::period_xy = 8; asonov::period_z = 0; asonov::set_flags(); set_geometry(gArnoldCat); }); add_thurston_race(XLAT("periodic Solv"), [] { stop_game(); sn::solrange_xy = 5; sn::solrange_z = 2; asonov::period_xy = 8; asonov::period_z = 0; asonov::set_flags(); set_geometry(gArnoldCat); });
#endif
add_thurston_race(XLAT("hyperbolic crystal"), [] { set_geometry(gCrystal344); vid.texture_step = 4; }); add_thurston_race(XLAT("hyperbolic crystal"), [] { set_geometry(gCrystal344); vid.texture_step = 4; });
add_thurston_race(XLAT("torus x E"), [] { stop_game(); euc::eu_input = euc::torus3(4, 4, 0); set_geometry(gCubeTiling); }); add_thurston_race(XLAT("torus x E"), [] { stop_game(); euc::eu_input = euc::torus3(4, 4, 0); set_geometry(gCubeTiling); });
add_thurston_race(XLAT("hyperbolic regular"), [] { set_geometry(gSpace534); }); add_thurston_race(XLAT("hyperbolic regular"), [] { set_geometry(gSpace534); });
@ -1108,6 +1113,7 @@ void race_projection() {
dialog::addBack(); dialog::addBack();
dialog::display(); dialog::display();
} }
#endif
void raceconfigurer() { void raceconfigurer() {
@ -1195,8 +1201,10 @@ void race_projection() {
}); });
} }
#if MAXMDIM >= 4
dialog::addItem(XLAT("racing in Thurston geometries"), 'T'); dialog::addItem(XLAT("racing in Thurston geometries"), 'T');
dialog::add_action_push(thurston_racing); dialog::add_action_push(thurston_racing);
#endif
dialog::addBack(); dialog::addBack();
dialog::display(); dialog::display();
@ -1373,8 +1381,7 @@ EX void drawStats() {
if(!racing::on) return; if(!racing::on) return;
dynamicval<eModel> pm(pmodel, flat_model()); flat_model_enabler fme;
glClear(GL_DEPTH_BUFFER_BIT);
initquickqueue(); initquickqueue();
int bsize = vid.fsize * 2; int bsize = vid.fsize * 2;

View File

@ -26,7 +26,7 @@ pair<bool, hyperpoint> makeradar(hyperpoint h) {
ld d = hdist0(h); ld d = hdist0(h);
if(sol && nisot::geodesic_movement) { if(sol && nisot::geodesic_movement) {
h = inverse_exp(h, iLazy); h = inverse_exp(h, pQUICK);
ld r = hypot_d(3, h); ld r = hypot_d(3, h);
if(r < 1) h = h * (atanh(r) / r); if(r < 1) h = h * (atanh(r) / r);
else return {false, h}; else return {false, h};

View File

@ -25,7 +25,7 @@ EX int want_use = 1;
EX ld exp_start = 1, exp_decay_exp = 4, exp_decay_poly = 10; EX ld exp_start = 1, exp_decay_exp = 4, exp_decay_poly = 10;
EX ld maxstep_sol = .02; EX ld maxstep_sol = .05;
EX ld maxstep_nil = .1; EX ld maxstep_nil = .1;
EX ld minstep = .001; EX ld minstep = .001;
@ -41,16 +41,17 @@ EX int max_cells = 2048;
EX bool rays_generate = true; EX bool rays_generate = true;
EX ld& exp_decay_current() { EX ld& exp_decay_current() {
return (sn::in() || hyperbolic) ? exp_decay_exp : exp_decay_poly; if(fake::in()) return *FPIU(&exp_decay_current());
return (sn::in() || hyperbolic || sl2) ? exp_decay_exp : exp_decay_poly;
} }
EX int& max_iter_current() { EX int& max_iter_current() {
if(nonisotropic) return max_iter_sol; if(nonisotropic || stretch::in()) return max_iter_sol;
else return max_iter_iso; else return max_iter_iso;
} }
ld& maxstep_current() { ld& maxstep_current() {
if(sn::in()) return maxstep_sol; if(sn::in() || stretch::in()) return maxstep_sol;
else return maxstep_nil; else return maxstep_nil;
} }
@ -65,13 +66,17 @@ EX bool available() {
if(WDIM == 2) return false; if(WDIM == 2) return false;
if(hyperbolic && pmodel == mdPerspective && !kite::in()) if(hyperbolic && pmodel == mdPerspective && !kite::in())
return true; return true;
if(sphere && pmodel == mdPerspective && !rotspace)
return true;
if(nil && S7 == 8) if(nil && S7 == 8)
return false; return false;
if((sn::in() || nil) && pmodel == mdGeodesic) if((sn::in() || nil || sl2) && pmodel == mdGeodesic)
return true; return true;
if(euclid && pmodel == mdPerspective && !bt::in()) if(euclid && pmodel == mdPerspective && !bt::in())
return true; return true;
if(prod && PURE) if(prod && (PURE || BITRUNCATED))
return true;
if(pmodel == mdPerspective && stretch::in())
return true; return true;
return false; return false;
} }
@ -79,13 +84,14 @@ EX bool available() {
/** do we want to use the raycaster? */ /** do we want to use the raycaster? */
EX bool requested() { EX bool requested() {
if(cgflags & qRAYONLY) return true; if(cgflags & qRAYONLY) return true;
if(stretch::in()) return true;
if(!want_use) return false; if(!want_use) return false;
#if CAP_TEXTURE #if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive) return false; if(texture::config.tstate == texture::tsActive) return false;
#endif #endif
if(!available()) return false; if(!available()) return false;
if(want_use == 2) return true; if(want_use == 2) return true;
return racing::on || quotient; return racing::on || quotient || fake::in();
} }
#if HDR #if HDR
@ -97,6 +103,7 @@ struct raycaster : glhr::GLprogram {
GLint uLinearSightRange, uExpStart, uExpDecay; GLint uLinearSightRange, uExpStart, uExpDecay;
GLint uBLevel; GLint uBLevel;
GLint uPosX, uPosY; GLint uPosX, uPosY;
GLint uWallOffset, uSides;
raycaster(string vsh, string fsh); raycaster(string vsh, string fsh);
}; };
@ -134,6 +141,9 @@ raycaster::raycaster(string vsh, string fsh) : GLprogram(vsh, fsh) {
tWallcolor = glGetUniformLocation(_program, "tWallcolor"); tWallcolor = glGetUniformLocation(_program, "tWallcolor");
tTextureMap = glGetUniformLocation(_program, "tTextureMap"); tTextureMap = glGetUniformLocation(_program, "tTextureMap");
uWallOffset = glGetUniformLocation(_program, "uWallOffset");
uSides = glGetUniformLocation(_program, "uSides");
uPosX = glGetUniformLocation(_program, "uPosX"); uPosX = glGetUniformLocation(_program, "uPosX");
uPosY = glGetUniformLocation(_program, "uPosY"); uPosY = glGetUniformLocation(_program, "uPosY");
} }
@ -169,16 +179,27 @@ string build_getter(string type, string name, int index) {
#define GET(array, index) array "[" index "]" #define GET(array, index) array "[" index "]"
#endif #endif
EX hookset<void(string&, string&)> *hooks_rayshader; EX hookset<void(string&, string&)> hooks_rayshader;
EX hookset<bool(shared_ptr<raycaster>)> *hooks_rayset; EX hookset<bool(shared_ptr<raycaster>)> hooks_rayset;
void enable_raycaster() { void enable_raycaster() {
if(geometry != last_geometry) reset_raycaster(); using glhr::to_glsl;
if(geometry != last_geometry) {
reset_raycaster();
}
deg = 0;
auto samples = hybrid::gen_sample_list();
for(int i=0; i<isize(samples)-1; i++)
deg = max(deg, samples[i+1].first - samples[i].first);
last_geometry = geometry; last_geometry = geometry;
deg = S7; if(prod) deg += 2;
if(!our_raycaster) { if(!our_raycaster) {
bool asonov = hr::asonov::in(); bool asonov = hr::asonov::in();
bool use_reflect = reflect_val && !nil && !levellines; bool use_reflect = reflect_val && !nil && !levellines;
bool bi = BITRUNCATED;
string vsh = string vsh =
"attribute mediump vec4 aPosition;\n" "attribute mediump vec4 aPosition;\n"
@ -211,7 +232,7 @@ void enable_raycaster() {
"uniform mediump vec4 uWallX["+rays+"];\n" "uniform mediump vec4 uWallX["+rays+"];\n"
"uniform mediump vec4 uWallY["+rays+"];\n" "uniform mediump vec4 uWallY["+rays+"];\n"
"uniform mediump vec4 uFogColor;\n" "uniform mediump vec4 uFogColor;\n"
"uniform mediump int uWallstart["+its(deg+1)+"];\n" "uniform mediump int uWallstart["+its(isize(cgi.wallstart))+"];\n"
"uniform mediump float uLinearSightRange, uExpStart, uExpDecay;\n"; "uniform mediump float uLinearSightRange, uExpStart, uExpDecay;\n";
#ifdef GLES_ONLY #ifdef GLES_ONLY
@ -225,15 +246,19 @@ void enable_raycaster() {
"uniform mediump float uPLevel;\n" "uniform mediump float uPLevel;\n"
"uniform mediump mat4 uLP;\n"; "uniform mediump mat4 uLP;\n";
int flat1 = 0, flat2 = S7; if(bi) fsh +=
"uniform int uWallOffset, uSides;\n";
int flat1 = 0, flat2 = deg;
if(prod || rotspace) flat2 -= 2;
if(hyperbolic && bt::in()) { if(hyperbolic && bt::in()) {
fsh += "uniform mediump float uBLevel;\n"; fsh += "uniform mediump float uBLevel;\n";
flat1 = bt::dirs_outer(); flat1 = bt::dirs_outer();
flat2 -= bt::dirs_inner(); flat2 -= bt::dirs_inner();
} }
if(IN_ODS || hyperbolic) fsh += if(hyperbolic) fsh +=
"mediump mat4 xpush(float x) { return mat4(" "mediump mat4 xpush(float x) { return mat4("
"cosh(x), 0., 0., sinh(x),\n" "cosh(x), 0., 0., sinh(x),\n"
@ -241,7 +266,16 @@ void enable_raycaster() {
"0., 0., 1., 0.,\n" "0., 0., 1., 0.,\n"
"sinh(x), 0., 0., cosh(x)" "sinh(x), 0., 0., cosh(x)"
");}\n"; ");}\n";
if(sphere) fsh +=
"mediump mat4 xpush(float x) { return mat4("
"cos(x), 0., 0., sin(x),\n"
"0., 1., 0., 0.,\n"
"0., 0., 1., 0.,\n"
"-sin(x), 0., 0., cos(x)"
");}\n";
if(IN_ODS) fsh += if(IN_ODS) fsh +=
"mediump mat4 xzspin(float x) { return mat4(" "mediump mat4 xzspin(float x) { return mat4("
@ -258,13 +292,22 @@ void enable_raycaster() {
"0., 0., 0., 1." "0., 0., 0., 1."
");}\n"; ");}\n";
if(bi) {
fsh += "int walloffset, sides;\n";
}
else {
fsh += "const int walloffset = 0;\n"
"const int sides = " + its(centerover->type) + ";\n";
}
fsh += fsh +=
"mediump vec2 map_texture(mediump vec4 pos, int which) {\n"; "mediump vec2 map_texture(mediump vec4 pos, int which) {\n";
if(nil) fsh += "if(which == 2 || which == 5) pos.z = 0.;\n"; if(nil) fsh += "if(which == 2 || which == 5) pos.z = 0.;\n";
else if(hyperbolic && bt::in()) fsh += else if(hyperbolic && bt::in()) fsh +=
"pos = vec4(-log(pos.w-pos.x), pos.y, pos.z, 1);\n" "pos = vec4(-log(pos.w-pos.x), pos.y, pos.z, 1);\n"
"pos.yz *= exp(pos.x);\n"; "pos.yz *= exp(pos.x);\n";
else if(hyperbolic) fsh += else if(hyperbolic || sphere) fsh +=
"pos /= pos.w;\n"; "pos /= pos.w;\n";
else if(prod) fsh += else if(prod) fsh +=
"pos = vec4(pos.x/pos.z, pos.y/pos.z, pos.w, 0);\n"; "pos = vec4(pos.x/pos.z, pos.y/pos.z, pos.w, 0);\n";
@ -279,6 +322,8 @@ void enable_raycaster() {
"}\n" "}\n"
"return vec2(1, 1);\n" "return vec2(1, 1);\n"
"}\n"; "}\n";
bool stepbased = nonisotropic || stretch::in();
string fmain = "void main() {\n"; string fmain = "void main() {\n";
@ -303,9 +348,13 @@ void enable_raycaster() {
" at0.xyz = at0.xyz / length(at0.xyz);\n"; " at0.xyz = at0.xyz / length(at0.xyz);\n";
if(hyperbolic) fsh += " mediump float len(mediump vec4 x) { return x[3]; }\n"; if(hyperbolic) fsh += " mediump float len(mediump vec4 x) { return x[3]; }\n";
else if(sphere && rotspace) fsh += " mediump float len(mediump vec4 x) { return 1.+x.x*x.x+x.y*x.y-x.z*x.z-x.w*x.w; }\n";
else if(sl2) fsh += " mediump float len(mediump vec4 x) { return 1.+x.x*x.x+x.y*x.y; }\n";
else if(sphere) fsh += " mediump float len(mediump vec4 x) { return 1.-x[3]; }\n";
else fsh += " mediump float len(mediump vec4 x) { return length(x.xyz); }\n"; else fsh += " mediump float len(mediump vec4 x) { return length(x.xyz); }\n";
if(nonisotropic) fmain += if(stepbased) fmain +=
" const mediump float maxstep = " + fts(maxstep_current()) + ";\n" " const mediump float maxstep = " + fts(maxstep_current()) + ";\n"
" const mediump float minstep = " + fts(minstep) + ";\n" " const mediump float minstep = " + fts(minstep) + ";\n"
" mediump float next = maxstep;\n"; " mediump float next = maxstep;\n";
@ -328,6 +377,16 @@ void enable_raycaster() {
else fmain += else fmain +=
" mediump vec4 position = vw * vec4(0., 0., 0., 1.);\n" " mediump vec4 position = vw * vec4(0., 0., 0., 1.);\n"
" mediump vec4 tangent = vw * at0;\n"; " mediump vec4 tangent = vw * at0;\n";
if(stretch::in()) {
fmain +=
"tangent = s_itranslate(position) * tangent;\n"
"tangent[2] /= " + to_glsl(stretch::not_squared()) + ";\n"
"tangent = s_translate(position) * tangent;\n";
;
}
if(bi) fmain += " walloffset = uWallOffset; sides = uSides;\n";
fmain += fmain +=
" mediump float go = 0.;\n" " mediump float go = 0.;\n"
@ -345,39 +404,39 @@ void enable_raycaster() {
if(IN_ODS) fmain += if(IN_ODS) fmain +=
" if(go == 0.) {\n" " if(go == 0.) {\n"
" mediump float best = len(position);\n" " mediump float best = len(position);\n"
" for(int i=0; i<"+its(S7)+"; i++) {\n" " for(int i=0; i<sides; i++) {\n"
" mediump float cand = len(uM[i] * position);\n" " mediump float cand = len(uM[i] * position);\n"
" if(cand < best - .001) { dist = 0.; best = cand; which = i; }\n" " if(cand < best - .001) { dist = 0.; best = cand; which = i; }\n"
" }\n" " }\n"
" }\n"; " }\n";
if(!nonisotropic) { if(!stepbased) {
fmain += fmain +=
" if(which == -1) {\n"; " if(which == -1) {\n";
fmain += "for(int i="+its(flat1)+"; i<"+its(flat2)+"; i++) {\n"; fmain += "for(int i="+its(flat1)+"; i<"+(prod ? "sides-2" : its(flat2))+"; i++) {\n";
if(in_h2xe()) fmain += if(in_h2xe()) fmain +=
" mediump float v = ((position - uM[i] * position)[2] / (uM[i] * tangent - tangent)[2]);\n" " mediump float v = ((position - uM[walloffset+i] * position)[2] / (uM[walloffset+i] * tangent - tangent)[2]);\n"
" if(v > 1. || v < -1.) continue;\n" " if(v > 1. || v < -1.) continue;\n"
" mediump float d = atanh(v);\n" " mediump float d = atanh(v);\n"
" mediump vec4 next_tangent = position * sinh(d) + tangent * cosh(d);\n" " mediump vec4 next_tangent = position * sinh(d) + tangent * cosh(d);\n"
" if(next_tangent[2] < (uM[i] * next_tangent)[2]) continue;\n" " if(next_tangent[2] < (uM[walloffset+i] * next_tangent)[2]) continue;\n"
" d /= xspeed;\n"; " d /= xspeed;\n";
else if(in_s2xe()) fmain += else if(in_s2xe()) fmain +=
" mediump float v = ((position - uM[i] * position)[2] / (uM[i] * tangent - tangent)[2]);\n" " mediump float v = ((position - uM[walloffset+i] * position)[2] / (uM[walloffset+i] * tangent - tangent)[2]);\n"
" mediump float d = atan(v);\n" " mediump float d = atan(v);\n"
" mediump vec4 next_tangent = tangent * cos(d) - position * sin(d);\n" " mediump vec4 next_tangent = tangent * cos(d) - position * sin(d);\n"
" if(next_tangent[2] > (uM[i] * next_tangent)[2]) continue;\n" " if(next_tangent[2] > (uM[walloffset+i] * next_tangent)[2]) continue;\n"
" d /= xspeed;\n"; " d /= xspeed;\n";
else if(in_e2xe()) fmain += else if(in_e2xe()) fmain +=
" mediump float deno = dot(position, tangent) - dot(uM[i]*position, uM[i]*tangent);\n" " mediump float deno = dot(position, tangent) - dot(uM[walloffset+i]*position, uM[walloffset+i]*tangent);\n"
" if(deno < 1e-6 && deno > -1e-6) continue;\n" " if(deno < 1e-6 && deno > -1e-6) continue;\n"
" mediump float d = (dot(uM[i]*position, uM[i]*position) - dot(position, position)) / 2. / deno;\n" " mediump float d = (dot(uM[walloffset+i]*position, uM[walloffset+i]*position) - dot(position, position)) / 2. / deno;\n"
" if(d < 0.) continue;\n" " if(d < 0.) continue;\n"
" mediump vec4 next_position = position + d * tangent;\n" " mediump vec4 next_position = position + d * tangent;\n"
" if(dot(next_position, tangent) < dot(uM[i]*next_position, uM[i]*tangent)) continue;\n" " if(dot(next_position, tangent) < dot(uM[walloffset+i]*next_position, uM[walloffset+i]*tangent)) continue;\n"
" d /= xspeed;\n"; " d /= xspeed;\n";
else if(hyperbolic) fmain += else if(hyperbolic) fmain +=
" mediump float v = ((position - uM[i] * position)[3] / (uM[i] * tangent - tangent)[3]);\n" " mediump float v = ((position - uM[i] * position)[3] / (uM[i] * tangent - tangent)[3]);\n"
@ -385,6 +444,11 @@ void enable_raycaster() {
" mediump float d = atanh(v);\n" " mediump float d = atanh(v);\n"
" mediump vec4 next_tangent = position * sinh(d) + tangent * cosh(d);\n" " mediump vec4 next_tangent = position * sinh(d) + tangent * cosh(d);\n"
" if(next_tangent[3] < (uM[i] * next_tangent)[3]) continue;\n"; " if(next_tangent[3] < (uM[i] * next_tangent)[3]) continue;\n";
else if(sphere) fmain +=
" mediump float v = ((position - uM[i] * position)[3] / (uM[i] * tangent - tangent)[3]);\n"
" mediump float d = atan(v);\n"
" mediump vec4 next_tangent = -position * sin(d) + tangent * cos(d);\n"
" if(next_tangent[3] > (uM[i] * next_tangent)[3]) continue;\n";
else fmain += else fmain +=
" mediump float deno = dot(position, tangent) - dot(uM[i]*position, uM[i]*tangent);\n" " mediump float deno = dot(position, tangent) - dot(uM[i]*position, uM[i]*tangent);\n"
" if(deno < 1e-6 && deno > -1e-6) continue;\n" " if(deno < 1e-6 && deno > -1e-6) continue;\n"
@ -423,8 +487,8 @@ void enable_raycaster() {
} }
if(prod) fmain += if(prod) fmain +=
"if(zspeed > 0.) { mediump float d = (uPLevel - zpos) / zspeed; if(d < dist) { dist = d; which = "+its(S7)+"+1; }}\n" "if(zspeed > 0.) { mediump float d = (uPLevel - zpos) / zspeed; if(d < dist) { dist = d; which = sides-1; }}\n"
"if(zspeed < 0.) { mediump float d = (-uPLevel - zpos) / zspeed; if(d < dist) { dist = d; which = "+its(S7)+"; }}\n"; "if(zspeed < 0.) { mediump float d = (-uPLevel - zpos) / zspeed; if(d < dist) { dist = d; which = sides-2; }}\n";
fmain += "}\n"; fmain += "}\n";
@ -434,7 +498,7 @@ void enable_raycaster() {
fmain += fmain +=
" if(which == -1 && dist == 0.) return;"; " if(which == -1 && dist == 0.) return;";
} }
// shift d units // shift d units
if(use_reflect) fmain += if(use_reflect) fmain +=
"bool reflect = false;\n"; "bool reflect = false;\n";
@ -454,12 +518,19 @@ void enable_raycaster() {
else if(in_e2xe()) fmain += else if(in_e2xe()) fmain +=
" position = position + tangent * dist * xspeed;\n" " position = position + tangent * dist * xspeed;\n"
" zpos += dist * zspeed;\n"; " zpos += dist * zspeed;\n";
else if(hyperbolic) fmain += else if(hyperbolic && !stepbased) fmain +=
" mediump float ch = cosh(dist); mediump float sh = sinh(dist);\n" " mediump float ch = cosh(dist); mediump float sh = sinh(dist);\n"
" mediump vec4 v = position * ch + tangent * sh;\n" " mediump vec4 v = position * ch + tangent * sh;\n"
" tangent = tangent * ch + position * sh;\n" " tangent = tangent * ch + position * sh;\n"
" position = v;\n"; " position = v;\n";
else if(nonisotropic) { else if(sphere && !stepbased) fmain +=
" mediump float ch = cos(dist); mediump float sh = sin(dist);\n"
" mediump vec4 v = position * ch + tangent * sh;\n"
" tangent = tangent * ch - position * sh;\n"
" position = v;\n";
else if(stepbased) {
bool use_christoffel = true;
if(sol && nih) fsh += if(sol && nih) fsh +=
"mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n" "mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n"
@ -473,12 +544,49 @@ void enable_raycaster() {
"mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n" "mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n"
" return vec4(-vel.z*tra.x - vel.x*tra.z, vel.z*tra.y + vel.y * tra.z, vel.x*tra.x * exp(2.*pos.z) - vel.y * tra.y * exp(-2.*pos.z), 0.);\n" " return vec4(-vel.z*tra.x - vel.x*tra.z, vel.z*tra.y + vel.y * tra.z, vel.x*tra.x * exp(2.*pos.z) - vel.y * tra.y * exp(-2.*pos.z), 0.);\n"
" }\n"; " }\n";
else fsh += else if(nil && false) fsh +=
"mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n" "mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n"
" mediump float x = pos.x;\n" " mediump float x = pos.x;\n"
" return vec4(x*vel.y*tra.y - 0.5*dot(vel.yz,tra.zy), -.5*x*dot(vel.yx,tra.xy) + .5 * dot(vel.zx,tra.xz), -.5*(x*x-1.)*dot(vel.yx,tra.xy)+.5*x*dot(vel.zx,tra.xz), 0.);\n" " return vec4(x*vel.y*tra.y - 0.5*dot(vel.yz,tra.zy), -.5*x*dot(vel.yx,tra.xy) + .5 * dot(vel.zx,tra.xz), -.5*(x*x-1.)*dot(vel.yx,tra.xy)+.5*x*dot(vel.zx,tra.xz), 0.);\n"
// " return vec4(0.,0.,0.,0.);\n" // " return vec4(0.,0.,0.,0.);\n"
" }\n"; " }\n";
else if(sl2) {
fsh += "mediump mat4 s_translate(vec4 h) {\n"
"return mat4(h.w,h.z,h.y,h.x,-h.z,h.w,-h.x,h.y,h.y,-h.x,h.w,-h.z,h.x,h.y,h.z,h.w);\n"
"}\n";
fsh += "mediump mat4 s_itranslate(vec4 h) {\n"
"h.xyz = -h.xyz; return s_translate(h);\n"
"}\n";
fsh += "mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n"
"vel = s_itranslate(pos) * vel;\n"
"tra = s_itranslate(pos) * tra;\n"
"return s_translate(pos) * vec4(\n"
" (vel.y*tra.z+vel.z*tra.y) * " + to_glsl(-(-stretch::factor-2)) + ", "
" (vel.x*tra.z+vel.z*tra.x) * " + to_glsl(-stretch::factor-2.) + ", "
" 0, 0);\n"
"}\n";
}
else if(stretch::in()) {
fsh += "mediump mat4 s_translate(vec4 h) {\n"
"return mat4(h.w,h.z,-h.y,-h.x,-h.z,h.w,h.x,-h.y,h.y,-h.x,h.w,-h.z,h.x,h.y,h.z,h.w);\n"
"}\n";
fsh += "mediump mat4 s_itranslate(vec4 h) {\n"
"h.xyz = -h.xyz; return s_translate(h);\n"
"}\n";
fsh += "mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\n"
"vel = s_itranslate(pos) * vel;\n"
"tra = s_itranslate(pos) * tra;\n"
"return s_translate(pos) * vec4(\n"
" (vel.y*tra.z+vel.z*tra.y) * " + to_glsl(-stretch::factor) + ", "
" (vel.x*tra.z+vel.z*tra.x) * " + to_glsl(stretch::factor) + ", "
" 0, 0);\n"
"}\n";
}
else use_christoffel = false;
if(use_christoffel) fsh += "mediump vec4 get_acc(mediump vec4 pos, mediump vec4 vel) {\n"
" return christoffel(pos, vel, vel);\n"
" }\n";
if(sn::in() && !asonov) fsh += "uniform mediump float uBinaryWidth;\n"; if(sn::in() && !asonov) fsh += "uniform mediump float uBinaryWidth;\n";
@ -501,13 +609,20 @@ void enable_raycaster() {
if(nil) fmain += "tangent = translate(position, itranslate(position, tangent));\n"; if(nil) fmain += "tangent = translate(position, itranslate(position, tangent));\n";
if(sn::in()) fmain += if(use_christoffel) fmain +=
"mediump vec4 acc = christoffel(position, tangent, tangent);\n" "mediump vec4 vel = tangent * dist;\n"
"mediump vec4 pos2 = position + tangent * dist / 2.;\n" "mediump vec4 acc1 = get_acc(position, vel);\n"
"mediump vec4 tan2 = tangent + acc * dist / 2.;\n" "mediump vec4 acc2 = get_acc(position + vel / 2., vel + acc1/2.);\n"
"mediump vec4 acc2 = christoffel(pos2, tan2, tan2);\n" "mediump vec4 acc3 = get_acc(position + vel / 2. + acc1/4., vel + acc2/2.);\n"
"mediump vec4 nposition = position + tangent * dist + acc2 / 2. * dist * dist;\n"; "mediump vec4 acc4 = get_acc(position + vel + acc2/2., vel + acc3/2.);\n"
"mediump vec4 nposition = position + vel + (acc1+acc2+acc3)/6.;\n";
if(sl2) fmain +=
"nposition = nposition / sqrt(dot(position.zw, position.zw) - dot(nposition.xy, nposition.xy));\n";
else if(stretch::in()) fmain +=
"nposition = nposition / sqrt(dot(nposition, nposition));\n";
if(nil) { if(nil) {
fmain += fmain +=
"mediump vec4 xp, xt;\n" "mediump vec4 xp, xt;\n"
@ -550,11 +665,49 @@ void enable_raycaster() {
fsh += "uniform mediump mat4 uStraighten;\n"; fsh += "uniform mediump mat4 uStraighten;\n";
fmain += "mediump vec4 sp = uStraighten * nposition;\n"; fmain += "mediump vec4 sp = uStraighten * nposition;\n";
} }
if(hyperbolic) {
fmain +=
" mediump float ch = cosh(dist); mediump float sh = sinh(dist);\n"
" mediump vec4 v = position * ch + tangent * sh;\n"
" mediump vec4 ntangent = tangent * ch + position * sh;\n"
" mediump vec4 nposition = v;\n";
}
if(sphere && !use_christoffel) {
fmain +=
" mediump float ch = cos(dist); mediump float sh = sin(dist);\n"
" mediump vec4 v = position * ch + tangent * sh;\n"
" mediump vec4 ntangent = tangent * ch - position * sh;\n"
" mediump vec4 nposition = v;\n";
}
bool reg = hyperbolic || sphere || euclid || sl2;
if(reg) {
fsh += "mediump float len_h(vec4 h) { return 1. - h[3]; }\n";
string s = rotspace ? "-2" : "";
fmain +=
" mediump float best = len(nposition);\n"
" for(int i=0; i<sides"+s+"; i++) {\n"
" mediump float cand = len(uM[walloffset+i] * nposition);\n"
" if(cand < best) { best = cand; which = i; }\n"
" }\n";
if(rotspace) fmain +=
" if(which == -1) {\n"
" best = len_h(nposition);\n"
" mediump float cand1 = len_h(uM[walloffset+sides-2]*nposition);\n"
" if(cand1 < best) { best = cand1; which = sides-2; }\n"
" mediump float cand2 = len_h(uM[walloffset+sides-1]*nposition);\n"
" if(cand2 < best) { best = cand2; which = sides-1; }\n"
" }\n";
}
fmain += fmain +=
"if(next >= minstep) {\n"; "if(next >= minstep) {\n";
if(asonov) fmain += if(reg) fmain += "if(which != -1) {\n";
else if(asonov) fmain +=
"if(abs(sp.x) > 1. || abs(sp.y) > 1. || abs(sp.z) > 1.) {\n"; "if(abs(sp.x) > 1. || abs(sp.y) > 1. || abs(sp.z) > 1.) {\n";
else if(nih) fmain += else if(nih) fmain +=
"if(abs(nposition.x) > uBinaryWidth || abs(nposition.y) > uBinaryWidth || abs(nposition.z) > .5) {\n"; "if(abs(nposition.x) > uBinaryWidth || abs(nposition.y) > uBinaryWidth || abs(nposition.z) > .5) {\n";
@ -610,7 +763,7 @@ void enable_raycaster() {
"if(nposition.z > log(2.)/2.) which = nposition.x > 0. ? 3 : 2;\n" "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"; "if(nposition.z <-log(2.)/2.) which = nposition.y > 0. ? 7 : 6;\n";
} }
else fmain += else if(nil) fmain +=
"if(nposition.x > .5) which = 3;\n" "if(nposition.x > .5) which = 3;\n"
"if(nposition.x <-.5) which = 0;\n" "if(nposition.x <-.5) which = 0;\n"
"if(nposition.y > .5) which = 4;\n" "if(nposition.y > .5) which = 4;\n"
@ -622,14 +775,24 @@ void enable_raycaster() {
"next = maxstep;\n" "next = maxstep;\n"
"}\n"; "}\n";
if(nil) fmain +=
"tangent = translatev(position, xt);\n";
fmain += fmain +=
"position = nposition;\n"; "position = nposition;\n";
if(!nil) fmain += if(use_christoffel) fmain +=
"tangent = tangent + acc * dist;\n"; "tangent = tangent + (acc1+2.*acc2+2.*acc3+acc4)/(6.*dist);\n";
else if(nil) fmain +=
"tangent = translatev(position, xt);\n";
else fmain +=
"tangent = ntangent;\n";
if(stretch::in() || sl2) {
fmain +=
"tangent = s_itranslate(position) * tangent;\n"
"tangent[3] = 0.;\n"
"float nvelsquared = dot(tangent.xy, tangent.xy) + " + to_glsl(stretch::squared()) + " * tangent.z * tangent.z;\n"
"tangent /= sqrt(nvelsquared);\n"
"tangent = s_translate(position) * tangent;\n";
}
} }
else fmain += else fmain +=
"position = position + tangent * dist;\n"; "position = position + tangent * dist;\n";
@ -673,10 +836,10 @@ void enable_raycaster() {
" if(col[3] > 0.0) {\n"; " if(col[3] > 0.0) {\n";
if(hard_limit < NO_LIMIT) if(hard_limit < NO_LIMIT)
fmain += " if(go > float(" + fts(hard_limit) + ")) { gl_FragDepth = 1.; return; }\n"; fmain += " if(go > " + to_glsl(hard_limit) + ") { gl_FragDepth = 1.; return; }\n";
if(!(levellines && disable_texture)) fmain += if(!(levellines && disable_texture)) fmain +=
" mediump vec2 inface = map_texture(position, which);\n" " mediump vec2 inface = map_texture(position, which+walloffset);\n"
" mediump vec3 tmap = texture2D(tTextureMap, u).rgb;\n" " mediump vec3 tmap = texture2D(tTextureMap, u).rgb;\n"
" if(tmap.z == 0.) col.xyz *= min(1., (1.-inface.x)/ tmap.x);\n" " if(tmap.z == 0.) col.xyz *= min(1., (1.-inface.x)/ tmap.x);\n"
" else {\n" " else {\n"
@ -693,7 +856,7 @@ void enable_raycaster() {
if(use_reflect) fmain += if(use_reflect) fmain +=
" if(col.w == 1.) {\n" " if(col.w == 1.) {\n"
" col.w = float("+fts(1-reflect_val)+");\n" " col.w = " + to_glsl(1-reflect_val)+";\n"
" reflect = true;\n" " reflect = true;\n"
" }\n"; " }\n";
@ -725,7 +888,7 @@ void enable_raycaster() {
#ifndef GLES_ONLY #ifndef GLES_ONLY
fmain += fmain +=
" gl_FragDepth = (-float("+fts(vnear+vfar)+")+w*float("+fts(2*vnear*vfar)+")/z)/float("+fts(vnear-vfar)+");\n" " gl_FragDepth = (" + to_glsl(-vnear+vfar)+"+w*" + to_glsl(2*vnear*vfar)+"/z)/" + to_glsl(vnear-vfar)+";\n"
" gl_FragDepth = (gl_FragDepth + 1.) / 2.;\n"; " gl_FragDepth = (gl_FragDepth + 1.) / 2.;\n";
#endif #endif
@ -740,7 +903,7 @@ void enable_raycaster() {
" }\n"; " }\n";
if(use_reflect) { if(use_reflect) {
if(prod) fmain += "if(reflect && which >= "+its(S7)+") { zspeed = -zspeed; continue; }\n"; if(prod) fmain += "if(reflect && which >= "+its(deg-2)+") { zspeed = -zspeed; continue; }\n";
if(hyperbolic && bt::in()) fmain += if(hyperbolic && bt::in()) fmain +=
"if(reflect && (which < "+its(flat1)+" || which >= "+its(flat2)+")) {\n" "if(reflect && (which < "+its(flat1)+" || which >= "+its(flat2)+")) {\n"
" mediump float x = -log(position.w - position.x);\n" " mediump float x = -log(position.w - position.x);\n"
@ -795,15 +958,23 @@ void enable_raycaster() {
" cid = connection.xy;\n"; " cid = connection.xy;\n";
if(prod) fmain += if(prod) fmain +=
" if(which == "+its(S7)+") { zpos += uPLevel+uPLevel; }\n" " if(which == sides-2) { zpos += uPLevel+uPLevel; }\n"
" if(which == "+its(S7)+"+1) { zpos -= uPLevel+uPLevel; }\n"; " if(which == sides-1) { zpos -= uPLevel+uPLevel; }\n";
fmain += fmain +=
" int mid = int(connection.z * 1024.);\n" " int mid = int(connection.z * 1024.);\n"
" mediump mat4 m = " GET("uM", "mid") " * " GET("uM", "which") ";\n" " mediump mat4 m = " GET("uM", "mid") " * " GET("uM", "walloffset+which") ";\n"
" position = m * position;\n" " position = m * position;\n"
" tangent = m * tangent;\n"; " tangent = m * tangent;\n";
if(bi) {
fmain +=
"walloffset = int(connection.w * 256.);\n"
"sides = int(connection.w * 4096.) - 16 * walloffset;\n";
// fmain += "if(sides != 8) { gl_FragColor = vec4(.5,float(sides)/8.,.5,1); return; }";
}
fmain += fmain +=
" }\n" " }\n"
" gl_FragColor.xyz += left * uFogColor.xyz;\n"; " gl_FragColor.xyz += left * uFogColor.xyz;\n";
@ -891,8 +1062,6 @@ EX void cast() {
glUniform1f(o->uPosY, -((cd->ycenter-cd->ytop)*2./cd->ysize - 1)); glUniform1f(o->uPosY, -((cd->ycenter-cd->ytop)*2./cd->ysize - 1));
if(!callhandlers(false, hooks_rayset, o)) { if(!callhandlers(false, hooks_rayset, o)) {
deg = S7;
if(prod) deg += 2;
length = 4096; length = 4096;
per_row = length / deg; per_row = length / deg;
@ -944,14 +1113,29 @@ EX void cast() {
GLERR("uniform mediump startid"); GLERR("uniform mediump startid");
glUniform1f(o->uIPD, vid.ipd); glUniform1f(o->uIPD, vid.ipd);
GLERR("uniform mediump IPD"); GLERR("uniform mediump IPD");
if(o->uWallOffset != -1) {
glUniform1i(o->uWallOffset, wall_offset(centerover));
glUniform1i(o->uSides, centerover->type);
}
vector<transmatrix> ms; auto sa = hybrid::gen_sample_list();
for(int j=0; j<S7; j++) ms.push_back(currentmap->iadj(cwt.at, j));
if(prod) ms.push_back(mscale(Id, +cgi.plevel)); vector<transmatrix> ms(sa.back().first, Id);
if(prod) ms.push_back(mscale(Id, -cgi.plevel));
for(auto& p: sa) {
int id = p.first;
cell *c = p.second;
if(!c) continue;
for(int j=0; j<c->type; j++)
ms[id+j] = hybrid::ray_iadj(c, j);
}
// println(hlog, ms);
if(!sol && !nil && reflect_val) { if(!sol && !nil && reflect_val) {
for(int j=0; j<S7; j++) { if(BITRUNCATED) exit(1);
for(int j=0; j<centerover->type; j++) {
transmatrix T = inverse(ms[j]); transmatrix T = inverse(ms[j]);
hyperpoint h = tC0(T); hyperpoint h = tC0(T);
ld d = hdist0(h); ld d = hdist0(h);
@ -1006,18 +1190,26 @@ EX void cast() {
} }
} }
transmatrix T = currentmap->iadj(c, i) * inverse(ms[i]); transmatrix T = currentmap->iadj(c, i) * inverse(ms[wall_offset(c) + i]);
for(int k=0; k<=isize(ms); k++) { for(int k=0; k<=isize(ms); k++) {
if(k < isize(ms) && !eqmatrix(ms[k], T)) continue; if(k < isize(ms) && !eqmatrix(ms[k], T)) continue;
if(k == isize(ms)) ms.push_back(T); if(k == isize(ms)) ms.push_back(T);
connections[u][2] = (k+.5) / 1024.; connections[u][2] = (k+.5) / 1024.;
break; break;
} }
connections[u][3] = (wall_offset(c1) / 256.) + (c1->type + .5) / 4096.;
} }
} }
if(prod) ms[S7] = ms[S7+1] = Id;
if(prod) {
for(auto p: sa) {
int id =p.first;
if(id == 0) continue;
ms[id-2] = Id;
ms[id-1] = Id;
}
}
vector<GLint> wallstart; vector<GLint> wallstart;
for(auto i: cgi.wallstart) wallstart.push_back(i); for(auto i: cgi.wallstart) wallstart.push_back(i);
glUniform1iv(o->uWallstart, isize(wallstart), &wallstart[0]); glUniform1iv(o->uWallstart, isize(wallstart), &wallstart[0]);
@ -1135,10 +1327,10 @@ EX void configure() {
}); });
} }
if(nonisotropic) { if(nonisotropic || stretch::in()) {
dialog::addSelItem(XLAT("max step"), fts(maxstep_current()), 'x'); dialog::addSelItem(XLAT("max step"), fts(maxstep_current()), 'x');
dialog::add_action([] { dialog::add_action([] {
dialog::editNumber(maxstep_current(), 1e-6, 1, 0.1, sol ? 0.03 : 0.1, XLAT("max step"), "affects the precision of solving the geodesic equation in Solv"); dialog::editNumber(maxstep_current(), 1e-6, 1, 0.1, sol ? 0.05 : 0.1, XLAT("max step"), "affects the precision of solving the geodesic equation in Solv");
dialog::scaleLog(); dialog::scaleLog();
dialog::bound_low(1e-9); dialog::bound_low(1e-9);
dialog::reaction = reset_raycaster; dialog::reaction = reset_raycaster;
@ -1196,6 +1388,12 @@ int readArgs() {
PHASEFROM(2); PHASEFROM(2);
want_use = 1; want_use = 1;
} }
else if(argis("-ray-range")) {
PHASEFROM(2);
shift_arg_formula(exp_start);
shift_arg_formula(exp_decay_current());
want_use = 1;
}
else if(argis("-ray-out")) { else if(argis("-ray-out")) {
PHASEFROM(2); shift(); color_out_of_range = arghex(); PHASEFROM(2); shift(); color_out_of_range = arghex();
} }
@ -1203,6 +1401,12 @@ int readArgs() {
PHASEFROM(2); PHASEFROM(2);
comparison_mode = true; comparison_mode = true;
} }
else if(argis("-ray-sol")) {
PHASEFROM(2);
shift(); max_iter_sol = argi();
shift_arg_formula(maxstep_sol, reset_raycaster);
reset_raycaster();
}
else if(argis("-ray-cells")) { else if(argis("-ray-cells")) {
PHASEFROM(2); shift(); PHASEFROM(2); shift();
rays_generate = true; rays_generate = true;

View File

@ -28,10 +28,16 @@ EX namespace reg3 {
#endif #endif
EX bool in() { EX bool in() {
if(fake::in()) return FPIU(in());
return GDIM == 3 && !euclid && !bt::in() && !nonisotropic && !hybri && !kite::in(); return GDIM == 3 && !euclid && !bt::in() && !nonisotropic && !hybri && !kite::in();
} }
EX void generate() { EX void generate() {
if(fake::in()) {
fake::generate();
return;
}
int& loop = cgi.loop; int& loop = cgi.loop;
int& face = cgi.face; int& face = cgi.face;
@ -122,8 +128,8 @@ EX namespace reg3 {
vertex_distance = binsearch(0, M_PI, [&] (ld d) { vertex_distance = binsearch(0, M_PI, [&] (ld d) {
// sometimes breaks in elliptic // sometimes breaks in elliptic
dynamicval<eGeometry> g(geometry, elliptic ? gCell120 : geometry); dynamicval<eGeometry> g(geometry, elliptic ? gCell120 : geometry);
hyperpoint v2 = direct_exp(dir_v2 * d, iTable); hyperpoint v2 = direct_exp(dir_v2 * d);
hyperpoint v3 = direct_exp(dir_v3 * d, iTable); hyperpoint v3 = direct_exp(dir_v3 * d);
return hdist(v2, v3) >= edge_length; return hdist(v2, v3) >= edge_length;
}); });
} }
@ -131,7 +137,7 @@ EX namespace reg3 {
DEBB(DF_GEOM, ("vertex_distance = ", vertex_distance)); DEBB(DF_GEOM, ("vertex_distance = ", vertex_distance));
/* actual vertex */ /* actual vertex */
hyperpoint v2 = direct_exp(dir_v2 * vertex_distance, iTable); hyperpoint v2 = direct_exp(dir_v2 * vertex_distance);
hyperpoint mid = Hypc; hyperpoint mid = Hypc;
for(int i=0; i<face; i++) mid += cspin(1, 2, 2*i*M_PI/face) * v2; for(int i=0; i<face; i++) mid += cspin(1, 2, 2*i*M_PI/face) * v2;
@ -186,6 +192,13 @@ EX namespace reg3 {
if(loop == 4) cgi.strafedist = adjcheck; if(loop == 4) cgi.strafedist = adjcheck;
else cgi.strafedist = hdist(cgi.adjmoves[0] * C0, cgi.adjmoves[1] * C0); else cgi.strafedist = hdist(cgi.adjmoves[0] * C0, cgi.adjmoves[1] * C0);
if(stretch::applicable()) {
transmatrix T = cspin(0, 2, 90 * degree);
transmatrix iT = inverse(T);
for(auto& v: cgi.adjmoves) v = T * v * iT;
for(auto& v: cellshape) v = T * v;
}
vertices_only.clear(); vertices_only.clear();
for(hyperpoint h: cellshape) { for(hyperpoint h: cellshape) {
@ -282,6 +295,7 @@ EX namespace reg3 {
return Id; return Id;
} }
#if CAP_CRYSTAL
int encode_coord(const crystal::coord& co) { int encode_coord(const crystal::coord& co) {
int c = 0; int c = 0;
for(int i=0; i<4; i++) c |= ((co[i]>>1) & 3) << (2*i); for(int i=0; i<4; i++) c |= ((co[i]>>1) & 3) << (2*i);
@ -314,6 +328,7 @@ EX namespace reg3 {
} }
} }
}; };
#endif
struct hrmap_field3 : reg3::hrmap_quotient3 { struct hrmap_field3 : reg3::hrmap_quotient3 {
@ -691,10 +706,15 @@ EX namespace reg3 {
dynamicval<hrmap*> cm(currentmap, binary_map); dynamicval<hrmap*> cm(currentmap, binary_map);
binary_map->virtualRebase(alt, T); binary_map->virtualRebase(alt, T);
} }
fixmatrix(T); fixmatrix(T);
auto hT = tC0(T); auto hT = tC0(T);
bool hopf = stretch::applicable();
if(hopf)
T = stretch::translate(hT);
if(DEB) println(hlog, "searching at ", alt, ":", hT); if(DEB) println(hlog, "searching at ", alt, ":", hT);
if(DEB) for(auto& p2: altmap[alt]) println(hlog, "for ", tC0(p2.second), " intval is ", intval(tC0(p2.second), hT)); if(DEB) for(auto& p2: altmap[alt]) println(hlog, "for ", tC0(p2.second), " intval is ", intval(tC0(p2.second), hT));
@ -706,7 +726,8 @@ EX namespace reg3 {
// println(hlog, "YES found in ", isize(altmap[alt])); // println(hlog, "YES found in ", isize(altmap[alt]));
if(DEB) println(hlog, "-> found ", p2.first); if(DEB) println(hlog, "-> found ", p2.first);
int fb = 0; int fb = 0;
hyperpoint old = T * (inverse(T1) * tC0(p1.second)); hyperpoint old = tC0(p1.second);;
if(!hopf) T * (inverse(T1) * old);
#if CAP_FIELD #if CAP_FIELD
if(quotient_map) { if(quotient_map) {
p2.first->c.connect(counterpart(parent)->c.spin(d), parent, d, false); p2.first->c.connect(counterpart(parent)->c.spin(d), parent, d, false);
@ -745,6 +766,19 @@ EX namespace reg3 {
fv = cp->c.move(d)->fieldval; fv = cp->c.move(d)->fieldval;
} }
#endif #endif
if(hopf) {
hyperpoint old = tC0(p1.second);
for(d2=0; d2<S7; d2++) {
hyperpoint back = T * tC0(cgi.adjmoves[d2]);
if((err = intval(back, old)) < 1e-3)
break;
}
if(d2 == S7) {
d2 = 0;
println(hlog, "Hopf connection failed");
}
println(hlog, "found d2 = ", d2);
}
heptagon *created = tailored_alloc<heptagon> (S7); heptagon *created = tailored_alloc<heptagon> (S7);
created->c7 = newCell(S7, created); created->c7 = newCell(S7, created);
if(sphere) spherecells.push_back(created->c7); if(sphere) spherecells.push_back(created->c7);
@ -1304,6 +1338,7 @@ EX int celldistance(cell *c1, cell *c2) {
EX bool pseudohept(cell *c) { EX bool pseudohept(cell *c) {
auto m = regmap(); auto m = regmap();
if(cgflags & qSINGLE) return true; if(cgflags & qSINGLE) return true;
if(fake::in()) return FPIU(reg3::pseudohept(c));
if(sphere) { if(sphere) {
hyperpoint h = tC0(m->relative_matrix(c->master, regmap()->origin, C0)); hyperpoint h = tC0(m->relative_matrix(c->master, regmap()->origin, C0));
if(S7 == 12) { if(S7 == 12) {
@ -1472,6 +1507,7 @@ int dist_alt(cell *c) {
#if MAXMDIM >= 4 #if MAXMDIM >= 4
EX cellwalker strafe(cellwalker cw, int j) { EX cellwalker strafe(cellwalker cw, int j) {
hyperpoint hfront = tC0(cgi.adjmoves[cw.spin]); hyperpoint hfront = tC0(cgi.adjmoves[cw.spin]);
cw.at->cmove(j);
transmatrix T = currentmap->adj(cw.at, j); transmatrix T = currentmap->adj(cw.at, j);
for(int i=0; i<S7; i++) if(i != cw.at->c.spin(j)) for(int i=0; i<S7; i++) if(i != cw.at->c.spin(j))
if(hdist(hfront, T * tC0(cgi.adjmoves[i])) < cgi.strafedist + .01) if(hdist(hfront, T * tC0(cgi.adjmoves[i])) < cgi.strafedist + .01)

View File

@ -395,15 +395,15 @@ void bantar_frame() {
map<int, map<int, vector<unique_ptr<drawqueueitem>>>> xptds; map<int, map<int, vector<unique_ptr<drawqueueitem>>>> xptds;
for(int i=0; i<4; i++) for(auto& p: subscr[i]) for(int i=0; i<4; i++) for(auto& p: subscr[i])
xptds[int(p->prio)][i].push_back(move(p)); xptds[int(p->prio)][i].push_back(move(p));
for(auto& sm: xptds) for(auto& sm2: sm.second) { for(auto& sm: xptds) for(auto& sm2: sm.second) {
int i = sm2.first; int i = sm2.first;
ptds.clear(); ptds.clear();
for(auto& p: sm2.second) ptds.push_back(move(p)); for(auto& p: sm2.second) ptds.push_back(move(p));
vid.scale = .5; pconf.scale = .5;
vid.xposition = (!(i&2)) ? xdst : -xdst; pconf.xposition = (!(i&2)) ? xdst : -xdst;
vid.yposition = (!(i&1)) ? ydst : -ydst; pconf.yposition = (!(i&1)) ? ydst : -ydst;
calcparam(); calcparam();
drawqueue(); drawqueue();
} }
@ -436,8 +436,8 @@ void bantar_anim() {
breakanim = true; breakanim = true;
} }
mapeditor::drawplayer = true; mapeditor::drawplayer = true;
vid.xposition = vid.yposition = 0; pconf.xposition = pconf.yposition = 0;
vid.scale = 1; pconf.scale = 1;
} }
bool bmap; bool bmap;

View File

@ -1,4 +1,3 @@
#include "../hyper.h"
#include "rogueviz.h" #include "rogueviz.h"
namespace rogueviz { namespace rogueviz {
@ -145,7 +144,7 @@ void collatz_video(const string &fname) {
if(vizid == &collatz_id) { if(vizid == &collatz_id) {
sightrange_bonus = 3; sightrange_bonus = 3;
genrange_bonus = 3; genrange_bonus = 3;
dronemode = true; vid.camera_angle = -45; rog3 = true; patterns::whichShape = '8'; dronemode = true; pconf.camera_angle = -45; rog3 = true; patterns::whichShape = '8';
vid.aurastr = 512; vid.aurastr = 512;
collatz::lookup(763, 60); collatz::lookup(763, 60);

View File

@ -103,7 +103,7 @@ namespace flocking {
for(int i=0; i<isize(cl.lst); i++) { for(int i=0; i<isize(cl.lst); i++) {
cell *c2 = cl.lst[i]; cell *c2 = cl.lst[i];
transmatrix T = calc_relative_matrix(c2, c1, C0); transmatrix T = calc_relative_matrix(c2, c1, C0);
if(hypot_d(WDIM, inverse_exp(tC0(T), iTable, false)) <= check_range) { if(hypot_d(WDIM, inverse_exp(tC0(T))) <= check_range) {
relmatrices[c1][c2] = T; relmatrices[c1][c2] = T;
forCellEx(c3, c2) cl.add(c3); forCellEx(c3, c2) cl.add(c3);
} }
@ -198,7 +198,7 @@ namespace flocking {
// at2 is like m2->at but relative to m->at // at2 is like m2->at but relative to m->at
// m2's position relative to m (tC0 means *(0,0,1)) // m2's position relative to m (tC0 means *(0,0,1))
hyperpoint ac = inverse_exp(tC0(at2), iTable, false); hyperpoint ac = inverse_exp(tC0(at2));
if(use_rot) ac = Rot * ac; if(use_rot) ac = Rot * ac;
// distance and azimuth to m2 // distance and azimuth to m2

View File

@ -281,6 +281,7 @@ auto hchook = addHook(hooks_drawcell, 100, draw_ptriangle)
[] (presmode mode) { [] (presmode mode) {
setCanvas(mode, '0'); setCanvas(mode, '0');
slidecommand = "animation";
if(mode == pmKey) { if(mode == pmKey) {
tour::slide_backup(cylanim, !cylanim); tour::slide_backup(cylanim, !cylanim);
} }

View File

@ -1,6 +1,8 @@
/* explore the Janko group J1: https://en.wikipedia.org/wiki/Janko_group_J1 */ /* explore the Janko group J1: https://en.wikipedia.org/wiki/Janko_group_J1 */
#include "../hyper.h" #include "rogueviz.h"
#if !ISWEB
namespace hr { namespace hr {
@ -27,7 +29,7 @@ struct jmatrix : array<array<int, 7>, 7> {
}; };
vector<jmatrix> jms; vector<jmatrix> jms;
unordered_map<jmatrix, int> ids; std::unordered_map<jmatrix, int> ids;
jmatrix J, Z, id; jmatrix J, Z, id;
@ -142,3 +144,4 @@ auto shot_hooks = addHook(hooks_args, 100, [] {
} }
#endif

View File

@ -1,16 +1,11 @@
#include "../hyper.h" #include "rogueviz.h"
namespace hr { namespace rogueviz {
#if CAP_CRYSTAL #if CAP_CRYSTAL
void curvepoint(const hyperpoint& H1);
dqi_poly& queuecurve(color_t linecol, color_t fillcol, PPR prio);
namespace magic { namespace magic {
bool on = false;
int back = 0x202020; int back = 0x202020;
int magiccolors[14] = { 0xFFFFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF0000, 0xFF8000, 0x800080, 0x808080, 0x00FFFF, 0x80FFFF, 0x4040C0, 0x40C040, 0xC04040, 0xC0A040 }; int magiccolors[14] = { 0xFFFFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF0000, 0xFF8000, 0x800080, 0x808080, 0x00FFFF, 0x80FFFF, 0x4040C0, 0x40C040, 0xC04040, 0xC0A040 };
@ -31,8 +26,10 @@ void build(crystal::coord co, int at) {
setdist(c, 7, NULL); setdist(c, 7, NULL);
if(twos == 0) if(twos == 0)
c->landparam = back; c->landparam = back;
else if(twos == 1) else if(twos == 1) {
c->landparam = magiccolors[index]; c->landparam = magiccolors[index];
if(WDIM == 3) c->wall = waWaxWall;
}
println(hlog, co, " twos = ", twos, " index = ", index, " set = ", format("%06X", c->landparam)); println(hlog, co, " twos = ", twos, " index = ", index, " set = ", format("%06X", c->landparam));
@ -52,7 +49,7 @@ void magic(int sides) {
start_game(); start_game();
build(crystal::c0, 0); build(crystal::c0, 0);
on = true; vizid = (void*) &magic;
} }
void curveline(hyperpoint a, hyperpoint b, int lev) { void curveline(hyperpoint a, hyperpoint b, int lev) {
@ -65,9 +62,13 @@ void curveline(hyperpoint a, hyperpoint b, int lev) {
} }
bool magic_markers(cell *c, const transmatrix& V) { bool magic_markers(cell *c, const transmatrix& V) {
if(!on) return false; if(vizid != &magic) return false;
timerghost = false; timerghost = false;
if(c->landparam == back) { if(c->landparam == back) {
if(GDIM == 2) {
auto co = crystal::get_coord(c->master);
for(int a=0; a<S7/2; a++) if(co[a] >= 6 || co[a] <= -6) c->landparam = 0;
}
if(GDIM == 2) if(GDIM == 2)
for(int i=0; i<S7; i++) { for(int i=0; i<S7; i++) {
cell *c2 = c->move(i); cell *c2 = c->move(i);
@ -132,7 +133,7 @@ void twos_to_fours(vector<int>& zeros, crystal::coord co, int d) {
} }
bool magic_rotate(cell *c) { bool magic_rotate(cell *c) {
if(!on) return false; if(vizid != &magic) return false;
if(c->landparam != back) return false; if(c->landparam != back) return false;
vector<int> zeros; vector<int> zeros;
auto co = crystal::get_coord(c->master); auto co = crystal::get_coord(c->master);
@ -140,6 +141,7 @@ bool magic_rotate(cell *c) {
for(int i=0; i<crystal::get_dim(); i++) { for(int i=0; i<crystal::get_dim(); i++) {
if(co[i] == 0) zeros.push_back(i); if(co[i] == 0) zeros.push_back(i);
else if(co[i] == 2 || co[i] == -2) ; else if(co[i] == 2 || co[i] == -2) ;
else if(co[i] == 4 || co[i] == -4) ;
else return false; else return false;
} }
println(hlog, "zeros = ", zeros); println(hlog, "zeros = ", zeros);
@ -149,20 +151,21 @@ bool magic_rotate(cell *c) {
} }
bool magic_rugkey(int sym, int uni) { bool magic_rugkey(int sym, int uni) {
if((cmode & sm::NORMAL) && uni == 'p' && on) { if(vizid != &magic) return false;
if((cmode & sm::NORMAL) && uni == 'p') {
rug::texturesize = 4096; rug::texturesize = 4096;
if(rug::rugged) rug::close(); if(rug::rugged) rug::close();
else rug::init(); else rug::init();
return true; return true;
} }
if((cmode & sm::NORMAL) && uni == 'r' && on) { if((cmode & sm::NORMAL) && uni == 'r') {
mine::performMarkCommand(mouseover); mine::performMarkCommand(mouseover);
return true; return true;
} }
if((cmode & sm::NORMAL) && uni == 'R' && on) { if((cmode & sm::NORMAL) && uni == 'R') {
build(crystal::c0, 0); build(crystal::c0, 0);
} }
if((cmode & sm::NORMAL) && uni == 'k' && on) { if((cmode & sm::NORMAL) && uni == 'k') {
crystal::view_coordinates = !crystal::view_coordinates; crystal::view_coordinates = !crystal::view_coordinates;
return true; return true;
} }

View File

@ -2,16 +2,14 @@
// example commandline: -noplayer -rugtsize 4096 -smart 1 -canvas B -ncee // example commandline: -noplayer -rugtsize 4096 -smart 1 -canvas B -ncee
// set CAP_NCONF (and change the path) if you have access to newconformist // set CAP_NCONF (and change the path) if you have access to newconformist
#include "../hyper.h" #include "rogueviz.h"
#ifndef CAP_NCONF
#define CAP_NCONF 0
#endif
#if CAP_NCONF #if CAP_NCONF
#ifndef CAP_DRAW #ifndef CAP_DRAW
#define CAP_DRAW 0 #define CAP_DRAW 0
#endif #endif
#define main nconf_main #define main nconf_main
#undef unordered_map #undef unordered_map
#undef self #undef self
@ -638,11 +636,11 @@ void draw_ncee() {
nctinf2.tvertices.clear(); nctinf2.tvertices.clear();
ld map_ypos = vid.yres * (mapping_split + 1) / 2 - cd->ycenter; ld map_ypos = vid.yres * (mapping_split + 1) / 2 - cd->ycenter;
ld sca2 = (vid.yres * (1-mapping_split) / 2 - 10) / vid.scale; ld sca2 = (vid.yres * (1-mapping_split) / 2 - 10) / pconf.scale;
if(show_mapping) { if(show_mapping) {
for(int iter=-10; iter<=10; iter++) { for(int iter=-10; iter<=10; iter++) {
ld maxx = period * vid.scale / 4; ld maxx = period * pconf.scale / 4;
ld scax = sca2 * maxx / 0.5; ld scax = sca2 * maxx / 0.5;
ld xpos = scax * 2 * iter; ld xpos = scax * 2 * iter;
curvepoint(hpxy(xpos-scax, map_ypos-sca2)); curvepoint(hpxy(xpos-scax, map_ypos-sca2));
@ -714,7 +712,7 @@ void draw_ncee() {
z = !z; z = !z;
for(int s=0; s<3; s++) { for(int s=0; s<3; s++) {
curvepoint(hc(c[s].x, c[s].y)); curvepoint(hc(c[s].x, c[s].y));
nctinf.tvertices.push_back(glhr::makevertex((vx[c[s].y][c[s].x]/cscale-delta)*vid.scale/2+.5, vy[c[s].y][c[s].x]*vid.scale/2+.5, 0)); nctinf.tvertices.push_back(glhr::makevertex((vx[c[s].y][c[s].x]/cscale-delta)*pconf.scale/2+.5, vy[c[s].y][c[s].x]*pconf.scale/2+.5, 0));
} }
}; };
@ -800,7 +798,7 @@ void prepare_ncee_map() {
dynamicval<int> cgl(vid.cells_generated_limit, 9999999); dynamicval<int> cgl(vid.cells_generated_limit, 9999999);
dynamicval<bool> r(rug::display_warning, false); dynamicval<bool> r(rug::display_warning, false);
// vid.consider_shader_projection = false; // vid.consider_shader_projection = false;
vid.scale = 0.5; pconf.scale = 0.5;
rug::init(); rug::init();
rug::prepareTexture(); rug::prepareTexture();
rug::rugged = false; rug::rugged = false;

View File

@ -34,7 +34,7 @@ array<hyperpoint, 3> mts;
rug::rugpoint *pt(hyperpoint h, hyperpoint c, int id) { rug::rugpoint *pt(hyperpoint h, hyperpoint c, int id) {
auto r = rug::addRugpoint(C0, -1); auto r = rug::addRugpoint(C0, -1);
r->flat = h; r->native = h;
r->x1 = (1 + c[0]) / 16 + (id/8) / 8.; r->x1 = (1 + c[0]) / 16 + (id/8) / 8.;
r->y1 = (1 + c[1]) / 16 + (id%8) / 8.; r->y1 = (1 + c[1]) / 16 + (id%8) / 8.;
r->valid = true; r->valid = true;
@ -190,7 +190,7 @@ void run_snub(int v, int w) {
create_model(); create_model();
printf("points = %d tris = %d side = %d\n", isize(rug::points), isize(rug::triangles), isize(sideangles)); printf("points = %d tris = %d side = %d\n", isize(rug::points), isize(rug::triangles), isize(sideangles));
rug::model_distance = euclid ? 4 : 2; rug::model_distance = euclid ? 4 : 2;
rug::rug_perspective = hyperbolic; vid.rug_config.model = hyperbolic ? mdPerspective : mdEquidistant;
showstartmenu = false; showstartmenu = false;
snubon = true; snubon = true;
rug::invert_depth = hyperbolic; rug::invert_depth = hyperbolic;
@ -346,7 +346,7 @@ bool handleKey(int sym, int uni) {
auto xhook = addHook(hooks_args, 100, readArgs) auto xhook = addHook(hooks_args, 100, readArgs)
+ addHook(hooks_handleKey, 0, handleKey) + addHook(hooks_handleKey, 0, handleKey)
+ addHook(hooks_prestats, 0, frame) + addHook(hooks_prestats, 0, frame)
+ addHook(clearmemory, 40, [] () { snubon = false; } ) + addHook(hooks_clearmemory, 40, [] () { snubon = false; } )
+ addHook(rvtour::hooks_build_rvtour, 142, [] (vector<tour::slide>& v) { + addHook(rvtour::hooks_build_rvtour, 142, [] (vector<tour::slide>& v) {
using namespace tour; using namespace tour;
v.push_back( v.push_back(

View File

@ -21,6 +21,8 @@ namespace hybrid { extern hrmap *pmap; }
namespace qtm { namespace qtm {
int mode;
color_t rcolor() { color_t rcolor() {
color_t res; color_t res;
part(res, 0) = hrand(0x80); part(res, 0) = hrand(0x80);
@ -30,6 +32,30 @@ color_t rcolor() {
swap(part(res, 2), part(res, rand() % 3)); swap(part(res, 2), part(res, rand() % 3));
return res; return res;
} }
color_t rainbow_color(hyperpoint h) {
ld sat = 1 - 1 / h[2];
ld hue = atan2(h[0], h[1]) / (2 * M_PI);
hue = frac(hue);
if(hue < 0) hue++;
hue *= 6;
color_t res;
if(hue<1) res = gradient(0xFF0000, 0xFFFF00, 0, hue, 1);
else if(hue<2) res = gradient(0x00FF00, 0xFFFF00, 2, hue, 1);
else if(hue<3) res = gradient(0x00FF00, 0x00FFFF, 2, hue, 3);
else if(hue<4) res = gradient(0x0000FF, 0x00FFFF, 4, hue, 3);
else if(hue<5) res = gradient(0x0000FF, 0xFF00FF, 4, hue, 5);
else if(hue<6) res = gradient(0xFF0000, 0xFF00FF, 6, hue, 5);
println(hlog, "sat = ", sat, " hue = ", hue);
return gradient(0xFFFFFF, res, 0, sat, 1);
}
void set_cell(cell *c) { void set_cell(cell *c) {
if(hybri) { if(hybri) {
@ -40,12 +66,22 @@ void set_cell(cell *c) {
c->landparam = c1->landparam; c->landparam = c1->landparam;
c->item = itNone; c->item = itNone;
c->monst = moNone; c->monst = moNone;
if(mode == 1) {
if(hybrid::get_where(c).second == 0)
c->landparam = 0xFFFFFF;
}
if(mode == 2) {
if(hybrid::get_where(c).second != 0)
c->wall = waNone;
}
} }
else { else {
if(c->land == laHive) return; if(c->land == laHive) return;
color_t col; color_t col;
if(hyperbolic) if(hyperbolic) {
col = rcolor(); hyperpoint h = calc_relative_matrix(c, currentmap->gamestart(), C0) * C0;
col = rainbow_color(h);
}
else if(nil) { else if(nil) {
part(col, 0) = 128 + c->master->zebraval * 50; part(col, 0) = 128 + c->master->zebraval * 50;
part(col, 1) = 128 + c->master->emeraldval * 50; part(col, 1) = 128 + c->master->emeraldval * 50;
@ -76,11 +112,58 @@ int args() {
using namespace arg; using namespace arg;
if(0) ; if(0) ;
else if(argis("-qtm-stripe")) {
mode = 1;
}
else if(argis("-qtm-no-stripe")) {
mode = 0;
}
else if(argis("-qtm-stripe-only")) {
mode = 2;
}
else if(argis("-qtm")) { else if(argis("-qtm")) {
PHASEFROM(2); PHASEFROM(2);
qtm_on = true; qtm_on = true;
} }
else if(argis("-spheredemo")) {
start_game();
auto c = currentmap->allcells();
for(cell* cx: c) cx->wall = waNone, cx->item = itNone, cx->land = laCanvas, cx->landparam = 0;
c[1]->wall = waPalace;
c[1]->land = laPalace;
int N = isize(c);
int i = 1+N/4;
int j = 1+2*N/4 + 4;
int k = 1+3*N/4;
j %= N;
c[i]->wall = waIcewall;
c[i]->land = laIce;
c[j]->wall = waBigTree;
c[j]->land = laDryForest;
c[k]->wall = waWaxWall;
c[k]->landparam = 0xFF0000;
}
else if(argis("-two-way")) {
start_game();
cwt.at->move(0)->wall = waWaxWall;
cwt.at->move(0)->landparam = 0xFF0000;
cwt.at->move(6)->wall = waWaxWall;
cwt.at->move(6)->landparam = 0xFFFF40;
}
else if(argis("-one-center")) {
start_game();
cwt.at->wall = waWaxWall;
cwt.at->landparam = 0xFFD500;
}
else return 1; else return 1;
return 0; return 0;
} }

View File

@ -585,7 +585,7 @@ bool drawVertex(const transmatrix &V, cell *c, shmup::monster *m) {
int lid = shmup::lmousetarget ? shmup::lmousetarget->pid : -2; int lid = shmup::lmousetarget ? shmup::lmousetarget->pid : -2;
if(!leftclick) for(int j=0; j<isize(vd.edges); j++) { if(!lshiftclick) for(int j=0; j<isize(vd.edges); j++) {
edgeinfo *ei = vd.edges[j].second; edgeinfo *ei = vd.edges[j].second;
vertexdata& vd1 = vdata[ei->i]; vertexdata& vd1 = vdata[ei->i];
vertexdata& vd2 = vdata[ei->j]; vertexdata& vd2 = vdata[ei->j];
@ -913,10 +913,6 @@ void close() {
relmatrices.clear(); relmatrices.clear();
} }
#ifndef CAP_RVSLIDES
#define CAP_RVSLIDES (CAP_TOUR && !ISWEB)
#endif
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
int readArgs() { int readArgs() {
using namespace arg; using namespace arg;
@ -1232,7 +1228,7 @@ auto hooks =
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
addHook(hooks_args, 100, readArgs) + addHook(hooks_args, 100, readArgs) +
#endif #endif
addHook(clearmemory, 0, close) + addHook(hooks_clearmemory, 0, close) +
addHook(hooks_prestats, 100, rogueviz_hud) + addHook(hooks_prestats, 100, rogueviz_hud) +
addHook(shmup::hooks_draw, 100, drawVertex) + addHook(shmup::hooks_draw, 100, drawVertex) +
addHook(shmup::hooks_describe, 100, describe_monster) + addHook(shmup::hooks_describe, 100, describe_monster) +
@ -1240,7 +1236,7 @@ auto hooks =
addHook(hooks_o_key, 100, o_key) + addHook(hooks_o_key, 100, o_key) +
#if CAP_RVSLIDES #if CAP_RVSLIDES
addHook(tour::ss::extra_slideshows, 100, [] (bool view) { addHook(tour::ss::hooks_extra_slideshows, 100, [] (bool view) {
if(!view) return 1; if(!view) return 1;
dialog::addBoolItem(XLAT("RogueViz Tour"), tour::ss::wts == &rvtour::rvslides[0], 'r'); dialog::addBoolItem(XLAT("RogueViz Tour"), tour::ss::wts == &rvtour::rvslides[0], 'r');
dialog::add_action([] { tour::ss::wts = rvtour::gen_rvtour(); popScreen(); }); dialog::add_action([] { tour::ss::wts = rvtour::gen_rvtour(); popScreen(); });

View File

@ -6,6 +6,14 @@
#define RVPATH HYPERPATH "rogueviz/" #define RVPATH HYPERPATH "rogueviz/"
#ifndef CAP_NCONF
#define CAP_NCONF 0
#endif
#ifndef CAP_RVSLIDES
#define CAP_RVSLIDES (CAP_TOUR && !ISWEB)
#endif
namespace rogueviz { namespace rogueviz {
using namespace hr; using namespace hr;
@ -107,11 +115,11 @@ namespace rogueviz {
extern colorpair dftcolor; extern colorpair dftcolor;
inline hookset<void(vertexdata&, cell*, shmup::monster*, int)> *hooks_drawvertex; inline hookset<void(vertexdata&, cell*, shmup::monster*, int)> hooks_drawvertex;
inline hookset<bool(edgeinfo*, bool store)> *hooks_alt_edges; inline hookset<bool(edgeinfo*, bool store)> hooks_alt_edges;
inline purehookset hooks_rvmenu; inline purehookset hooks_rvmenu;
inline hookset<bool()> *hooks_rvmenu_replace; inline hookset<bool()> hooks_rvmenu_replace;
inline hookset<bool(int&, string&, FILE*)> *hooks_readcolor; inline hookset<bool(int&, string&, FILE*)> hooks_readcolor;
inline purehookset hooks_close; inline purehookset hooks_close;
void readcolor(const string& cfname); void readcolor(const string& cfname);
@ -121,7 +129,7 @@ namespace rogueviz {
namespace rvtour { namespace rvtour {
using namespace hr::tour; using namespace hr::tour;
inline hookset<void(vector<slide>&)> *hooks_build_rvtour; inline hookset<void(vector<slide>&)> hooks_build_rvtour;
slide *gen_rvtour(); slide *gen_rvtour();
template<class T> function<void(presmode)> roguevizslide(char c, const T& t) { template<class T> function<void(presmode)> roguevizslide(char c, const T& t) {

View File

@ -150,6 +150,7 @@ void snow_slide(vector<tour::slide>& v, string title, string desc, reaction_t t)
[t] (presmode mode) { [t] (presmode mode) {
setCanvas(mode, '0'); setCanvas(mode, '0');
slidecommand = "auto-movement";
if(mode == pmKey) { if(mode == pmKey) {
using namespace anims; using namespace anims;
tour::slide_backup(ma, ma == maTranslation ? maNone : maTranslation); tour::slide_backup(ma, ma == maTranslation ? maNone : maTranslation);
@ -199,7 +200,7 @@ named_functionality o_key() {
auto hchook = addHook(hooks_drawcell, 100, draw_snow) auto hchook = addHook(hooks_drawcell, 100, draw_snow)
+ addHook(clearmemory, 40, [] () { + addHook(hooks_clearmemory, 40, [] () {
matrices_at.clear(); matrices_at.clear();
}) })
@ -238,7 +239,7 @@ auto hchook = addHook(hooks_drawcell, 100, draw_snow)
+ addHook(rvtour::hooks_build_rvtour, 140, [] (vector<tour::slide>& v) { + addHook(rvtour::hooks_build_rvtour, 140, [] (vector<tour::slide>& v) {
v.push_back(tour::slide{ v.push_back(tour::slide{
cap+"intro", 10, tour::LEGAL::NONE | tour::QUICKSKIP, cap+"snowball visualization", 10, tour::LEGAL::NONE | tour::QUICKSKIP,
"Non-Euclidean visualizations usually show some regular constructions. Could we visualize the geometries themselves? Let's distribute the snowballs randomly." "Non-Euclidean visualizations usually show some regular constructions. Could we visualize the geometries themselves? Let's distribute the snowballs randomly."
"\n\n" "\n\n"
"You can use mouse to look in different directions. Press 5 to turn the automatic movement on or off. Press 'o' to change density and shape." "You can use mouse to look in different directions. Press 5 to turn the automatic movement on or off. Press 'o' to change density and shape."
@ -291,16 +292,17 @@ auto hchook = addHook(hooks_drawcell, 100, draw_snow)
tour::slide_backup(snow_shape, 2); tour::slide_backup(snow_shape, 2);
snow_lambda = 5; snow_lambda = 5;
}); });
snow_slide(v, "SL(2,R)", "Here is SL(2,R), like Nil but based on hyperbolic plane instead. Geometric lensing effects are strong in both Nil and SL(2,R). (Starting with spherical plane yields spherical geometry.)", [] { snow_slide(v, "SL(2,R)", "Here is SL(2,R), like Nil but based on hyperbolic plane instead. Geometric lensing effects are strong in both Nil and SL(2,R). (Starting with S^2 yields spherical geometry.)", [] {
set_geometry(gNormal); set_geometry(gNormal);
set_variation(eVariation::pure); set_variation(eVariation::pure);
set_geometry(gRotSpace); set_geometry(gRotSpace);
snow_lambda = 5; snow_lambda = 5;
}); });
#if CAP_SOLV #if CAP_SOLV
snow_slide(v, "Solv", "Solv geometry. Like the non-isotropic hyperbolic space (#4) but where the horizontal and vertical curvatures work in the other way.", [] { snow_slide(v, "Solv", "Solv geometry. Like the non-isotropic hyperbolic geometry but where the horizontal and vertical curvatures work in the other way.", [] {
set_geometry(gSol); set_geometry(gSol);
snow_lambda = 20; // tour::slide_backup(snow_shape, 2);
snow_lambda = 3;
}); });
#endif #endif
}); });

View File

@ -30,7 +30,7 @@ hyperpoint spcoord(hyperpoint h) {
rug::rugpoint *pt(hyperpoint h, hyperpoint c) { rug::rugpoint *pt(hyperpoint h, hyperpoint c) {
auto r = rug::addRugpoint(C0, -1); auto r = rug::addRugpoint(C0, -1);
r->flat = spcoord(h); r->native = spcoord(h);
r->x1 = c[0]; r->x1 = c[0];
r->y1 = c[1]; r->y1 = c[1];
r->valid = true; r->valid = true;
@ -120,17 +120,17 @@ void make_staircase() {
println(hlog, "scurvature = ", scurvature, " progress = ", progress, " strafe=", strafex, ",", strafey); println(hlog, "scurvature = ", scurvature, " progress = ", progress, " strafe=", strafex, ",", strafey);
rug::renderonce = true; rug::renderonce = true;
rug::rug_perspective = true; vid.rug_config.model = mdPerspective;
if(scurvature > -1e-6 && scurvature < 1e-6) { if(scurvature > -1e-6 && scurvature < 1e-6) {
rug::gwhere = gEuclid; rug::gwhere = rug::rgEuclid;
acurvature = 1; acurvature = 1;
} }
else if(scurvature < 0) { else if(scurvature < 0) {
rug::gwhere = gNormal; rug::gwhere = rug::rgHyperbolic;
acurvature = -scurvature; acurvature = -scurvature;
} }
else { else {
rug::gwhere = gSphere; rug::gwhere = rug::rgSphere;
acurvature = scurvature; acurvature = scurvature;
} }
rug::ruggospeed = acurvature; rug::ruggospeed = acurvature;

View File

@ -87,13 +87,9 @@ bool sunflower_cell(cell *c, transmatrix V) {
if(adjust_rug) { if(adjust_rug) {
using namespace rug; using namespace rug;
if(rug_perspective)
push_all_points(2, +model_distance);
model_distance = sqrt(zdensity) * distance_per_rug; model_distance = sqrt(zdensity) * distance_per_rug;
if(rug_perspective)
push_all_points(2, -model_distance);
} }
iqty = qty; iqty = qty;

View File

@ -113,7 +113,7 @@ int readArgs() {
if(0) ; if(0) ;
else if(argis("-tol")) { else if(argis("-tree")) {
PHASE(3); shift(); tree::read(args()); PHASE(3); shift(); tree::read(args());
} }

View File

@ -37,6 +37,14 @@ int how1 = how - 1;
// precision: number of substeps to simulate (best if divisible by how and how1) // precision: number of substeps to simulate (best if divisible by how and how1)
int isteps = 4 * 1024; int isteps = 4 * 1024;
/* the generators correspond to: */
nilv::mvec a(1,0,0);
nilv::mvec b(0,1,0);
nilv::mvec c = (a * b).inverse();
vector<nilv::mvec> gens = { a, b, c, a.inverse(), b.inverse(), c.inverse() };
struct triangledata { struct triangledata {
hyperpoint at; hyperpoint at;
bool computed; bool computed;
@ -74,15 +82,15 @@ struct trianglemaker {
hyperpoint start = point31(0, 0, 0); hyperpoint start = point31(0, 0, 0);
double lastz;
double lasta;
double ca; double ca;
// compute how to scale this in Nil so that everything fits // compute how to scale this in Nil so that everything fits
for(ld a = 1e-5;; a+=1e-5) { ld amin = 0, amax = 1;
for(int it=0; it<100; it++) {
ld a = (amin + amax) / 2;
ca = a;
hyperpoint at = start; hyperpoint at = start;
for(int d=0; d<3; d++) { for(int d=0; d<3; d++) {
for(int i=0; i<isteps; i++) { for(int i=0; i<isteps; i++) {
@ -92,11 +100,11 @@ struct trianglemaker {
println(hlog, "at = ", at, " for a = ", a, " sq = ", at[2] / a / a); println(hlog, "at = ", at, " for a = ", a, " sq = ", at[2] / a / a);
if(at[2] > 0) { if(at[2] > 0) {
ld z = at[2]; amax = a;
ca = lerp(lasta, a, ilerp(lastz, z, 0)); }
break; else {
amin = a;
} }
lastz = at[2]; lasta =a;
} }
// compute the shift between the cubes // compute the shift between the cubes
@ -111,9 +119,9 @@ struct trianglemaker {
// println(hlog, "uds = ", uds); // println(hlog, "uds = ", uds);
for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(start + ds[a] * ca, iTable, false))); for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(start + ds[a] * ca)));
for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(uds[a], iTable, false))); for(int a=0; a<3; a++) println(hlog, sqhypot_d(3, inverse_exp(uds[a])));
// compute cube vertices // compute cube vertices
@ -414,7 +422,9 @@ void find_coefficients() {
} }
void growthrate() { void growthrate() {
/*
cnts.resize(20);
for(int a=0; a<CTO; a++) { for(int a=0; a<CTO; a++) {
int cnt = 0; int cnt = 0;
map<cell*, int> howmany; map<cell*, int> howmany;
@ -427,22 +437,28 @@ void growthrate() {
cnts[a] = cnt; cnts[a] = cnt;
if(a >= 4) println(hlog, "D4 = ", cnts[a-4] - 4 * cnts[a-3] + 6 * cnts[a-2] - 4 * cnts[a-1] + cnts[a]); if(a >= 4) println(hlog, "D4 = ", cnts[a-4] - 4 * cnts[a-3] + 6 * cnts[a-2] - 4 * cnts[a-1] + cnts[a]);
println(hlog, "cnts = ", cnts); println(hlog, "cnts = ", cnts);
}*/ }
cnts = {1,7,31,113,299,681,1363,2501,4181,6570,9874,14256,20027,27601,37171,48815,62993,79912,100181,123868,151680,184339,222347,265733,314523,369424,431221,500952,578350,665794,763300,871250,988488,1116635,1256293,1409165,1575969,1758327,1958977,2174877}; auto cnt2 = cnts;
find_coefficients(); for(int i=isize(cnt2)-1; i>=1; i--) cnt2[i] -= cnt2[i-1];
println(hlog, "cnts dif = ", cnt2);
// this was computed on integers, not using the program above
cnts =
{1,6,24,80,186,368,644,1046,1574,2260,3128,4198,5482,7006,8788,10860,13228,15918,18948,22350,26130,30314,34926,39986,45506,51518,58034,65086,72680,80842,89596,98968,108964,119610,130930,142950,155676,169140,183354,198350,214140,230744,248186,266492,285668,305746,326744,348688,371584,395464,420346,446256,473206,501216,530310,560520,591846,624320,657960,692792,728828,766094,804608,844396,885470,927856,971572,1016650,1063090,1110924,1160176,1210866,1263006,1316622,1371732,1428368,1486536,1546262,1607564,1670474,1734998,1801162,1868990,1938502,2009710,2082646,2157322,2233770,2311996,2392026,2473884,2557596,2643168,2730626,2819994,2911298,3004544,3099764,3196970,3296194,3397448,3500752,3606130,3713608,3823192};
println(hlog, "coefficients_known = ", coefficients_known); println(hlog, "coefficients_known = ", coefficients_known);
if(coefficients_known == 2) { if(coefficients_known == 2) {
string fmt = "a(d+" + its(isize(coef)) + ") = "; string fmt = "a(d+" + its(isize(coef)) + ") = ";
bool first = true; bool first = true;
for(int i=0; i<isize(coef); i++) if(coef[i]) { for(int i=0; i<isize(coef); i++) if(kz(coef[i])) {
if(first && coef[i] == 1) ; if(first && !kz(coef[i]-1)) ;
else if(first) fmt += its(coef[i]); else if(first) fmt += fts(coef[i]);
else if(coef[i] == 1) fmt += " + "; else if(!kz(coef[i]-1)) fmt += " + ";
else if(coef[i] == -1) fmt += " - "; else if(!kz(coef[i]+1)) fmt += " - ";
else if(coef[i] > 1) fmt += " + " + its(coef[i]); else if(coef[i] > 0) fmt += " + " + fts(coef[i]);
else if(coef[i] < -1) fmt += " - " + its(-coef[i]); else if(coef[i] < 0) fmt += " - " + fts(-coef[i]);
fmt += "a(d"; fmt += "a(d";
if(i != isize(coef) - 1) if(i != isize(coef) - 1)
fmt += "+" + its(isize(coef) - 1 - i); fmt += "+" + its(isize(coef) - 1 - i);
@ -464,7 +480,7 @@ bool draw_ptriangle(cell *c, const transmatrix& V) {
if(mkr && mkr->icgi != &cgi) reset(); if(mkr && mkr->icgi != &cgi) reset();
if(!mkr) { mkr = new trianglemaker; mkr->init(); if(!mkr) { mkr = new trianglemaker; mkr->init();
growthrate(); // growthrate();
} }
for(auto& td: mkr->tds[c]) { for(auto& td: mkr->tds[c]) {
@ -523,7 +539,7 @@ auto hchook = addHook(hooks_drawcell, 100, draw_ptriangle)
else if(argis("-tri-net")) { else if(argis("-tri-net")) {
on = true; net = true; on = true; net = true;
} }
else if(argis("-tri-net")) { else if(argis("-tri-one")) {
on = true; net = false; on = true; net = false;
} }
else return 1; else return 1;

View File

@ -1,302 +0,0 @@
#include "../hyper.h"
// Twisted S2xE.
// We use a model with coordinates (r,phi,z), where (r,phi) are the polar coordinates in S2.
// Metric: ds^2 = (dr)^2 + (sin r * dphi)^2 + (dz + K * (1-cos(r)) dphi)^2
// See https://youtu.be/lZCkEuud6aU and https://youtu.be/rfu6m_xGxWY
namespace hr {
EX namespace ts2 {
eGeometry ts2 = eGeometry(-1);
EX bool in() { return geometry == gTS; }
EX ld K = -.9;
EX hyperpoint at = point3(.5, 0, 0);
EX transmatrix camera;
void init() {
ld r = .5;
at = point3(r, 0, 0);
camera = build_matrix(
point3(1, 0, 0),
point3(0, 1/sin(r), K/sin(r)*(cos(r)-1)),
point3(0, 0, 1),
point31(0,0,0)
);
camera = camera * cspin(1, 2, 90*degree);
}
// a dummy map that does nothing
struct hrmap_ts2 : hrmap {
heptagon *origin;
heptagon *getOrigin() override { return origin; }
struct transmatrix relative_matrix(heptagon *h2, heptagon *h1, const hyperpoint& hint) override {
return Id;
}
hrmap_ts2() {
init();
heptagon*& h =origin;
h = tailored_alloc<heptagon> (S7);
h->c7 = newCell(S7, h);
h->distance = 0;
h->dm4 = 0;
h->fieldval = 0;
h->cdata = NULL;
h->alt = NULL;
}
heptagon *create_step(heptagon *parent, int d) override {
parent->c.connect(d, parent, d, false);
return parent;
}
void draw() override {
println(hlog, "at = ", at);
for(int i=0; i<3; i++)
println(hlog, i, ": ", camera * point3(i==0, i==1, i==2));
}
};
EX hrmap* new_map() { return new hrmap_ts2; };
EX hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) {
const ld r = at[0];
hyperpoint c = point3(0,0,0);
// const ld r2 = r * r;
const ld K2 = K * K;
const ld sr = sin(r);
// const ld sr2 = sr * sr;
const ld cr = cos(r) - 1;
const ld cr2 = cr * cr;
c[ 0 ] = 0
+ velocity[ 1 ] * transported[ 1 ] * (-K2*(cr) + cos(r))*sr
+ velocity[ 1 ] * transported[ 2 ] * K*sr/2
+ velocity[ 2 ] * transported[ 1 ] * K*sr/2;
c[ 1 ] = 0
+ velocity[ 0 ] * transported[ 1 ] * (K2*cos(r) - K2 - 2*cos(r))/(2*sr)
+ velocity[ 0 ] * transported[ 2 ] * -K/(2*sr)
+ velocity[ 1 ] * transported[ 0 ] * (K2*cos(r) - K2 - 2*cos(r))/(2*sr)
+ velocity[ 2 ] * transported[ 0 ] * -K/(2*sr);
c[ 2 ] = 0
+ velocity[ 0 ] * transported[ 1 ] * K*(K2 - 1)*cr2/(2*sr)
+ velocity[ 0 ] * transported[ 2 ] * K2*(1 - cos(r))/(2*sr)
+ velocity[ 1 ] * transported[ 0 ] * K*(K2 - 1)*cr2/(2*sr)
+ velocity[ 2 ] * transported[ 0 ] * K2*(1 - cos(r))/(2*sr);
return c;
}
void geodesic_step(hyperpoint& at, hyperpoint& velocity) {
auto acc = ts2::christoffel(at, velocity, velocity);
auto at2 = at + velocity / 2;
auto velocity2 = velocity + acc / 2;
auto acc2 = ts2::christoffel(at2, velocity2, velocity2);
at = at + velocity + acc2 / 2;
velocity = velocity + acc;
}
EX bool shift_view(hyperpoint dist) {
if(!in()) return false;
auto tPos = transpose(camera);
hyperpoint h = camera * dist;
int steps = 100;
h /= steps;
for(int i=0; i<steps; i++) {
for(int j=0; j<3; j++)
tPos[j] += christoffel(at, h, tPos[j]);
geodesic_step(at, h);
}
camera = transpose(tPos);
return true;
}
EX bool rotate_view(transmatrix T) {
if(!in()) return false;
camera = camera * inverse(T);
return true;
}
EX void radar() {
hyperpoint a = at;
hyperpoint v = camera * point3(0,0,1) / 100.;
int it;
for(it=0; it<1000; it++) {
geodesic_step(a, v);
// if(a[0] < .1) break;
if(a[0] > .5 && a[0] < .6 && cos(a[1]) > .9 && cos(a[2]) > .9) break;
}
println(hlog, "radar = ", it);
}
void twist() {
for(K = -2; K<=2; K += .1)
for(ld start: {0.5f, 1.f}) {
hyperpoint at = point3(start, 0, 0);
hyperpoint vel = point3(0, 1e-5, 0);
// println(hlog, "simulating");
hyperpoint at1 = at, at2 = at;
int it = 0;
for(; ; it++) {
at2 = at1; at1 = at;
// if(it % 1000 == 0) println(hlog, format("%6d. ", it), at, " vel = ", vel);
geodesic_step(at, vel);
if(at2[0] > at1[0] && at1[0] < at[0]) break;
}
println(hlog, format("%8d. ", it), lalign(40, kz(at)), " vel = ", lalign(40, kz(vel)), " K = ", K);
}
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-ts2")) {
if(ts2 == (eGeometry)(-1)) {
ts2 = (eGeometry) isize(ginf);
ginf.push_back(geometryinfo{
"TS", "none", "TS", "ts", 1, 1, qEXPERIMENTAL | qRAYONLY, giSphere3, 0x31400, {{7, 2}}, eVariation::pure
});
}
set_geometry(ts2);
}
else if(argis("-twist")) {
twist();
exit(1);
}
else return 1;
return 0;
}
auto fundamentalhook = addHook(hooks_args, 100, readArgs);
EX string fragmentshader() {
return
"varying mediump vec4 at;\n"
"uniform mediump vec4 uStart;\n"
"uniform mediump mat4 uLP;\n"
"const float K = float(" + fts(ts2::K) + ");\n"
"const float maxdist = 20.;\n"
"vec4 christoffel(vec4 at, vec4 vel) {\n"
"float r = at.x;\n"
"float K2 = K * K;\n"
"float sr = sin(r);\n"
"float cr = cos(r) - 1.;\n"
"float cr2 = cr * cr;\n"
"vec4 c;\n"
"c[ 0 ] = 0.\n"
"+ vel.y * vel.y * (-K2*(cr) + cos(r))*sr\n"
"+ vel.y * vel.z * K*sr/2.\n"
"+ vel.z * vel.y * K*sr/2.;\n"
"c[ 1 ] = 0."
"+ vel.x * vel.y * (K2*cos(r) - K2 - 2.*cos(r))/(2.*sr)\n"
"+ vel.x * vel.z * -K/(2.*sr)\n"
"+ vel.y * vel.x * (K2*cos(r) - K2 - 2.*cos(r))/(2.*sr)\n"
"+ vel.z * vel.x * -K/(2.*sr);\n"
"c[ 2 ] = 0."
"+ vel.x * vel.y * K*(K2 - 1.)*cr2/(2.*sr)\n"
"+ vel.x * vel.z * K2*(1. - cos(r))/(2.*sr)\n"
"+ vel.y * vel.x * K*(K2 - 1.)*cr2/(2.*sr)\n"
"+ vel.z * vel.x * K2*(1. - cos(r))/(2.*sr);\n"
"return c;}\n"
"void main() {\n"
" mediump vec4 at0 = at;\n"
" at0.y = -at.y;\n"
" at0.xyz = at0.xyz / length(at0.xyz);\n"
" mediump vec4 position = uStart;\n"
" mediump vec4 tangent = uLP * at0;\n"
" tangent = tangent;\n"
" float dist = 0.;\n"
" int iter = 0;"
" while(dist < maxdist && iter < 10000) {"
// we make smaller steps if we are close to the singularities at the poles
" float step = sin(position.x) * .05;"
" dist = dist + step;\n"
" iter++;\n"
" tangent = tangent * step;\n"
" vec4 acc = christoffel(position, tangent);"
" vec4 at2 = position + tangent / 2.;"
" vec4 tangent2 = tangent + acc / 2.;"
" vec4 acc2 = christoffel(at2, tangent2);"
" position = position + tangent + acc2 / 2.;"
" tangent = tangent + acc;\n"
" tangent = tangent / step;\n"
" if(position.x > .5 && position.x < .6 && cos(position.y) > .9 && cos(position.z/2./K) > .9) {\n"
" float bri = float(1. - dist / maxdist);\n"
" int e = 0;\n"
" if(position.x < .51 || position.x > .59) e++;\n"
" if(cos(position.y) < .91) e++;\n"
" if(cos(position.z/2./K) < .91) e++;\n"
" if(e >= 2) bri /= 2.;\n"
" gl_FragColor = vec4(bri, bri, bri, 1.);\n"
" return;"
" }"
" }\n"
" gl_FragColor = vec4(0.,0.,0.,1.);\n"
" }";
}
int ah2 = addHook(ray::hooks_rayshader, 100, [] (string &vsh, string &fsh) { fsh = ts2::fragmentshader(); })
+ addHook(ray::hooks_rayset, 100, [] (shared_ptr<ray::raycaster> o) {
if(!in()) return false;
glUniformMatrix4fv(o->uLP, 1, 0, glhr::tmtogl_transpose3(ts2::camera).as_array());
auto pg = glhr::pointtogl(ts2::at);
glUniform4f(o->uStart, pg[0], pg[1], pg[2], pg[3]);
return true;
})
+ addHook(hooks_newmap, 100, [] () { if(in()) return ts2::new_map(); return (hrmap*) nullptr; })
+ addHook(hooks_rotate_view, 100, rotate_view)
+ addHook(hooks_shift_view, 100, shift_view)
;
EX }
}

1042
rug.cpp

File diff suppressed because it is too large Load Diff

View File

@ -285,7 +285,7 @@ EX void show_memory_menu() {
} }
EX bool protect_memory() { EX bool protect_memory() {
if(!CAP_MEMORY_RESERVE) return false; #if CAP_MEMORY_RESERVE
apply_memory_reserve(); apply_memory_reserve();
if(reserve_limit && reserve_count < reserve_limit && !ignored_memory_warning) { if(reserve_limit && reserve_count < reserve_limit && !ignored_memory_warning) {
pushScreen(show_memory_menu); pushScreen(show_memory_menu);
@ -295,6 +295,7 @@ EX bool protect_memory() {
pushScreen(show_memory_menu); pushScreen(show_memory_menu);
return true; return true;
} }
#endif
return false; return false;
} }

View File

@ -86,10 +86,10 @@ EX always_false in;
EX void circle(int x, int y, int size, color_t col, color_t fillcol, double linewidth) { EX void circle(int x, int y, int size, color_t col, color_t fillcol, double linewidth) {
if(!invisible(col) || !invisible(fillcol)) { if(!invisible(col) || !invisible(fillcol)) {
if(vid.stretch == 1) if(pconf.stretch == 1)
println(f, "<circle cx='", coord(x), "' cy='", coord(y), "' r='", coord(size), "' ", stylestr(fillcol, col, linewidth), "/>"); println(f, "<circle cx='", coord(x), "' cy='", coord(y), "' r='", coord(size), "' ", stylestr(fillcol, col, linewidth), "/>");
else else
println(f, "<ellipse cx='", coord(x), "' cy='", coord(y), "' rx='", coord(size), "' ry='", coord(size*vid.stretch), "' ", stylestr(fillcol, col), "/>"); println(f, "<ellipse cx='", coord(x), "' cy='", coord(y), "' rx='", coord(size), "' ry='", coord(size*pconf.stretch), "' ", stylestr(fillcol, col), "/>");
} }
} }
@ -215,7 +215,7 @@ int read_args() {
else if(argis("-svgshot")) { else if(argis("-svgshot")) {
PHASE(3); shift(); start_game(); PHASE(3); shift(); start_game();
printf("saving SVG screenshot to %s\n", argcs()); printf("saving SVG screenshot to %s\n", argcs());
shot::make_svg = true; shot::format = shot::screenshot_format::svg;
shot::take(argcs()); shot::take(argcs());
} }
else if(argis("-svgtwm")) { else if(argis("-svgtwm")) {
@ -233,6 +233,375 @@ auto ah = addHook(hooks_args, 0, read_args);
#endif #endif
EX } EX }
/** wrl renderer */
EX namespace wrl {
#if !CAP_WRL
EX always_false in;
#endif
#if CAP_WRL
EX bool in;
EX bool print;
EX bool textures = true;
EX ld rug_width = .01;
fhstream f;
string filename;
string coord(ld val) {
char buf[100];
snprintf(buf, 100, "%f", val);
return buf;
}
string coord(const hyperpoint& v, int q) {
char buf[100];
if(q == 3) snprintf(buf, 100, "%f, %f, %f", v[0], v[1], v[2]);
if(q == 2) snprintf(buf, 100, "%f, %f", v[0], v[1]);
return buf;
}
string color(color_t col, ld v) {
char buf[100];
ld cols[4];
for(int i=0; i<4; i++) {
cols[i] = part(col, i);
cols[i] /= 255;
cols[i] = pow(cols[i], shot::gamma) * shot::fade * v;
}
snprintf(buf, 100, "%.3f %.3f %.3f", cols[3], cols[2], cols[1]);
return buf;
}
typedef unsigned long long hashtype;
hashtype hash(ld x) { return hashtype(x * 1000000 + .5); }
hashtype hash(hyperpoint h) {
return hash(h[0]) + 7 * hash(h[1]) + 13 * hash(h[2]);
}
EX void fatten(vector<hyperpoint>& data, vector<glvertex>& tdata) {
map<hashtype, hyperpoint> normals;
for(int i=0; i<isize(data); i++)
normals[hash(data[i])] = Hypc;
for(int i=0; i<isize(data); i++) {
int j = i%3 ? i-1 : i+2;
int k = j%3 ? j-1 : j+2;
hyperpoint normal = (data[j] - data[i]) ^ (data[k] - data[i]);
normal[3] = 0;
if(sqhypot_d(3, normal) < 1e-6) {
println(hlog, "bug ", tie(data[i], data[j], data[k]));
}
normal /= hypot_d(3, normal);
auto& res = normals[hash(data[i])];
ld q = res[3];
if((res | normal) < 0) res -= normal;
else res += normal;
res[3] = q + 1;
}
for(auto& p: normals) {
auto w = hypot_d(3, p.second);
if(w == 0) println(hlog, "width is 0, ", p.second, " appeared ", p.second[3], " times");
if(isnan(w)) println(hlog, "width is NAN, ", p.second, " appeared ", p.second[3], " times");
p.second = p.second * (rug_width / w);
}
vector<hyperpoint> data2;
vector<glvertex> tdata2;
for(int i=0; i<isize(data); i+=3) {
auto a = data[i], b = data[i+1], c = data[i+2];
hyperpoint normal = (b-a) ^ (c-a);
auto na = normals[hash(a)];
auto nb = normals[hash(b)];
auto nc = normals[hash(c)];
if((normal | na) > 0) na = -na;
if((normal | nb) > 0) nb = -nb;
if((normal | nc) > 0) nc = -nc;
bool bad = false;
for(int i=0; i<3; i++) {
if(isnan(na[i]) || isnan(nb[i]) || isnan(nc[i])) bad = true;
}
if(bad) {
println(hlog, "bad vertex");
continue;
}
data2.push_back(a+na); data2.push_back(b+nb); data2.push_back(c+nc);
data2.push_back(b+nb); data2.push_back(a+na); data2.push_back(a-na);
data2.push_back(b+nb); data2.push_back(a-na); data2.push_back(b-nb);
data2.push_back(c+nc); data2.push_back(b+nb); data2.push_back(b-nb);
data2.push_back(c+nc); data2.push_back(b-nb); data2.push_back(c-nc);
data2.push_back(a+na); data2.push_back(c+nc); data2.push_back(c-nc);
data2.push_back(a+na); data2.push_back(c-nc); data2.push_back(a-na);
data2.push_back(b-nb); data2.push_back(a-na); data2.push_back(c-nc);
if(isize(tdata)) {
auto ta = tdata[i], tb = tdata[i+1], tc = tdata[i+2];
for(auto p: {ta, tb, tc, tb, ta, ta, tb, ta, tb, tc, tb, tb, tc, tb, tc, ta, tc, tc, ta, tc, ta, tb, ta, tc})
tdata2.push_back(p);
}
}
data = data2;
tdata = tdata2;
}
bool used_rug;
map<pair<color_t, glvertex>, int> texture_position;
map<color_t, int> gradient_position;
pair<color_t, glvertex> texid(dqi_poly& p) {
return make_pair(p.color, p.tinf->tvertices[0]);
}
/** 0 = no/unknown/disabled texture, 1 = rug, 2 = gradient, 3 = floor texture */
EX int texture_type(dqi_poly& p) {
if(!p.tinf) return 0;
#if CAP_PNG
if(!textures) return 0;
if(p.tinf == &rug::tinf) return 1;
#if MAXMDIM >= 4
if(p.tinf->texture_id == (int) floor_textures->renderedTexture)
return (p.tinf->tvertices[0][0] == 0) ? 2 : 3;
#endif
#endif
return 0;
}
EX void prepare(dqi_poly& p) {
if(print && !(p.flags & POLY_PRINTABLE)) return;
if(!(p.flags & POLY_TRIANGLES)) return;
int tt = texture_type(p);
if(tt == 2) gradient_position[p.color] = 0;
if(tt == 3) texture_position[texid(p)] = 0;
}
#if MAXMDIM >= 4
int fts_int, fts, fts_row;
#endif
map<string, pair<vector<hyperpoint>, vector<glvertex>>> all_data;
EX void polygon(dqi_poly& p) {
if(print && !(p.flags & POLY_PRINTABLE)) return;
if(!(p.flags & POLY_TRIANGLES)) return;
int tt = texture_type(p);
vector<hyperpoint> data;
vector<glvertex> tdata;
for(int i=0; i<p.cnt; i++) {
glvertex v = p.tab[0][p.offset+i];
data.push_back(glhr::gltopoint(v));
if(p.tinf)
tdata.push_back(p.tinf->tvertices[p.offset_texture+i]);
}
for(auto& d: data) {
hyperpoint h;
h = p.V * d;
applymodel(h, d);
}
if(print && (p.flags & POLY_FAT)) {
fatten(data, tdata);
p.cnt = isize(data);
}
else if(print) {
hyperpoint ctr1;
applymodel(p.V * p.intester, ctr1);
println(hlog, "intester = ", p.intester);
ld sdet = 0;
if(1) {
dynamicval<eGeometry> g(geometry, gEuclid);
for(int i=0; i<p.cnt; i+=3) {
transmatrix T;
T[0] = data[i] - ctr1;
T[1] = data[i+1] - ctr1;
T[2] = data[i+2] - ctr1;
sdet += det(T);
}
println(hlog, "sdet = ", sdet);
if(sdet > 0)
for(int i=0; i<p.cnt; i+=3) {
swap(data[i+1], data[i+2]);
if(!tdata.empty())
swap(tdata[i+1], tdata[i+2]);
}
}
}
shstream app;
println(app, " material Material {");
if(!tt) println(app, " diffuseColor ", color(p.color, .8));
if(part(p.color, 0) != 255) println(app, " transparency ", (255 - part(p.color, 0)) / 255.);
println(app, " }");
if(tt == 1) {
println(f, " texture ImageTexture {");
println(app, " url \"", filename, "-rug.png\"");
println(app, " }");
used_rug = true;
}
if(tt == 2 || tt == 3) {
println(app, " texture ImageTexture {");
println(app, " url \"", filename, "-floors.png\"");
println(app, " }");
}
auto &ad = all_data[app.s];
for(auto& d: data) ad.first.push_back(d);
#if MAXMDIM >= 4
if(tt == 2) {
ld x = (fts - .5 - gradient_position[p.color]) / fts;
for(auto& d: tdata) d[0] = x;
}
if(tt == 3) {
int tp = texture_position[texid(p)];
auto xy = make_array<int>(tp % fts_row, tp / fts_row);
auto zero = p.tinf->tvertices[0];
ld sca = FLOORTEXTURESIZE*1./fts_int;
for(auto& d: tdata)
for(int c: {0, 1})
d[c] = ((d[c] - zero[c])*sca + xy[c] + .5) * fts_int / fts;
}
#endif
for(auto& d: tdata) ad.second.push_back(d);
}
EX void render() {
#if MAXMDIM >= 4
for(auto& p: ptds) {
auto p2 = dynamic_cast<dqi_poly*>(&*p);
if(p2)
prepare(*p2);
}
int tps = 0;
for(auto& p: texture_position) p.second = tps++;
int gps = 0;
for(auto& p: gradient_position) p.second = gps++;
fts_int = floor_texture_square_size * FLOORTEXTURESIZE + 4;
fts = 64;
while(fts < gps || (fts-gps)/fts_int * fts/fts_int < tps)
fts *= 2;
fts_row = (fts-gps)/fts_int;
#endif
for(auto& p: ptds) {
auto p2 = dynamic_cast<dqi_poly*>(&*p);
if(p2)
polygon(*p2);
}
}
EX void take(const string& fname, const function<void()>& what IS(shot::default_screenshot_content)) {
dynamicval<bool> v2(in, true);
dynamicval<bool> v3(noshadow, true);
filename = fname;
ptds.clear();
all_data.clear();
what();
f.f = fopen(fname.c_str(), "wt");
println(f, "#VRML V2.0 utf8");
println(f, "WorldInfo { title \"3D model exported from HyperRogue\" info [ \"3D models exported from HyperRogue are public domain\" ] }");
for(auto& p: all_data) {
const string& app = p.first;
auto& data = p.second.first;
auto& tdata = p.second.second;
println(f, "Shape {");
println(f, " appearance Appearance {");
println(f, app);
println(f, " }");
// println(f, "# V = ", p.V);
println(f, " geometry IndexedFaceSet {");
println(f, " coord Coordinate {");
println(f, " point [");
for(auto& d: data) println(f, " ", coord(d, 3), ",");
println(f, " ]");
println(f, " }");
if(!tdata.empty()) {
println(f, " texCoord TextureCoordinate {");
println(f, " point [");
for(auto& d: tdata)
println(f, " ", coord(glhr::gltopoint(d), 2), ",");
println(f, " ]");
println(f, " }");
}
println(f, " coordIndex [");
for(int i=0; i<isize(data); i+=3) {
println(f, " ", i, " ", i+1, " ", i+2, " -1,");
}
println(f, " ]");
if(print)
println(f, " creaseAngle 0.0 convex FALSE solid TRUE ccw FALSE");
else
println(f, " creaseAngle 0.0 convex FALSE solid FALSE");
println(f, " }");
println(f, " }");
}
#if CAP_PNG
if(used_rug) {
resetbuffer rb;
rug::glbuf->enable();
SDL_Surface *s = rug::glbuf->render();
dynamicval<int> dx(shot::shotx, rug::texturesize);
dynamicval<int> dy(shot::shoty, rug::texturesize);
shot::postprocess(filename + "-rug.png", s, s);
}
#if MAXMDIM >= 4
if(isize(texture_position) || isize(gradient_position)) {
SDL_Surface *s = shot::empty_surface(fts, fts, false);
for(auto& p: gradient_position) {
int x = fts - p.second - 1;
for(int y=0; y<fts; y++) {
qpixel(s, x, fts-y-1) = gradient(0, p.first, 0, y, fts-1) >> 8;
part(qpixel(s, x, y), 3) = 0xFF;
}
}
SDL_Surface *floor = floor_textures->render();
for(auto& p: texture_position) {
int nx = p.second % fts_row;
int ny = p.second / fts_row;
color_t col = p.first.first;
int xs = p.first.second[0] * FLOORTEXTURESIZE - fts_int/2;
int ys = p.first.second[1] * FLOORTEXTURESIZE - fts_int/2;
swap(xs, ys); // I do not understand why
for(int y=0; y<fts_int; y++)
for(int x=0; x<fts_int; x++) {
auto& tgt = qpixel(s, nx*fts_int+x, fts-1-(ny*fts_int+y));
auto& src = qpixel(floor, xs+x, FLOORTEXTURESIZE-1-(ys+y));
for(int p=0; p<3; p++)
part(tgt, p) = (part(src, p) * part(col, p+1) + 127) / 255;
part(tgt, 3) = 0xFF;
}
}
IMAGESAVE(s, (filename + "-floors.png").c_str());
SDL_FreeSurface(s);
}
#endif
#endif
fclose(f.f);
f.f = nullptr;
}
#endif
EX }
#if CAP_PNG #if CAP_PNG
void IMAGESAVE(SDL_Surface *s, const char *fname) { void IMAGESAVE(SDL_Surface *s, const char *fname) {
@ -247,9 +616,13 @@ EX namespace shot {
purehookset hooks_hqshot; purehookset hooks_hqshot;
#if HDR
enum screenshot_format { png, svg, wrl };
#endif
EX int shotx = 2000; EX int shotx = 2000;
EX int shoty = 2000; EX int shoty = 2000;
EX bool make_svg = false; EX screenshot_format format;
EX bool transparent = true; EX bool transparent = true;
EX ld gamma = 1; EX ld gamma = 1;
EX int shotformat = -1; EX int shotformat = -1;
@ -281,20 +654,26 @@ EX void default_screenshot_content() {
#endif #endif
drawfullmap(); drawfullmap();
rots::draw_underlying(false);
if(caption != "") if(caption != "")
displayfr(vid.xres/2, vid.fsize+vid.fsize/4, 3, vid.fsize*2, caption, forecolor, 8); displayfr(vid.xres/2, vid.fsize+vid.fsize/4, 3, vid.fsize*2, caption, forecolor, 8);
callhooks(hooks_hqshot); callhooks(hooks_hqshot);
drawStats(); drawStats();
} }
EX SDL_Surface *empty_surface(int x, int y, bool alpha) {
return SDL_CreateRGBSurface(SDL_SWSURFACE,x,y,32,0xFF<<16,0xFF<<8,0xFF, (alpha) ? (0xFF<<24) : 0);
}
#if CAP_PNG #if CAP_PNG
void postprocess(string fname, SDL_Surface *sdark, SDL_Surface *sbright) { EX void postprocess(string fname, SDL_Surface *sdark, SDL_Surface *sbright) {
if(gamma == 1 && shot_aa == 1 && sdark == sbright) { if(gamma == 1 && shot_aa == 1 && sdark == sbright) {
IMAGESAVE(sdark, fname.c_str()); IMAGESAVE(sdark, fname.c_str());
return; return;
} }
SDL_Surface *sout = SDL_CreateRGBSurface(SDL_SWSURFACE,shotx,shoty,32,0xFF<<16,0xFF<<8,0xFF, (sdark == sbright) ? 0 : (0xFF<<24)); SDL_Surface *sout = empty_surface(shotx, shoty, sdark != sbright);
for(int y=0; y<shoty; y++) for(int y=0; y<shoty; y++)
for(int x=0; x<shotx; x++) { for(int x=0; x<shotx; x++) {
int val[2][4]; int val[2][4];
@ -327,12 +706,46 @@ void postprocess(string fname, SDL_Surface *sdark, SDL_Surface *sbright) {
EX purehookset hooks_take; EX purehookset hooks_take;
#if CAP_PNG
void render_png(string fname, const function<void()>& what) {
resetbuffer rb;
renderbuffer glbuf(vid.xres, vid.yres, vid.usingGL);
glbuf.enable();
current_display->set_viewport(0);
dynamicval<color_t> v8(backcolor, transparent ? 0xFF000000 : backcolor);
#if CAP_RUG
if(rug::rugged && !rug::renderonce) rug::prepareTexture();
#endif
glbuf.clear(backcolor);
what();
SDL_Surface *sdark = glbuf.render();
if(transparent) {
renderbuffer glbuf1(vid.xres, vid.yres, vid.usingGL);
backcolor = 0xFFFFFFFF;
#if CAP_RUG
if(rug::rugged && !rug::renderonce) rug::prepareTexture();
#endif
glbuf1.enable();
glbuf1.clear(backcolor);
current_display->set_viewport(0);
what();
postprocess(fname, sdark, glbuf1.render());
}
else postprocess(fname, sdark, sdark);
}
#endif
EX void take(string fname, const function<void()>& what IS(default_screenshot_content)) { EX void take(string fname, const function<void()>& what IS(default_screenshot_content)) {
if(cheater) doOvergenerate(); if(cheater) doOvergenerate();
#if CAP_SVG #if CAP_SVG
int multiplier = make_svg ? svg::divby : shot_aa; int multiplier = (format == screenshot_format::svg) ? svg::divby : shot_aa;
#else #else
int multiplier = shot_aa; int multiplier = shot_aa;
#endif #endif
@ -352,45 +765,25 @@ EX void take(string fname, const function<void()>& what IS(default_screenshot_co
models::configure(); models::configure();
callhooks(hooks_take); callhooks(hooks_take);
if(make_svg) { switch(format) {
#if CAP_SVG case screenshot_format::wrl:
svg::render(fname, what); #if CAP_WRL
#endif wrl::take(fname);
}
else {
#if CAP_PNG
resetbuffer rb;
renderbuffer glbuf(vid.xres, vid.yres, vid.usingGL);
glbuf.enable();
current_display->set_viewport(0);
dynamicval<color_t> v8(backcolor, transparent ? 0xFF000000 : backcolor);
#if CAP_RUG
if(rug::rugged && !rug::renderonce) rug::prepareTexture();
#endif
glbuf.clear(backcolor);
what();
SDL_Surface *sdark = glbuf.render();
if(transparent) {
renderbuffer glbuf1(vid.xres, vid.yres, vid.usingGL);
backcolor = 0xFFFFFFFF;
#if CAP_RUG
if(rug::rugged && !rug::renderonce) rug::prepareTexture();
#endif #endif
glbuf1.enable(); return;
glbuf1.clear(backcolor);
current_display->set_viewport(0); case screenshot_format::svg:
what(); #if CAP_SVG
svg::render(fname, what);
postprocess(fname, sdark, glbuf1.render()); #endif
} return;
else postprocess(fname, sdark, sdark);
#endif case screenshot_format::png:
} #if CAP_PNG
render_png(fname, what);
#endif
return;
}
} }
#if CAP_COMMANDLINE #if CAP_COMMANDLINE
@ -399,7 +792,7 @@ int png_read_args() {
if(argis("-pngshot")) { if(argis("-pngshot")) {
PHASE(3); shift(); start_game(); PHASE(3); shift(); start_game();
printf("saving PNG screenshot to %s\n", argcs()); printf("saving PNG screenshot to %s\n", argcs());
make_svg = false; format = screenshot_format::png;
shot::take(argcs()); shot::take(argcs());
} }
else if(argis("-pngsize")) { else if(argis("-pngsize")) {
@ -417,6 +810,20 @@ int png_read_args() {
else if(argis("-shotaa")) { else if(argis("-shotaa")) {
shift(); shot_aa = argi(); shift(); shot_aa = argi();
} }
#if CAP_WRL
else if(argis("-modelshot")) {
PHASE(3); shift(); start_game();
printf("saving WRL model to %s\n", argcs());
shot::format = screenshot_format::wrl; wrl::print = false;
shot::take(argcs());
}
else if(argis("-printshot")) {
PHASE(3); shift(); start_game();
printf("saving 3D printable model to %s\n", argcs());
shot::format = screenshot_format::wrl; wrl::print = true;
shot::take(argcs());
}
#endif
else return 1; else return 1;
return 0; return 0;
} }
@ -424,30 +831,118 @@ int png_read_args() {
auto ah_png = addHook(hooks_args, 0, png_read_args); auto ah_png = addHook(hooks_args, 0, png_read_args);
#endif #endif
EX string format_name() {
if(format == screenshot_format::svg) return "SVG";
if(format == screenshot_format::wrl) return "WRL";
if(format == screenshot_format::png) return "PNG";
return "?";
}
EX string format_extension() {
if(format == screenshot_format::svg) return ".svg";
if(format == screenshot_format::wrl) return ".wrl";
if(format == screenshot_format::png) return ".png";
return "?";
}
EX void choose_screenshot_format() {
cmode = sm::SIDE;
gamescreen(0);
dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100);
#if CAP_PNG
dialog::addItem(XLAT("PNG"), 'p');
dialog::add_action([] { format = screenshot_format::png; popScreen(); });
#endif
#if CAP_SVG
dialog::addItem(XLAT("SVG"), 's');
dialog::add_action([] { format = screenshot_format::svg; popScreen(); });
#endif
#if CAP_WRL
dialog::addItem(XLAT("WRL"), 'w');
dialog::add_action([] { format = screenshot_format::wrl; popScreen(); });
#endif
dialog::addBack();
dialog::display();
}
EX void menu() { EX void menu() {
cmode = sm::SIDE; cmode = sm::SIDE;
gamescreen(0); gamescreen(0);
if(!CAP_SVG) make_svg = false; if(format == screenshot_format::svg && !CAP_SVG)
if(!CAP_PNG) make_svg = true; format = screenshot_format::png;
if(format == screenshot_format::png && !CAP_PNG)
format = screenshot_format::svg;
dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100); dialog::init(XLAT("screenshots"), iinf[itPalace].color, 150, 100);
dialog::addSelItem(XLAT("format"), make_svg ? "SVG" : "PNG", 'f'); dialog::addSelItem(XLAT("format"), format_name(), 'f');
dialog::add_action([] { make_svg = !make_svg; }); dialog::add_action_push(choose_screenshot_format);
dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x'); bool dowrl = format == screenshot_format::wrl;
dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); }); if(!dowrl) {
dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y'); dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x');
dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); }); dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); });
if(make_svg) { dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y');
#if CAP_SVG dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); });
using namespace svg;
dialog::addSelItem(XLAT("precision"), "1/"+its(divby), 'p');
dialog::add_action([] { divby *= 10; if(divby > 1000000) divby = 1; });
#endif
} }
else {
dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's'); switch(format) {
dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; }); case screenshot_format::svg: {
#if CAP_SVG
using namespace svg;
dialog::addSelItem(XLAT("precision"), "1/"+its(divby), 'p');
dialog::add_action([] { divby *= 10; if(divby > 1000000) divby = 1; });
#endif
if(models::is_3d(vpconf) || rug::rugged) {
dialog::addInfo("SVG screenshots do not work in this 3D mode", 0xFF0000);
if(GDIM == 2 && !rug::rugged) {
dialog::addSelItem(XLAT("projection"), current_proj_name(), '1');
dialog::add_action_push(models::model_menu);
}
#if CAP_WRL
else {
dialog::addItem(XLAT("WRL"), 'w');
dialog::add_action([] { format = screenshot_format::wrl; });
}
#endif
}
#if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive)
dialog::addInfo("SVG screenshots do not work with textures", 0xFF0000);
#endif
break;
}
case screenshot_format::png: {
#if CAP_PNG
dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's');
dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; });
#endif
break;
}
case screenshot_format::wrl: {
#if CAP_WRL
if(!models::is_3d(vpconf) && !rug::rugged) {
dialog::addInfo("this format is for 3D projections", 0xFF0000);
if(GDIM == 2) {
dialog::addItem(XLAT("hypersian rug mode"), 'u');
dialog::add_action_push(rug::show);
}
}
else if(rug::rugged ? rug::perspective() : models::is_perspective(vpconf.model)) {
dialog::addInfo("this does not work well in perspective projections", 0xFF8000);
dialog::addSelItem(XLAT("projection"), current_proj_name(), '1');
dialog::add_action_push(models::model_menu);
}
dialog::addBoolItem_action("generate a model for 3D printing", wrl::print, 'p');
#if CAP_PNG
dialog::addBoolItem_action("use textures", wrl::textures, 'u');
#endif
#endif
}
} }
dialog::addBoolItem_action(XLAT("transparent"), transparent, 't'); if(!dowrl) dialog::addBoolItem_action(XLAT("transparent"), transparent, 't');
dialog::addSelItem(XLAT("gamma"), fts(gamma), 'g'); dialog::addSelItem(XLAT("gamma"), fts(gamma), 'g');
dialog::add_action([] { dialog::editNumber(gamma, 0, 2, .1, .5, XLAT("gamma"), "higher value = darker"); }); dialog::add_action([] { dialog::editNumber(gamma, 0, 2, .1, .5, XLAT("gamma"), "higher value = darker"); });
@ -455,17 +950,29 @@ EX void menu() {
dialog::addSelItem(XLAT("brightness"), fts(fade), 'b'); dialog::addSelItem(XLAT("brightness"), fts(fade), 'b');
dialog::add_action([] { dialog::editNumber(fade, 0, 2, .1, 1, XLAT("brightness"), "higher value = lighter"); }); dialog::add_action([] { dialog::editNumber(fade, 0, 2, .1, 1, XLAT("brightness"), "higher value = lighter"); });
dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, 'h'); if(!dowrl) dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, 'h');
dialog::addBoolItem_action_neg(XLAT("hide the player"), mapeditor::drawplayer, 'H');
dialog::addBoolItem_action_neg(XLAT("hide the player"), mapeditor::drawplayer, 'H');
#if CAP_WRL
if(dowrl && wrl::print) dialog::lastItem().value = XLAT("N/A");
#endif
if(WDIM == 2) { if(WDIM == 2) {
dialog::addItem(XLAT("centering"), 'x'); dialog::addItem(XLAT("centering"), 'C');
dialog::add_action([] { dialog::add_action([] {
dialog::editNumber(vid.fixed_facing_dir, 0, 360, 15, 90, XLAT("centering"), dialog::editNumber(vid.fixed_facing_dir, 0, 360, 15, 90, XLAT("centering"),
XLAT("You can pick the angle. Note: the direction the PC is facing matters.")); XLAT("You can pick the angle. Note: the direction the PC is facing matters."));
dialog::reaction = fullcenter; dialog::reaction = fullcenter;
dialog::extra_options = [] () { dialog::extra_options = [] () {
dialog::addBoolItem(XLAT("rotate PC"), centering == eCentering::face, 'R');
dialog::add_action([] {
flipplayer = false;
cwt++;
mirror::act(1, mirror::SPINSINGLE);
cwt.at->mondir++;
cwt.at->mondir %= cwt.at->type;
fullcenter();
});
dialog::addBoolItem(XLAT("face"), centering == eCentering::face, 'F'); dialog::addBoolItem(XLAT("face"), centering == eCentering::face, 'F');
dialog::add_action([] { centering = eCentering::face; fullcenter(); }); dialog::add_action([] { centering = eCentering::face; fullcenter(); });
dialog::addBoolItem(XLAT("edge"), centering == eCentering::edge, 'E'); dialog::addBoolItem(XLAT("edge"), centering == eCentering::edge, 'E');
@ -483,18 +990,6 @@ EX void menu() {
dialog::addBreak(100); dialog::addBreak(100);
#if CAP_RUG
if(make_svg && rug::rugged)
dialog::addInfo("SVG screenshots do not work in this 3D mode", 0xFF0000);
else
#endif
#if CAP_TEXTURE
if(make_svg && texture::config.tstate == texture::tsActive)
dialog::addInfo("SVG screenshots do not work with textures", 0xFF0000);
else
#endif
dialog::addBreak(100);
dialog::addItem(XLAT("take screenshot"), 'z'); dialog::addItem(XLAT("take screenshot"), 'z');
dialog::add_action([] () { dialog::add_action([] () {
#if ISWEB #if ISWEB
@ -502,8 +997,13 @@ EX void menu() {
#else #else
static string pngfile = "hqshot.png"; static string pngfile = "hqshot.png";
static string svgfile = "svgshot.svg"; static string svgfile = "svgshot.svg";
string& file = make_svg ? svgfile : pngfile; static string wrlfile = "model.wrl";
dialog::openFileDialog(file, XLAT("screenshot"), make_svg ? ".svg" : ".png", [&file] () { string& file =
format == screenshot_format::png ? pngfile :
format == screenshot_format::svg ? svgfile :
wrlfile;
dialog::openFileDialog(file, XLAT("screenshot"), format_extension(), [&file] () {
dynamicval<int> cgl(vid.cells_generated_limit, 9999999); dynamicval<int> cgl(vid.cells_generated_limit, 9999999);
shot::take(file); shot::take(file);
return true; return true;
@ -727,32 +1227,30 @@ EX void apply() {
#if CAP_RUG #if CAP_RUG
if(rug::rugged) { if(rug::rugged) {
if(rug_rotation1) { if(rug_rotation1) {
rug::apply_rotation(cspin(1, 2, rug_angle * degree)); rug::rugView = cspin(1, 2, -rug_angle * degree) * cspin(0, 2, rug_rotation1 * 2 * M_PI * t / period) * cspin(1, 2, rug_angle * degree) * rug::rugView;
rug::apply_rotation(cspin(0, 2, rug_rotation1 * 2 * M_PI * t / period));
rug::apply_rotation(cspin(1, 2, -rug_angle * degree));
} }
if(rug_rotation2) { if(rug_rotation2) {
rug::apply_rotation(rug::currentrot * cspin(0, 1, rug_rotation2 * 2 * M_PI * t / period) * inverse(rug::currentrot)); rug::rugView = rug::rugView * cspin(0, 1, rug_rotation2 * 2 * M_PI * t / period);
} }
} }
#endif #endif
vid.skiprope += skiprope_rotation * t * 2 * M_PI / period; pconf.skiprope += skiprope_rotation * t * 2 * M_PI / period;
if(ballangle_rotation) { if(ballangle_rotation) {
if(models::model_has_orientation()) if(models::has_orientation(vpconf.model))
models::model_orientation += ballangle_rotation * 360 * t / period; vpconf.model_orientation += ballangle_rotation * 360 * t / period;
else else
vid.ballangle += ballangle_rotation * 360 * t / period; vpconf.ballangle += ballangle_rotation * 360 * t / period;
} }
if(joukowsky_anim) { if(joukowsky_anim) {
ld t = ticks / period; ld t = ticks / period;
t = t - floor(t); t = t - floor(t);
if(pmodel == mdBand) { if(pmodel == mdBand) {
models::model_transition = t * 4 - 1; vpconf.model_transition = t * 4 - 1;
} }
else { else {
models::model_transition = t / 1.1; vpconf.model_transition = t / 1.1;
vid.scale = (1 - models::model_transition) / 2.; vpconf.scale = (1 - vpconf.model_transition) / 2.;
} }
} }
apply_animated_parameters(); apply_animated_parameters();
@ -770,15 +1268,23 @@ EX string animfile = "animation-%04d.png";
int min_frame = 0, max_frame = 999999; int min_frame = 0, max_frame = 999999;
int numturns = 0;
bool record_animation() { bool record_animation() {
lastticks = 0; lastticks = 0;
ticks = 0; ticks = 0;
int oldturn = -1;
for(int i=0; i<noframes; i++) { for(int i=0; i<noframes; i++) {
if(i < min_frame || i > max_frame) continue; if(i < min_frame || i > max_frame) continue;
printf("%d/%d\n", i, noframes); printf("%d/%d\n", i, noframes);
int newticks = i * period / noframes; int newticks = i * period / noframes;
cmode = (env_shmup ? sm::NORMAL : 0); cmode = (env_shmup ? sm::NORMAL : 0);
while(ticks < newticks) shmup::turn(1), ticks++; while(ticks < newticks) shmup::turn(1), ticks++;
if(cheater && numturns) {
int nturn = numturns * i / noframes;
if(nturn != oldturn) monstersTurn();
oldturn = nturn;
}
if(playermoved) centerpc(INF), optimizeview(); if(playermoved) centerpc(INF), optimizeview();
dynamicval<bool> v2(inHighQual, true); dynamicval<bool> v2(inHighQual, true);
apply(); apply();
@ -987,6 +1493,12 @@ EX void show() {
animator(XLATN("Ocean"), env_ocean, 'o'); animator(XLATN("Ocean"), env_ocean, 'o');
animator(XLATN("Volcanic Wasteland"), env_volcano, 'v'); animator(XLATN("Volcanic Wasteland"), env_volcano, 'v');
if(shmup::on) dialog::addBoolItem_action(XLAT("shmup action"), env_shmup, 'T'); if(shmup::on) dialog::addBoolItem_action(XLAT("shmup action"), env_shmup, 'T');
if(cheater) {
dialog::addSelItem(XLAT("monster turns"), its(numturns), 'n');
dialog::add_action([] {
dialog::editNumber(numturns, 0, 100, 1, 0, XLAT("monster turns"), XLAT("Number of turns to pass. Useful when simulating butterflies or cellular automata."));
});
}
#if CAP_RUG #if CAP_RUG
if(rug::rugged) { if(rug::rugged) {
@ -1010,7 +1522,7 @@ EX void show() {
}); });
} }
#endif #endif
if(models::model_has_orientation()) if(models::has_orientation(vpconf.model))
animator(XLAT("model rotation"), ballangle_rotation, 'I'); animator(XLAT("model rotation"), ballangle_rotation, 'I');
else if(among(pmodel, mdHyperboloid, mdHemisphere, mdBall)) else if(among(pmodel, mdHyperboloid, mdHemisphere, mdBall))
animator(XLAT("3D rotation"), ballangle_rotation, '3'); animator(XLAT("3D rotation"), ballangle_rotation, '3');
@ -1042,7 +1554,8 @@ EX void show() {
dialog::add_action([] () { dialog::editNumber(noframes, 0, 300, 30, 5, XLAT("frames to record"), ""); }); dialog::add_action([] () { dialog::editNumber(noframes, 0, 300, 30, 5, XLAT("frames to record"), ""); });
dialog::addSelItem(XLAT("record to a file"), animfile, 'R'); dialog::addSelItem(XLAT("record to a file"), animfile, 'R');
dialog::add_action([] () { dialog::add_action([] () {
dialog::openFileDialog(animfile, XLAT("record to a file"), shot::make_svg ? ".svg" : ".png", record_animation); dialog::openFileDialog(animfile, XLAT("record to a file"),
shot::format_extension(), record_animation);
}); });
#endif #endif
dialog::addBack(); dialog::addBack();
@ -1188,9 +1701,9 @@ startanim null_animation { "", no_init, [] { gamescreen(2); }};
startanim joukowsky { "Joukowsky transform", no_init, [] { startanim joukowsky { "Joukowsky transform", no_init, [] {
dynamicval<eModel> dm(pmodel, mdJoukowskyInverted); dynamicval<eModel> dm(pmodel, mdJoukowskyInverted);
dynamicval<ld> dt(models::model_orientation, ticks / 25.); dynamicval<ld> dt(pconf.model_orientation, ticks / 25.);
dynamicval<int> dv(vid.use_smart_range, 2); dynamicval<int> dv(vid.use_smart_range, 2);
dynamicval<ld> ds(vid.scale, 1/4.); dynamicval<ld> ds(pconf.scale, 1/4.);
models::configure(); models::configure();
dynamicval<color_t> dc(ringcolor, 0); dynamicval<color_t> dc(ringcolor, 0);
gamescreen(2); gamescreen(2);
@ -1199,7 +1712,7 @@ startanim joukowsky { "Joukowsky transform", no_init, [] {
startanim bandspin { "spinning in the band model", no_init, [] { startanim bandspin { "spinning in the band model", no_init, [] {
dynamicval<eModel> dm(pmodel, mdBand); dynamicval<eModel> dm(pmodel, mdBand);
dynamicval<ld> dt(models::model_orientation, ticks / 25.); dynamicval<ld> dt(pconf.model_orientation, ticks / 25.);
dynamicval<int> dv(vid.use_smart_range, 2); dynamicval<int> dv(vid.use_smart_range, 2);
models::configure(); models::configure();
gamescreen(2); gamescreen(2);
@ -1212,28 +1725,32 @@ startanim perspective { "projection distance", no_init, [] {
x /= 2; x /= 2;
x *= 1.5; x *= 1.5;
x = tan(x); x = tan(x);
dynamicval<ld> da(vid.alpha, x); dynamicval<ld> da(pconf.alpha, x);
dynamicval<ld> ds(vid.scale, (1+x)/2); dynamicval<ld> ds(pconf.scale, (1+x)/2);
calcparam(); calcparam();
gamescreen(2); gamescreen(2);
explorable(projectionDialog); explorable(projectionDialog);
}}; }};
startanim rug { "Hypersian Rug", [] { startanim rug { "Hypersian Rug", [] {
if(!CAP_RUG) { pick(); return; } #if CAP_RUG
rug::init(), rug::rugged = false; }, [] { rug::init();
rug::rugged = false;
#else
pick();
#endif
}, [] {
dynamicval<bool> b(rug::rugged, true); dynamicval<bool> b(rug::rugged, true);
rug::physics(); rug::physics();
rug::apply_rotation(cspin(1, 2, ticks / 3000.)); dynamicval<transmatrix> t(rug::rugView, cspin(1, 2, ticks / 3000.) * rug::rugView);
gamescreen(2); gamescreen(2);
rug::apply_rotation(cspin(1, 2, -ticks / 3000.));
if(!rug::rugged) current = &null_animation; if(!rug::rugged) current = &null_animation;
explorable([] { rug::rugged = true; pushScreen(rug::show); }); explorable([] { rug::rugged = true; pushScreen(rug::show); });
}}; }};
startanim spin_around { "spinning around", no_init, [] { startanim spin_around { "spinning around", no_init, [] {
dynamicval<ld> da(vid.alpha, 999); dynamicval<ld> da(pconf.alpha, 999);
dynamicval<ld> ds(vid.scale, 500); dynamicval<ld> ds(pconf.scale, 500);
ld alpha = 2 * M_PI * ticks / 10000.; ld alpha = 2 * M_PI * ticks / 10000.;
ld circle_radius = acosh(2.); ld circle_radius = acosh(2.);
dynamicval<transmatrix> dv(View, spin(-cos_auto(circle_radius)*alpha) * xpush(circle_radius) * spin(alpha) * View); dynamicval<transmatrix> dv(View, spin(-cos_auto(circle_radius)*alpha) * xpush(circle_radius) * spin(alpha) * View);

View File

@ -16,8 +16,9 @@ constexpr flagtype GF_TEXTURE = 1;
constexpr flagtype GF_VARCOLOR = 2; constexpr flagtype GF_VARCOLOR = 2;
constexpr flagtype GF_LIGHTFOG = 4; constexpr flagtype GF_LIGHTFOG = 4;
constexpr flagtype GF_LEVELS = 8; constexpr flagtype GF_LEVELS = 8;
constexpr flagtype GF_TEXTURE_SHADED = 16;
constexpr flagtype GF_which = 15; constexpr flagtype GF_which = 31;
constexpr flagtype SF_PERS3 = 256; constexpr flagtype SF_PERS3 = 256;
constexpr flagtype SF_BAND = 512; constexpr flagtype SF_BAND = 512;
@ -31,6 +32,8 @@ constexpr flagtype SF_ZFOG = 65536;
constexpr flagtype SF_ODSBOX = (1<<17); constexpr flagtype SF_ODSBOX = (1<<17);
#endif #endif
EX bool solv_all;
#if HDR #if HDR
/* standard attribute bindings */ /* standard attribute bindings */
/* taken from: https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/attributes.php */ /* taken from: https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/attributes.php */
@ -62,7 +65,15 @@ shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
varying += "varying mediump vec4 vColor;\n"; varying += "varying mediump vec4 vColor;\n";
fmain += "gl_FragColor = vColor;\n"; fmain += "gl_FragColor = vColor;\n";
if(shader_flags & GF_TEXTURE) { if(shader_flags & GF_TEXTURE_SHADED) {
vsh += "attribute mediump vec3 aTexture;\n";
varying += "varying mediump vec3 vTexCoord;\n";
fsh += "uniform mediump sampler2D tTexture;\n";
vmain += "vTexCoord = aTexture;\n";
fmain += "gl_FragColor *= texture2D(tTexture, vTexCoord.xy);\n";
fmain += "gl_FragColor.rgb *= vTexCoord.z;\n";
}
else if(shader_flags & GF_TEXTURE) {
vsh += "attribute mediump vec2 aTexture;\n"; vsh += "attribute mediump vec2 aTexture;\n";
varying += "varying mediump vec2 vTexCoord;\n"; varying += "varying mediump vec2 vTexCoord;\n";
fsh += "uniform mediump sampler2D tTexture;\n"; fsh += "uniform mediump sampler2D tTexture;\n";
@ -180,6 +191,9 @@ shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
case gcSolNIH: case gcSolNIH:
switch(sn::geom()) { switch(sn::geom()) {
case gSol: case gSol:
if(solv_all) {
vsh += "\n#define SOLV_ALL\n";
}
vsh += sn::shader_symsol; vsh += sn::shader_symsol;
break; break;
case gNIH: case gNIH:
@ -311,6 +325,7 @@ void display_data::set_projection(int ed) {
id <<= 6; id |= spherephase; id <<= 6; id |= spherephase;
id <<= 1; if(vid.consider_shader_projection) id |= 1; id <<= 1; if(vid.consider_shader_projection) id |= 1;
id <<= 2; id |= (spherespecial & 3); id <<= 2; id |= (spherespecial & 3);
if(sol && solv_all) id |= 1;
if(in_h2xe()) id |= 1; if(in_h2xe()) id |= 1;
if(in_s2xe()) id |= 2; if(in_s2xe()) id |= 2;
shared_ptr<glhr::GLprogram> selected; shared_ptr<glhr::GLprogram> selected;
@ -359,7 +374,7 @@ void display_data::set_projection(int ed) {
if(pmodel == mdManual) return; if(pmodel == mdManual) return;
if(vid.stretch != 1 && (shader_flags & SF_DIRECT)) glhr::projection_multiply(glhr::scale(vid.stretch, 1, 1)); if(pconf.stretch != 1 && (shader_flags & SF_DIRECT) && pmodel != mdPixel) glhr::projection_multiply(glhr::scale(1, pconf.stretch, 1));
if(vid.stereo_mode != sODS) if(vid.stereo_mode != sODS)
eyewidth_translate(ed); eyewidth_translate(ed);
@ -367,10 +382,8 @@ void display_data::set_projection(int ed) {
auto ortho = [&] (ld x, ld y) { auto ortho = [&] (ld x, ld y) {
glhr::glmatrix M = glhr::ortho(x, y, 1); glhr::glmatrix M = glhr::ortho(x, y, 1);
if(shader_flags & SF_ZFOG) { if(shader_flags & SF_ZFOG) {
using models::clip_max; M[2][2] = 2 / (pconf.clip_max - pconf.clip_min);
using models::clip_min; M[3][2] = (pconf.clip_min + pconf.clip_max) / (pconf.clip_max - pconf.clip_min);
M[2][2] = 2 / (clip_max - clip_min);
M[3][2] = (clip_min + clip_max) / (clip_max - clip_min);
auto cols = glhr::acolor(darkena(backcolor, 0, 0xFF)); auto cols = glhr::acolor(darkena(backcolor, 0, 0xFF));
glUniform4f(selected->uFogColor, cols[0], cols[1], cols[2], cols[3]); glUniform4f(selected->uFogColor, cols[0], cols[1], cols[2], cols[3]);
} }
@ -409,7 +422,7 @@ void display_data::set_projection(int ed) {
glhr::fog_max(1/sightranges[geometry], darkena(backcolor, 0, 0xFF)); glhr::fog_max(1/sightranges[geometry], darkena(backcolor, 0, 0xFF));
} }
else { else {
if(vid.alpha > -1) { if(pconf.alpha > -1) {
// Because of the transformation from H3 to the Minkowski hyperboloid, // Because of the transformation from H3 to the Minkowski hyperboloid,
// points with negative Z can be generated in some 3D settings. // points with negative Z can be generated in some 3D settings.
// This happens for points below the camera, but above the plane. // This happens for points below the camera, but above the plane.
@ -420,14 +433,14 @@ void display_data::set_projection(int ed) {
GLfloat sc = current_display->radius / (cd->ysize/2.); GLfloat sc = current_display->radius / (cd->ysize/2.);
glhr::projection_multiply(glhr::frustum(cd->xsize / cd->ysize, 1)); glhr::projection_multiply(glhr::frustum(cd->xsize / cd->ysize, 1));
glhr::projection_multiply(glhr::scale(sc, -sc, -1)); glhr::projection_multiply(glhr::scale(sc, -sc, -1));
glhr::projection_multiply(glhr::translate(0, 0, vid.alpha)); glhr::projection_multiply(glhr::translate(0, 0, pconf.alpha));
if(ed) glhr::projection_multiply(glhr::translate(vid.ipd * ed/2, 0, 0)); if(ed) glhr::projection_multiply(glhr::translate(vid.ipd * ed/2, 0, 0));
} }
if(selected->uPP != -1) { if(selected->uPP != -1) {
glhr::glmatrix pp = glhr::id; glhr::glmatrix pp = glhr::id;
if(get_shader_flags() & SF_USE_ALPHA) if(get_shader_flags() & SF_USE_ALPHA)
pp[3][2] = GLfloat(vid.alpha); pp[3][2] = GLfloat(pconf.alpha);
if(get_shader_flags() & SF_ORIENT) { if(get_shader_flags() & SF_ORIENT) {
if(GDIM == 3) for(int a=0; a<4; a++) if(GDIM == 3) for(int a=0; a<4; a++)
@ -440,7 +453,7 @@ void display_data::set_projection(int ed) {
} }
if(selected->uAlpha != -1) if(selected->uAlpha != -1)
glhr::set_ualpha(vid.alpha); glhr::set_ualpha(pconf.alpha);
if(selected->uLevelLines != -1) { if(selected->uLevelLines != -1) {
glUniform1f(selected->uLevelLines, levellines); glUniform1f(selected->uLevelLines, levellines);
@ -458,12 +471,12 @@ void display_data::set_projection(int ed) {
if(selected->shader_flags & SF_HALFPLANE) { if(selected->shader_flags & SF_HALFPLANE) {
glhr::projection_multiply(glhr::translate(0, 1, 0)); glhr::projection_multiply(glhr::translate(0, 1, 0));
glhr::projection_multiply(glhr::scale(-1, 1, 1)); glhr::projection_multiply(glhr::scale(-1, 1, 1));
glhr::projection_multiply(glhr::scale(models::halfplane_scale, models::halfplane_scale, GDIM == 3 ? models::halfplane_scale : 1)); glhr::projection_multiply(glhr::scale(pconf.halfplane_scale, pconf.halfplane_scale, GDIM == 3 ? pconf.halfplane_scale : 1));
glhr::projection_multiply(glhr::translate(0, 0.5, 0)); glhr::projection_multiply(glhr::translate(0, 0.5, 0));
} }
if(vid.camera_angle && pmodel != mdPixel) { if(pconf.camera_angle && pmodel != mdPixel) {
ld cam = vid.camera_angle * degree; ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam); GLfloat cc = cos(cam);
GLfloat ss = sin(cam); GLfloat ss = sin(cam);

View File

@ -236,7 +236,7 @@ bool isBullet(monster *m) {
bool isPlayer(monster *m) { return m->type == moPlayer; } bool isPlayer(monster *m) { return m->type == moPlayer; }
bool isMonster(monster *m) { return m->type != moPlayer && m->type != moBullet; } bool isMonster(monster *m) { return m->type != moPlayer && m->type != moBullet; }
EX hookset<bool(shmup::monster*)> *hooks_kill; EX hookset<bool(shmup::monster*)> hooks_kill;
void killMonster(monster* m, eMonster who_kills, flagtype flags = 0) { void killMonster(monster* m, eMonster who_kills, flagtype flags = 0) {
int tk = tkills(); int tk = tkills();
@ -815,7 +815,7 @@ void movePlayer(monster *m, int delta) {
hyperpoint jh = hpxy(mdx/100.0, mdy/100.0); hyperpoint jh = hpxy(mdx/100.0, mdy/100.0);
hyperpoint ctr = m->pat * C0; hyperpoint ctr = m->pat * C0;
if(sphere && vid.alpha > 1.001) for(int i=0; i<3; i++) ctr[i] = -ctr[i]; if(sphere && pconf.alpha > 1.001) for(int i=0; i<3; i++) ctr[i] = -ctr[i];
hyperpoint h = inverse(m->pat) * rgpushxto0(ctr) * jh; hyperpoint h = inverse(m->pat) * rgpushxto0(ctr) * jh;
@ -1104,7 +1104,7 @@ void movePlayer(monster *m, int delta) {
int i0 = i; int i0 = i;
for(int a=0; a<3; a++) v[a] = (i0 % 3) - 1, i0 /= 3; for(int a=0; a<3; a++) v[a] = (i0 % 3) - 1, i0 /= 3;
v = v * .1 / hypot_d(3, v); v = v * .1 / hypot_d(3, v);
transmatrix T1 = (i == 13) ? nat : parallel_transport(nat, m->ori, v, 2); transmatrix T1 = (i == 13) ? nat : parallel_transport(nat, m->ori, v);
cell *c3 = c2; cell *c3 = c2;
while(true) { while(true) {
cell *c4 = findbaseAround(tC0(T1), c3, 1); cell *c4 = findbaseAround(tC0(T1), c3, 1);
@ -1619,10 +1619,10 @@ void moveBullet(monster *m, int delta) {
m->dead = true; m->dead = true;
if(inertia_based) { if(inertia_based) {
nat = parallel_transport(nat, m->ori, m->inertia * delta, 10); nat = parallel_transport(nat, m->ori, m->inertia * delta);
} }
else else
nat = parallel_transport(nat, m->ori, fronttangent(delta * SCALE * m->vel / speedfactor()), 10); nat = parallel_transport(nat, m->ori, fronttangent(delta * SCALE * m->vel / speedfactor()));
cell *c2 = m->findbase(nat, 1); cell *c2 = m->findbase(nat, 1);
if(m->parent && isPlayer(m->parent) && markOrb(itOrbLava) && c2 != m->base && !isPlayerOn(m->base)) if(m->parent && isPlayer(m->parent) && markOrb(itOrbLava) && c2 != m->base && !isPlayerOn(m->base))
@ -2106,14 +2106,14 @@ void moveMonster(monster *m, int delta) {
if(inertia_based) { if(inertia_based) {
if(igo) return; if(igo) return;
nat = parallel_transport(nat, m->ori, m->inertia * delta, 10); nat = parallel_transport(nat, m->ori, m->inertia * delta);
} }
else if(WDIM == 3 && igo) { else if(WDIM == 3 && igo) {
ld fspin = rand() % 1000; ld fspin = rand() % 1000;
nat = parallel_transport(nat0, m->ori, cspin(1,2,fspin) * spin(igospan[igo]) * xtangent(step), 10); nat = parallel_transport(nat0, m->ori, cspin(1,2,fspin) * spin(igospan[igo]) * xtangent(step));
} }
else { else {
nat = parallel_transport(nat0, m->ori, spin(igospan[igo]) * xtangent(step), 10); nat = parallel_transport(nat0, m->ori, spin(igospan[igo]) * xtangent(step));
} }
if(m->type != moRagingBull && !peace::on) if(m->type != moRagingBull && !peace::on)
@ -2456,7 +2456,7 @@ EX void fixStorage() {
for(monster *m: restore) m->store(); for(monster *m: restore) m->store();
} }
EX hookset<bool(int)> *hooks_turn; EX hookset<bool(int)> hooks_turn;
EX void turn(int delta) { EX void turn(int delta) {
@ -2771,7 +2771,7 @@ EX bool boatAt(cell *c) {
return false; return false;
} }
EX hookset<bool(const transmatrix&, cell*, shmup::monster*)> *hooks_draw; EX hookset<bool(const transmatrix&, cell*, shmup::monster*)> hooks_draw;
EX void clearMonsters() { EX void clearMonsters() {
for(mit it = monstersAt.begin(); it != monstersAt.end(); it++) for(mit it = monstersAt.begin(); it != monstersAt.end(); it++)
@ -2833,7 +2833,7 @@ EX void virtualRebase(shmup::monster *m) {
virtualRebase(m->base, m->at); virtualRebase(m->base, m->at);
} }
EX hookset<bool(shmup::monster*, string&)> *hooks_describe; EX hookset<bool(shmup::monster*, string&)> hooks_describe;
EX void addShmupHelp(string& out) { EX void addShmupHelp(string& out) {
if(shmup::mousetarget && sqdist(mouseh, tC0(shmup::mousetarget->pat)) < .1) { if(shmup::mousetarget && sqdist(mouseh, tC0(shmup::mousetarget->pat)) < .1) {
@ -2844,7 +2844,7 @@ EX void addShmupHelp(string& out) {
} }
} }
auto hooks = addHook(clearmemory, 0, shmup::clearMemory) + auto hooks = addHook(hooks_clearmemory, 0, shmup::clearMemory) +
addHook(hooks_gamedata, 0, shmup::gamedata) + addHook(hooks_gamedata, 0, shmup::gamedata) +
addHook(hooks_removecells, 0, [] () { addHook(hooks_removecells, 0, [] () {
for(mit it = monstersAt.begin(); it != monstersAt.end();) { for(mit it = monstersAt.begin(); it != monstersAt.end();) {

Binary file not shown.

Binary file not shown.

View File

@ -83,7 +83,7 @@ int musfadeval = 2000;
eLand cid = laNone; eLand cid = laNone;
hookset<bool(eLand&)> *hooks_music; hookset<bool(eLand&)> hooks_music;
bool music_out_of_focus = false; bool music_out_of_focus = false;
@ -129,7 +129,7 @@ EX void handlemusic() {
} }
} }
hookset<bool(eLand&)> *hooks_resetmusic; hookset<bool(eLand&)> hooks_resetmusic;
EX void resetmusic() { EX void resetmusic() {
if(audio && musicvolume) { if(audio && musicvolume) {
@ -218,7 +218,7 @@ string wheresounds = SOUNDDESTDIR;
string wheresounds = HYPERPATH "sounds/"; string wheresounds = HYPERPATH "sounds/";
#endif #endif
hookset<bool(const string& s, int vol)> *hooks_sound; hookset<bool(const string& s, int vol)> hooks_sound;
EX void playSound(cell *c, const string& fname, int vol) { EX void playSound(cell *c, const string& fname, int vol) {
LATE( hr::playSound(c, fname, vol); ) LATE( hr::playSound(c, fname, vol); )

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More