1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-06-24 06:03:23 +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) {
ld z = 1-x-y;
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);
});
}
void geometry_information::balltriangle(hyperpoint a, hyperpoint b, hyperpoint c, ld rad, int lev) {
if(lev == 0) {
hpcpush(direct_exp(a, 10));
hpcpush(direct_exp(b, 10));
hpcpush(direct_exp(c, 10));
hpcpush(direct_exp(a));
hpcpush(direct_exp(b));
hpcpush(direct_exp(c));
}
else {
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);
finishshape();
extra_vertices();

View File

@ -952,12 +952,12 @@ EX void achievement_display() {
col /= 10; col *= 0x10101;
displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, achievementMessage[0], col & 0xFFFF00, 8);
int w = 2 * vid.fsize;
#if ISMOBILE==0
#if !ISMOBILE
while(w>3 && textwidth(w, achievementMessage[1]) > vid.xres) w--;
#endif
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, w, achievementMessage[1], col, 8);
w = vid.fsize;
#if ISMOBILE==0
#if !ISMOBILE
while(w>3 && textwidth(w, achievementMessage[2]) > vid.xres) w--;
#endif
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; }
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() {
transmatrix at = Id;
vertices.clear();
int n = isize(angles);
hyperpoint ctr = Hypc;
vector<transmatrix> matrices;
for(int i=0; i<n; i++) {
matrices.push_back(at);
println(hlog, "at = ", at);
vertices.push_back(tC0(at));
ctr += tC0(at);
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) {
// this may happen for some spherical tilings
// 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; }
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";
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) {
fhstream f(fname, "rt");
string s;
@ -102,7 +191,7 @@ EX void load(const string& fname) {
int ai;
if(ep.next() == ')') ai = isize(c.shapes)-1;
else ai = ep.iparse();
verify_index(ai, c.shapes);
verify_index(ai, c.shapes, ep);
c.shapes[ai].flags |= f;
ep.force_eat(")");
};
@ -131,16 +220,19 @@ EX void load(const string& fname) {
ginf[gArbitrary].g = giEuclid2;
ginf[gArbitrary].sides = 7;
set_flag(ginf[gArbitrary].flags, qBOUNDED, false);
set_flag(ginf[gArbitrary].flags, qAFFINE, true);
}
else if(ep.eat("h2.")) {
ginf[gArbitrary].g = giHyperb2;
ginf[gArbitrary].sides = 7;
set_flag(ginf[gArbitrary].flags, qBOUNDED, false);
set_flag(ginf[gArbitrary].flags, qAFFINE, false);
}
else if(ep.eat("s2.")) {
ginf[gArbitrary].g = giSphere2;
ginf[gArbitrary].sides = 5;
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("angleofs(")) angleofs = real(ep.parsepar());
@ -157,43 +249,11 @@ EX void load(const string& fname) {
string tok = ep.next_token();
ep.force_eat("=");
ep.extra_params[tok] =ep.parsepar();
if(debugflags & DF_GEOM)
println(hlog, "let ", tok, " = ", real(ep.extra_params[tok]));
}
else if(ep.eat("unittile(")) {
c.shapes.emplace_back();
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("unittile(")) load_tile(ep, true);
else if(ep.eat("tile(")) load_tile(ep, false);
else if(ep.eat("conway(\"")) {
string s = "";
while(true) {
@ -201,7 +261,7 @@ EX void load(const string& fname) {
if(ep.eat("(")) m = 0;
else if(ep.eat("[")) m = 1;
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 as = ep.iparse();
@ -221,18 +281,18 @@ EX void load(const string& fname) {
ep.force_eat(")");
}
else if(ep.eat("c(")) {
int ai = ep.iparse(); verify_index(ai, c.shapes); ep.force_eat(",");
int as = ep.iparse(); verify_index(as, c.shapes[ai]); ep.force_eat(",");
int bi = ep.iparse(); verify_index(bi, c.shapes); ep.force_eat(",");
int bs = ep.iparse(); verify_index(bs, c.shapes[bi]); 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); 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); ep.force_eat(",");
int m = ep.iparse(); ep.force_eat(")");
c.shapes[ai].connections[as] = make_tuple(bi, bs, m);
c.shapes[bi].connections[bs] = make_tuple(ai, as, m);
}
else if(ep.eat("subline(")) {
int ai = ep.iparse(); verify_index(ai, c.shapes); ep.force_eat(",");
int as = ep.iparse(); verify_index(as, c.shapes[ai]); ep.force_eat(",");
int bs = ep.iparse(); verify_index(bs, c.shapes[ai]); 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); 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);
}
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() {
return ginf[gEuclid].g;
}
@ -268,6 +411,56 @@ EX hrmap *current_altmap;
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 {
heptagon *origin;
heptagon *getOrigin() override { return origin; }
@ -309,6 +502,8 @@ struct hrmap_arbi : hrmap {
celllister cl(origin->c7, 1000, 200, NULL);
ginf[geometry].distlimit[0] = cgi.base_distlimit = cl.dists.back();
if(sphere) cgi.base_distlimit = SEE_ALL;
if(cgflags & qAFFINE) cgi.base_distlimit = 3;
}
~hrmap_arbi() {
@ -330,58 +525,13 @@ struct hrmap_arbi : hrmap {
void verify() override { }
transmatrix adj(heptagon *h, int dl) override {
auto& c = current;
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;
return get_adj(current, id_of(h), dl, h->c.move(dl) ? h->c.spin(dl) : -1);
}
heptagon *create_step(heptagon *h, int d) override {
int t = id_of(h);
const auto& p = arbi_matrix[h];
heptagon *alt = p.first;
auto& sh = current.shapes[t];
auto& co = sh.connections[d];
@ -391,6 +541,48 @@ struct hrmap_arbi : hrmap {
int m = get<2>(co);
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);
if(hyperbolic) {
@ -470,6 +662,46 @@ struct hrmap_arbi : hrmap {
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
int readArgs() {
using namespace arg;
@ -477,17 +709,8 @@ int readArgs() {
if(0) ;
else if(argis("-arbi")) {
PHASEFROM(2);
stop_game();
shift();
set_geometry(gArbitrary);
try {
load(args());
}
catch(hr_parse_exception& ex) {
println(hlog, "failed: ", ex.s);
exit(3);
}
ginf[gArbitrary].tiling_name = current.name;
run(args());
}
else return 1;
return 0;
@ -498,7 +721,7 @@ auto hook = addHook(hooks_args, 100, readArgs);
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) {
return current.shapes[id_of(c->master)].flags & arcm::sfLINE;
@ -511,20 +734,10 @@ EX bool pseudohept(cell *c) {
EX void choose() {
dialog::openFileDialog(tes, XLAT("open a tiling"), ".tes",
[] () {
stop_game();
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();
run(tes);
return true;
});
}
EX }
}
}

View File

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

View File

@ -219,7 +219,7 @@ void setcameraangle(bool b) { }
#if CAP_GL
#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
EX void eyewidth_translate(int ed) {
@ -492,8 +492,6 @@ int gl_width(int size, const char *s) {
return x;
}
vector<glhr::textured_vertex> tver;
glhr::textured_vertex charvertex(int x1, int y1, ld tx, ld ty) {
glhr::textured_vertex res;
res.coords[0] = x1;
@ -842,12 +840,12 @@ EX ld realradius() {
ld vradius = current_display->radius;
if(sphere) {
if(sphereflipped())
vradius /= sqrt(vid.alpha*vid.alpha - 1);
vradius /= sqrt(pconf.alpha*pconf.alpha - 1);
else
vradius = 1e12; // use the following
}
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);
return vradius;
}
@ -857,14 +855,14 @@ EX void drawmessage(const string& s, int& y, color_t col) {
int space;
if(dual::state)
space = vid.xres;
else if(y > current_display->ycenter + rrad * vid.stretch)
else if(y > current_display->ycenter + rrad * pconf.stretch)
space = vid.xres;
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)
space = current_display->xcenter - rrad;
else if(y > current_display->ycenter - vid.fsize - rrad * vid.stretch)
space = current_display->xcenter - rhypot(rrad, (current_display->ycenter-vid.fsize-y) / 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) / pconf.stretch);
else
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;
for(int r=0; r<pts; r++) {
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);
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
gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size);
#elif CAP_SDLGFX
if(vid.stretch == 1) {
if(pconf.stretch == 1) {
if(fillcolor) filledCircleColor(s, x, y, size, fillcolor);
if(color) ((vid.antialias && AA_NOGL)?aacircleColor:circleColor) (s, x, y, size, color);
}
else {
if(fillcolor) filledEllipseColor(s, x, y, size, size * vid.stretch, fillcolor);
if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * vid.stretch, color);
if(fillcolor) filledEllipseColor(s, x, y, size, size * pconf.stretch, fillcolor);
if(color) ((vid.antialias && AA_NOGL)?aaellipseColor:ellipseColor) (s, x, y, size, size * pconf.stretch, color);
}
#elif CAP_SDL
int pts = size * 4;
@ -1022,7 +1020,7 @@ EX void displayColorButton(int x, int y, const string& name, int key, int align,
}
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;

View File

@ -1332,7 +1332,7 @@ EX bool quickfind(eLand l) {
#define I2000 (INVLUCK?600:2000)
#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) {
int i = callhandlers(-1, hooks_wallchance, c, deepOcean);

View File

@ -9,11 +9,17 @@
namespace hr {
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 */
EX bool in() { return cgflags & qBINARY; }
EX bool in() {
#if CAP_BT
return cgflags & qBINARY;
#else
return false;
#endif
}
#if CAP_BT
#if HDR
enum bindir {
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();
blizzardcells.clear();
bcells.clear();

View File

@ -202,6 +202,9 @@ EX cell *createMov(cell *c, int d) {
else if(kite::in())
kite::find_cell_connection(c, d);
#endif
else if(fake::in()) {
return FPIU(createMov(c, d));
}
#if CAP_IRR
else if(IRREGULAR) {
irr::link_cell(c, d);
@ -279,7 +282,7 @@ EX void eumerge(cell* c1, int s1, cell *c2, int s2, bool mirror) {
// map<pair<eucoord, eucoord>, cell*> euclidean;
EX hookset<hrmap*()> *hooks_newmap;
EX hookset<hrmap*()> hooks_newmap;
/** create a map in the current geometry */
EX void initcells() {
@ -287,6 +290,7 @@ EX void initcells() {
hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap);
if(res) currentmap = res;
else if(fake::in()) currentmap = fake::new_map();
else if(asonov::in()) currentmap = asonov::new_map();
else if(nonisotropic || hybri) currentmap = nisot::new_map();
#if CAP_CRYSTAL
@ -1175,7 +1179,7 @@ EX void clearCellMemory() {
gp::gp_adj.clear();
}
auto cellhooks = addHook(clearmemory, 500, clearCellMemory);
auto cellhooks = addHook(hooks_clearmemory, 500, clearCellMemory);
EX bool isNeighbor(cell *c1, cell *c2) {
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 laRose: case laPower: case laWildWest: case laHalloween: case laRedRock:
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 laRlyeh: case laHell: case laCrossroads: case laJungle:
case laAlchemist: case laFrog:
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:
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() && c->move(t) < c) continue;
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
@ -1580,6 +1600,10 @@ void celldrawer::draw_features_and_walls_3d() {
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);
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(a < 4 && among(geometry, gHoroTris, gBinary3) && 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 {
playerV = V * ddspin(c, cwt.spin, 0);
if(cwt.mirrored) playerV = playerV * Mirror;
if((!confusingGeometry() && !inmirrorcount) || eqmatrix(V, current_display->which_copy, 1e-2))
if((!confusingGeometry() && !fake::in() && !inmirrorcount) || eqmatrix(V, current_display->which_copy, 1e-2))
current_display->which_copy = V;
if(orig) cwtV = playerV;
}

View File

@ -3904,3 +3904,44 @@ Geometries:
- reverse FPP mode on iOS should be fixed
- fixed the missing message when PC could not stay in place
- 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,
gArnoldCat, gArbitrary, gInfOrder4, gCrystal534,
gSpace535, gSpace536, gSeifertCover, gSeifertWeber, gHomologySphere,
gInfOrderMixed, gSpace436,
gInfOrderMixed, gSpace436, gFake,
gGUARD};
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 qRAYONLY = Flag(23);
static const flagtype qAFFINE = Flag(24);
// note: dnext assumes that x&7 equals 7
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},
{"{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}","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}","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}","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}","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-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},
@ -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},
{"{?,oo}", "none", "{3/4,∞} (infinite triangles and squares)", "ooxm", 3, OINF, qIDEAL | qINFMIXED, giHyperb2, 0x49400, {{6, 6}}, eVariation::pure},
{"{4,3,6}","none", "{4,3,6} hyperbolic honeycomb", "436", 6, 6, qIDEAL, giHyperb3, 0x31400, {{7, 2}}, eVariation::pure},
{"?", "none", "fake", "", 0, 0, qRAYONLY, giHyperb3, 0x31400, {{7, 2}}, eVariation::pure}
};
// bits: 9, 10, 15, 16, (reserved for later) 17, 18

View File

@ -347,7 +347,7 @@ int arg::readCommon() {
EX purehookset hooks_config;
EX hookset<int()> *hooks_args;
EX hookset<int()> hooks_args;
namespace arg {
@ -357,12 +357,13 @@ namespace arg {
curphase = phase;
callhooks(hooks_config);
while(pos < isize(argument)) {
for(auto& h: *hooks_args) {
int r = h.second(); if(r == 2) return; if(r == 0) { lshift(); goto cont; }
int r = callhandlers(1, hooks_args);
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 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
EX string fillup(string s) {
@ -3372,6 +3380,12 @@ EX namespace ca {
shift(); wlive = eWall(argi());
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()[1] != 'c') return 1;
int livedead = args()[2] - '0';
@ -3395,13 +3409,16 @@ EX namespace ca {
EX void simulate() {
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);
std::vector<bool> willlive(dcs);
int old = 0, xold = 0;
for(int i=0; i<dcs; i++) {
cell *c = allcells[i];
if(c->land != laCA) return;
if(c->land != laCA) continue;
int nei = 0, live = 0;
for(cell *c2: adj_minefield_cells(c)) {
nei++; if(c2->wall == wlive) live++;
@ -3412,13 +3429,19 @@ EX namespace ca {
}
for(int i=0; i<dcs; i++) {
cell *c = allcells[i];
auto last = c->wall;
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 }
auto ccm = addHook(clearmemory, 0, [] () {
auto ccm = addHook(hooks_clearmemory, 0, [] () {
heat::offscreen_heat.clear();
heat::offscreen_fire.clear();
princess::clear();
@ -3825,12 +3848,12 @@ EX namespace halloween {
}
int id = hrand(100);
if(items[itTreat] == 1) {
#if ISMOBILE==0
#if !ISMOBILE
addMessage(XLAT("Hint: use arrow keys to scroll."));
#endif
}
else if(items[itTreat] == 2) {
#if ISMOBILE==0
#if !ISMOBILE
addMessage(XLAT("Hint: press 1 2 3 4 to change the projection."));
#endif
}

View File

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

View File

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

View File

@ -14,8 +14,11 @@ EX bool outoffocus = false;
EX int mousex, mousey;
EX hyperpoint mouseh, mouseoh;
EX bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick;
EX bool forcetarget, lshiftclick, lctrlclick, numlock_on;
EX bool pandora_leftclick, pandora_rightclick;
EX bool lshiftclick, rshiftclick, lctrlclick, rctrlclick, anyshiftclick, anyctrlclick, wheelclick;
EX bool targetclick, hiliteclick, forcetarget, numlock_on;
EX bool gtouched;
EX bool holdmouse;
@ -76,7 +79,7 @@ EX movedir vectodir(hyperpoint P) {
transmatrix U = ggmatrix(cwt.at);
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);
transmatrix Centered = sphereflip * rgpushxto0(H);
@ -87,8 +90,8 @@ EX movedir vectodir(hyperpoint P) {
for(int i=0; i<cwt.at->type; i++) {
transmatrix T = currentmap->adj(cwt.at, (cwt + i).spin);
ld d1 = geo_dist(U * T * C0, Centered * P, iTable);
ld d2 = geo_dist(U * T * C0, Centered * C0, iTable);
ld d1 = geo_dist(U * T * C0, Centered * P);
ld d2 = geo_dist(U * T * C0, Centered * C0);
dirdist[i] = d1 - d2;
//xspinpush0(-i * 2 * M_PI /cwt.at->type, .5), P);
}
@ -301,6 +304,80 @@ transmatrix zforward_push(ld z) {
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) {
if(mousepan && dual::split([=] { handlePanning(sym, uni); })) return;
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(rug::rugged || smooth_scrolling) {
return;
}
rug::using_rugview urv;
#if !ISPANDORA
if(sym == SDLK_END && GDIM == 3) {
shift_view(ztangent(-0.2*shiftmul)), didsomething = true, playermoved = false;
}
if(sym == SDLK_HOME && GDIM == 3) {
shift_view(ztangent(+0.2*shiftmul)), didsomething = true, playermoved = false;
}
if(sym == SDLK_RIGHT) {
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;
if(!smooth_scrolling) {
if(sym == SDLK_END) full_forward_camera(-0.2*shiftmul);
if(sym == SDLK_HOME) full_forward_camera(0.2*shiftmul);
if(sym == SDLK_RIGHT) full_rotate_camera(0, -0.2*shiftmul);
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_DOWN) full_rotate_camera(1, -0.2*shiftmul);
}
#endif
if(sym == SDLK_PAGEUP) {
if(history::on)
models::rotation++;
else
rotate_view(spin(M_PI/cgi.S21/2*shiftmul)), didsomething = true;
if(!smooth_scrolling) {
if(sym == SDLK_PAGEUP) full_rotate_view(1, M_PI/cgi.S21/2*shiftmul);
if(sym == SDLK_PAGEDOWN) full_rotate_view(-1, -M_PI/cgi.S21/2*shiftmul);
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
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) {
ld jx = (mousex - current_display->xcenter - .0) / current_display->radius / 10;
ld jy = (mousey - current_display->ycenter - .0) / current_display->radius / 10;
playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View;
rotate_view(gpushxto0(hpxy(jx, jy)));
sym = 1;
}
}
@ -433,8 +469,8 @@ EX void handleKeyNormal(int sym, int uni) {
if(DEFAULTCONTROL) {
if(sym == SDLK_RIGHT) movepckeydir(0);
if(sym == SDLK_LEFT) movepckeydir(4);
if(sym == SDLK_DOWN) movepckeydir(2 + (leftclick?1:0) - (rightclick?1:0));
if(sym == SDLK_UP) movepckeydir(6 - (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 - (pandora_leftclick?1:0) + (pandora_rightclick?1:0));
}
#endif
@ -493,8 +529,12 @@ EX void handleKeyNormal(int sym, int uni) {
pushScreen(inv::show);
#endif
if(((sym == SDLK_HOME && GDIM == 2) || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym))
fullcenter();
if((sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) {
if(rug::rug_control())
rug::reset_view();
else
fullcenter();
}
if(sym == 'v' && DEFAULTNOR(sym))
pushScreen(showMainMenu);
@ -611,7 +651,7 @@ EX void mainloopiter() {
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) {
oldmousepan = mousepan;
#if CAP_MOUSEGRAB
@ -657,13 +697,20 @@ EX void mainloopiter() {
}
Uint8 *keystate = SDL_GetKeyState(NULL);
rightclick = keystate[SDLK_RCTRL];
leftclick = keystate[SDLK_RSHIFT];
lctrlclick = keystate[SDLK_LCTRL];
pandora_rightclick = keystate[SDLK_RCTRL];
pandora_leftclick = keystate[SDLK_RSHIFT];
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];
anyshiftclick = keystate[SDLK_LSHIFT] | keystate[SDLK_RSHIFT];
wheelclick = false;
getcshift = 1;
@ -674,11 +721,14 @@ EX void mainloopiter() {
didsomething = false;
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];
#endif
}
else {
leftclick = keystate[SDLK_RSHIFT];
targetclick = true;
}
@ -689,42 +739,31 @@ EX void mainloopiter() {
SDL_Event ev;
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
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;
#endif
}
if(smooth_scrolling && !shmup::on && !rug::rugged) {
if(smooth_scrolling && !shmup::on) {
rug::using_rugview urv;
static int lastticks;
ld t = (ticks - lastticks) * shiftmul / 1000.;
lastticks = ticks;
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_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME))
shift_view(ctangent(2, t)), didsomething = true, playermoved = false;
if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT))
rotate_view(GDIM == 2 ? xpush(-t) : cspin(0, 2, -t)), didsomething = true, playermoved = playermoved && GDIM == 3;
if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT))
rotate_view(GDIM == 2 ? xpush(t) : cspin(0, 2, t)), didsomething = true, playermoved = playermoved && GDIM == 3;
if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP))
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;
}
if(keystate[SDLK_END] && GDIM == 3 && DEFAULTNOR(SDLK_END)) full_forward_camera(-t);
if(keystate[SDLK_HOME] && GDIM == 3 && DEFAULTNOR(SDLK_HOME)) full_forward_camera(t);
if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) full_rotate_camera(0, -t);
if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) full_rotate_camera(0, t);
if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) full_rotate_camera(1, t);
if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) full_rotate_camera(1, -t);
if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) full_rotate_view(t * 180 / M_PI, t);
if(keystate[SDLK_PAGEDOWN] && DEFAULTNOR(SDLK_PAGEDOWN)) full_rotate_view(-t * 180 / M_PI, t);
}
achievement_pump();
@ -737,8 +776,6 @@ EX void mainloopiter() {
EX void handle_event(SDL_Event& ev) {
bool normal = cmode & sm::NORMAL;
Uint8 *keystate = SDL_GetKeyState(NULL);
DEBB(DF_GRAPH, ("got event type #%d\n", ev.type));
int sym = 0;
int uni = 0;
@ -833,9 +870,6 @@ EX void handle_event(SDL_Event& ev) {
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) {
mousepressed = ev.type == SDL_MOUSEBUTTONDOWN;
if(mousepressed) flashMessages();
@ -858,49 +892,47 @@ EX void handle_event(SDL_Event& ev) {
if(was_holdmouse && ev.type == SDL_MOUSEBUTTONUP)
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) ;
else if(ev.button.button==SDL_BUTTON_RIGHT || leftclick)
else if(ev.button.button==SDL_BUTTON_RIGHT)
sym = SDLK_F1;
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick) {
else if(ev.button.button==SDL_BUTTON_MIDDLE)
sym = 1, didsomething = true;
if(anyshift)
vid.xposition = vid.yposition = 0;
}
else if(ev.button.button == SDL_BUTTON_LEFT) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
}
else if(ev.button.button==SDL_BUTTON_WHEELDOWN) {
if(anyctrl && anyshift && !rug::rugged && GDIM == 2) {
mapeditor::scaleall(1/1.2);
vid.alpha /= 1.2;
else if(ev.button.button==SDL_BUTTON_WHEELDOWN || ev.button.button == SDL_BUTTON_WHEELUP) {
ld dir = ev.button.button == SDL_BUTTON_WHEELUP ? 0.25 : -0.25;
if(lshiftclick && rshiftclick && !rug::rugged && GDIM == 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) {
sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true;
sym = getcstat, uni = getcstat, shiftmul = -dir*4*getcshift, wheelclick = true;
}
else {
sym = uni = 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;
sym = uni = dir > 0 ? PSEUDOKEY_WHEELUP : PSEUDOKEY_WHEELDOWN;
}
}
}
@ -926,11 +958,11 @@ EX void handle_event(SDL_Event& ev) {
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();
if(anyctrl) {
vid.xposition += (mousex - lmousex) * 1. / current_display->scrsize,
vid.yposition += (mousey - lmousey) * 1. / current_display->scrsize;
if(lctrlclick) {
pconf.xposition += (mousex - lmousex) * 1. / current_display->scrsize,
pconf.yposition += (mousey - lmousey) * 1. / current_display->scrsize;
}
else if(mouseh[LDIM] < 50 && mouseoh[LDIM] < 50) {
panning(mouseoh, mouseh);
@ -959,17 +991,17 @@ EX void handle_event(SDL_Event& ev) {
else quitmainloop = true;
}
if(sym == SDLK_F4 && anyshift) {
if(sym == SDLK_F4 && anyshiftclick) {
nomap = !nomap;
sym = 0;
}
if(sym == SDLK_F2 && anyshift) {
if(sym == SDLK_F2 && anyshiftclick) {
nohud = !nohud;
sym = 0;
}
if(sym == SDLK_F3 && anyshift) {
if(sym == SDLK_F3 && anyshiftclick) {
nofps = !nofps;
sym = 0;
}
@ -989,7 +1021,7 @@ EX void mainloop() {
#endif
}
#if ISMOBILE==1
#if ISMOBILE
EX void displayabutton(int px, int py, string s, int col) {
// TMP
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(among(NUMBERKEY, '1', '2', '3') && !rug::rugged && euclid && WDIM == 2) {
vid.xposition = vid.yposition = 0;
pconf.xposition = pconf.yposition = 0;
ld maxs = 0;
auto& cd = current_display;
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[1] / cd->ysize);
}
vid.alpha = 1;
vid.scale = vid.scale / 2 / maxs / cd->radius;
if(NUMBERKEY == '3') vid.scale *= 2;
if(NUMBERKEY == '1') vid.scale /= 2;
pconf.alpha = 1;
pconf.scale = pconf.scale / 2 / maxs / cd->radius;
if(NUMBERKEY == '3') pconf.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 == '2' && !rug::rugged) { vid.alpha = 1; vid.scale = 0.4; vid.xposition = vid.yposition = 0; }
else if(NUMBERKEY == '3' && !rug::rugged) { vid.alpha = 1; vid.scale = 1; vid.xposition = vid.yposition = 0; }
else if(NUMBERKEY == '4' && !rug::rugged) { vid.alpha = 0; vid.scale = 1; 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) { pconf.alpha = 1; pconf.scale = 0.4; pconf.xposition = pconf.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) { 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 == '8') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 6; }
else if(uni == '%') {
@ -1183,10 +1215,10 @@ EX void show() {
dialog::addItem(XLAT("experiment with geometry"), 'g');
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::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z');
dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z');
dialog::add_action([] () { editScale(); });
dialog::addItem(XLAT("spherical VR"), 'v');
@ -1195,7 +1227,7 @@ EX void show() {
mode = 0; fullcenter();
mode = 2; sensitivity = 1;
vid.stereo_mode = sLR; vid.ipd = 0.2;
vid.alpha = 0; vid.scale = 1;
vpconf.alpha = 0; vpconf.scale = 1;
});
dialog::addBreak(100);

View File

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

View File

@ -6,6 +6,20 @@ namespace hr {
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() {
using namespace arg;
@ -58,16 +72,30 @@ int readArgs() {
int cx = (co + 1) % 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)); };
ld shrunk_x = geo_dist(oxy(0,0,-1), oxy(.01,0,-1), iTable);
ld shrunk_y = geo_dist(oxy(0,0,-1), oxy(0,.01,-1), iTable);
ld expand_x = geo_dist(oxy(0,0,+1), oxy(.01,0,+1), iTable);
ld expand_y = geo_dist(oxy(0,0,+1), oxy(0,.01,+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));
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));
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));
if(geometry == gArnoldCat)
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;
return 0;
}

View File

@ -803,7 +803,7 @@ EX namespace dialog {
else
addSlider(ne.sc.direct(ne.vmin), ne.sc.direct(*ne.editwhat), ne.sc.direct(ne.vmax), 500);
addBreak(100);
#if ISMOBILE==0
#if !ISMOBILE
addHelp(XLAT("You can scroll with arrow keys -- Ctrl to fine-tune"));
addBreak(100);
#endif
@ -1263,6 +1263,10 @@ EX namespace dialog {
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) {
return [act] { do_if_confirmed(act); };
}

View File

@ -66,7 +66,7 @@ void launch(int seed, int elimit, int hlimit) {
dual::switch_to(0);
specialland = firstland = laCanvas;
canvas_default_wall = waSea;
vid.scale = .5;
pconf.scale = .5;
dual::switch_to(1);
specialland = firstland = laCanvas;
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_INTENSE = (1<<23); // extra intense colors
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.
*
@ -181,6 +184,7 @@ vector<glvertex> line_vertices;
#endif
EX void glflush() {
DEBBI(DF_GRAPH, ("glflush"));
#if MINIMIZE_GL_CALLS
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));
@ -235,7 +239,7 @@ EX void glflush() {
}
#endif
#if ISMOBILE==0
#if !ISMOBILE
SDL_Surface *aux;
#endif
@ -262,13 +266,13 @@ void add1(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) {
// H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - vid.alpha
// H2[2]- BEHIND_LIMIT + vid.alpha = t * (H2[2] - H1[2])
ld t = (H2[2] - BEHIND_LIMIT + vid.alpha) / (H2[2] - H1[2]);
// H1[2] * t + H2[2] * (1-t) == BEHIND_LIMIT - pconf.alpha
// H2[2]- BEHIND_LIMIT + pconf.alpha = t * (H2[2] - H1[2])
ld t = (H2[2] - BEHIND_LIMIT + pconf.alpha) / (H2[2] - H1[2]);
return H1 * t + H2 * (1-t);
}
@ -289,14 +293,14 @@ EX bool two_sided_model() {
if(pmodel == mdDisk) return sphere;
if(pmodel == mdHemisphere) 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;
}
EX int get_side(const hyperpoint& H) {
if(pmodel == mdDisk && sphere) {
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;
}
if(pmodel == mdRotatedHyperboles)
@ -310,7 +314,7 @@ EX int get_side(const hyperpoint& H) {
applymodel(H, res);
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 0;
@ -334,13 +338,13 @@ void fixpoint(glvertex& hscr, hyperpoint H) {
}
hyperpoint 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) {
if(true) {
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) {
@ -350,7 +354,7 @@ void addpoint(const hyperpoint& H) {
}
else if(sphere && (poly_flags & POLY_ISSIDE)) {
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;
if(last_infront && nif_error_in(glcoords.back()[0], glcoords.back()[1], H[0], H[1]))
poly_flags |= POLY_NIF_ERROR;
@ -358,8 +362,8 @@ void addpoint(const hyperpoint& H) {
last_infront = true;
z *=
(sqrt(curnorm - horizon*horizon) / (vid.alpha - horizon)) /
(sqrt(curnorm - H[2]*H[2]) / (vid.alpha+H[2]));
(sqrt(curnorm - horizon*horizon) / (pconf.alpha - horizon)) /
(sqrt(curnorm - H[2]*H[2]) / (pconf.alpha+H[2]));
}
else {
poly_flags |= POLY_NOTINFRONT;
@ -385,12 +389,12 @@ void addpoint(const hyperpoint& H) {
}
if(GDIM == 2) {
for(int i=0; i<3; i++) Hscr[i] *= z;
Hscr[1] *= vid.stretch;
Hscr[1] *= pconf.stretch;
}
else {
Hscr[0] *= z;
Hscr[1] *= z * vid.stretch;
Hscr[2] = 1 - 2 * (-Hscr[2] - models::clip_min) / (models::clip_max - models::clip_min);
Hscr[1] *= z * pconf.stretch;
Hscr[2] = 1 - 2 * (-Hscr[2] - pconf.clip_min) / (pconf.clip_max - pconf.clip_min);
}
add1(Hscr);
}
@ -409,7 +413,7 @@ void coords_to_poly() {
bool behind3(hyperpoint h) {
if(pmodel == mdGeodesic)
h = lp_apply(inverse_exp(h, iTable));
h = lp_apply(inverse_exp(h));
return h[2] < 0;
}
@ -482,11 +486,11 @@ void addpoly(const transmatrix& V, const vector<glvertex> &tab, int ofs, int cnt
/*
hyperpoint 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, Hscr[1]*current_display->radius*vid.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, Hscr[1]*current_display->radius*vid.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*pconf.stretch+10, 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*pconf.stretch-10, 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) {
glhr::be_textured();
if(flags & POLY_SHADE_TEXTURE) current_display->next_shader_flags |= GF_TEXTURE_SHADED;
glBindTexture(GL_TEXTURE_2D, tinf->texture_id);
glhr::vertices_texture(v, tinf->tvertices, offset, offset_texture);
ioffset = 0;
@ -715,7 +720,7 @@ EX ld scale_at(const transmatrix& T) {
EX ld linewidthat(const hyperpoint& h) {
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];
if(dz < 1) return 1;
else {
@ -750,7 +755,7 @@ vector<ld> periods;
ld period_at(ld y) {
ld m = current_display->radius;
y /= (m * vid.stretch);
y /= (m * pconf.stretch);
switch(pmodel) {
case mdBand:
@ -760,8 +765,8 @@ ld period_at(ld y) {
case mdMollweide:
return m * 2 * sqrt(1 - y*y*4);
case mdCollignon: {
if(vid.collignon_reflected && y > 0) y = -y;
y += signed_sqrt(vid.collignon_parameter);
if(pconf.collignon_reflected && y > 0) y = -y;
y += signed_sqrt(pconf.collignon_parameter);
return abs(m*y*2/1.2);
}
default:
@ -785,7 +790,7 @@ void adjust(bool tinf) {
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) {
case mdSinusoidal: case mdBandEquidistant: case mdMollweide:
@ -797,9 +802,9 @@ void adjust(bool tinf) {
break;
case mdCollignon:
dmin = z * (signed_sqrt(vid.collignon_parameter - 1) - signed_sqrt(vid.collignon_parameter));
if(vid.collignon_reflected) dmax = -dmin;
else dmax = 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(pconf.collignon_reflected) dmax = -dmin;
else dmax = z * (signed_sqrt(pconf.collignon_parameter + 1) - signed_sqrt(pconf.collignon_parameter));
break;
default: ;
@ -889,7 +894,7 @@ void compute_side_by_centerin(dqi_poly *p, bool& nofill) {
else
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++) {
double x1 = glcoords[i][0] - hscr[0];
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) {
glcoords.push_back(glhr::makevertex(hscr[0]+10, hscr[1]*vid.stretch, hscr[2]));
glcoords.push_back(glhr::makevertex(hscr[0], hscr[1]*vid.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], hscr[1]*vid.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]*pconf.stretch+10, 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]*pconf.stretch-10, 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() {
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(vid.stereo_mode == sODS) {
ods::draw_ods(this);
@ -1430,7 +1440,7 @@ void dqi_poly::draw() {
for(int j=0; j<MAX_PHASE; j++) {
twopoint_sphere_flips = j;
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)
phases[j].push_back(h);
else {
@ -1458,7 +1468,7 @@ void dqi_poly::draw() {
for(int i=0; i<cnt; 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));
// 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;
hyperpoint h = ah1 * c1 + ah2 * c2;
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;
}
@ -1530,7 +1540,7 @@ void dqi_poly::draw() {
last_infront = false;
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 dy = glcoords[i][1] - glcoords[i-1][1];
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(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;
for(int i=0; i<isize(glcoords)-1; i++) {
double x1 = glcoords[i][0];
@ -1574,9 +1584,9 @@ void dqi_poly::draw() {
bool can_have_inverse = false;
if(sphere && pmodel == mdDisk && (spherespecial > 0 || equi)) can_have_inverse = true;
if(pmodel == mdJoukowsky) can_have_inverse = true;
if(pmodel == mdJoukowskyInverted && vid.skiprope) can_have_inverse = true;
if(pmodel == mdDisk && hyperbolic && vid.alpha <= -1) can_have_inverse = true;
if(pmodel == mdSpiral && vid.skiprope) can_have_inverse = true;
if(pmodel == mdJoukowskyInverted && pconf.skiprope) can_have_inverse = true;
if(pmodel == mdDisk && hyperbolic && pconf.alpha <= -1) can_have_inverse = true;
if(pmodel == mdSpiral && pconf.skiprope) can_have_inverse = true;
if(pmodel == mdCentralInversion) can_have_inverse = true;
if(can_have_inverse && !(poly_flags & POLY_ISSIDE)) {
@ -1591,7 +1601,7 @@ void dqi_poly::draw() {
}
if(poly_flags & POLY_INVERSE) {
if(curradius < vid.alpha - 1e-6) return;
if(curradius < pconf.alpha - 1e-6) return;
if(!sphere) return;
}
@ -1620,7 +1630,7 @@ void dqi_poly::draw() {
ld h = atan2(glcoords[0][0], glcoords[0][1]);
for(int i=0; i<=360; i++) {
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;
}
@ -1651,7 +1661,7 @@ void dqi_poly::draw() {
}
#endif
#if CAP_SVG==1
#if CAP_SVG
if(svg::in) {
coords_to_poly();
color_t col = color;
@ -1668,10 +1678,10 @@ void dqi_poly::draw() {
coords_to_poly();
#if CAP_XGD==1
#if CAP_XGD
gdpush(1); gdpush(color); gdpush(outline); gdpush(polyi);
for(int i=0; i<polyi; i++) gdpush(polyx[i]), gdpush(polyy[i]);
#elif CAP_SDLGFX==1
#elif CAP_SDLGFX
if(tinf) {
#if CAP_TEXTURE
@ -1793,7 +1803,7 @@ void dqi_string::draw() {
return;
}
#endif
#if ISMOBILE==0
#if !ISMOBILE
int fr = frame & 255;
displayfrSP(x, y, shift, fr, size, str, color, align, frame >> 8);
#else
@ -1846,14 +1856,14 @@ ld xintval(const hyperpoint& h) {
EX ld backbrightness = .25;
purehookset hook_drawqueue;
purehookset hooks_drawqueue;
constexpr int PMAX = int(PPR::MAX);
int qp[PMAX], qp0[PMAX];
color_t darken_color(color_t& color, bool outline) {
int alpha = color & 255;
if(sphere && pmodel == mdDisk && vid.alpha <= 1)
if(sphere && pmodel == mdDisk && pconf.alpha <= 1)
return 0;
else {
if(outline && alpha < 255)
@ -1875,6 +1885,8 @@ void dqi_line::draw_back() {
}
EX void sort_drawqueue() {
DEBBI(DF_GRAPH, ("sort_drawqueue"));
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
EX void draw_backside() {
DEBBI(DF_GRAPH, ("draw_backside"));
if(pmodel == mdHyperboloid && hyperbolic) {
dynamicval<eModel> dv (pmodel, mdHyperboloidFlat);
for(auto& ptd: ptds)
@ -1964,7 +1977,17 @@ EX void reverse_transparent_walls() {
}
EX void draw_main() {
DEBBI(DF_GRAPH, ("draw_main"));
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}) {
if(elliptic && p < 2) continue;
glhr::set_depthwrite(true);
@ -1998,17 +2021,21 @@ EX void draw_main() {
}
}
else {
DEBB(DF_GRAPH, ("draw_main1"));
if(ray::in_use && !ray::comparison_mode) {
ray::cast();
reset_projection();
if(stretch::in()) return; /*primitive not implemented */
}
DEBB(DF_GRAPH, ("outcircle"));
for(auto& ptd: ptds) if(ptd->prio == PPR::OUTCIRCLE)
ptd->draw();
if(two_sided_model()) draw_backside();
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);
ptd->draw();
}
@ -2028,11 +2055,18 @@ EX void draw_main() {
}
#if CAP_VR
EX hookset<bool()> *hooks_vr_draw_all;
EX hookset<bool()> hooks_vr_draw_all;
#endif
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;
reset_projection();
// reset_projection() is not sufficient here, because we need to know shaderside_projection
@ -2045,6 +2079,8 @@ EX void drawqueue() {
profile_start(3);
sort_drawqueue();
DEBB(DF_GRAPH, ("sort walls"));
if(GDIM == 2)
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;
applymodel(h, hscr);
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;
// EYETODO sc = vid.eye * current_display->radius * hscr[2];
}

View File

@ -306,10 +306,13 @@ EX namespace euc {
};
hrmap_euclidean* cubemap() {
if(fake::in()) return FPIU(cubemap());
return ((hrmap_euclidean*) currentmap);
}
hrmap_euclidean* eucmap() { return cubemap(); }
hrmap_euclidean* eucmap() {
return cubemap();
}
EX vector<coord>& get_current_shifttable() { return cubemap()->shifttable; }
EX map<coord, heptagon*>& get_spacemap() { return cubemap()->spacemap; }
@ -1169,7 +1172,84 @@ EX int cyldist(gp::loc a, gp::loc b) {
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, int s7) { return in(dim) && S7 == s7; }

View File

@ -376,7 +376,7 @@ EX int curr_dist(cell *c) {
case dfWorld:
if(!mod_allowed() && !among(c->land, laOcean, laIvoryTower, laEndorian, laDungeon, laTemple, laWhirlpool, laCanvas))
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 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
EX ld floor_texture_square_size;
void draw_shape_for_texture(floorshape* sh) {
int id = sh->id;
@ -1064,23 +1066,30 @@ void draw_shape_for_texture(floorshape* sh) {
hyperpoint inmodel;
applymodel(center, inmodel);
glvertex tmap;
tmap[0] = (1 + inmodel[0] * vid.scale) / 2;
tmap[1] = (1 - inmodel[1] * vid.scale) / 2;
tmap[0] = (1 + inmodel[0] * pconf.scale) / 2;
tmap[1] = (1 - inmodel[1] * pconf.scale) / 2;
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;
}
// SL2 needs 6 times more
texture_order([&] (ld x, ld y) {
auto tvec_at = [&] (ld x, ld y) {
hyperpoint h = center + v1 * x + v2 * y;
hyperpoint inmodel;
applymodel(h, inmodel);
glvec2 v;
v[0] = (1 + inmodel[0] * vid.scale) / 2;
v[1] = (1 - inmodel[1] * vid.scale) / 2;
v[0] = (1 + inmodel[0] * pconf.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));
});
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 */
@ -1102,7 +1111,9 @@ EX void bind_floor_texture(hpcshape& li, int id) {
ensure_vertex_number(li);
}
#if HDR
const int FLOORTEXTURESIZE = 4096;
#endif
void geometry_information::make_floor_textures_here() {
require_shapes();
@ -1110,9 +1121,9 @@ void geometry_information::make_floor_textures_here() {
dynamicval<videopar> vi(vid, vid);
vid.xres = FLOORTEXTURESIZE;
vid.yres = FLOORTEXTURESIZE;
vid.scale = 0.125;
vid.camera_angle = 0;
vid.alpha = 1;
pconf.scale = 0.125;
pconf.camera_angle = 0;
pconf.alpha = 1;
dynamicval<ld> lw(vid.linewidth, 2);
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->xcenter = cd->ycenter = cd->scrsize = FLOORTEXTURESIZE/2;
cd->radius = cd->scrsize * vid.scale;
cd->radius = cd->scrsize * pconf.scale;
floor_textures->enable();
floor_textures->clear(0); // 0xE8E8E8 = 1

View File

@ -359,6 +359,7 @@ void ge_select_tiling() {
if(arb::in() && (ISMOBILE || ISWEB)) continue;
if(WDIM == 3 && MAXMDIM == 3) continue;
if(geometry == gFieldQuotient && !CAP_FIELD) continue;
if(geometry == gFake) continue;
if(!current_filter->test()) continue;
if(orig_el) {
for(int j=0; j<isize(ginf); j++)
@ -381,21 +382,21 @@ void ge_select_tiling() {
EX string current_proj_name() {
bool h = hyperbolic || sn::in();
if(pmodel != mdDisk)
return models::get_model_name(pmodel);
else if(h && vid.alpha == 1)
if(vpconf.model != mdDisk)
return models::get_model_name(vpconf.model);
else if(h && vpconf.alpha == 1)
return XLAT("Poincaré model");
else if(h && vid.alpha == 0)
else if(h && vpconf.alpha == 0)
return XLAT("Klein-Beltrami model");
else if(h && vid.alpha == -1)
else if(h && vpconf.alpha == -1)
return XLAT("inverted Poincaré model");
else if(sphere && vid.alpha == 1)
else if(sphere && vpconf.alpha == 1)
return XLAT("stereographic projection");
else if(sphere && vid.alpha == 0)
else if(sphere && vpconf.alpha == 0)
return XLAT("gnomonic projection");
else if(sphere && vid.alpha >= 999)
else if(sphere && vpconf.alpha >= 999)
return XLAT("orthographic projection");
else if(h && vid.alpha >= 999)
else if(h && vpconf.alpha >= 999)
return XLAT("Gans model");
else
return XLAT("general perspective");
@ -405,7 +406,7 @@ EX string dim_name() {
return " (" + its(WDIM) + "D)";
}
#if CAP_THREAD
#if CAP_THREAD && MAXMDIM >= 4
EX void showQuotientConfig3() {
using namespace fieldpattern;
@ -512,6 +513,7 @@ EX void select_quotient_screen() {
char key = 'a';
for(int i=0; i<isize(ginf); i++) {
auto g = eGeometry(i);
if(ginf[g].flags & qDEPRECATED) continue;
if(same_tiling(g)) {
dialog::addBoolItem(
(ginf[g].flags & qANYQ) ?
@ -528,7 +530,7 @@ EX void select_quotient_screen() {
println(hlog, "set prime = ", currfp.Prime);
start_game();
}
#if CAP_THREAD
#if CAP_THREAD && MAXMDIM >= 4
pushScreen(showQuotientConfig3);
#endif
}
@ -742,6 +744,16 @@ EX void showEuclideanMenu() {
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(hyperbolic && IRREGULAR) {
nom = isize(irr::cells);
@ -813,7 +825,7 @@ EX void showEuclideanMenu() {
dialog::add_action([] {
dialog::editNumber(rots::underlying_scale, 0, 1, 0.05, 0.25, XLAT("view the underlying geometry"),
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.") +
XLAT(
"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::addSelItem(XLAT("land"), XLAT1(linf[specialland].name), 'l');
dialog::add_action_push(ge_land_selection);
@ -940,7 +966,7 @@ EX void runGeometryExperiments() {
#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);
bool numeric = true;
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;
vector<int> walloffsets;
vector<pair<int, cell*>> walloffsets;
vector<array<int, 3>> symmetriesAt;
@ -433,16 +433,16 @@ hpcshape
ld alpha;
int area;
};
shared_ptr<gpdata_t> gpdata;
shared_ptr<gpdata_t> gpdata = nullptr;
#endif
int state;
int usershape_state;
int state = 0;
int usershape_state = 0;
/** contains the texture point coordinates for 3D models */
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_shapes() { if(state & 2) return; state |= 2; prepare_shapes(); }
@ -573,6 +573,7 @@ void geometry_information::prepare_basics() {
#endif
#if MAXMDIM >= 4
if(reg3::in()) reg3::generate();
if(euc::in(3)) euc::generate();
#endif
hybrid_finish:
@ -710,24 +711,24 @@ EX namespace geom3 {
void geometry_information::prepare_compute3() {
using namespace geom3;
DEBBI(DF_INIT | DF_POLY | DF_GEOM, ("geom3::compute"));
// tanh(depth) / tanh(camera) == vid.alpha
// tanh(depth) / tanh(camera) == pconf.alpha
invalid = "";
if(GDIM == 3) ;
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) {
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;
else vid.depth = atan_auto(v);
}
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;
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 != "") {
INFDEEP = .7;
@ -851,20 +852,20 @@ EX void switch_always3() {
EX void switch_tpp() {
if(dual::split(switch_fpp)) return;
if(pmodel == mdDisk && vid.camera_angle) {
if(pmodel == mdDisk && pconf.camera_angle) {
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.fixed_facing = false;
}
else {
vid.yshift = -0.3;
vid.camera_angle = -45;
vid.scale = 18/16. * vid.xres / vid.yres / multi::players;
vid.xposition = 0;
vid.yposition = -0.9;
pconf.camera_angle = -45;
pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
pconf.xposition = 0;
pconf.yposition = -0.9;
vid.fixed_facing = true;
vid.fixed_facing_dir = 90;
}
@ -926,12 +927,19 @@ EX int last_texture_step;
int ntimestamp;
EX void check_cgi() {
EX string cgi_string() {
string s;
auto V = [&] (string a, string b) { s += a; s += ": "; s += b; s += "; "; };
V("GEO", its(int(geometry)));
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(IRREGULAR) V("IRR", its(irr::irrid));
@ -976,9 +984,16 @@ EX void check_cgi() {
V("LQ", its(vid.linequality));
return s;
}
EX void check_cgi() {
string s = cgi_string();
cgip = &cgis[s];
cgi.timestamp = ++ntimestamp;
if(hybri) hybrid::underlying_cgip->timestamp = ntimestamp;
if(fake::in()) fake::underlying_cgip->timestamp = ntimestamp;
if(isize(cgis) > 4) {
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>
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) {
auto d = product_decompose(check(at)).first;
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 CAP_SHADER && CAP_NOSHADER
#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 string to_glsl(ld x) {
char buf[64];
snprintf(buf, 64, "float(%.10e)", x);
return buf;
}
#if HDR
struct glmatrix {
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])
EX hookset<bool(int sym, int uni)> *hooks_handleKey;
EX hookset<bool(cell *c, const transmatrix& V)> *hooks_drawcell;
EX hookset<bool(int sym, int uni)> hooks_handleKey;
EX hookset<bool(cell *c, const transmatrix& V)> hooks_drawcell;
EX purehookset hooks_frame, hooks_markers;
EX ld animation_factor = 1;
@ -1390,7 +1390,7 @@ EX bool drawMonsterType(eMonster m, cell *where, const transmatrix& V1, color_t
}
else {
queuepoly(VHEAD1, cgi.shPHead, 0xF0A0D0FF);
queuepoly(VBS, cgi.shFlowerHand, 0xC00000FF);
queuepoly(VBODY * VBS, cgi.shFlowerHand, 0xC00000FF);
queuepoly(VBODY2 * VBS, cgi.shSuspenders, 0xC00000FF);
}
}
@ -2858,10 +2858,10 @@ int haveaura_cached;
EX int haveaura() {
if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) 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;
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;
@ -2884,7 +2884,7 @@ void apply_joukowsky_aura(hyperpoint& h) {
h = ret;
}
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;
void drawaura() {
DEBBI(DF_GRAPH, ("draw aura"));
if(!haveaura()) return;
if(vid.stereo_mode) return;
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) {
ld h = 1;
h /= vid.fisheye_param;
h /= pconf.fisheye_param;
ld nrad = h / sqrt(2 + h*h);
rad *= nrad;
}
@ -2959,9 +2960,9 @@ void drawaura() {
for(int x=0; x<vid.xres; x++) {
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);
if(fac < 1) continue;
@ -3007,8 +3008,8 @@ void drawaura() {
facs[10] = 10;
cmul[1] = cmul[0];
bool inversion = vid.alpha <= -1 || pmodel == mdJoukowsky;
bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && models::model_transition < 1;
bool inversion = pconf.alpha <= -1 || pmodel == mdJoukowsky;
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++) {
float rr = (M_PI * 2 * r) / AURA;
@ -3024,7 +3025,7 @@ void drawaura() {
else
models::apply_orientation(c1, s1);
ld& mt = models::model_transition;
ld& mt = pconf.model_transition;
ld mt2 = 1 - mt;
ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2);
@ -3034,7 +3035,7 @@ void drawaura() {
}
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++)
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 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)
return 0x202020 >> darken;
int rd1 = rosedist(c1), rd2 = rosedist(c2);
@ -3667,8 +3668,8 @@ bool celldrawer::cell_clipped() {
hyperpoint H = tC0(V);
if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ;
else {
hyperpoint H2 = inverse_exp(H, iLazy);
for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -.4) return true;
hyperpoint H2 = inverse_exp(H, pQUICK);
for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -.6) return true;
}
noclipped++;
}
@ -3676,7 +3677,7 @@ bool celldrawer::cell_clipped() {
hyperpoint H = tC0(V);
if(abs(H[0]) <= 3 && abs(H[1]) <= 3 && abs(H[2]) <= 3 ) ;
else {
hyperpoint H2 = inverse_exp(H, iLazy);
hyperpoint H2 = inverse_exp(H, pQUICK);
for(hyperpoint& cpoint: clipping_planes) if((H2|cpoint) < -2) return true;
}
noclipped++;
@ -3697,7 +3698,8 @@ EX void gridline(const transmatrix& V1, const hyperpoint h1, const transmatrix&
if(WDIM == 3 && fat_edges) {
transmatrix T = V1 * rgpushxto0(h1);
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;
}
#endif
@ -4050,7 +4052,7 @@ EX void queuecircleat(cell *c, double rad, color_t col) {
}
#endif
#if ISMOBILE==1
#if ISMOBILE
#define MOBON (clicked)
#else
#define MOBON true
@ -4470,12 +4472,12 @@ EX void precise_mouseover() {
if(WDIM == 3) {
mouseover2 = mouseover = centerover;
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);
forCellIdEx(c1, i, mouseover2) {
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;
}
return;
@ -4559,7 +4561,7 @@ EX void drawthemap() {
#endif
if(non_spatial_model())
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(GDIM == 3) wmspatial = mmspatial = true;
@ -4628,6 +4630,8 @@ EX void drawthemap() {
profile_stop(4);
drawFlashes();
mapeditor::draw_dtshapes();
if(multi::players > 1 && !shmup::on) {
if(multi::centerplayer != -1)
cwtV = multi::whereis[multi::centerplayer];
@ -4762,7 +4766,7 @@ EX void calcparam() {
cd->xcenter = cd->xtop + cd->xsize / 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);
@ -4784,11 +4788,11 @@ EX void calcparam() {
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;
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; }
@ -4800,8 +4804,8 @@ EX void calcparam() {
cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius;
}
cd->xcenter += cd->scrsize * vid.xposition;
cd->ycenter += cd->scrsize * vid.yposition;
cd->xcenter += cd->scrsize * pconf.xposition;
cd->ycenter += cd->scrsize * pconf.yposition;
cd->tanfov = tan(vid.fov * degree / 2);
@ -5150,7 +5154,7 @@ EX void clearAnimations() {
fallanims.clear();
}
auto graphcm = addHook(clearmemory, 0, [] () {
auto graphcm = addHook(hooks_clearmemory, 0, [] () {
DEBBI(DF_MEMORY, ("clear graph memory"));
mouseover = centerover = lmouseover = NULL;
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;
vector<string> extra_keys = {
"1 = orthogonal/Gans model",
"2 = small Poincare model/stereographic projection",
"3 = big Poincare model/stereographic projection",
vector<string> quick_keys = {
"1 = orthogonal/Gans model/FPP",
"2 = small Poincare model/stereographic projection/SPP",
"3 = big Poincare model/stereographic projection/TPP",
"4 = Klein model/gnomonic projection",
"5 = change wall display mode",
"6 = change grid",
"7 = change heptagon marking",
// "8 = change background color",
// "9 = hyperboloid model",
"8 = monster display mode"
};
vector<string> normal_keys = {
"qweasdzxc, hjklyubn, numpad = move/skip turn",
"arrows = panning",
"o = world overview",
"g = drop a Dead Orb",
"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",
"F1 = help",
"F5 = restart game",
@ -46,22 +52,37 @@ vector<string> extra_keys = {
"Esc = quest status",
"Alt+Enter = full screen",
"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",
"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",
"mousewheel up = panning",
"hold middle mouse button = panning",
"lctrl + hold middle button = move the screen",
"mousewheel down = move/skip",
"shift + mousewheel = change projection",
"ctrl + mousewheel = change zoom",
"ctrl + shift + mousewheel = change both projection and zoom",
"ctrl + hold middle button = move the screen",
"shift + middle button = reset position",
"rshift + mousewheel = change projection",
"lshift + mousewheel = change zoom (lctrl to keep center)",
"lctrl + mousewheel = reset the map center",
"shift + F2 = disable the HUD",
"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() {
@ -147,7 +168,7 @@ void buildHelpText() {
#else
if(DEFAULTCONTROL)
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"
);
help += XLAT(
@ -173,7 +194,18 @@ void buildHelpText() {
#if ISMOBILE == 0
help_extensions.push_back(help_extension{'k', XLAT("advanced keyboard shortcuts"), [] () {
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
}
@ -325,7 +357,7 @@ EX string generateHelpForItem(eItem it) {
" You need to go deep to collect lots of them.");
}
#if ISMOBILE==1
#if ISMOBILE
if(it == itOrbSafety)
help += XLAT("This might be very useful for devices with limited memory.");
#else
@ -493,7 +525,7 @@ void addMinefieldExplanation(string& s) {
);
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.");
#else
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;
int windtotal;
EX hookset<void(cell*)> *hooks_mouseover;
EX hookset<void(cell*)> hooks_mouseover;
EX void 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) {
help = h;
@ -1030,6 +1062,13 @@ EX void gotoHelp(const string& h) {
#if CAP_RUG
if(rug::rugged) {
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{'h', XLAT("HyperRogue help"), [] () { buildHelpText(); }});
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)) {
int d = h->c.spin(0);
int d1 = (d+S7-1)%S7;
heptagon* h1 = createStep(h->move(0), d1);
if(h1->distance <= h->move(0)->distance)
h->distance = h->move(0)->distance+1;
bool missing0 = !h->move(0)->move(d1);
if(missing0) {
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) {
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)
h->distance = h->move(0)->distance+1;
}
if(h->c.spin(0) == S7-1 && h->move(0)->distance != 0)
h->distance = min(
h->move(0)->move(0)->distance + 2,
createStep(h, S7-1)->distance + 1
);
if(h->c.spin(0) == S7-1 && (h->move(0)->s != hsOrigin) && BITRUNCATED) {
bool missing = !h->move(S7-1);
if(missing) {
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();
#if CAP_GP
@ -190,6 +209,9 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0, int fix
}
else {
h->distance = parent->distance - gp::dist_2();
if(S3 == 4 && S7 > 5 && BITRUNCATED) {
h->distance = parent->distance - 2;
}
if(S3 == 4 && S7 == 5) {
if(h->s == hsOrigin) {
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);
EX hookset<void(heptagon*, int)> *hooks_createStep;
EX hookset<void(heptagon*, int)> hooks_createStep;
// create h->move(d) if not created yet
heptagon *createStep(heptagon *h, int d) {

View File

@ -362,7 +362,7 @@ EX namespace history {
}
ld measureLength() {
ld r = bandhalf * vid.scale;
ld r = bandhalf * pconf.scale;
ld tpixels = 0;
int siz = isize(v);
@ -526,7 +526,7 @@ EX namespace history {
dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i');
// 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');
@ -689,7 +689,7 @@ EX namespace history {
auto hookArg = addHook(hooks_args, 100, readArgs);
#endif
auto hooks = addHook(clearmemory, 0, [] () {
auto hooks = addHook(hooks_clearmemory, 0, [] () {
history::renderAutoband();
history::on = false;
history::killhistory.clear();

View File

@ -25,7 +25,8 @@ EX FILE *debugfile;
#define DF_GP 2048
#define DF_POLY 4096
#define DF_LOG 8192
#define DF_KEYS "imwesxufgbtopl"
#define DF_VERTEX 16384
#define DF_KEYS "imwesxufgbtoplv"
#endif
EX int debugflags = DF_INIT | DF_ERROR | DF_WARN | DF_MSG | DF_TIME | DF_LOG;
@ -278,6 +279,22 @@ struct indenter_finish : indenter {
#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)) {
std::stringstream ss;
ss.precision(prec);
@ -354,6 +371,13 @@ EX transmatrix kz(transmatrix 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 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 hookset<bool()> *hooks_prestats;
EX hookset<bool()> hooks_prestats;
#if CAP_SHAPES
void drawMobileArrow(int i) {
@ -401,20 +401,12 @@ EX void drawStats() {
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 xc = cd->xcenter;
auto yc = cd->ycenter;
calcparam();
flat_model_enabler fme;
if(crosshair_color && crosshair_size > 0) {
initquickqueue();
@ -469,7 +461,7 @@ EX void drawStats() {
int spots = 0;
for(int u=vid.fsize; u<vid.xres/2-s; u += 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++;
}
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 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;
int cx = u;

View File

@ -29,7 +29,7 @@ void moreStack() {
#endif
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) {
using namespace hr;

View File

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

122
hyper.h
View File

@ -13,8 +13,8 @@
#define _HYPER_H_
// version numbers
#define VER "11.3i"
#define VERNUM_HEX 0xA829
#define VER "11.3l"
#define VERNUM_HEX 0xA82C
#include "sysconfig.h"
@ -142,6 +142,7 @@ void addMessage(string s, char spamtype = 0);
#define sl2 (cgclass == gcSL2)
#define prod (cgclass == gcProduct)
#define hybri (cgflags & qHYBRID)
#define rotspace (geometry == gRotSpace)
#define hyperbolic (cgclass == gcHyperbolic)
#define nonisotropic (among(cgclass, gcSolNIH, gcNil, gcSL2))
#define translatable (euclid || nonisotropic)
@ -228,9 +229,46 @@ enum eStereo { sOFF, sAnaglyph, sLR, sODS };
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 {
ld scale, alpha, sspeed, mspeed, yshift, camera_angle;
ld ballangle, ballproj, euclid_to_sphere, twopoint_param, fisheye_param, stretch, binary_width, fixed_facing_dir;
projection_configuration projection_config, rug_config;
ld yshift;
ld sspeed, mspeed;
ld binary_width, fixed_facing_dir;
int mobilecompasssize;
int radarsize; // radar for 3D geometries
ld radarrange;
@ -251,8 +289,6 @@ struct videopar {
int xscr, yscr;
ld xposition, yposition;
bool grid;
bool particles;
@ -304,8 +340,6 @@ struct videopar {
int cells_drawn_limit;
int cells_generated_limit; // limit on cells generated per frame
ld skiprope;
eStereo stereo_mode;
ld ipd;
ld lr_eyewidth, anaglyph_eyewidth;
@ -319,7 +353,6 @@ struct videopar {
ld depth; // world level below the plane
ld camera; // camera level above the plane
ld wall_height, creature_scale, height_width;
eModel vpmodel;
ld lake_top, lake_bottom;
ld rock_wall_ratio;
ld human_wall_ratio;
@ -331,7 +364,6 @@ struct videopar {
ld eye;
bool auto_eye;
ld collignon_parameter; bool collignon_reflected;
ld plevel_factor;
bool bubbles_special, bubbles_threshold, bubbles_all;
int joysmooth;
@ -341,7 +373,7 @@ extern videopar vid;
#define WDIM cginf.g.gameplay_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 cclass g.kind
@ -407,8 +439,41 @@ struct movedir {
// shmup
template<class T> class hookset : public map<int, function<T>> {};
typedef hookset<void()> *purehookset;
template<class T>
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;
@ -621,33 +686,24 @@ enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMul
#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);
static const int DISTANCE_UNKNOWN = 127;
#include <functional>
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> int addHook(hookset<T>& m, int prio, U&& hook) {
return m.add(prio, static_cast<U&&>(hook));
}
template<class T, class... U> void callhooks(hookset<T> *h, U&&... args) {
if(h) for(auto& p: *h) p.second(std::forward<U>(args)...);
template<class T, class... U> void callhooks(const hookset<T>& h, 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) {
if(h) for(auto& p: *h) {
auto z = p.second(std::forward<U>(args)...);
if(z != zero) return z;
}
return zero;
template<class T, class V, class... U> V callhandlers(V zero, const hookset<T>& h, U&&... args) {
return h.callhandlers(zero, static_cast<U&&>(args)...);
}
string XLAT(string);
@ -670,7 +726,7 @@ struct colortable: vector<color_t> {
namespace scores { void load(); }
#if ISMOBILE==1
#if ISMOBILE
namespace leader { void showMenu(); void handleKey(int sym, int uni); }
#endif
@ -679,7 +735,7 @@ int textwidth(int siz, const string &str);
int gl_width(int size, const char *s);
#endif
#ifdef ISMOBILE
#if ISMOBILE
extern int andmode;
extern bool longclick;
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) {
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) {
if(sl2) return sqrt(-intval(h, Hypc));
else if(translatable) return h[LDIM];
@ -412,6 +419,14 @@ EX hyperpoint normalize(hyperpoint 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 */
EX hyperpoint normalize_flat(hyperpoint h) {
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) {
if(translatable) {
if(nonisotropic)
return co > 0 ? eupush(H) : inverse(eupush(H));
return eupush(co * H);
}
if(prod) {
@ -742,6 +759,7 @@ EX transmatrix rgpushxto0(const hyperpoint& H) {
EX void fixmatrix(transmatrix& T) {
if(nonisotropic) ; // T may be inverse... do not do that
else if(cgflags & qAFFINE) ; // affine
else if(prod) {
auto z = zlevel(tC0(T));
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 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);
else if(prod) {
hyperpoint h = product::direct_exp(ori * direction);
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) {
@ -1151,8 +1169,8 @@ EX hyperpoint tangent_length(hyperpoint dir, ld length) {
}
/** exponential function: follow the geodesic given by v */
EX hyperpoint direct_exp(hyperpoint v, int steps) {
if(sn::in()) return nisot::numerical_exp(v, steps);
EX hyperpoint direct_exp(hyperpoint v) {
if(sn::in()) return nisot::numerical_exp(v);
if(nil) return nilv::formula_exp(v);
if(sl2) return slr::formula_exp(v);
if(prod) return product::direct_exp(v);
@ -1163,20 +1181,26 @@ EX hyperpoint direct_exp(hyperpoint v, int steps) {
}
#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
/** 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(sn::in()) {
if(nih)
return sn::get_inverse_exp_nsym(h, p == iLazy, just_direction);
return sn::get_inverse_exp_nsym(h, prec);
else
return sn::get_inverse_exp_symsol(h, p == iLazy, just_direction);
return sn::get_inverse_exp_symsol(h, prec);
}
#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(prod) return product::inverse_exp(h);
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;
}
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);
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) {

View File

@ -30,10 +30,6 @@
#define EMSCRIPTEN
#endif
#ifndef CAP_ORIENTATION
#define CAP_ORIENTATION 1
#endif
#ifdef FAKEWEB
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(); }

View File

@ -8,21 +8,20 @@
#include "hyper.h"
namespace hr {
ld ghx, ghy, ghgx, ghgy;
hyperpoint ghpm = C0;
hyperpoint ghxy, ghgxy, ghpm = C0;
#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
void ghcheck(hyperpoint &ret, const hyperpoint &H) {
if(hypot(ret[0]-ghx, ret[1]-ghy) < hypot(ghgx-ghx, ghgy-ghy)) {
ghpm = H; ghgx = ret[0]; ghgy = ret[1];
if(hypot_d(2, ret-ghxy) < hypot_d(2, ghgxy-ghxy)) {
ghpm = H; ghgxy = ret;
}
}
EX void camrotate(ld& hx, ld& hy) {
ld cam = vid.camera_angle * degree;
ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
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);
}
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];
if(gc == gcEuclid)
@ -59,7 +58,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet
B /= A; C /= A;
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);
@ -72,7 +71,7 @@ EX hyperpoint perspective_to_space(hyperpoint h, ld alpha IS(vid.alpha), eGeomet
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]);
z[0] *= 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) {
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) {
ghx = hx, ghy = hy;
return ghpm;
ghxy = hxy;
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));
}
void ballmodel(hyperpoint& ret, double alpha, double d, double zl) {
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 ay = H[1] / tzh;
@ -165,7 +192,7 @@ ld find_zlev(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;
return tz;
}
@ -237,7 +264,7 @@ void band_conformal(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 dright = hypot_auto(x+p, y);
if(sphere) {
@ -264,7 +291,7 @@ hyperpoint mobius(hyperpoint h, ld angle, ld scale = 1) {
}
hyperpoint compute_hybrid(hyperpoint H, int rootid) {
auto& t = vid.twopoint_param;
auto& t = pconf.twopoint_param;
hyperpoint Hl = xpush(+t) * H;
hyperpoint Hr = xpush(-t) * H;
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;
if(models::product_model()) {
if(models::product_model(pmodel)) {
ld zlev = zlevel(H);
H /= exp(zlev);
hybrid::in_underlying_geometry([&] { applymodel(H, ret); });
ret[2] = zlev * models::product_z_scale;
ret[2] = zlev * pconf.product_z_scale;
ret = NLP * ret;
return;
}
@ -337,7 +364,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
}
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;
ret[0] = S[0]/S[2] * ratio;
ret[1] = S[1]/S[2] * ratio;
@ -360,23 +387,23 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdDisk: {
if(nonisotropic) {
ret = lp_apply(inverse_exp(H, iTable, true));
ret = lp_apply(inverse_exp(H, pNORMAL | pfNO_DISTANCE));
ld w;
if(sn::in()) {
// w = 1 / sqrt(1 - sqhypot_d(3, ret));
// w = w / (vid.alpha + w);
w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * vid.alpha + 1);
// w = w / (pconf.alpha + w);
w = 1 / (sqrt(1 - sqhypot_d(3, ret)) * pconf.alpha + 1);
}
else {
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;
ret[3] = 1;
break;
}
ld tz = get_tz(H);
if(!vid.camera_angle) {
if(!pconf.camera_angle) {
ret[0] = H[0] / tz;
ret[1] = H[1] / tz;
if(GDIM == 3) ret[2] = H[2] / tz;
@ -386,7 +413,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
else {
ld tx = H[0];
ld ty = H[1];
ld cam = vid.camera_angle * degree;
ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
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: {
ld tz = get_tz(H);
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;
for(int d=0; d<GDIM; d++) r += ret[d]*ret[d];
for(int d=0; d<GDIM; d++) ret[d] /= r;
@ -422,7 +450,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
if(GDIM == 3) {
// a bit simpler when we do not care about 3D
H *= models::halfplane_scale;
H *= pconf.halfplane_scale;
ret[0] = -H[0];
ret[1] = 1 + H[1];
ret[2] = H[2];
@ -434,7 +462,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
models::apply_orientation(H[0], H[1]);
H *= models::halfplane_scale;
H *= pconf.halfplane_scale;
ret[0] = -models::osin - H[0];
if(zlev != 1) {
@ -485,14 +513,14 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case gcEuclid: default: {
// 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);
else {
ld x = 2 * hd / (1 + hd * 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 = ret * (1 + (H[2]-1) * y / vid.euclid_to_sphere);
ret = ret * (1 + (H[2]-1) * y / pconf.euclid_to_sphere);
}
break;
}
@ -523,7 +551,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
break;
}
if(pmodel == mdHyperboloid) {
ld& topz = models::top_z;
ld& topz = pconf.top_z;
if(H[2] > topz) {
ld scale = sqrt(topz*topz-1) / hypot_d(2, H);
H *= scale;
@ -531,8 +559,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
}
}
else {
H = space_to_perspective(H, vid.alpha);
H[2] = 1 - vid.alpha;
H = space_to_perspective(H, pconf.alpha);
H[2] = 1 - pconf.alpha;
}
ret[0] = H[0] / 3;
@ -546,14 +574,14 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdFisheye: {
ld zlev;
if(nonisotropic) {
H = lp_apply(inverse_exp(H, iTable, false));
H = lp_apply(inverse_exp(H));
zlev = 1;
}
else {
zlev = find_zlev(H);
H = space_to_perspective(H);
}
H /= vid.fisheye_param;
H /= pconf.fisheye_param;
H[LDIM] = zlev;
ret = H / sqrt(1 + sqhypot_d(GDIM+1, H));
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(H[0], H[1]);
auto yz = move_z_to_y(H);
hyperpoint Hl = xpush(-vid.twopoint_param) * H;
hyperpoint Hr = xpush(+vid.twopoint_param) * H;
hyperpoint Hl = xpush(-pconf.twopoint_param) * H;
hyperpoint Hr = xpush(+pconf.twopoint_param) * H;
ld lyx = (Hl[1] + 1e-7) / (Hl[0] + 1e-8);
ld ryx = (Hr[1] + 1e-7) / (Hr[0] + 1e-8);
// (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 = - 2 * t * lyx * ryx / lyx / ryx
ret[0] = -vid.twopoint_param * (ryx + lyx) / (lyx - ryx);
ret[1] = (ret[0] + vid.twopoint_param) * lyx;
ret[0] = -pconf.twopoint_param * (ryx + lyx) / (lyx - ryx);
ret[1] = (ret[0] + pconf.twopoint_param) * lyx;
ret[2] = 0;
move_y_to_z(ret, yz);
@ -603,11 +631,11 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
models::apply_orientation(H[0], H[1]);
// with equal speed skiprope: models::apply_orientation(H[1], H[0]);
if(vid.skiprope) {
if(pconf.skiprope) {
static ld last_skiprope = 0;
static transmatrix lastmatrix;
if(vid.skiprope != last_skiprope) {
ret = mobius(C0, -vid.skiprope, 2);
if(pconf.skiprope != last_skiprope) {
ret = mobius(C0, -pconf.skiprope, 2);
const cld c1(1, 0);
const cld c2(2, 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 inhyp = perspective_to_space(zr, 1, gcHyperbolic);
last_skiprope = vid.skiprope;
last_skiprope = pconf.skiprope;
lastmatrix = rgpushxto0(inhyp);
}
H = lastmatrix * H;
@ -628,7 +656,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ld r = hypot_d(2, H);
ld c = H[0] / r;
ld s = H[1] / r;
ld& mt = models::model_transition;
ld& mt = pconf.model_transition;
ld a = 1 - .5 * mt, b = .5 * mt;
swap(a, b);
@ -636,8 +664,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = (a * r - b/r) * s / 2;
ret[2] = 0;
if(vid.skiprope)
ret = mobius(ret, vid.skiprope, 2);
if(pconf.skiprope)
ret = mobius(ret, pconf.skiprope, 2);
if(pmodel == mdJoukowskyInverted) {
ld r2 = sqhypot_d(2, ret);
@ -681,8 +709,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
}
case mdBand:
if(models::model_transition != 1) {
ld& mt = models::model_transition;
if(pconf.model_transition != 1) {
ld& mt = pconf.model_transition;
H = space_to_perspective(H);
@ -745,10 +773,10 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
find_zlev(H);
makeband(H, ret, [] (ld& x, ld& y) {
ld sgn = 1;
if(vid.collignon_reflected && y > 0) y = -y, sgn = -1;
y = signed_sqrt(sin_auto(y) + vid.collignon_parameter);
if(pconf.collignon_reflected && y > 0) y = -y, sgn = -1;
y = signed_sqrt(sin_auto(y) + pconf.collignon_parameter);
x *= y / 1.2;
y -= signed_sqrt(vid.collignon_parameter);
y -= signed_sqrt(pconf.collignon_parameter);
y *= sgn;
y *= M_PI;
});
@ -768,7 +796,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
case mdEquidistant: case mdEquiarea: case mdEquivolume: {
if(nonisotropic || prod) {
ret = lp_apply(inverse_exp(H, iTable, false));
ret = lp_apply(inverse_exp(H));
ret[3] = 1;
break;
}
@ -813,7 +841,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = cosh(x) * factor;
ret[2] = 0;
if(models::use_atan) {
if(pconf.use_atan) {
ret[0] = atan(ret[0]);
ret[1] = atan(ret[1]);
}
@ -822,7 +850,7 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
}
case mdFormula: {
dynamicval<eModel> m(pmodel, models::basic_model);
dynamicval<eModel> m(pmodel, pconf.basic_model);
applymodel(H, ret);
exp_parser ep;
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["uy"] = H[1];
ep.extra_params["uz"] = H[2];
ep.s = models::formula;
ep.s = pconf.formula;
cld res;
try {
res = ep.parse();
@ -852,15 +880,15 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
else ret = H;
z = cld(ret[0], ret[1]) * models::spiral_multiplier;
if(models::spiral_cone < 360) {
ld alpha = imag(z) * 360 / models::spiral_cone;
if(pconf.spiral_cone < 360) {
ld alpha = imag(z) * 360 / pconf.spiral_cone;
ld r = real(z);
r = exp(r);
ret[0] = -sin(alpha) * r;
ret[1] = cos(alpha) * r;
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]);
}
@ -870,8 +898,8 @@ EX void applymodel(hyperpoint H, hyperpoint& ret) {
ret[1] = imag(z);
if(euclid) ret = models::euclidean_spin * ret;
if(vid.skiprope)
ret = mobius(ret, vid.skiprope, 1);
if(pconf.skiprope)
ret = mobius(ret, pconf.skiprope, 1);
}
}
@ -929,12 +957,12 @@ EX bool behindsphere(const hyperpoint& h) {
if(mdBandAny()) return false;
if(vid.alpha > 1) {
if(h[LDIM] > -1/vid.alpha) return true;
if(pconf.alpha > 1) {
if(h[LDIM] > -1/pconf.alpha) return true;
}
if(vid.alpha <= 1) {
if(h[LDIM] < .2-vid.alpha) return true;
if(pconf.alpha <= 1) {
if(h[LDIM] < .2-pconf.alpha) return true;
}
return false;
@ -949,11 +977,11 @@ ld to01(ld a0, ld a1, ld x) {
EX ld spherity(const hyperpoint& h) {
if(!sphere) return 1;
if(vid.alpha > 1) {
return to01(1/vid.alpha, 1, abs(h[2]));
if(pconf.alpha > 1) {
return to01(1/pconf.alpha, 1, abs(h[2]));
}
if(vid.alpha <= 1) {
if(pconf.alpha <= 1) {
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) {
if(sphere) 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);
h = lp_apply(h);
return h[2] < 1e-8;
@ -1039,15 +1067,15 @@ EX bool in_smart_range(const transmatrix& T) {
applymodel(h, h1);
if(invalid_point(h1)) return false;
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 * 1) 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(GDIM == 3) {
if(-h1[2] < models::clip_min * 2 - models::clip_max) return false;
if(-h1[2] > models::clip_max * 2 - models::clip_min) return false;
if(-h1[2] < pconf.clip_min * 2 - pconf.clip_max) return false;
if(-h1[2] > pconf.clip_max * 2 - pconf.clip_min) return false;
}
ld epsilon = 0.01;
@ -1057,14 +1085,14 @@ EX bool in_smart_range(const transmatrix& T) {
hyperpoint h2;
applymodel(T * cpush0(i, epsilon), h2);
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);
if(GDIM == 3) dz = max(dz, abs(h2[2] - h1[2]));
dh[i] = hypot(x1, y1);
}
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);
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;
@ -1191,7 +1219,7 @@ void hrmap_standard::draw() {
if(sphere && pmodel == mdSpiral && !in_multi) {
in_multi = true;
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;
for(int i=-qty; i < qty; 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) {
ld downspin = 0;
auto& ds = downseek;
@ -1271,33 +1317,20 @@ EX void spinEdge(ld aspd) {
downspin = atan2(H[1], H[0]);
downspin += vid.fixed_facing_dir * degree;
if(flipplayer) downspin += M_PI;
while(downspin < -M_PI) downspin += 2*M_PI;
while(downspin > +M_PI) downspin -= 2*M_PI;
cyclefix(downspin, 0);
aspd = (1 + 2 * abs(downspin)) * aspd;
}
else if((WDIM == 2 || prod) && GDIM == 3 && vid.fixed_yz && !CAP_ORIENTATION) {
aspd = 999999;
auto& vo = get_view_orientation();
// does not work well (also need change auto& to auto)
// if(hybri && !prod) vo = vo * inverse(nisot::translate(tC0(vo)));
else if(keep_vertical()) {
hyperpoint h = vertical_vector();
downspin = -atan2(h[0], h[1]);
if(ds.qty && GDIM == 2) {
downspin += models::rotation * degree;
}
if(ds.qty) {
auto sdp = ds.point;
if(prod) sdp = vo * product::inverse_exp(sdp);
if(sdp[0])
downspin = models::rotation * degree - atan2(sdp[0], sdp[1]);
cyclefix(downspin, 0);
downspin = downspin * min(ds.speed, (double)1);
}
else {
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);
else aspd = 999999;
}
if(downspin > aspd) downspin = aspd;
if(downspin < -aspd) downspin = -aspd;
@ -1505,8 +1538,8 @@ EX void fullcenter() {
transmatrix screenpos(ld x, ld y) {
transmatrix V = Id;
V[0][2] += (x - current_display->xcenter) / current_display->radius * (1+vid.alpha);
V[1][2] += (y - current_display->ycenter) / 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+pconf.alpha);
return V;
}
@ -1517,6 +1550,27 @@ transmatrix screenpos(ld x, ld y) {
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) {
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) {
#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;
applymodel(xpush0(radius), 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
for(int i=0; i<=360; i++) curvepoint(xspinpush0(i * degree, 10));
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;
if(pmodel == mdJoukowsky)
c.flags |= POLY_FORCE_INVERTED;
@ -1571,7 +1625,7 @@ EX void draw_model_elements() {
switch(pmodel) {
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;
}
@ -1581,11 +1635,11 @@ EX void draw_model_elements() {
for(int mode=0; mode<4; mode++) {
for(int s=-200; s<=200; s ++) {
ld p = tanh(s / 40.);
ld a = vid.twopoint_param * (1+p);
ld b = vid.twopoint_param * (1-p);
ld a = pconf.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)));
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);
models::apply_orientation(res[0], res[1]);
@ -1600,9 +1654,9 @@ EX void draw_model_elements() {
}
case mdTwoPoint: case mdSimulatedPerspective: {
ld a = -models::model_orientation * degree;
queuestr(xspinpush0(a, +vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
queuestr(xspinpush0(a, -vid.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
ld a = -pconf.model_orientation * degree;
queuestr(xspinpush0(a, +pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
queuestr(xspinpush0(a, -pconf.twopoint_param), vid.xres / 100, "X", ringcolor >> 8);
return;
}
@ -1616,10 +1670,10 @@ EX void draw_model_elements() {
if(hyperbolic) {
#if CAP_QUEUE
curvepoint(point3(0,0,1));
curvepoint(point3(0,0,-vid.alpha));
curvepoint(point3(0,0,-pconf.alpha));
queuecurve(ringcolor, 0, PPR::CIRCLE);
ld& tz = models::top_z;
ld& tz = pconf.top_z;
ld z = acosh(tz);
hyperpoint a = xpush0(z);
@ -1629,12 +1683,12 @@ EX void draw_model_elements() {
a[1] = sb * a[2] / -cb;
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(point3(0,0,0));
a[0] = -a[0];
curvepoint(a);
curvepoint(point3(0,0,-vid.alpha));
curvepoint(point3(0,0,-pconf.alpha));
queuecurve(ringcolor, 0, PPR::CIRCLE);
curvepoint(point3(-1,0,0));
@ -1643,10 +1697,10 @@ EX void draw_model_elements() {
a[1] = sb * tz / -cb;
a[0] = sqrt(tz * tz - a[1] * a[1]);
a[2] = tz - vid.alpha;
a[2] = tz - pconf.alpha;
curvepoint(a);
curvepoint(point3(0,0,-vid.alpha));
curvepoint(point3(0,0,-pconf.alpha));
a[0] = -a[0];
curvepoint(a);
queuecurve(ringcolor, 0, PPR::CIRCLE);
@ -1727,7 +1781,7 @@ EX void draw_boundary(int w) {
for(int b=-1; b<=1; b+=2)
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 z = -sqrt(1 - x*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: {
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);
transmatrix T = spin(-models::model_orientation * degree);
transmatrix T = spin(-pconf.model_orientation * degree);
ld right = M_PI/2 - 1e-5;
if(bndband)
queuestraight(T * ypush0(hyperbolic ? 10 : right), 2, lc, fc, p);
@ -1773,7 +1827,7 @@ EX void draw_boundary(int w) {
case mdHalfplane:
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;
}
break;
@ -1810,7 +1864,7 @@ EX void draw_boundary(int w) {
case mdHyperboloid: {
if(hyperbolic) {
ld& tz = models::top_z;
ld& tz = pconf.top_z;
ld mz = acosh(tz);
ld cb = models::cos_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) {
cld z = exp(cld(a, a * imag(sm) / real(sm) + M_PI));
hyperpoint ret = point2(real(z), imag(z));
ret = mobius(ret, vid.skiprope, 1);
ret = mobius(ret, pconf.skiprope, 1);
ret *= current_display->radius;
curvepoint(ret);
}
@ -1884,8 +1938,8 @@ EX void draw_boundary(int w) {
default: break;
}
if(sphere && pmodel == mdDisk && vid.alpha > 1) {
double rad = current_display->radius / sqrt(vid.alpha*vid.alpha - 1);
if(sphere && pmodel == mdDisk && pconf.alpha > 1) {
double rad = current_display->radius / sqrt(pconf.alpha*pconf.alpha - 1);
queuecircle(current_display->xcenter, current_display->ycenter, rad, lc, p, fc);
return;
}
@ -1898,7 +1952,7 @@ EX void draw_boundary(int w) {
EX ld band_shift = 0;
EX void fix_the_band(transmatrix& T) {
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);
find_zlev(H);
@ -1911,7 +1965,7 @@ EX void fix_the_band(transmatrix& T) {
band_shift += x;
T = xpush(-x) * 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 < 50) { limited_generation(c); return true; }
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 <= 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;
}
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);
if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : cgi.corner_bonus)) 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;
auto zth = [&] (cld z) {
ld alpha = imag(z) * 360 / models::spiral_cone;
ld alpha = imag(z) * 360 / pconf.spiral_cone;
ld r = real(z);
r = exp(r);
@ -2064,7 +2118,7 @@ EX int cone_side(const hyperpoint H) {
ret[0] = -sin(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]);
return ret;
@ -2082,21 +2136,21 @@ EX transmatrix& get_view_orientation() {
return prod ? NLP : View;
}
EX hookset<bool(const transmatrix&)> *hooks_rotate_view;
EX hookset<bool(const hyperpoint&)> *hooks_shift_view;
EX hookset<bool(const transmatrix&)> hooks_rotate_view;
EX hookset<bool(const hyperpoint&)> hooks_shift_view;
/** rotate the view using the given rotation matrix */
EX void rotate_view(transmatrix T) {
if(callhandlers(false, hooks_rotate_view, T)) return;
transmatrix& which = get_view_orientation();
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 */
EX transmatrix get_shift_view_of(const hyperpoint H, const transmatrix V) {
if(!nonisotropic) {
return rgpushxto0(direct_exp(lp_iapply(H), 100)) * V;
if(!nonisotropic && !stretch::in()) {
return rgpushxto0(direct_exp(lp_iapply(H))) * V;
}
else if(!nisot::geodesic_movement) {
transmatrix IV = inverse(V);
@ -2115,7 +2169,7 @@ EX void shift_view(hyperpoint H) {
auto oView = View;
View = get_shift_view_of(H, View);
auto& wc = current_display->which_copy;
if(nonisotropic) {
if(nonisotropic || stretch::in()) {
transmatrix ioldv = eupush(tC0(inverse(oView)));
transmatrix newv = inverse(eupush(tC0(inverse(View))));
wc = newv * ioldv * wc;
@ -2132,7 +2186,7 @@ void multiply_view(transmatrix T) {
EX void shift_view_to(hyperpoint 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) {
@ -2141,7 +2195,7 @@ EX void shift_view_towards(hyperpoint H, ld l) {
else if(nonisotropic && !nisot::geodesic_movement)
shift_view(tangent_length(H-C0, -l));
else {
hyperpoint ie = inverse_exp(H, iTable, true);
hyperpoint ie = inverse_exp(H, pNORMAL | pfNO_DISTANCE);
if(prod) ie = lp_apply(ie);
shift_view(tangent_length(ie, -l));
}

View File

@ -513,14 +513,7 @@ EX namespace inv {
int j = 0, oc = 6;
if(1) {
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);
calcparam();
flat_model_enabler fme;
for(int i=0; i<ittypes; 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);
}
#if ISMOBILE==0
#if !ISMOBILE
string hot = XLAT1("Hotkey: "); hot += getcstat;
displaystr(vid.xres/2, vid.yres - vid.fsize*5, 2, vid.fsize, hot, icol, 8);
#endif
@ -689,7 +682,7 @@ EX namespace inv {
#if CAP_SAVE
EX void applyBox(eItem it) {
scores::applyBoxNum(inv::usedup[it]);
scores::applyBoxNum(inv::usedup[it], "@inv-" + dnameof(it));
}
#endif

View File

@ -467,7 +467,7 @@ EX void gainItem(eItem it) {
if(chaosmode && gold() >= 300)
achievement_gain_once("CHAOS", rg::chaos);
#if ISMOBILE==1
#if ISMOBILE
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."));
#endif

View File

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

View File

@ -276,7 +276,7 @@ EX bool createOnSea(eLand old) {
(old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant));
}
EX hookset<eLand(eLand)> *hooks_nextland;
EX hookset<eLand(eLand)> hooks_nextland;
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 {
std::map<std::string, T> m;
void add(const std::string& s, T val) {
if(m.count(s)) add(s + " [repeat]", std::move(val));
else m[s] = std::move(val);
auto it = m.find(s);
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]; }
int count(const std::string& s) const { return m.count(s); }
@ -53,6 +58,12 @@ struct noun {
std::string nom, nomp, acc, abl;
noun() = default;
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];
@ -93,14 +104,7 @@ typedef unsigned hashcode;
hashcode hashval;
bool isrepeat(const std::string& s) {
return s.find(" [repeat]") != std::string::npos;
}
hashcode langhash(const std::string& s) {
if(isrepeat(s)) {
return langhash(s.substr(0, s.size() - 9)) + 1;
}
hashcode r = 0;
for (char ch : s) r = hashval * r + ch;
return r;
@ -265,13 +269,11 @@ void compute_completeness(const T& dict)
else
mis1 += which;
}
if(mis != "" && !isrepeat(elt))
if(mis != "")
printf("// #warning Missing [%s/%s]: %s\n", mis.c_str(), mis1.c_str(), escape(elt, "?"));
if(!isrepeat(elt)) {
completeness[0]++;
for(int i=1; i<NUMLAN; i++) if(dict[i].count(elt)) completeness[i]++;
}
completeness[0]++;
for(int i=1; i<NUMLAN; i++) if(dict[i].count(elt)) completeness[i]++;
}
}
@ -389,11 +391,9 @@ int main() {
for(auto&& elt : ms) {
const std::string& s = elt.second;
if(isrepeat(s)) printf("#if REPEATED\n");
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));
printf(" }},\n");
if(isrepeat(s)) printf("#endif\n");
}
printf(" };\n\n");
@ -401,7 +401,6 @@ int main() {
for(auto&& elt : mn) {
const std::string& s = elt.second;
if(isrepeat(s)) printf("#if REPEATED\n");
printf(" {0x%x, %d, { // \"%s\"\n", elt.first,
(nothe.count(s) ? 1:0) + (plural.count(s) ? 2:0),
escape(s, s));
@ -415,7 +414,6 @@ int main() {
}
printf(" }},\n");
if(isrepeat(s)) printf("#endif\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, "
"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",
"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: ")
@ -4587,11 +4587,11 @@ S(
"are not about points on the ground level, but "
"about the matching points on the plane P -- "
"divide them by the factor above to get actual "
"distances.",
"distances.)",
"(Vzdálenosti v editoru vektorové "
"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, "
"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("%The1 is destroyed by lava!", "Láva zničila %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
//-----------------
@ -6693,7 +6693,7 @@ N("Chrysoberyl", GEN_O, "Chryzoberyl", "Chryzoberyly", "Chryzoberyl", "Chryzober
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")
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 "
"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")
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.")
@ -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")
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í")
@ -7845,7 +7845,7 @@ S(
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:
@ -7936,7 +7936,7 @@ S("Crystal", "Krystal")
#define Cell(x) \
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")
#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("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. ")
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 "
"ü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",
"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?
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 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")
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 "
"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",
"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")
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 "
"about the matching points on the plane P -- "
"divide them by the factor above to get actual "
"distances.",
"distances.)",
"(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, "
"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("(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",
"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("%The1 is destroyed by lava!", "%The1 jest zniszczon%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
//-----------------
@ -6535,7 +6535,7 @@ N("Chrysoberyl", GEN_O, "Chryzoberyl", "Chryzoberyle", "Chryzoberyl", "Chryzober
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ą")
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.")
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ą")
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. ")
@ -7572,7 +7572,7 @@ S(
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:
@ -7661,7 +7661,7 @@ S("Crystal", "Kryształ")
#define Cell(x) \
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")
#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 "
"about the matching points on the plane P -- "
"divide them by the factor above to get actual "
"distances.",
"distances.)",
"Poziom podłoża jest w rzeczywistości ekwidystantną powierzchnią "
"%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 "
"P -- ale nie wpływa to na mechanikę gry w żaden sposób. "
"(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, "
"from a point c absolute units above the plane, this corresponds "

View File

@ -484,10 +484,10 @@ S(
"информацию про объекты. Кликая по изображаемым числам, можно смотреть, "
"что они означают.\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",
"Двигайтесь с помощью мышки, нумпада, qweadzxc или hjklyubn. Ждите, нажимая 's' или '.'. "
"Поворачивайте карту стрелками, PageUp/Down или Home/Space. "
"Поворачивайте карту стрелками, PageUp/Down или Space. "
"Чтобы сохраниться, Вам нужна сфера безопасности. Нажмите 'v' для настроек, Esc для статуса квеста или меню.")
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 "
"about the matching points on the plane P -- "
"divide them by the factor above to get actual "
"distances.",
"distances.)",
"(Расстояния в редакторе векторной графики относятся"
"не к точкам на поверхности, а к их проекциям на плоскость P.)")
@ -6220,7 +6220,7 @@ S("would be destroyed in %the1", "будет уничтожено %abl1")
S(" to go cold", " чтобы остыть")
S("%The1 is destroyed 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
//-----------------
@ -6760,7 +6760,7 @@ N("Chrysoberyl", GEN_O, "Хризоберилл", "Хризобериллы", "
S("Fragment of the past glory.", "Фрагмент былой славы.")
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, "Серый Рейдер", "Серые Рейдеры", "Серого Рейдера", "Серым Рейдером")

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."
" (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",
"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: ")

View File

@ -49,7 +49,7 @@ void loadOldConfig(FILE *f) {
float a, b, c, d;
err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d);
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);
vid.usingGL = gl;

View File

@ -10,12 +10,288 @@ namespace hr {
EX namespace mapeditor {
EX bool drawing_tool;
#if HDR
enum eShapegroup { sgPlayer, sgMonster, sgItem, sgFloor, sgWall };
static const int USERSHAPEGROUPS = 5;
#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;
ld front_edit = 0.5;
enum class eFront { sphere_camera, sphere_center, equidistants, const_x, const_y };
@ -33,26 +309,20 @@ EX namespace mapeditor {
EX editwhat ew, ewsearch;
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
// (mrx,mry) * (scale-scale') =
// ss * ((xpos',ypos')-(xpos,ypos))
// mx = xb + ssiz*xpos + mrx * scale
// mx = xb + ssiz*xpos' + mrx * scale'
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;
if(keep_mouse) {
ld mrx = (.0 + mousex - current_display->xcenter) / vpconf.scale;
ld mry = (.0 + mousey - current_display->ycenter) / vpconf.scale;
if(vid.xres > vid.yres) {
vpconf.xposition += (vpconf.scale - vpconf.scale*z) * mrx / current_display->scrsize;
vpconf.yposition += (vpconf.scale - vpconf.scale*z) * mry / current_display->scrsize;
}
}
vid.scale *= z;
// printf("scale = " LDF "\n", vid.scale);
vpconf.scale *= z;
// printf("scale = " LDF "\n", vpconf.scale);
#if CAP_TEXTURE
// display(texture::itt);
texture::config.itt = xyscale(texture::config.itt, 1/z);
@ -101,6 +371,39 @@ namespace mapstream {
vector<cell*> cellbyid;
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) {
if(cellids.count(c)) return;
@ -325,6 +628,8 @@ namespace mapstream {
int32_t n = -1; f.write(n);
int32_t id = cellids.count(cwt.at) ? cellids[cwt.at] : -1;
f.write(id);
save_drawing_tool(f);
f.write(vid.always3);
f.write(mutantphase);
@ -507,6 +812,8 @@ namespace mapstream {
savecount = 0; savetime = 0;
cheater = 1;
load_drawing_tool(f);
dynamicval<bool> a3(vid.always3, vid.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*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*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
displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0);
#endif
@ -983,6 +1290,32 @@ namespace mapeditor {
if(d == -1 && fix) d = hrand(mouseover->type);
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() {
dialog::v.clear();
@ -1083,31 +1416,10 @@ namespace mapeditor {
else if(uni == 'G')
push_debug_screen();
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) {
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;
}
});
}
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;
}
});
else if(sym == SDLK_F2) save_level();
else if(sym == SDLK_F3) load_level();
#if CAP_SHOT
else if(sym == SDLK_F6) {
pushScreen(shot::menu);
@ -1188,7 +1500,7 @@ namespace mapeditor {
unsigned gridcolor = 0xC0C0C040;
hyperpoint in_front_dist(ld d) {
return direct_exp(lp_iapply(ztangent(d)), 100);
return direct_exp(lp_iapply(ztangent(d)));
}
hyperpoint find_mouseh3() {
@ -1203,7 +1515,7 @@ namespace mapeditor {
ld d1 = front_edit;
hyperpoint h1 = in_front_dist(d);
if(front_config == eFront::sphere_center)
d1 = geo_dist(drawtrans * C0, h1, iTable);
d1 = geo_dist(drawtrans * C0, h1);
if(front_config == eFront::equidistants) {
hyperpoint h = idt * in_front_dist(d);
d1 = asin_auto(h[2]);
@ -1232,6 +1544,7 @@ namespace mapeditor {
ld equi_range = 1;
EX void drawGrid() {
if(!drawcell) drawcell = cwt.at;
color_t lightgrid = gridcolor;
lightgrid -= (lightgrid & 0xFF) / 2;
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) {
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++) {
ld a = ai * 360 / parallels;
@ -1363,6 +1676,9 @@ namespace mapeditor {
usershape *us = NULL;
bool intexture = false;
(void) intexture;
bool freedraw = drawing_tool;
#if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive) {
@ -1371,12 +1687,12 @@ namespace mapeditor {
line2 = "";
texture::config.data.update();
intexture = true;
freedraw = true;
drawing_tool = false;
}
#else
if(0);
#endif
else {
if(!freedraw) {
sg = drawcellShapeGroup();
@ -1419,7 +1735,7 @@ namespace mapeditor {
// displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
displayfr(8, 8+fs, 2, vid.fsize, line1, 0xC0C0C0, 0);
if(!intexture) {
if(!freedraw) {
if(sg == sgFloor)
displayButton(8, 8+fs*2, line2 + XLAT(" (r = complex tesselations)"), 'r', 0);
else
@ -1466,17 +1782,21 @@ namespace mapeditor {
}
#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);
if(mousekey == 'g')
displayButton(8, 8+fs*16, XLAT("p = grid color"), 'p', 0);
else
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);
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('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);
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);
#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)
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;
}
if(texture::config.tstate != texture::tsActive)
if(!freedraw)
displaymm('e', vid.xres-8, 8+fs, 2, vid.fsize, XLAT("e = edit this"), 16);
#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);
if(MDIM == 4)
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);
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);
@ -1933,12 +2253,35 @@ namespace mapeditor {
if(mkuni == 'g')
coldcenter = ccenter, ccenter = mh, clickused = true;
if(uni == 'd' || uni == 'l' || uni == 'c')
if(uni == 'd' || uni == 'l' || uni == 'c' || uni == 'e')
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)) {
popScreen();
pushScreen(showMapEditor);
drawing_tool = !drawing_tool;
if(!drawing_tool) {
popScreen();
pushScreen(showMapEditor);
}
}
if(uni == 'z' && GDIM == 3) {
@ -2001,35 +2344,57 @@ namespace mapeditor {
if(sym == SDLK_F10) popScreen();
#if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive) {
(void)clickused;
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(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;
}
else {
else if(intexture) {
if(!holdmouse) texture::config.data.undoLock();
texture::drawPixel(mouseover, mouseh, tcolor);
holdmouse = true; lstartcell = NULL;
}
else {
dt_add_free(mouseh);
holdmouse = true;
}
}
if(sym == PSEUDOKEY_RELEASE) {
printf("release\n");
if(mousekey == 'l') {
if(mousekey == 'l' && intexture) {
texture::config.data.undoLock();
texture::where = mouseover;
texture::drawPixel(mouseover, mouseh, tcolor);
texture::drawLine(mouseh, lstart, tcolor);
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();
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;
transmatrix T = rgpushxto0(lstart);
texture::where = lstartcell;
@ -2037,13 +2402,21 @@ namespace mapeditor {
texture::drawPixel(T * xspinpush0(2 * M_PI * i / circp, rad), tcolor);
lstartcell = NULL;
}
else if(mousekey == 'c') {
dt_add_circle(lstart, mouseh);
lstartcell = NULL;
}
else {
cfree = nullptr;
cfree_at = nullptr;
}
}
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)
texture::penwidth = brush_sizes[uni - 2000];
dtwidth = brush_sizes[uni - 2000];
if(uni == '0')
texture::texturesym = !texture::texturesym;
@ -2054,16 +2427,19 @@ namespace mapeditor {
if(uni == 'p') {
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')
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 {
dslayer %= USERLAYERS;
@ -2128,7 +2504,7 @@ namespace mapeditor {
}
#endif
auto hooks = addHook(clearmemory, 0, [] () {
auto hooks = addHook(hooks_clearmemory, 0, [] () {
if(mapeditor::painttype == 4)
mapeditor::painttype = 0, mapeditor::paintwhat = 0,
mapeditor::paintwhat_str = "clear monster";
@ -2137,6 +2513,10 @@ namespace mapeditor {
if(!cheater) patterns::displaycodes = false;
if(!cheater) patterns::whichShape = 0;
modelcell.clear();
mapeditor::dtshapes.clear();
mapeditor::cfree = nullptr;
mapeditor::cfree_at = nullptr;
drawcell = nullptr;
}) +
addHook(hooks_removecells, 0, [] () {
modelcell.clear();
@ -2159,8 +2539,7 @@ namespace mapeditor {
transmatrix textrans;
#if CAP_TEXTURE
void queue_hcircle(transmatrix Ctr, ld radius) {
EX void queue_hcircle(transmatrix Ctr, ld radius) {
vector<hyperpoint> pts;
int circp = int(6 * pow(2, vid.linequality));
if(radius > 0.04) circp *= 2;
@ -2170,9 +2549,8 @@ namespace mapeditor {
pts.push_back(Ctr * xspinpush0(M_PI*j*2/circp, radius));
for(int j=0; j<circp; j++) curvepoint(pts[j]);
curvepoint(pts[0]);
queuecurve(texture::config.paint_color, 0, PPR::LINE);
queuecurve(dtcolor, 0, PPR::LINE);
}
#endif
#if CAP_POLY
EX bool haveUserShape(eShapegroup group, int id) {
@ -2210,10 +2588,10 @@ namespace mapeditor {
queue_hcircle(M2 * ml, hdist(lstart, mouseh));
break;
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;
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(); }
else if(argis("-pic")) { shift(); picfile = 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
else if(argis("-picload")) { PHASE(3); shift(); mapeditor::loadPicFile(args()); }
#endif

View File

@ -230,7 +230,7 @@ EX void showMainMenu() {
dialog::addItem(XLAT(inSpecialMode() ? "reset special modes" : "back to the start menu"), 'R');
string q;
#if ISMOBILE==1
#if ISMOBILE
dialog::addItem(XLAT("visit the website"), 'q');
#else
q = quitsaves() ? "save" : "quit";
@ -248,7 +248,7 @@ EX void showMainMenu() {
if(inv::on)
dialog::addItem(XLAT("inventory"), 'i');
#if ISMOBILE==1
#if ISMOBILE
#if CAP_ACHIEVE
dialog::addItem(XLAT("leaderboards/achievements"), '3');
#endif
@ -305,7 +305,7 @@ EX void showMainMenu() {
#endif
else if(sym == SDLK_ESCAPE)
showMissionScreen();
#if ISMOBILE==1
#if ISMOBILE
#ifdef HAVE_ACHIEVEMENTS
else if(NUMBERKEY == '3') {
achievement_final(false);
@ -323,7 +323,7 @@ EX void showMainMenu() {
// -- display modes --
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."));
dialog::scaleSinh();
}
@ -335,10 +335,10 @@ EX void showGraphQuickKeys() {
dialog::init(XLAT("quick options"));
if(GDIM == 2) {
dialog::addBoolItem(XLAT("orthogonal projection"), vid.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 ? "zoomed stereographic projection" : euclid ? "zoomed in" : "big Poincaré model"), vid.alpha == 1 && vid.scale >= 1, '3');
dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vid.alpha == 0, '4');
dialog::addBoolItem(XLAT("orthogonal projection"), vpconf.alpha >= 500, '1');
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"), vpconf.alpha == 1 && vpconf.scale >= 1, '3');
dialog::addBoolItem(XLAT((sphere || euclid) ? "gnomonic projection" : "Klein-Beltrami model"), vpconf.alpha == 0, '4');
}
else {
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([] {
cheater++;
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."));
#endif
popScreen();
@ -433,13 +433,25 @@ EX void showCreative() {
#endif
#if CAP_EDIT
dialog::addItem(XLAT("vector graphics editor"), 'g');
dialog::addItem(XLAT("shape editor"), 'g');
dialog::add_action([] {
mapeditor::drawing_tool = false;
pushScreen(mapeditor::showDrawEditor);
mapeditor::initdraw(cwt.at);
});
#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
#if CAP_MODEL
if(GDIM == 2) {
@ -875,11 +887,11 @@ EX void showStartMenu() {
specialland = laHalloween;
set_geometry(gSphere);
start_game();
vid.alpha = 999;
vid.scale = 998;
pconf.alpha = 999;
pconf.scale = 998;
}
}
#if CAP_RACING
#if CAP_RACING && MAXMDIM >= 4
else if(uni == 'r' - 96) {
popScreenAll();
resetModes();
@ -889,13 +901,13 @@ EX void showStartMenu() {
specialland = racing::race_lands[rand() % isize(racing::race_lands)];
start_game();
pmodel = mdBand;
models::model_orientation = racing::race_angle;
pconf.model_orientation = racing::race_angle;
racing::race_advance = 1;
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.use_smart_range = 1;
vid.smart_range_detail = 3;
}
@ -947,18 +959,22 @@ EX void showStartMenu() {
// -- overview --
#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); }); }
#endif
EX hookset<named_functionality()> *hooks_o_key;
EX hookset<named_functionality()> hooks_o_key;
EX named_functionality get_o_key() {
if(hooks_o_key) for(auto& h: *hooks_o_key) {
auto res = h.second();
if(res.first != "") return res;
}
auto res = callhandlers(named_functionality(), hooks_o_key);
if (res != named_functionality()) return res;
#if CAP_DAILY
if(daily::on)

View File

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

View File

@ -115,20 +115,13 @@ inline bool mdPseudocylindrical() { return mdBandAny() && !(mdinf[pmodel].flags
EX namespace models {
EX string formula = "z^2";
EX eModel basic_model;
EX ld rotation = 0;
EX ld rotation_xz = 90;
EX ld rotation_xy2 = 90;
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 cos_ball, sin_ball;
EX bool model_straight, model_straight_yz;
EX ld top_z = 5;
EX ld model_transition = 1;
#if HDR
// 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);
}
EX ld spiral_angle = 70;
EX ld spiral_x = 10;
EX ld spiral_y = 7;
int spiral_id = 7;
EX bool use_atan = false;
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 bool ring_not_spiral;
/** the matrix to rotate the Euclidean view from the standard coordinates to the screen coordinates */
EX transmatrix euclidean_spin;
EX ld product_z_scale = 1;
EX void configure() {
ld ball = -vid.ballangle * degree;
ld ball = -pconf.ballangle * degree;
cos_ball = cos(ball), sin_ball = sin(ball);
ocos = cos(model_orientation * degree);
osin = sin(model_orientation * degree);
ocos_yz = cos(model_orientation_yz * degree);
osin_yz = sin(model_orientation_yz * degree);
ocos = cos(pconf.model_orientation * degree);
osin = sin(pconf.model_orientation * degree);
ocos_yz = cos(pconf.model_orientation_yz * degree);
osin_yz = sin(pconf.model_orientation_yz * degree);
model_straight = (ocos > 1 - 1e-9);
model_straight_yz = GDIM == 2 || (ocos_yz > 1-1e-9);
if(history::on) history::apply();
if(!euclid) {
ld b = spiral_angle * degree;
ld b = pconf.spiral_angle * degree;
ld cos_spiral = cos(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;
ld mul = 1;
if(sphere) mul = .5 * sphere_spiral_multiplier;
else if(ring_not_spiral) mul = right_spiral_multiplier;
else mul = any_spiral_multiplier * cos_spiral;
if(sphere) mul = .5 * pconf.sphere_spiral_multiplier;
else if(ring_not_spiral) mul = pconf.right_spiral_multiplier;
else mul = pconf.any_spiral_multiplier * cos_spiral;
spiral_multiplier = cld(cos_spiral, sin_spiral) * cld(spiral_cone_rad * mul / 2., 0);
}
if(euclid) {
euclidean_spin = pispin * inverse(cview() * master_relative(centerover, true));
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]);
}
@ -222,18 +205,27 @@ EX namespace models {
return true;
}
EX bool model_has_orientation() {
EX bool has_orientation(eModel m) {
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() {
return among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdBand) && GDIM == 2;
EX bool is_perspective(eModel m) {
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(among(pmodel, mdPerspective, mdHyperboloid, mdEquidistant)) return false;
if(among(m, mdPerspective, mdHyperboloid, mdEquidistant)) return false;
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::reaction = [] () {
auto& co = torus_zeros[spiral_id];
spiral_x = co.first;
spiral_y = co.second;
vpconf.spiral_x = co.first;
vpconf.spiral_y = co.second;
};
dialog::bound_low(0);
dialog::bound_up(isize(torus_zeros)-1);
}
EX void edit_formula() {
if(pmodel != mdFormula) basic_model = pmodel;
dialog::edit_string(formula, "formula",
if(vpconf.model != mdFormula) vpconf.basic_model = vpconf.model;
dialog::edit_string(vpconf.formula, "formula",
XLAT(
"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; "
@ -314,12 +306,12 @@ EX namespace models {
curvepoint(point2(a*current_display->radius, +M_PI/2*current_display->radius));
queuecurve(forecolor, 0, PPR::LINE);
}
queuereset(pmodel, PPR::LINE);
queuereset(vpconf.model, PPR::LINE);
quickqueue();
};
#endif
dialog::reaction_final = [] () {
pmodel = mdFormula;
vpconf.model = mdFormula;
};
}
@ -351,24 +343,25 @@ EX namespace models {
cmode = sm::SIDE | sm::MAYDARK | sm::CENTER;
gamescreen(0);
dialog::init(XLAT("models & projections"));
USING_NATIVE_GEOMETRY_IN_RUG;
for(int i=0; i<mdGUARD; i++) {
eModel m = eModel(i);
if(m == mdFormula && ISMOBILE) continue;
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] () {
if(m == mdFormula) {
edit_formula();
return;
}
pmodel = m;
vpconf.model = m;
polygonal::solve();
vid.alpha = 1; vid.scale = 1;
vpconf.alpha = 1; vpconf.scale = 1;
if(pmodel == mdBand && sphere)
vid.scale = .3;
vpconf.scale = .3;
if(pmodel == mdDisk && sphere)
vid.scale = .4;
vpconf.scale = .4;
popScreen();
});
}
@ -378,24 +371,24 @@ EX namespace models {
}
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."
);
dialog::extra_options = [] () {
dialog::addBreak(100);
if(sphere && pmodel == mdBandEquiarea) {
dialog::addBoolItem("Gall-Peters", vid.stretch == 2, 'O');
dialog::add_action([] { vid.stretch = 2; dialog::ne.s = "2"; });
dialog::addBoolItem("Gall-Peters", vpconf.stretch == 2, 'O');
dialog::add_action([] { vpconf.stretch = 2; dialog::ne.s = "2"; });
}
if(pmodel == mdBandEquiarea) {
// y = K * sin(phi)
// cos(phi) * cos(phi) = 1/K
if(sphere && vid.stretch >= 1) {
ld phi = acos(sqrt(1/vid.stretch));
if(sphere && vpconf.stretch >= 1) {
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)));
}
else if(hyperbolic && abs(vid.stretch) <= 1 && abs(vid.stretch) >= 1e-9) {
ld phi = acosh(abs(sqrt(1/vid.stretch)));
else if(hyperbolic && abs(vpconf.stretch) <= 1 && abs(vpconf.stretch) >= 1e-9) {
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)));
}
else dialog::addInfo("");
@ -406,9 +399,12 @@ EX namespace models {
EX void model_menu() {
cmode = sm::SIDE | sm::MAYDARK | sm::CENTER;
gamescreen(0);
USING_NATIVE_GEOMETRY_IN_RUG;
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);
if(nonisotropic && !sl2)
@ -420,61 +416,61 @@ EX namespace models {
dialog::lastItem().value += " " + its(rotation) + "°";
else
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()) {
dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z');
dialog::addSelItem(XLAT("scale factor"), fts(vpconf.scale), 'z');
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::addInfo("NOTE: this works 'correctly' only if the Poincaré model/stereographic projection is used.");
dialog::addBreak(50);
}
if(among(pmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) {
dialog::addSelItem(XLAT("projection distance"), fts(vid.alpha) + " (" + current_proj_name() + ")", 'p');
if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) {
dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", 'p');
dialog::add_action(projectionDialog);
}
if(model_has_orientation()) {
dialog::addSelItem(XLAT("model orientation"), fts(model_orientation) + "°", 'l');
if(has_orientation(vpmodel)) {
dialog::addSelItem(XLAT("model orientation"), fts(vpconf.model_orientation) + "°", 'l');
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) {
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::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(
"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 "
"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::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 "
"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 "
"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::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 "
"smaller than this parameter are not shown; it also affects the fog effect"
" (near clipping plane = 0% fog, far clipping plane = 100% fog)."));
});
}
if(pmodel == mdPolynomial) {
if(vpmodel == mdPolynomial) {
dialog::addSelItem(XLAT("coefficient"),
fts(polygonal::coefr[polygonal::coefid]), 'x');
dialog::add_action([] () {
@ -496,26 +492,26 @@ EX namespace models {
});
}
if(pmodel == mdHalfplane) {
dialog::addSelItem(XLAT("half-plane scale"), fts(halfplane_scale), 'b');
if(vpmodel == mdHalfplane) {
dialog::addSelItem(XLAT("half-plane scale"), fts(vpconf.halfplane_scale), 'b');
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) {
dialog::addBoolItem_action(XLAT("use atan to make it finite"), use_atan, 'x');
if(vpmodel == mdRotatedHyperboles) {
dialog::addBoolItem_action(XLAT("use atan to make it finite"), vpconf.use_atan, 'x');
}
if(pmodel == mdBall) {
dialog::addSelItem(XLAT("projection in ball model"), fts(vid.ballproj), 'x');
if(vpmodel == mdBall) {
dialog::addSelItem(XLAT("projection in ball model"), fts(vpconf.ballproj), 'x');
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.");
});
}
if(pmodel == mdPolygonal) {
if(vpmodel == mdPolygonal) {
dialog::addSelItem(XLAT("polygon sides"), its(polygonal::SI), 'x');
dialog::add_action([] () {
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)) {
dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vid.ballangle) + "°", 'b');
if(is_3d(vpconf) && GDIM == 2) {
dialog::addSelItem(XLAT("camera rotation in 3D models"), fts(vpconf.ballangle) + "°", 'b');
dialog::add_action(config_camera_rotation);
}
if(pmodel == mdHyperboloid) {
dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(top_z), 'l');
if(vpmodel == mdHyperboloid) {
dialog::addSelItem(XLAT("maximum z coordinate to show"), fts(vpconf.top_z), 'l');
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()) {
dialog::addSelItem(XLAT("model transition"), fts(model_transition), 't');
if(has_transition(vpmodel)) {
dialog::addSelItem(XLAT("model transition"), fts(vpconf.model_transition), 't');
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."
);
});
}
if(among(pmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) {
dialog::addSelItem(XLAT("Möbius transformations"), fts(vid.skiprope) + "°", 'S');
if(among(vpmodel, mdJoukowsky, mdJoukowskyInverted, mdSpiral) && GDIM == 2) {
dialog::addSelItem(XLAT("Möbius transformations"), fts(vpconf.skiprope) + "°", 'S');
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) {
dialog::addSelItem(XLAT("parameter"), fts(vid.euclid_to_sphere), 'l');
if(vpmodel == mdHemisphere && euclid) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.euclid_to_sphere), 'l');
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."
);
dialog::scaleLog();
});
}
if(among(pmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) {
dialog::addSelItem(XLAT("parameter"), fts(vid.twopoint_param), 'b');
dialog::add_action([](){
dialog::editNumber(vid.twopoint_param, 1e-3, 10, .1, 1, XLAT("parameter"),
if(among(vpmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.twopoint_param), 'b');
dialog::add_action([vpmodel](){
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 "
"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."
);
dialog::scaleLog();
});
}
if(pmodel == mdFisheye) {
dialog::addSelItem(XLAT("parameter"), fts(vid.fisheye_param), 'b');
if(vpmodel == mdFisheye) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.fisheye_param), 'b');
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."
);
dialog::scaleLog();
});
}
if(pmodel == mdCollignon) {
dialog::addSelItem(XLAT("parameter"), fts(vid.collignon_parameter) + (vid.collignon_reflected ? " (r)" : ""), 'b');
if(vpmodel == mdCollignon) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.collignon_parameter) + (vpconf.collignon_reflected ? " (r)" : ""), 'b');
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::addBoolItem_action(XLAT("reflect"), vid.collignon_reflected, 'R');
dialog::addBoolItem_action(XLAT("reflect"), vpconf.collignon_reflected, 'R');
};
});
}
if(pmodel == mdSpiral && !euclid) {
dialog::addSelItem(XLAT("spiral angle"), fts(spiral_angle) + "°", 'x');
if(vpmodel == mdSpiral && !euclid) {
dialog::addSelItem(XLAT("spiral angle"), fts(vpconf.spiral_angle) + "°", 'x');
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")
);
});
ld& which =
sphere ? sphere_spiral_multiplier :
ring_not_spiral ? right_spiral_multiplier :
any_spiral_multiplier;
sphere ? vpconf.sphere_spiral_multiplier :
ring_not_spiral ? vpconf.right_spiral_multiplier :
vpconf.any_spiral_multiplier;
dialog::addSelItem(XLAT("spiral multiplier"), fts(which) + "°", 'M');
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::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) {
dialog::addSelItem(XLAT("spiral period: x"), fts(spiral_x), 'x');
if(vpmodel == mdSpiral && euclid) {
dialog::addSelItem(XLAT("spiral period: x"), fts(vpconf.spiral_x), 'x');
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::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) {
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);
if(product_model()) {
dialog::addSelItem(XLAT("product Z stretch"), fts(product_z_scale), 'Z');
if(product_model(vpmodel)) {
dialog::addSelItem(XLAT("product Z stretch"), fts(vpconf.product_z_scale), 'Z');
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();
});
}
@ -667,7 +664,7 @@ EX namespace models {
bool shaderside_projection = get_shader_flags() & SF_DIRECT;
if(vid.consider_shader_projection && !shaderside_projection)
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::add_action([] { vid.consider_shader_projection = !vid.consider_shader_projection; });
@ -677,7 +674,7 @@ EX namespace models {
dialog::addItem(XLAT("history mode"), 'a');
dialog::add_action_push(history::history_menu);
#if CAP_RUG
if(GDIM == 2) {
if(GDIM == 2 || rug::rugged) {
dialog::addItem(XLAT("hypersian rug mode"), 'u');
dialog::add_action_push(rug::show);
}
@ -707,39 +704,39 @@ EX namespace models {
shift_arg_formula(history::extra_line_steps);
}
else if(argis("-stretch")) {
PHASEFROM(2); shift_arg_formula(vid.stretch);
PHASEFROM(2); shift_arg_formula(vpconf.stretch);
}
else if(argis("-PM")) {
PHASEFROM(2); shift(); pmodel = read_model(args());
if(pmodel == mdFormula) {
shift(); basic_model = eModel(argi());
shift(); formula = args();
PHASEFROM(2); shift(); vpconf.model = read_model(args());
if(vpconf.model == mdFormula) {
shift(); vpconf.basic_model = eModel(argi());
shift(); vpconf.formula = args();
}
}
else if(argis("-ballangle")) {
PHASEFROM(2);
shift_arg_formula(vid.ballangle);
shift_arg_formula(vpconf.ballangle);
}
else if(argis("-topz")) {
PHASEFROM(2);
shift_arg_formula(models::top_z);
shift_arg_formula(vpconf.top_z);
}
else if(argis("-twopoint")) {
PHASEFROM(2);
shift_arg_formula(vid.twopoint_param);
shift_arg_formula(vpconf.twopoint_param);
}
else if(argis("-hp")) {
PHASEFROM(2);
shift_arg_formula(models::halfplane_scale);
shift_arg_formula(vpconf.halfplane_scale);
}
else if(argis("-mori")) {
PHASEFROM(2);
shift_arg_formula(models::model_orientation);
shift_arg_formula(vpconf.model_orientation);
}
else if(argis("-mori2")) {
PHASEFROM(2);
shift_arg_formula(models::model_orientation);
shift_arg_formula(models::model_orientation_yz);
shift_arg_formula(vpconf.model_orientation);
shift_arg_formula(vpconf.model_orientation_yz);
}
else if(argis("-crot")) {
PHASEFROM(2);
@ -749,43 +746,43 @@ EX namespace models {
}
else if(argis("-clip")) {
PHASEFROM(2);
shift_arg_formula(models::clip_min);
shift_arg_formula(models::clip_max);
shift_arg_formula(vpconf.clip_min);
shift_arg_formula(vpconf.clip_max);
}
else if(argis("-mtrans")) {
PHASEFROM(2);
shift_arg_formula(models::model_transition);
shift_arg_formula(vpconf.model_transition);
}
else if(argis("-sang")) {
PHASEFROM(2);
shift_arg_formula(models::spiral_angle);
shift_arg_formula(vpconf.spiral_angle);
if(sphere)
shift_arg_formula(models::sphere_spiral_multiplier);
else if(models::spiral_angle == 90)
shift_arg_formula(models::right_spiral_multiplier);
shift_arg_formula(vpconf.sphere_spiral_multiplier);
else if(vpconf.spiral_angle == 90)
shift_arg_formula(vpconf.right_spiral_multiplier);
}
else if(argis("-ssm")) {
PHASEFROM(2);
shift_arg_formula(models::any_spiral_multiplier);
shift_arg_formula(vpconf.any_spiral_multiplier);
}
else if(argis("-scone")) {
PHASEFROM(2);
shift_arg_formula(models::spiral_cone);
shift_arg_formula(vpconf.spiral_cone);
}
else if(argis("-sxy")) {
PHASEFROM(2);
shift_arg_formula(models::spiral_x);
shift_arg_formula(models::spiral_y);
shift_arg_formula(vpconf.spiral_x);
shift_arg_formula(vpconf.spiral_y);
}
else if(argis("-mob")) {
PHASEFROM(2);
shift_arg_formula(vid.skiprope);
shift_arg_formula(vpconf.skiprope);
}
else if(argis("-zoom")) {
PHASEFROM(2); shift_arg_formula(vid.scale);
PHASEFROM(2); shift_arg_formula(vpconf.scale);
}
else if(argis("-alpha")) {
PHASEFROM(2); shift_arg_formula(vid.alpha);
PHASEFROM(2); shift_arg_formula(vpconf.alpha);
}
else if(argis("-d:model"))
launch_dialog(model_menu);

View File

@ -354,8 +354,9 @@ EX int angledist(int t, int d1, int d2) {
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;
if(mirrored) dd = -dd;
while(dd<0) dd += t;
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 == 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)
return 1500 - bulldist(c2);
@ -1410,7 +1411,7 @@ EX cell *lastmountpos[MAXPLAYER];
EX void clearshadow() {
shpos.resize(SHSIZE);
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() {

View File

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

View File

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

View File

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

View File

@ -104,7 +104,7 @@ EX namespace sn {
hyperpoint res;
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 {
@ -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();
s.load();
@ -562,18 +562,17 @@ EX namespace sn {
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[0] < 0.) res[0] = -res[0];
if(h[1] < 0.) res[1] = -res[1];
if(!just_direction) return table_to_azeq(res);
return res;
if(flags & pfNO_DISTANCE) return res;
return table_to_azeq(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();
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 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[1] < 0.) res[1] = -res[1];
if(!just_direction) return table_to_azeq(res);
return res;
if(flags & pfNO_DISTANCE) return res;
return table_to_azeq(res);
}
EX string shader_symsol = sn::common +
@ -609,9 +607,25 @@ EX namespace sn {
"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 > .65 + iz * .25 && iy > .55) res = vec4(0.,0.,0.,1.); "
"else "
"\n#ifndef SOLV_ALL\n"
"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));"
@ -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 side = h[2] - h[0] * h[1] / 2;
@ -769,11 +783,13 @@ EX namespace nilv {
ld s = sin(2 * alpha_total);
int max_iter = (prec & pfLOW_BS_ITER) ? 5 : 20;
for(int it=0;; it++) {
ld w = (wmin + wmax) / 2;
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 c = b / sin(w/2);
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) {
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);
}
@ -1048,6 +1064,26 @@ EX namespace hybrid {
EX geometry_information *underlying_cgip;
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) {
if(WDIM == 3) return;
@ -1243,8 +1279,10 @@ EX namespace hybrid {
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);
if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, -1);
int &wo = cgi.walloffsets[id];
if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, {-1, nullptr});
auto &wop = cgi.walloffsets[id];
int &wo = wop.first;
if(!wop.second) wop.second = c;
if(wo == -1) {
cell *c1 = hybrid::get_where(c).first;
wo = isize(cgi.shWall3D);
@ -1296,6 +1334,20 @@ EX namespace hybrid {
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;
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([&] {
hyperpoint h = tC0(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]);
distance = hdist0(h);
beta = atan2(h[1], h[0]);
@ -1831,6 +1883,34 @@ EX namespace rots {
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 {
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 */
EX transmatrix qtm(hyperpoint h) {
ld& x = h[0];
ld& y = h[1];
ld& z = h[2];
@ -1894,6 +1975,16 @@ EX namespace rots {
M[1][2] = -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;
}
@ -1930,13 +2021,18 @@ EX namespace rots {
dynamicval<bool> pf(playerfound, true);
dynamicval<cell*> m5(centerover, co);
dynamicval<transmatrix> m2(View, inprod ? pView : ypush(0) * qtm(h));
if(PURE) View = View * pispin;
dynamicval<transmatrix> m3(playerV, Id);
dynamicval<transmatrix> m4(actual_view_transform, Id);
dynamicval<eModel> pm(pmodel, mdDisk);
dynamicval<ld> pss(vid.scale, (sphere ? 10 : 1) * underlying_scale);
dynamicval<ld> psa(vid.alpha, sphere ? 10 : 1);
dynamicval<ld> pss(pconf.scale, (sphere ? 10 : 1) * underlying_scale);
dynamicval<ld> psa(pconf.alpha, sphere ? 10 : 1);
dynamicval<hrmap*> p(hybrid::pmap, NULL);
dynamicval<int> psr(sightrange_bonus, 0);
dynamicval<int> psx(vid.use_smart_range, 2);
dynamicval<ld> psy(vid.smart_range_detail, 1);
calcparam();
reset_projection(); current_display->set_all(0);
ptds.clear();
@ -1953,6 +2049,86 @@ EX namespace rots {
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 hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) {
@ -1960,6 +2136,7 @@ EX namespace nisot {
#if CAP_SOLV
else if(sn::in()) return sn::christoffel(at, velocity, transported);
#endif
else if(stretch::in()) return stretch::christoffel(at, velocity, transported);
else if(sl2) return slr::christoffel(at, velocity, transported);
else return point3(0, 0, 0);
}
@ -1970,37 +2147,43 @@ EX namespace nisot {
#endif
return true;
}
EX void geodesic_step(hyperpoint& at, hyperpoint& velocity) {
auto acc = christoffel(at, velocity, velocity);
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 get_acceleration(const hyperpoint& at, const hyperpoint& vel) {
return christoffel(at, vel, vel);
}
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);
v /= steps;
v /= rk_steps;
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;
}
EX transmatrix parallel_transport_bare(transmatrix Pos, hyperpoint h) {
bool stretch = stretch::in();
h[3] = 0;
auto tPos = transpose(Pos);
const ld eps = 1e-4;
if(sl2) {
if(sl2 && !stretch) {
hyperpoint p = slr::to_phigans(tPos[3]);
for(int i=0; i<3; i++)
tPos[i] = (slr::to_phigans(tPos[3] + tPos[i] * eps) - p) / eps;
@ -2009,16 +2192,70 @@ EX namespace nisot {
}
else h = Pos * h;
int steps = 100;
int steps = rk_steps;
h /= steps;
for(int i=0; i<steps; i++) {
for(int j=0; j<3; j++)
tPos[j] += christoffel(tPos[3], h, tPos[j]);
geodesic_step(tPos[3], h);
auto& at = tPos[3];
auto& vel = 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]);
for(int i=0; i<3; i++)
tPos[i] = (slr::from_phigans(tPos[3] + tPos[i] * eps) - p) / eps;
@ -2029,6 +2266,7 @@ EX namespace nisot {
}
EX void fixmatrix(transmatrix& T) {
if(sphere) return hr::fixmatrix(T);
transmatrix push = eupush( tC0(T) );
transmatrix push_back = inverse(push);
transmatrix gtl = push_back * T;
@ -2043,12 +2281,12 @@ EX namespace nisot {
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);
transmatrix push_back = inverse(translate(at));
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;
@ -2131,6 +2369,11 @@ EX namespace nisot {
shift_arg_formula(nilv::nilwidth);
return 0;
}
else if(argis("-rk-steps")) {
PHASEFROM(2);
shift(); rk_steps = argi();
return 0;
}
else if(argis("-nilv")) {
PHASEFROM(2);
if(nil) stop_game();
@ -2153,6 +2396,11 @@ EX namespace nisot {
hybrid::reconfigure();
return 0;
}
else if(argis("-rot-stretch")) {
PHASEFROM(2);
shift_arg_formula(stretch::factor, ray::reset_raycaster);
return 0;
}
else if(argis("-prodturn")) {
PHASEFROM(2);
if(prod) stop_game();

View File

@ -58,14 +58,14 @@ int eupattern4(cell *c) {
EX bool ishept(cell *c) {
// EUCLIDEAN
if(euclid && PURE) return eupattern(c) == 0;
if(euc::in() && PURE) return eupattern(c) == 0;
else if(hybri) { cell *c1 = hybrid::get_where(c).first; return c1 == c1->master->c7; }
else return c == c->master->c7;
}
EX bool ishex1(cell *c) {
// EUCLIDEAN
if(euclid && PURE) return eupattern(c) == 1;
if(euc::in() && PURE) return eupattern(c) == 1;
#if CAP_GP
else if(GOLDBERG) return c->master->c7 != c && !pseudohept(c->move(0));
#endif
@ -74,14 +74,14 @@ EX bool ishex1(cell *c) {
bool ishex2(cell *c) {
// EUCLIDEAN
if(euclid && PURE) return eupattern(c) == 1;
if(euc::in() && PURE) return eupattern(c) == 1;
#if CAP_GP
else if(GOLDBERG) return c->master->c7 != c && gp::pseudohept_val(c) == 1;
#endif
else return c->master->c7 != c;
}
int chessvalue(cell *c) {
EX int chessvalue(cell *c) {
#if CAP_ARCM
if(arcm::in())
return arcm::chessvalue(c);
@ -372,6 +372,7 @@ EX pair<int, bool> fieldval(cell *c) {
}
EX int fieldval_uniq(cell *c) {
if(fake::in()) return FPIU(fieldval_uniq(c));
if(experimental) return 0;
else if(hybri) {
auto c1 = hybrid::get_where(c).first;
@ -388,7 +389,7 @@ EX int fieldval_uniq(cell *c) {
if(ctof(c)) return c->master->fieldval;
else return createMov(c, 0)->master->fieldval + 256 * createMov(c,2)->master->fieldval + (1<<16) * createMov(c,4)->master->fieldval;
}
else if(euclid && !kite::in() && !arcm::in()) {
else if(euc::in()) {
auto p = euc2_coordinates(c);
if(bounded) return p.first + (p.second << 16);
return gmod(p.first - 22 * p.second, 3*127);
@ -1405,7 +1406,7 @@ EX bool pseudohept(cell *c) {
#if MAXMDIM == 4
if(WDIM == 3) {
if(geometry == gField435) return false;
else if(euclid) return euc::pseudohept(c);
else if(euc::in()) return euc::pseudohept(c);
else return reg3::pseudohept(c);
}
#endif
@ -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) {
@ -1737,7 +1742,8 @@ EX namespace patterns {
case 'g':
return canvasback;
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;
return r;
}
@ -1786,6 +1792,8 @@ EX namespace patterns {
return colortables['x'][zebra3(c)];
case 'w':
return colortables['w'][randpattern(c, subcanvas) ? 1 : 0];
case 'H':
return colortables['c'][c->master->c7 == c ? 0 : 1];
case 'l':
return random_landscape(c, 3, 1, 17, 0x808080);
case 'd':
@ -1801,16 +1809,24 @@ EX namespace patterns {
case 'v':
return colortables['v'][sevenval(c)];
case 'j': {
if(c == currentmap->gamestart()) return canvasback;
int d = c->master->distance;
if(geometry == gNil) d = c->master->zebraval;
if(d % 2 == 0 || d < -5 || d > 5) return canvasback;
return colortables['j'][(d+5)/2];
if(euc::in()) d = euc::get_ispacemap()[c->master][0];
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': {
if(c == currentmap->gamestart()) return canvasback;
int d = c->master->distance;
if(geometry == gNil) d = c->master->zebraval;
if(d % 2 == 0 || d < -5 || d > 5) return hrand(100) < 10 ? 0xFFFFFFFF : canvasback;
return hrand(100) < 50 ? 0 : colortables['j'][(d+5)/2];
if((d&3) != 2) return hrand(100) < jblock ? 0xFFFFFFFF : canvasback;
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': {
color_t res;
@ -2876,7 +2892,7 @@ EX namespace linepatterns {
dialog::addSelItem("line width", fts(width), 'W');
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');
@ -2938,6 +2954,10 @@ int read_pattern_args() {
lp->color = arghex();
}
else if(argis("-fat-edges")) {
PHASEFROM(2); shift(); fat_edges = argi();
}
else if(argis("-palw")) {
PHASEFROM(2);
shift(); string ss = args();
@ -2985,6 +3005,13 @@ int read_pattern_args() {
else patterns::canvasback = arghex();
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")) {
PHASEFROM(2);
stop_game();
@ -3013,7 +3040,7 @@ int read_pattern_args() {
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
}

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) {
eMonster m = c->monst;
if(c->wall == waCavewall) markOrb(bb ? itOrbSword2: itOrbSword);
if(c->wall == waSmallTree || c->wall == waBigTree || c->wall == waRose || c->wall == waCTree || c->wall == waVinePlant ||
thruVine(mt, c) || c->wall == waBigBush || c->wall == waSmallBush || c->wall == waSolidBranch || c->wall == waWeakBranch) {
if(among(c->wall, waSmallTree, waBigTree, waRose, waCTree, waVinePlant, waBigBush, waSmallBush, waSolidBranch, waWeakBranch, waShrub)
|| thruVine(mt, c)) {
changes.ccell(c);
playSound(NULL, "hit-axe"+pick123());
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);
}
vector<ld> equal_weights(20, 1);
vector<ld> equal_weights(1000, 1);
#if !(CAP_BT && MAXMDIM >= 4)
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;
bshape(shWall3D[id], PPR::WALL);
last->flags |= POLY_TRIANGLES;
last->flags |= POLY_TRIANGLES | POLY_PRINTABLE;
hyperpoint center = Hypc;
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);
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]);
hpcpush(res);
});
@ -829,7 +829,7 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
}
if(nil)
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]);
hpcpush(res);
}
@ -842,8 +842,13 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
hpcpush(mid(C0, hpc[a]));
if(shWall3D[id].flags & POLY_TRIANGLES)
last->flags |= POLY_TRIANGLES;
if(shWall3D[id].flags & POLY_PRINTABLE)
last->flags |= POLY_PRINTABLE;
finishshape();
shWall3D[id].intester = C0;
shMiniWall3D[id].intester = C0;
shPlainWall3D[id] = shWall3D[id]; // force_triangles ? shWall3D[id] : shWireframe3D[id];
}
@ -967,66 +972,14 @@ void geometry_information::create_wall3d() {
walloffsets.clear();
}
if(GDIM == 3 && euclid && S7 == 6) {
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()) {
if(euc::in() || reg3::in()) {
int facesize = isize(cgi.cellshape) / S7;
int next = 0;
for(int w=0; w<S7; w++) {
vector<hyperpoint> vertices;
if(S7 == 14) facesize = (w%7 < 3 ? 4 : 6);
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);
}
}
@ -1189,6 +1142,8 @@ void geometry_information::prepare_shapes() {
if(GDIM == 3 && !floor_textures) make_floor_textures();
#endif
if(fake::in()) { FPIU( cgi.require_shapes() ); }
symmetriesAt.clear();
allshapes.clear();
#if CAP_GP

View File

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

View File

@ -835,7 +835,7 @@ heptspin sview;
#if CAP_COMMANDLINE
auto hook =
addHook(hooks_args, 100, readArgs)
+ addHook(clearmemory, 0, []() {
+ addHook(hooks_clearmemory, 0, []() {
track_ready = false;
track.clear();
rti.clear();
@ -939,15 +939,15 @@ void race_projection() {
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([] () {
pmodel = mdDisk;
race_advance = 0;
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.use_smart_range = 0;
vid.smart_range_detail = 3;
});
@ -955,13 +955,13 @@ void race_projection() {
dialog::addBoolItem(XLAT("band"), pmodel == mdBand, '2');
dialog::add_action([] () {
pmodel = mdBand;
models::model_orientation = race_angle;
pconf.model_orientation = race_angle;
race_advance = 1;
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.use_smart_range = 1;
vid.smart_range_detail = 3;
});
@ -969,26 +969,26 @@ void race_projection() {
dialog::addBoolItem(XLAT("half-plane"), pmodel == mdHalfplane, '3');
dialog::add_action([] () {
pmodel = mdHalfplane;
models::model_orientation = race_angle + 90;
pconf.model_orientation = race_angle + 90;
race_advance = 0.5;
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
pconf.camera_angle = 0;
pconf.xposition = 0;
pconf.yposition = 0;
pconf.scale = 1;
vid.use_smart_range = 1;
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([] () {
pmodel = mdDisk;
race_advance = 0;
vid.yshift = -0.3;
vid.camera_angle = -45;
vid.scale = 18/16. * vid.xres / vid.yres / multi::players;
vid.xposition = 0;
vid.yposition = -0.9;
pconf.camera_angle = -45;
pconf.scale = 18/16. * vid.xres / vid.yres / multi::players;
pconf.xposition = 0;
pconf.yposition = -0.9;
vid.use_smart_range = 1;
vid.smart_range_detail = 3;
});
@ -1011,8 +1011,8 @@ void race_projection() {
dialog::addSelItem(XLAT("race angle"), fts(race_angle), 'a');
dialog::add_action([] () {
dialog::editNumber(race_angle, 0, 360, 15, 90, XLAT("race angle"), "");
int q = models::model_orientation - race_angle;
dialog::reaction = [q] () { models::model_orientation = race_angle + q; };
int q = pconf.model_orientation - race_angle;
dialog::reaction = [q] () { pconf.model_orientation = race_angle + q; };
});
}
@ -1060,6 +1060,7 @@ void race_projection() {
bool alternate = false;
#if MAXMDIM >= 4
EX void thurston_racing() {
gamescreen(1);
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("hyperbolic"), [] { set_geometry(gBinary3); vid.texture_step = 4; });
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); });
#endif
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("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); });
}
else {
#if CAP_SOLV
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("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("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); });
@ -1108,6 +1113,7 @@ void race_projection() {
dialog::addBack();
dialog::display();
}
#endif
void raceconfigurer() {
@ -1195,8 +1201,10 @@ void race_projection() {
});
}
#if MAXMDIM >= 4
dialog::addItem(XLAT("racing in Thurston geometries"), 'T');
dialog::add_action_push(thurston_racing);
#endif
dialog::addBack();
dialog::display();
@ -1373,8 +1381,7 @@ EX void drawStats() {
if(!racing::on) return;
dynamicval<eModel> pm(pmodel, flat_model());
glClear(GL_DEPTH_BUFFER_BIT);
flat_model_enabler fme;
initquickqueue();
int bsize = vid.fsize * 2;

View File

@ -26,7 +26,7 @@ pair<bool, hyperpoint> makeradar(hyperpoint h) {
ld d = hdist0(h);
if(sol && nisot::geodesic_movement) {
h = inverse_exp(h, iLazy);
h = inverse_exp(h, pQUICK);
ld r = hypot_d(3, h);
if(r < 1) h = h * (atanh(r) / r);
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 maxstep_sol = .02;
EX ld maxstep_sol = .05;
EX ld maxstep_nil = .1;
EX ld minstep = .001;
@ -41,16 +41,17 @@ EX int max_cells = 2048;
EX bool rays_generate = true;
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() {
if(nonisotropic) return max_iter_sol;
if(nonisotropic || stretch::in()) return max_iter_sol;
else return max_iter_iso;
}
ld& maxstep_current() {
if(sn::in()) return maxstep_sol;
if(sn::in() || stretch::in()) return maxstep_sol;
else return maxstep_nil;
}
@ -65,13 +66,17 @@ EX bool available() {
if(WDIM == 2) return false;
if(hyperbolic && pmodel == mdPerspective && !kite::in())
return true;
if(sphere && pmodel == mdPerspective && !rotspace)
return true;
if(nil && S7 == 8)
return false;
if((sn::in() || nil) && pmodel == mdGeodesic)
if((sn::in() || nil || sl2) && pmodel == mdGeodesic)
return true;
if(euclid && pmodel == mdPerspective && !bt::in())
return true;
if(prod && PURE)
if(prod && (PURE || BITRUNCATED))
return true;
if(pmodel == mdPerspective && stretch::in())
return true;
return false;
}
@ -79,13 +84,14 @@ EX bool available() {
/** do we want to use the raycaster? */
EX bool requested() {
if(cgflags & qRAYONLY) return true;
if(stretch::in()) return true;
if(!want_use) return false;
#if CAP_TEXTURE
if(texture::config.tstate == texture::tsActive) return false;
#endif
if(!available()) return false;
if(want_use == 2) return true;
return racing::on || quotient;
return racing::on || quotient || fake::in();
}
#if HDR
@ -97,6 +103,7 @@ struct raycaster : glhr::GLprogram {
GLint uLinearSightRange, uExpStart, uExpDecay;
GLint uBLevel;
GLint uPosX, uPosY;
GLint uWallOffset, uSides;
raycaster(string vsh, string fsh);
};
@ -134,6 +141,9 @@ raycaster::raycaster(string vsh, string fsh) : GLprogram(vsh, fsh) {
tWallcolor = glGetUniformLocation(_program, "tWallcolor");
tTextureMap = glGetUniformLocation(_program, "tTextureMap");
uWallOffset = glGetUniformLocation(_program, "uWallOffset");
uSides = glGetUniformLocation(_program, "uSides");
uPosX = glGetUniformLocation(_program, "uPosX");
uPosY = glGetUniformLocation(_program, "uPosY");
}
@ -169,16 +179,27 @@ string build_getter(string type, string name, int index) {
#define GET(array, index) array "[" index "]"
#endif
EX hookset<void(string&, string&)> *hooks_rayshader;
EX hookset<bool(shared_ptr<raycaster>)> *hooks_rayset;
EX hookset<void(string&, string&)> hooks_rayshader;
EX hookset<bool(shared_ptr<raycaster>)> hooks_rayset;
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;
deg = S7; if(prod) deg += 2;
if(!our_raycaster) {
bool asonov = hr::asonov::in();
bool use_reflect = reflect_val && !nil && !levellines;
bool bi = BITRUNCATED;
string vsh =
"attribute mediump vec4 aPosition;\n"
@ -211,7 +232,7 @@ void enable_raycaster() {
"uniform mediump vec4 uWallX["+rays+"];\n"
"uniform mediump vec4 uWallY["+rays+"];\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";
#ifdef GLES_ONLY
@ -225,15 +246,19 @@ void enable_raycaster() {
"uniform mediump float uPLevel;\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()) {
fsh += "uniform mediump float uBLevel;\n";
flat1 = bt::dirs_outer();
flat2 -= bt::dirs_inner();
}
if(IN_ODS || hyperbolic) fsh +=
if(hyperbolic) fsh +=
"mediump mat4 xpush(float x) { return mat4("
"cosh(x), 0., 0., sinh(x),\n"
@ -241,7 +266,16 @@ void enable_raycaster() {
"0., 0., 1., 0.,\n"
"sinh(x), 0., 0., cosh(x)"
");}\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 +=
"mediump mat4 xzspin(float x) { return mat4("
@ -258,13 +292,22 @@ void enable_raycaster() {
"0., 0., 0., 1."
");}\n";
if(bi) {
fsh += "int walloffset, sides;\n";
}
else {
fsh += "const int walloffset = 0;\n"
"const int sides = " + its(centerover->type) + ";\n";
}
fsh +=
"mediump vec2 map_texture(mediump vec4 pos, int which) {\n";
if(nil) fsh += "if(which == 2 || which == 5) pos.z = 0.;\n";
else if(hyperbolic && bt::in()) fsh +=
"pos = vec4(-log(pos.w-pos.x), pos.y, pos.z, 1);\n"
"pos.yz *= exp(pos.x);\n";
else if(hyperbolic) fsh +=
else if(hyperbolic || sphere) fsh +=
"pos /= pos.w;\n";
else if(prod) fsh +=
"pos = vec4(pos.x/pos.z, pos.y/pos.z, pos.w, 0);\n";
@ -279,6 +322,8 @@ void enable_raycaster() {
"}\n"
"return vec2(1, 1);\n"
"}\n";
bool stepbased = nonisotropic || stretch::in();
string fmain = "void main() {\n";
@ -303,9 +348,13 @@ void enable_raycaster() {
" at0.xyz = at0.xyz / length(at0.xyz);\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";
if(nonisotropic) fmain +=
if(stepbased) fmain +=
" const mediump float maxstep = " + fts(maxstep_current()) + ";\n"
" const mediump float minstep = " + fts(minstep) + ";\n"
" mediump float next = maxstep;\n";
@ -328,6 +377,16 @@ void enable_raycaster() {
else fmain +=
" mediump vec4 position = vw * vec4(0., 0., 0., 1.);\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 +=
" mediump float go = 0.;\n"
@ -345,39 +404,39 @@ void enable_raycaster() {
if(IN_ODS) fmain +=
" if(go == 0.) {\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"
" if(cand < best - .001) { dist = 0.; best = cand; which = i; }\n"
" }\n"
" }\n";
if(!nonisotropic) {
if(!stepbased) {
fmain +=
" 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 +=
" 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"
" mediump float d = atanh(v);\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";
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 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";
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"
" 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"
" 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";
else if(hyperbolic) fmain +=
" 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 vec4 next_tangent = position * sinh(d) + tangent * cosh(d);\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 +=
" mediump float deno = dot(position, tangent) - dot(uM[i]*position, uM[i]*tangent);\n"
" if(deno < 1e-6 && deno > -1e-6) continue;\n"
@ -423,8 +487,8 @@ void enable_raycaster() {
}
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 = "+its(S7)+"; }}\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 = sides-2; }}\n";
fmain += "}\n";
@ -434,7 +498,7 @@ void enable_raycaster() {
fmain +=
" if(which == -1 && dist == 0.) return;";
}
// shift d units
if(use_reflect) fmain +=
"bool reflect = false;\n";
@ -454,12 +518,19 @@ void enable_raycaster() {
else if(in_e2xe()) fmain +=
" position = position + tangent * dist * xspeed;\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 vec4 v = position * ch + tangent * sh;\n"
" tangent = tangent * ch + position * sh;\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 +=
"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"
" 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";
else fsh +=
else if(nil && false) fsh +=
"mediump vec4 christoffel(mediump vec4 pos, mediump vec4 vel, mediump vec4 tra) {\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(0.,0.,0.,0.);\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";
@ -501,13 +609,20 @@ void enable_raycaster() {
if(nil) fmain += "tangent = translate(position, itranslate(position, tangent));\n";
if(sn::in()) fmain +=
"mediump vec4 acc = christoffel(position, tangent, tangent);\n"
"mediump vec4 pos2 = position + tangent * dist / 2.;\n"
"mediump vec4 tan2 = tangent + acc * dist / 2.;\n"
"mediump vec4 acc2 = christoffel(pos2, tan2, tan2);\n"
"mediump vec4 nposition = position + tangent * dist + acc2 / 2. * dist * dist;\n";
if(use_christoffel) fmain +=
"mediump vec4 vel = tangent * dist;\n"
"mediump vec4 acc1 = get_acc(position, vel);\n"
"mediump vec4 acc2 = get_acc(position + vel / 2., vel + acc1/2.);\n"
"mediump vec4 acc3 = get_acc(position + vel / 2. + acc1/4., vel + acc2/2.);\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) {
fmain +=
"mediump vec4 xp, xt;\n"
@ -550,11 +665,49 @@ void enable_raycaster() {
fsh += "uniform mediump mat4 uStraighten;\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 +=
"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";
else if(nih) fmain +=
"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.y > 0. ? 7 : 6;\n";
}
else fmain +=
else if(nil) fmain +=
"if(nposition.x > .5) which = 3;\n"
"if(nposition.x <-.5) which = 0;\n"
"if(nposition.y > .5) which = 4;\n"
@ -622,14 +775,24 @@ void enable_raycaster() {
"next = maxstep;\n"
"}\n";
if(nil) fmain +=
"tangent = translatev(position, xt);\n";
fmain +=
"position = nposition;\n";
if(!nil) fmain +=
"tangent = tangent + acc * dist;\n";
if(use_christoffel) fmain +=
"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 +=
"position = position + tangent * dist;\n";
@ -673,10 +836,10 @@ void enable_raycaster() {
" if(col[3] > 0.0) {\n";
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 +=
" 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"
" if(tmap.z == 0.) col.xyz *= min(1., (1.-inface.x)/ tmap.x);\n"
" else {\n"
@ -693,7 +856,7 @@ void enable_raycaster() {
if(use_reflect) fmain +=
" if(col.w == 1.) {\n"
" col.w = float("+fts(1-reflect_val)+");\n"
" col.w = " + to_glsl(1-reflect_val)+";\n"
" reflect = true;\n"
" }\n";
@ -725,7 +888,7 @@ void enable_raycaster() {
#ifndef GLES_ONLY
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";
#endif
@ -740,7 +903,7 @@ void enable_raycaster() {
" }\n";
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(reflect && (which < "+its(flat1)+" || which >= "+its(flat2)+")) {\n"
" mediump float x = -log(position.w - position.x);\n"
@ -795,15 +958,23 @@ void enable_raycaster() {
" cid = connection.xy;\n";
if(prod) fmain +=
" if(which == "+its(S7)+") { zpos += uPLevel+uPLevel; }\n"
" if(which == "+its(S7)+"+1) { zpos -= uPLevel+uPLevel; }\n";
" if(which == sides-2) { zpos += uPLevel+uPLevel; }\n"
" if(which == sides-1) { zpos -= uPLevel+uPLevel; }\n";
fmain +=
" 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"
" 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 +=
" }\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));
if(!callhandlers(false, hooks_rayset, o)) {
deg = S7;
if(prod) deg += 2;
length = 4096;
per_row = length / deg;
@ -944,14 +1113,29 @@ EX void cast() {
GLERR("uniform mediump startid");
glUniform1f(o->uIPD, vid.ipd);
GLERR("uniform mediump IPD");
if(o->uWallOffset != -1) {
glUniform1i(o->uWallOffset, wall_offset(centerover));
glUniform1i(o->uSides, centerover->type);
}
vector<transmatrix> ms;
for(int j=0; j<S7; j++) ms.push_back(currentmap->iadj(cwt.at, j));
if(prod) ms.push_back(mscale(Id, +cgi.plevel));
if(prod) ms.push_back(mscale(Id, -cgi.plevel));
auto sa = hybrid::gen_sample_list();
vector<transmatrix> ms(sa.back().first, Id);
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) {
for(int j=0; j<S7; j++) {
if(BITRUNCATED) exit(1);
for(int j=0; j<centerover->type; j++) {
transmatrix T = inverse(ms[j]);
hyperpoint h = tC0(T);
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++) {
if(k < isize(ms) && !eqmatrix(ms[k], T)) continue;
if(k == isize(ms)) ms.push_back(T);
connections[u][2] = (k+.5) / 1024.;
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;
for(auto i: cgi.wallstart) wallstart.push_back(i);
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::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::bound_low(1e-9);
dialog::reaction = reset_raycaster;
@ -1196,6 +1388,12 @@ int readArgs() {
PHASEFROM(2);
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")) {
PHASEFROM(2); shift(); color_out_of_range = arghex();
}
@ -1203,6 +1401,12 @@ int readArgs() {
PHASEFROM(2);
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")) {
PHASEFROM(2); shift();
rays_generate = true;

View File

@ -28,10 +28,16 @@ EX namespace reg3 {
#endif
EX bool in() {
if(fake::in()) return FPIU(in());
return GDIM == 3 && !euclid && !bt::in() && !nonisotropic && !hybri && !kite::in();
}
EX void generate() {
if(fake::in()) {
fake::generate();
return;
}
int& loop = cgi.loop;
int& face = cgi.face;
@ -122,8 +128,8 @@ EX namespace reg3 {
vertex_distance = binsearch(0, M_PI, [&] (ld d) {
// sometimes breaks in elliptic
dynamicval<eGeometry> g(geometry, elliptic ? gCell120 : geometry);
hyperpoint v2 = direct_exp(dir_v2 * d, iTable);
hyperpoint v3 = direct_exp(dir_v3 * d, iTable);
hyperpoint v2 = direct_exp(dir_v2 * d);
hyperpoint v3 = direct_exp(dir_v3 * d);
return hdist(v2, v3) >= edge_length;
});
}
@ -131,7 +137,7 @@ EX namespace reg3 {
DEBB(DF_GEOM, ("vertex_distance = ", vertex_distance));
/* actual vertex */
hyperpoint v2 = direct_exp(dir_v2 * vertex_distance, iTable);
hyperpoint v2 = direct_exp(dir_v2 * vertex_distance);
hyperpoint mid = Hypc;
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;
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();
for(hyperpoint h: cellshape) {
@ -282,6 +295,7 @@ EX namespace reg3 {
return Id;
}
#if CAP_CRYSTAL
int encode_coord(const crystal::coord& co) {
int c = 0;
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 {
@ -691,10 +706,15 @@ EX namespace reg3 {
dynamicval<hrmap*> cm(currentmap, binary_map);
binary_map->virtualRebase(alt, T);
}
fixmatrix(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) 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]));
if(DEB) println(hlog, "-> found ", p2.first);
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(quotient_map) {
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;
}
#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);
created->c7 = newCell(S7, created);
if(sphere) spherecells.push_back(created->c7);
@ -1304,6 +1338,7 @@ EX int celldistance(cell *c1, cell *c2) {
EX bool pseudohept(cell *c) {
auto m = regmap();
if(cgflags & qSINGLE) return true;
if(fake::in()) return FPIU(reg3::pseudohept(c));
if(sphere) {
hyperpoint h = tC0(m->relative_matrix(c->master, regmap()->origin, C0));
if(S7 == 12) {
@ -1472,6 +1507,7 @@ int dist_alt(cell *c) {
#if MAXMDIM >= 4
EX cellwalker strafe(cellwalker cw, int j) {
hyperpoint hfront = tC0(cgi.adjmoves[cw.spin]);
cw.at->cmove(j);
transmatrix T = currentmap->adj(cw.at, 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)

View File

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

View File

@ -1,4 +1,3 @@
#include "../hyper.h"
#include "rogueviz.h"
namespace rogueviz {
@ -145,7 +144,7 @@ void collatz_video(const string &fname) {
if(vizid == &collatz_id) {
sightrange_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;
collatz::lookup(763, 60);

View File

@ -103,7 +103,7 @@ namespace flocking {
for(int i=0; i<isize(cl.lst); i++) {
cell *c2 = cl.lst[i];
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;
forCellEx(c3, c2) cl.add(c3);
}
@ -198,7 +198,7 @@ namespace flocking {
// at2 is like m2->at but relative to m->at
// 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;
// distance and azimuth to m2

View File

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

View File

@ -1,6 +1,8 @@
/* explore the Janko group J1: https://en.wikipedia.org/wiki/Janko_group_J1 */
#include "../hyper.h"
#include "rogueviz.h"
#if !ISWEB
namespace hr {
@ -27,7 +29,7 @@ struct jmatrix : array<array<int, 7>, 7> {
};
vector<jmatrix> jms;
unordered_map<jmatrix, int> ids;
std::unordered_map<jmatrix, int> ids;
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
void curvepoint(const hyperpoint& H1);
dqi_poly& queuecurve(color_t linecol, color_t fillcol, PPR prio);
namespace magic {
bool on = false;
int back = 0x202020;
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);
if(twos == 0)
c->landparam = back;
else if(twos == 1)
else if(twos == 1) {
c->landparam = magiccolors[index];
if(WDIM == 3) c->wall = waWaxWall;
}
println(hlog, co, " twos = ", twos, " index = ", index, " set = ", format("%06X", c->landparam));
@ -52,7 +49,7 @@ void magic(int sides) {
start_game();
build(crystal::c0, 0);
on = true;
vizid = (void*) &magic;
}
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) {
if(!on) return false;
if(vizid != &magic) return false;
timerghost = false;
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)
for(int i=0; i<S7; 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) {
if(!on) return false;
if(vizid != &magic) return false;
if(c->landparam != back) return false;
vector<int> zeros;
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++) {
if(co[i] == 0) zeros.push_back(i);
else if(co[i] == 2 || co[i] == -2) ;
else if(co[i] == 4 || co[i] == -4) ;
else return false;
}
println(hlog, "zeros = ", zeros);
@ -149,20 +151,21 @@ bool magic_rotate(cell *c) {
}
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;
if(rug::rugged) rug::close();
else rug::init();
return true;
}
if((cmode & sm::NORMAL) && uni == 'r' && on) {
if((cmode & sm::NORMAL) && uni == 'r') {
mine::performMarkCommand(mouseover);
return true;
}
if((cmode & sm::NORMAL) && uni == 'R' && on) {
if((cmode & sm::NORMAL) && uni == 'R') {
build(crystal::c0, 0);
}
if((cmode & sm::NORMAL) && uni == 'k' && on) {
}
if((cmode & sm::NORMAL) && uni == 'k') {
crystal::view_coordinates = !crystal::view_coordinates;
return true;
}

View File

@ -2,16 +2,14 @@
// example commandline: -noplayer -rugtsize 4096 -smart 1 -canvas B -ncee
// set CAP_NCONF (and change the path) if you have access to newconformist
#include "../hyper.h"
#ifndef CAP_NCONF
#define CAP_NCONF 0
#endif
#include "rogueviz.h"
#if CAP_NCONF
#ifndef CAP_DRAW
#define CAP_DRAW 0
#endif
#define main nconf_main
#undef unordered_map
#undef self
@ -638,11 +636,11 @@ void draw_ncee() {
nctinf2.tvertices.clear();
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) {
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 xpos = scax * 2 * iter;
curvepoint(hpxy(xpos-scax, map_ypos-sca2));
@ -714,7 +712,7 @@ void draw_ncee() {
z = !z;
for(int s=0; s<3; s++) {
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<bool> r(rug::display_warning, false);
// vid.consider_shader_projection = false;
vid.scale = 0.5;
pconf.scale = 0.5;
rug::init();
rug::prepareTexture();
rug::rugged = false;

View File

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

View File

@ -21,6 +21,8 @@ namespace hybrid { extern hrmap *pmap; }
namespace qtm {
int mode;
color_t rcolor() {
color_t res;
part(res, 0) = hrand(0x80);
@ -30,6 +32,30 @@ color_t rcolor() {
swap(part(res, 2), part(res, rand() % 3));
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) {
if(hybri) {
@ -40,12 +66,22 @@ void set_cell(cell *c) {
c->landparam = c1->landparam;
c->item = itNone;
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 {
if(c->land == laHive) return;
color_t col;
if(hyperbolic)
col = rcolor();
if(hyperbolic) {
hyperpoint h = calc_relative_matrix(c, currentmap->gamestart(), C0) * C0;
col = rainbow_color(h);
}
else if(nil) {
part(col, 0) = 128 + c->master->zebraval * 50;
part(col, 1) = 128 + c->master->emeraldval * 50;
@ -76,11 +112,58 @@ int args() {
using namespace arg;
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")) {
PHASEFROM(2);
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;
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;
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;
vertexdata& vd1 = vdata[ei->i];
vertexdata& vd2 = vdata[ei->j];
@ -913,10 +913,6 @@ void close() {
relmatrices.clear();
}
#ifndef CAP_RVSLIDES
#define CAP_RVSLIDES (CAP_TOUR && !ISWEB)
#endif
#if CAP_COMMANDLINE
int readArgs() {
using namespace arg;
@ -1232,7 +1228,7 @@ auto hooks =
#if CAP_COMMANDLINE
addHook(hooks_args, 100, readArgs) +
#endif
addHook(clearmemory, 0, close) +
addHook(hooks_clearmemory, 0, close) +
addHook(hooks_prestats, 100, rogueviz_hud) +
addHook(shmup::hooks_draw, 100, drawVertex) +
addHook(shmup::hooks_describe, 100, describe_monster) +
@ -1240,7 +1236,7 @@ auto hooks =
addHook(hooks_o_key, 100, o_key) +
#if CAP_RVSLIDES
addHook(tour::ss::extra_slideshows, 100, [] (bool view) {
addHook(tour::ss::hooks_extra_slideshows, 100, [] (bool view) {
if(!view) return 1;
dialog::addBoolItem(XLAT("RogueViz Tour"), tour::ss::wts == &rvtour::rvslides[0], 'r');
dialog::add_action([] { tour::ss::wts = rvtour::gen_rvtour(); popScreen(); });

View File

@ -6,6 +6,14 @@
#define RVPATH HYPERPATH "rogueviz/"
#ifndef CAP_NCONF
#define CAP_NCONF 0
#endif
#ifndef CAP_RVSLIDES
#define CAP_RVSLIDES (CAP_TOUR && !ISWEB)
#endif
namespace rogueviz {
using namespace hr;
@ -107,11 +115,11 @@ namespace rogueviz {
extern colorpair dftcolor;
inline hookset<void(vertexdata&, cell*, shmup::monster*, int)> *hooks_drawvertex;
inline hookset<bool(edgeinfo*, bool store)> *hooks_alt_edges;
inline hookset<void(vertexdata&, cell*, shmup::monster*, int)> hooks_drawvertex;
inline hookset<bool(edgeinfo*, bool store)> hooks_alt_edges;
inline purehookset hooks_rvmenu;
inline hookset<bool()> *hooks_rvmenu_replace;
inline hookset<bool(int&, string&, FILE*)> *hooks_readcolor;
inline hookset<bool()> hooks_rvmenu_replace;
inline hookset<bool(int&, string&, FILE*)> hooks_readcolor;
inline purehookset hooks_close;
void readcolor(const string& cfname);
@ -121,7 +129,7 @@ namespace rogueviz {
namespace rvtour {
using namespace hr::tour;
inline hookset<void(vector<slide>&)> *hooks_build_rvtour;
inline hookset<void(vector<slide>&)> hooks_build_rvtour;
slide *gen_rvtour();
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) {
setCanvas(mode, '0');
slidecommand = "auto-movement";
if(mode == pmKey) {
using namespace anims;
tour::slide_backup(ma, ma == maTranslation ? maNone : maTranslation);
@ -199,7 +200,7 @@ named_functionality o_key() {
auto hchook = addHook(hooks_drawcell, 100, draw_snow)
+ addHook(clearmemory, 40, [] () {
+ addHook(hooks_clearmemory, 40, [] () {
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) {
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."
"\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."
@ -291,16 +292,17 @@ auto hchook = addHook(hooks_drawcell, 100, draw_snow)
tour::slide_backup(snow_shape, 2);
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_variation(eVariation::pure);
set_geometry(gRotSpace);
snow_lambda = 5;
});
#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);
snow_lambda = 20;
// tour::slide_backup(snow_shape, 2);
snow_lambda = 3;
});
#endif
});

View File

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

View File

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

View File

@ -113,7 +113,7 @@ int readArgs() {
if(0) ;
else if(argis("-tol")) {
else if(argis("-tree")) {
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)
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 {
hyperpoint at;
bool computed;
@ -74,15 +82,15 @@ struct trianglemaker {
hyperpoint start = point31(0, 0, 0);
double lastz;
double lasta;
double ca;
// 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;
for(int d=0; d<3; d++) {
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);
if(at[2] > 0) {
ld z = at[2];
ca = lerp(lasta, a, ilerp(lastz, z, 0));
break;
amax = a;
}
else {
amin = a;
}
lastz = at[2]; lasta =a;
}
// compute the shift between the cubes
@ -111,9 +119,9 @@ struct trianglemaker {
// 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
@ -414,7 +422,9 @@ void find_coefficients() {
}
void growthrate() {
/*
cnts.resize(20);
for(int a=0; a<CTO; a++) {
int cnt = 0;
map<cell*, int> howmany;
@ -427,22 +437,28 @@ void growthrate() {
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]);
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};
find_coefficients();
auto cnt2 = cnts;
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);
if(coefficients_known == 2) {
string fmt = "a(d+" + its(isize(coef)) + ") = ";
bool first = true;
for(int i=0; i<isize(coef); i++) if(coef[i]) {
if(first && coef[i] == 1) ;
else if(first) fmt += its(coef[i]);
else if(coef[i] == 1) fmt += " + ";
else if(coef[i] == -1) fmt += " - ";
else if(coef[i] > 1) fmt += " + " + its(coef[i]);
else if(coef[i] < -1) fmt += " - " + its(-coef[i]);
for(int i=0; i<isize(coef); i++) if(kz(coef[i])) {
if(first && !kz(coef[i]-1)) ;
else if(first) fmt += fts(coef[i]);
else if(!kz(coef[i]-1)) fmt += " + ";
else if(!kz(coef[i]+1)) fmt += " - ";
else if(coef[i] > 0) fmt += " + " + fts(coef[i]);
else if(coef[i] < 0) fmt += " - " + fts(-coef[i]);
fmt += "a(d";
if(i != isize(coef) - 1)
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 = new trianglemaker; mkr->init();
growthrate();
// growthrate();
}
for(auto& td: mkr->tds[c]) {
@ -523,7 +539,7 @@ auto hchook = addHook(hooks_drawcell, 100, draw_ptriangle)
else if(argis("-tri-net")) {
on = true; net = true;
}
else if(argis("-tri-net")) {
else if(argis("-tri-one")) {
on = true; net = false;
}
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() {
if(!CAP_MEMORY_RESERVE) return false;
#if CAP_MEMORY_RESERVE
apply_memory_reserve();
if(reserve_limit && reserve_count < reserve_limit && !ignored_memory_warning) {
pushScreen(show_memory_menu);
@ -295,6 +295,7 @@ EX bool protect_memory() {
pushScreen(show_memory_menu);
return true;
}
#endif
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) {
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), "/>");
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")) {
PHASE(3); shift(); start_game();
printf("saving SVG screenshot to %s\n", argcs());
shot::make_svg = true;
shot::format = shot::screenshot_format::svg;
shot::take(argcs());
}
else if(argis("-svgtwm")) {
@ -233,6 +233,375 @@ auto ah = addHook(hooks_args, 0, read_args);
#endif
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
void IMAGESAVE(SDL_Surface *s, const char *fname) {
@ -247,9 +616,13 @@ EX namespace shot {
purehookset hooks_hqshot;
#if HDR
enum screenshot_format { png, svg, wrl };
#endif
EX int shotx = 2000;
EX int shoty = 2000;
EX bool make_svg = false;
EX screenshot_format format;
EX bool transparent = true;
EX ld gamma = 1;
EX int shotformat = -1;
@ -281,20 +654,26 @@ EX void default_screenshot_content() {
#endif
drawfullmap();
rots::draw_underlying(false);
if(caption != "")
displayfr(vid.xres/2, vid.fsize+vid.fsize/4, 3, vid.fsize*2, caption, forecolor, 8);
callhooks(hooks_hqshot);
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
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) {
IMAGESAVE(sdark, fname.c_str());
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 x=0; x<shotx; x++) {
int val[2][4];
@ -327,12 +706,46 @@ void postprocess(string fname, SDL_Surface *sdark, SDL_Surface *sbright) {
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)) {
if(cheater) doOvergenerate();
#if CAP_SVG
int multiplier = make_svg ? svg::divby : shot_aa;
int multiplier = (format == screenshot_format::svg) ? svg::divby : shot_aa;
#else
int multiplier = shot_aa;
#endif
@ -352,45 +765,25 @@ EX void take(string fname, const function<void()>& what IS(default_screenshot_co
models::configure();
callhooks(hooks_take);
if(make_svg) {
#if CAP_SVG
svg::render(fname, what);
#endif
}
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();
switch(format) {
case screenshot_format::wrl:
#if CAP_WRL
wrl::take(fname);
#endif
glbuf1.enable();
glbuf1.clear(backcolor);
current_display->set_viewport(0);
what();
postprocess(fname, sdark, glbuf1.render());
}
else postprocess(fname, sdark, sdark);
#endif
}
return;
case screenshot_format::svg:
#if CAP_SVG
svg::render(fname, what);
#endif
return;
case screenshot_format::png:
#if CAP_PNG
render_png(fname, what);
#endif
return;
}
}
#if CAP_COMMANDLINE
@ -399,7 +792,7 @@ int png_read_args() {
if(argis("-pngshot")) {
PHASE(3); shift(); start_game();
printf("saving PNG screenshot to %s\n", argcs());
make_svg = false;
format = screenshot_format::png;
shot::take(argcs());
}
else if(argis("-pngsize")) {
@ -417,6 +810,20 @@ int png_read_args() {
else if(argis("-shotaa")) {
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;
return 0;
}
@ -424,30 +831,118 @@ int png_read_args() {
auto ah_png = addHook(hooks_args, 0, png_read_args);
#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() {
cmode = sm::SIDE;
gamescreen(0);
if(!CAP_SVG) make_svg = false;
if(!CAP_PNG) make_svg = true;
if(format == screenshot_format::svg && !CAP_SVG)
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::addSelItem(XLAT("format"), make_svg ? "SVG" : "PNG", 'f');
dialog::add_action([] { make_svg = !make_svg; });
dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x');
dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); });
dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y');
dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); });
if(make_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
dialog::addSelItem(XLAT("format"), format_name(), 'f');
dialog::add_action_push(choose_screenshot_format);
bool dowrl = format == screenshot_format::wrl;
if(!dowrl) {
dialog::addSelItem(XLAT("pixels (X)"), its(shotx), 'x');
dialog::add_action([] { shotformat = -1; dialog::editNumber(shotx, 500, 8000, 100, 2000, XLAT("pixels (X)"), ""); });
dialog::addSelItem(XLAT("pixels (Y)"), its(shoty), 'y');
dialog::add_action([] { shotformat = -1; dialog::editNumber(shoty, 500, 8000, 100, 2000, XLAT("pixels (Y)"), ""); });
}
else {
dialog::addSelItem(XLAT("supersampling"), its(shot_aa), 's');
dialog::add_action([] { shot_aa *= 2; if(shot_aa > 16) shot_aa = 1; });
switch(format) {
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::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::add_action([] { dialog::editNumber(fade, 0, 2, .1, 1, XLAT("brightness"), "higher value = lighter"); });
dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, 'h');
dialog::addBoolItem_action_neg(XLAT("hide the player"), mapeditor::drawplayer, 'H');
if(!dowrl) dialog::addBoolItem_action(XLAT("disable the HUD"), hide_hud, '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) {
dialog::addItem(XLAT("centering"), 'x');
dialog::addItem(XLAT("centering"), 'C');
dialog::add_action([] {
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."));
dialog::reaction = fullcenter;
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::add_action([] { centering = eCentering::face; fullcenter(); });
dialog::addBoolItem(XLAT("edge"), centering == eCentering::edge, 'E');
@ -483,18 +990,6 @@ EX void menu() {
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::add_action([] () {
#if ISWEB
@ -502,8 +997,13 @@ EX void menu() {
#else
static string pngfile = "hqshot.png";
static string svgfile = "svgshot.svg";
string& file = make_svg ? svgfile : pngfile;
dialog::openFileDialog(file, XLAT("screenshot"), make_svg ? ".svg" : ".png", [&file] () {
static string wrlfile = "model.wrl";
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);
shot::take(file);
return true;
@ -727,32 +1227,30 @@ EX void apply() {
#if CAP_RUG
if(rug::rugged) {
if(rug_rotation1) {
rug::apply_rotation(cspin(1, 2, rug_angle * degree));
rug::apply_rotation(cspin(0, 2, rug_rotation1 * 2 * M_PI * t / period));
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;
}
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
vid.skiprope += skiprope_rotation * t * 2 * M_PI / period;
pconf.skiprope += skiprope_rotation * t * 2 * M_PI / period;
if(ballangle_rotation) {
if(models::model_has_orientation())
models::model_orientation += ballangle_rotation * 360 * t / period;
if(models::has_orientation(vpconf.model))
vpconf.model_orientation += ballangle_rotation * 360 * t / period;
else
vid.ballangle += ballangle_rotation * 360 * t / period;
vpconf.ballangle += ballangle_rotation * 360 * t / period;
}
if(joukowsky_anim) {
ld t = ticks / period;
t = t - floor(t);
if(pmodel == mdBand) {
models::model_transition = t * 4 - 1;
vpconf.model_transition = t * 4 - 1;
}
else {
models::model_transition = t / 1.1;
vid.scale = (1 - models::model_transition) / 2.;
vpconf.model_transition = t / 1.1;
vpconf.scale = (1 - vpconf.model_transition) / 2.;
}
}
apply_animated_parameters();
@ -770,15 +1268,23 @@ EX string animfile = "animation-%04d.png";
int min_frame = 0, max_frame = 999999;
int numturns = 0;
bool record_animation() {
lastticks = 0;
ticks = 0;
int oldturn = -1;
for(int i=0; i<noframes; i++) {
if(i < min_frame || i > max_frame) continue;
printf("%d/%d\n", i, noframes);
int newticks = i * period / noframes;
cmode = (env_shmup ? sm::NORMAL : 0);
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();
dynamicval<bool> v2(inHighQual, true);
apply();
@ -987,6 +1493,12 @@ EX void show() {
animator(XLATN("Ocean"), env_ocean, 'o');
animator(XLATN("Volcanic Wasteland"), env_volcano, 'v');
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(rug::rugged) {
@ -1010,7 +1522,7 @@ EX void show() {
});
}
#endif
if(models::model_has_orientation())
if(models::has_orientation(vpconf.model))
animator(XLAT("model rotation"), ballangle_rotation, 'I');
else if(among(pmodel, mdHyperboloid, mdHemisphere, mdBall))
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::addSelItem(XLAT("record to a file"), animfile, 'R');
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
dialog::addBack();
@ -1188,9 +1701,9 @@ startanim null_animation { "", no_init, [] { gamescreen(2); }};
startanim joukowsky { "Joukowsky transform", no_init, [] {
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<ld> ds(vid.scale, 1/4.);
dynamicval<ld> ds(pconf.scale, 1/4.);
models::configure();
dynamicval<color_t> dc(ringcolor, 0);
gamescreen(2);
@ -1199,7 +1712,7 @@ startanim joukowsky { "Joukowsky transform", no_init, [] {
startanim bandspin { "spinning in the band model", no_init, [] {
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);
models::configure();
gamescreen(2);
@ -1212,28 +1725,32 @@ startanim perspective { "projection distance", no_init, [] {
x /= 2;
x *= 1.5;
x = tan(x);
dynamicval<ld> da(vid.alpha, x);
dynamicval<ld> ds(vid.scale, (1+x)/2);
dynamicval<ld> da(pconf.alpha, x);
dynamicval<ld> ds(pconf.scale, (1+x)/2);
calcparam();
gamescreen(2);
explorable(projectionDialog);
}};
startanim rug { "Hypersian Rug", [] {
if(!CAP_RUG) { pick(); return; }
rug::init(), rug::rugged = false; }, [] {
#if CAP_RUG
rug::init();
rug::rugged = false;
#else
pick();
#endif
}, [] {
dynamicval<bool> b(rug::rugged, true);
rug::physics();
rug::apply_rotation(cspin(1, 2, ticks / 3000.));
dynamicval<transmatrix> t(rug::rugView, cspin(1, 2, ticks / 3000.) * rug::rugView);
gamescreen(2);
rug::apply_rotation(cspin(1, 2, -ticks / 3000.));
if(!rug::rugged) current = &null_animation;
explorable([] { rug::rugged = true; pushScreen(rug::show); });
}};
startanim spin_around { "spinning around", no_init, [] {
dynamicval<ld> da(vid.alpha, 999);
dynamicval<ld> ds(vid.scale, 500);
dynamicval<ld> da(pconf.alpha, 999);
dynamicval<ld> ds(pconf.scale, 500);
ld alpha = 2 * M_PI * ticks / 10000.;
ld circle_radius = acosh(2.);
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_LIGHTFOG = 4;
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_BAND = 512;
@ -31,6 +32,8 @@ constexpr flagtype SF_ZFOG = 65536;
constexpr flagtype SF_ODSBOX = (1<<17);
#endif
EX bool solv_all;
#if HDR
/* standard attribute bindings */
/* 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";
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";
varying += "varying mediump vec2 vTexCoord;\n";
fsh += "uniform mediump sampler2D tTexture;\n";
@ -180,6 +191,9 @@ shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
case gcSolNIH:
switch(sn::geom()) {
case gSol:
if(solv_all) {
vsh += "\n#define SOLV_ALL\n";
}
vsh += sn::shader_symsol;
break;
case gNIH:
@ -311,6 +325,7 @@ void display_data::set_projection(int ed) {
id <<= 6; id |= spherephase;
id <<= 1; if(vid.consider_shader_projection) id |= 1;
id <<= 2; id |= (spherespecial & 3);
if(sol && solv_all) id |= 1;
if(in_h2xe()) id |= 1;
if(in_s2xe()) id |= 2;
shared_ptr<glhr::GLprogram> selected;
@ -359,7 +374,7 @@ void display_data::set_projection(int ed) {
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)
eyewidth_translate(ed);
@ -367,10 +382,8 @@ void display_data::set_projection(int ed) {
auto ortho = [&] (ld x, ld y) {
glhr::glmatrix M = glhr::ortho(x, y, 1);
if(shader_flags & SF_ZFOG) {
using models::clip_max;
using models::clip_min;
M[2][2] = 2 / (clip_max - clip_min);
M[3][2] = (clip_min + clip_max) / (clip_max - clip_min);
M[2][2] = 2 / (pconf.clip_max - pconf.clip_min);
M[3][2] = (pconf.clip_min + pconf.clip_max) / (pconf.clip_max - pconf.clip_min);
auto cols = glhr::acolor(darkena(backcolor, 0, 0xFF));
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));
}
else {
if(vid.alpha > -1) {
if(pconf.alpha > -1) {
// Because of the transformation from H3 to the Minkowski hyperboloid,
// points with negative Z can be generated in some 3D settings.
// 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.);
glhr::projection_multiply(glhr::frustum(cd->xsize / cd->ysize, 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(selected->uPP != -1) {
glhr::glmatrix pp = glhr::id;
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(GDIM == 3) for(int a=0; a<4; a++)
@ -440,7 +453,7 @@ void display_data::set_projection(int ed) {
}
if(selected->uAlpha != -1)
glhr::set_ualpha(vid.alpha);
glhr::set_ualpha(pconf.alpha);
if(selected->uLevelLines != -1) {
glUniform1f(selected->uLevelLines, levellines);
@ -458,12 +471,12 @@ void display_data::set_projection(int ed) {
if(selected->shader_flags & SF_HALFPLANE) {
glhr::projection_multiply(glhr::translate(0, 1, 0));
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));
}
if(vid.camera_angle && pmodel != mdPixel) {
ld cam = vid.camera_angle * degree;
if(pconf.camera_angle && pmodel != mdPixel) {
ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);

View File

@ -236,7 +236,7 @@ bool isBullet(monster *m) {
bool isPlayer(monster *m) { return m->type == moPlayer; }
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) {
int tk = tkills();
@ -815,7 +815,7 @@ void movePlayer(monster *m, int delta) {
hyperpoint jh = hpxy(mdx/100.0, mdy/100.0);
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;
@ -1104,7 +1104,7 @@ void movePlayer(monster *m, int delta) {
int i0 = i;
for(int a=0; a<3; a++) v[a] = (i0 % 3) - 1, i0 /= 3;
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;
while(true) {
cell *c4 = findbaseAround(tC0(T1), c3, 1);
@ -1619,10 +1619,10 @@ void moveBullet(monster *m, int delta) {
m->dead = true;
if(inertia_based) {
nat = parallel_transport(nat, m->ori, m->inertia * delta, 10);
nat = parallel_transport(nat, m->ori, m->inertia * delta);
}
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);
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(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) {
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 {
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)
@ -2456,7 +2456,7 @@ EX void fixStorage() {
for(monster *m: restore) m->store();
}
EX hookset<bool(int)> *hooks_turn;
EX hookset<bool(int)> hooks_turn;
EX void turn(int delta) {
@ -2771,7 +2771,7 @@ EX bool boatAt(cell *c) {
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() {
for(mit it = monstersAt.begin(); it != monstersAt.end(); it++)
@ -2833,7 +2833,7 @@ EX void virtualRebase(shmup::monster *m) {
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) {
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_removecells, 0, [] () {
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;
hookset<bool(eLand&)> *hooks_music;
hookset<bool(eLand&)> hooks_music;
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() {
if(audio && musicvolume) {
@ -218,7 +218,7 @@ string wheresounds = SOUNDDESTDIR;
string wheresounds = HYPERPATH "sounds/";
#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) {
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