Merge branch 'master' into hooks-naming

This commit is contained in:
Zeno Rogue 2020-04-19 13:39:07 +02:00 committed by GitHub
commit 6f6586b3f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 3910 additions and 2984 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

@ -78,7 +78,7 @@ 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";
@ -102,7 +102,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(")");
};
@ -171,7 +171,12 @@ EX void load(const string& fname) {
else if(ep.eat(")")) break;
else throw hr_parse_exception("expecting , or )");
}
cc.build_from_angles_edges();
try {
cc.build_from_angles_edges();
}
catch(hr_parse_exception& ex) {
throw hr_parse_exception(ex.s + ep.where());
}
cc.connections.resize(cc.size());
for(int i=0; i<isize(cc.connections); i++)
cc.connections[i] = make_tuple(cc.id, i, false);
@ -191,8 +196,15 @@ EX void load(const string& fname) {
else if(ep.eat(")")) break;
else throw hr_parse_exception("expecting , or )");
}
cc.build_from_angles_edges();
try {
cc.build_from_angles_edges();
}
catch(hr_parse_exception& ex) {
throw hr_parse_exception(ex.s + ep.where());
}
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("conway(\"")) {
string s = "";
@ -201,7 +213,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 +233,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,7 +264,7 @@ EX void load(const string& fname) {
}
}
}
else throw hr_parse_exception("expecting command");
else throw hr_parse_exception("expecting command, " + ep.where());
}
}
@ -520,6 +532,8 @@ EX void choose() {
catch(hr_parse_exception& ex) {
println(hlog, "failed: ", ex.s);
set_geometry(gNormal);
start_game();
addMessage("failed: " + ex.s);
}
start_game();
return true;

View File

@ -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

@ -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;
@ -1580,6 +1590,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;

View File

@ -3904,3 +3904,14 @@ 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

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,9 +3429,15 @@ 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 }

View File

@ -559,10 +559,14 @@ 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
@ -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"), "");
@ -1431,7 +1446,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 +1464,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 +1579,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 -- "
@ -1648,9 +1663,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 +1691,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,7 +1745,7 @@ 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);
}
@ -1902,7 +1917,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 +1974,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);
@ -2465,7 +2476,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 +2484,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 +2545,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 +2593,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 +2618,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 +2645,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 +2657,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

@ -76,7 +76,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 +87,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);
}
@ -308,9 +308,7 @@ 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) {
@ -319,60 +317,62 @@ EX void handlePanning(int sym, int uni) {
if(sym == SDLK_HOME && GDIM == 3) {
shift_view(ztangent(+0.2*shiftmul)), didsomething = true, playermoved = false;
}
if(sym == SDLK_RIGHT) {
auto roll = [&] (int dir, ld val) {
if(GDIM == 3 && anyshiftclick)
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)
rotate_view(cspin(dir, 2, val)), didsomething = true;
else
View = cpush(dir, val) * View, playermoved = false, didsomething = true;
};
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;
else roll(0, -0.2*shiftmul);
}
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;
else roll(0, 0.2*shiftmul);
}
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;
else roll(1, 0.2*shiftmul);
}
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;
else roll(1, -0.2*shiftmul);
}
#endif
if(sym == SDLK_PAGEUP) {
if(history::on)
if(history::on && !rug::rug_control())
models::rotation++;
else
rotate_view(spin(M_PI/cgi.S21/2*shiftmul)), didsomething = true;
}
if(sym == SDLK_PAGEDOWN) {
if(history::on)
if(history::on && !rug::rug_control())
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(isGravityLand(cwt.at->land) && !rug::rug_control()) 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;
}
}
@ -493,8 +493,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_HOME && GDIM == 2 && !rug::rugged) || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym)) {
if(rug::rug_control())
rug::reset_view();
else
fullcenter();
}
if(sym == 'v' && DEFAULTNOR(sym))
pushScreen(showMainMenu);
@ -696,7 +700,8 @@ EX void mainloopiter() {
#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;
@ -705,14 +710,23 @@ EX void mainloopiter() {
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;
auto roll = [&] (int dir, ld val) {
if(GDIM == 3 && anyshiftclick)
shift_view(ctangent(dir, -val)); /* -val because shift reverses */
#if CAP_CRYSTAL
else if(rug::rug_control() && rug::in_crystal())
crystal::apply_rotation(cspin(dir, 2, val));
#endif
else
rotate_view(GDIM == 2 ? cpush(dir, val) : cspin(dir, 2, val));
didsomething = true, playermoved = playermoved && GDIM == 3;
};
if(keystate[SDLK_RIGHT] && DEFAULTNOR(SDLK_RIGHT)) roll(0, -t);
if(keystate[SDLK_LEFT] && DEFAULTNOR(SDLK_LEFT)) roll(0, t);
if(keystate[SDLK_UP] && DEFAULTNOR(SDLK_UP)) roll(1, t);
if(keystate[SDLK_DOWN] && DEFAULTNOR(SDLK_DOWN)) roll(1, -t);
if(keystate[SDLK_PAGEUP] && DEFAULTNOR(SDLK_PAGEUP)) {
if(history::on)
models::rotation+=t;
@ -865,7 +879,7 @@ EX void handle_event(SDL_Event& ev) {
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick) {
sym = 1, didsomething = true;
if(anyshift)
vid.xposition = vid.yposition = 0;
pconf.xposition = pconf.yposition = 0;
}
else if(ev.button.button == SDL_BUTTON_LEFT) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
@ -874,12 +888,12 @@ EX void handle_event(SDL_Event& ev) {
else if(ev.button.button==SDL_BUTTON_WHEELDOWN) {
if(anyctrl && anyshift && !rug::rugged && GDIM == 2) {
mapeditor::scaleall(1/1.2);
vid.alpha /= 1.2;
pconf.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;
pconf.alpha -= 0.25;
else if(rollchange) {
sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true;
}
@ -890,12 +904,12 @@ EX void handle_event(SDL_Event& ev) {
if(ev.button.button==SDL_BUTTON_WHEELUP) {
if(anyctrl && anyshift && !rug::rugged && GDIM == 2) {
mapeditor::scaleall(1.2);
vid.alpha *= 1.2;
pconf.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;
pconf.alpha += 0.25;
else if(rollchange) {
sym = getcstat, uni = getcstat, shiftmul = -getcshift, wheelclick = true;
}
@ -929,8 +943,8 @@ EX void handle_event(SDL_Event& ev) {
if((rightclick || (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;
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);
@ -1047,7 +1061,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 +1071,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 +1197,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 +1209,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

@ -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

@ -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));
@ -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]));
} */
}
@ -1430,7 +1435,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 +1463,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 +1479,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 +1535,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 +1561,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 +1579,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 +1596,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 +1625,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;
}
@ -1853,7 +1858,7 @@ 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 +1880,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 +1932,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,6 +1972,7 @@ EX void reverse_transparent_walls() {
}
EX void draw_main() {
DEBBI(DF_GRAPH, ("draw_main"));
if(sphere && GDIM == 3 && pmodel == mdPerspective) {
for(int p: {1, 0, 2, 3}) {
if(elliptic && p < 2) continue;
@ -2032,7 +2041,14 @@ EX hookset<bool()> hooks_vr_draw_all;
#endif
EX void drawqueue() {
callhooks(hooks_drawqueue);
DEBBI(DF_GRAPH, ("drawqueue"));
#if CAP_WRL
if(wrl::in) { wrl::render(); return; }
#endif
callhooks(hook_drawqueue);
current_display->next_shader_flags = 0;
reset_projection();
// reset_projection() is not sufficient here, because we need to know shaderside_projection
@ -2045,6 +2061,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 +2371,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];
}

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

@ -381,21 +381,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 +405,7 @@ EX string dim_name() {
return " (" + its(WDIM) + "D)";
}
#if CAP_THREAD
#if CAP_THREAD && MAXMDIM >= 4
EX void showQuotientConfig3() {
using namespace fieldpattern;
@ -528,7 +528,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
}
@ -940,7 +940,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

@ -710,24 +710,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 +851,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;
}

View File

@ -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,7 +3668,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) < -.4) 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
@ -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);

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');

14
hud.cpp
View File

@ -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;

57
hyper.h
View File

@ -13,8 +13,8 @@
#define _HYPER_H_
// version numbers
#define VER "11.3i"
#define VERNUM_HEX 0xA829
#define VER "11.3j"
#define VERNUM_HEX 0xA82A
#include "sysconfig.h"
@ -228,9 +228,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 +288,6 @@ struct videopar {
int xscr, yscr;
ld xposition, yposition;
bool grid;
bool particles;
@ -304,8 +339,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 +352,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 +363,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 +372,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
@ -660,7 +691,9 @@ 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);

View File

@ -702,6 +702,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) {
@ -1030,13 +1032,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 +1153,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 +1165,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 +1194,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

@ -12,7 +12,7 @@ ld ghx, ghy, ghgx, ghgy;
hyperpoint 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) {
@ -22,7 +22,7 @@ void ghcheck(hyperpoint &ret, const hyperpoint &H) {
}
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 +37,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 +59,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 +72,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 +88,21 @@ 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;
if(pmodel) {
ghx = hx, ghy = hy;
return ghpm;
}
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 +165,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 +237,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 +264,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 +315,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 +337,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 +360,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 +386,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 +400,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 +423,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 +435,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 +486,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 +524,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 +532,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 +547,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 +565,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 +575,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 +604,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 +618,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 +629,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 +637,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 +682,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 +746,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 +769,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 +814,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 +823,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 +833,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 +853,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 +871,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 +930,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 +950,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 +1003,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 +1040,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 +1058,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 +1192,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;
@ -1505,8 +1506,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 +1518,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 +1563,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 +1574,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 +1593,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 +1603,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 +1622,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 +1638,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 +1651,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 +1665,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 +1749,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 +1769,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 +1795,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 +1832,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 +1893,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 +1906,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 +1920,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 +1933,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 +2017,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 +2026,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 +2078,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 +2086,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;
@ -2096,7 +2118,7 @@ EX void rotate_view(transmatrix T) {
/** 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;
return rgpushxto0(direct_exp(lp_iapply(H))) * V;
}
else if(!nisot::geodesic_movement) {
transmatrix IV = inverse(V);
@ -2132,7 +2154,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 +2163,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);
@ -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

@ -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:

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,241 @@ 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 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 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);
}
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 };
@ -43,16 +272,16 @@ EX namespace mapeditor {
// 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;
ld mrx = (.0 + mousex - current_display->xcenter) / vpconf.scale;
ld mry = (.0 + mousey - current_display->ycenter) / vpconf.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;
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 +330,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 +587,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 +771,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 +996,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 +1249,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 +1375,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 +1459,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 +1474,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 +1503,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 +1518,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 +1635,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 +1646,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 +1694,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 +1741,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 +1777,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 +1803,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 +2212,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 +2303,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 +2361,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 +2386,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;
@ -2137,6 +2472,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 +2498,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 +2508,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 +2547,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);
}
}
}

View File

@ -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');
@ -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;
}

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

@ -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 +
@ -745,7 +743,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 +767,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 +967,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);
}
@ -1822,7 +1822,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]);
@ -1933,8 +1933,8 @@ EX namespace rots {
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);
calcparam();
@ -1970,25 +1970,29 @@ 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;
}
@ -2009,13 +2013,34 @@ EX namespace nisot {
}
else h = Pos * h;
int steps = 100;
int steps = rk_steps;
h /= steps;
auto& at = tPos[3];
auto& vel = h;
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 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(sl2) {
@ -2043,12 +2068,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 +2156,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();

View File

@ -81,7 +81,7 @@ bool ishex2(cell *c) {
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);
@ -2938,6 +2938,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();

View File

@ -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);
@ -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];
}

View File

@ -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');

View File

@ -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;
@ -479,6 +479,10 @@ void enable_raycaster() {
" 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";
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";
@ -502,12 +506,13 @@ 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";
"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(nil) {
fmain +=
"mediump vec4 xp, xt;\n"
@ -629,7 +634,7 @@ void enable_raycaster() {
"position = nposition;\n";
if(!nil) fmain +=
"tangent = tangent + acc * dist;\n";
"tangent = tangent + (acc1+2.*acc2+2.*acc3+acc4)/(6.*dist);\n";
}
else fmain +=
"position = position + tangent * dist;\n";
@ -1138,7 +1143,7 @@ EX void configure() {
if(nonisotropic) {
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;

View File

@ -122,8 +122,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 +131,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;

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

@ -145,7 +145,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

@ -2,6 +2,8 @@
#include "../hyper.h"
#if !ISWEB
namespace hr {
eGeometry gJanko1(eGeometry(-1));
@ -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

@ -638,11 +638,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 +714,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 +800,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;

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

@ -111,9 +111,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

1006
rug.cpp

File diff suppressed because it is too large Load Diff

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,374 @@ 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) return 0;
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
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 +615,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;
@ -287,14 +659,18 @@ EX void default_screenshot_content() {
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 +703,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 +762,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 +789,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 +807,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 +828,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,10 +947,13 @@ 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::add_action([] {
@ -483,18 +978,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 +985,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 +1215,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();
@ -779,6 +1265,7 @@ bool record_animation() {
int newticks = i * period / noframes;
cmode = (env_shmup ? sm::NORMAL : 0);
while(ticks < newticks) shmup::turn(1), ticks++;
ca::simulate();
if(playermoved) centerpc(INF), optimizeview();
dynamicval<bool> v2(inHighQual, true);
apply();
@ -1010,7 +1497,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 +1529,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 +1676,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 +1687,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,8 +1700,8 @@ 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);
@ -1224,16 +1712,15 @@ startanim rug { "Hypersian Rug", [] {
rug::init(), rug::rugged = false; }, [] {
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;
@ -62,7 +63,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";
@ -359,7 +368,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 +376,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 +416,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 +427,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 +447,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 +465,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

@ -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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -24,7 +24,7 @@ string shape_name[] = { "hypersian rug", "tractricoid", "Dini's surface", "Kuen
EX eShape sh;
hyperpoint unit_vector[3] = {hpxyz(1,0,0), hpxyz(0,1,0), hpxyz(0,0,1)};
hyperpoint unit_vector[3] = {point3(1,0,0), point3(0,1,0), point3(0,0,1)};
ld last_int_of = 0, last_int = 0;
@ -67,7 +67,7 @@ hyperpoint coord(hyperpoint h) {
ld r = 1 / cosh(t);
ld x = t - tanh(t);
return hpxyz( r * sin(v), r * cos(v), x );
return point31( r * sin(v), r * cos(v), x );
break;
}
@ -77,7 +77,7 @@ hyperpoint coord(hyperpoint h) {
ld a = sqrt(1-dini_b*dini_b);
return hpxyz( a * sin(v) * sin(t), a * cos(v) * sin(t), a * (cos(t) + log(tan(t/2))) + dini_b * v );
return point31( a * sin(v) * sin(t), a * cos(v) * sin(t), a * (cos(t) + log(tan(t/2))) + dini_b * v );
break;
}
@ -87,7 +87,7 @@ hyperpoint coord(hyperpoint h) {
ld deno = 1 / (1 + u * u * sin(v) * sin(v));
return hpxyz(
return point31(
2 * (cos(u) + u * sin(u)) * sin(v) * deno,
2 * (sin(u) - u * cos(u)) * sin(v) * deno,
log(tan(v/2)) + 2 * cos(v) * deno
@ -101,7 +101,7 @@ hyperpoint coord(hyperpoint h) {
ld phi = hyper_b * cosh(v);
ld psi = integral(v);
return hpxyz( phi * cos(u), phi * sin(u), psi );
return point31( phi * cos(u), phi * sin(u), psi );
}
default:
@ -122,10 +122,10 @@ hyperpoint coord_derivative(hyperpoint h, int cc) {
ld v = h[1];
if(cc == 0) {
ld phi = hyper_b * cosh(v);
return hpxyz( phi * -sin(u), phi * cos(u), 0 );
return point3( phi * -sin(u), phi * cos(u), 0 );
}
else {
return hpxyz( hyper_b * sinh(v) * cos(u), hyper_b * sinh(v) * sin(u), f(v) );
return point3( hyper_b * sinh(v) * cos(u), hyper_b * sinh(v) * sin(u), f(v) );
}
}
case dsKuen: {
@ -134,12 +134,12 @@ hyperpoint coord_derivative(hyperpoint h, int cc) {
ld denom = pow(sin(v),2)*(u*u)+1;
ld denom2 = denom * denom;
if(cc == 1)
return hpxyz (
return point3(
2*sin(v)/denom*u*cos(u)+-4*(sin(u)*u+cos(u))*pow(sin(v),3)/denom2*u,
-4*pow(sin(v),3)*(sin(u)-u*cos(u))/denom2*u+2*sin(u)*sin(v)/denom*u,
-4*pow(sin(v),2)/denom2*u*cos(v)
);
else return hpxyz (
else return point3(
2*(sin(u)*u+cos(u))/denom*cos(v)+-4*(sin(u)*u+cos(u))*pow(sin(v),2)/denom2*(u*u)*cos(v),
2*(sin(u)-u*cos(u))/denom*cos(v)+-4*pow(sin(v),2)*(sin(u)-u*cos(u))/denom2*(u*u)*cos(v),
-4*sin(v)/denom2*(u*u)*pow(cos(v),2)+1/tan(v/2)*(pow(tan(v/2),2)+1)/2+-2*sin(v)/denom
@ -166,13 +166,13 @@ ld compute_curvature(hyperpoint at) {
hyperpoint shape_origin() {
switch(sh) {
case dsDini:
return hpxyz(M_PI * .82, 0, 0);
return point31(M_PI * .82, 0, 0);
case dsTractricoid:
return hpxyz(1, 0, 0);
return point31(1, 0, 0);
case dsKuen:
return hpxyz(M_PI * .500001, M_PI * 1, 0);
return point31(M_PI * .500001, M_PI * 1, 0);
case dsHyperlike:
return hpxyz(0,0,0);
return point31(0,0,0);
default:
return Hypc;
}
@ -284,7 +284,7 @@ transmatrix create_M_matrix(hyperpoint zero, hyperpoint v1) {
transmatrix T = build_matrix(Te0, Te1, Hypc, C03);
v1 = v1 / hypot_d(3, T*v1);
hyperpoint v2 = hpxyz(1e-3, 1e-4, 0);
hyperpoint v2 = point3(1e-3, 1e-4, 0);
v2 = v2 - v1 * ((T*v1) | (T*v2)) / hypot_d(3, T*v1);
v2 = v2 / hypot_d(3, T*v2);
@ -307,9 +307,9 @@ dexp_origin at_zero(hyperpoint zero, transmatrix start) {
println(hlog, "zero = ", zero);
println(hlog, "curvature at zero = ", compute_curvature(zero));
println(hlog, "curvature at X1 = ", compute_curvature(zero + hpxyz(.3, 0, 0)));
println(hlog, "curvature at X2 = ", compute_curvature(zero + hpxyz(0, .3, 0)));
println(hlog, "curvature at X3 = ", compute_curvature(zero + hpxyz(.4, .3, 0)));
println(hlog, "curvature at X1 = ", compute_curvature(zero + point3(.3, 0, 0)));
println(hlog, "curvature at X2 = ", compute_curvature(zero + point3(0, .3, 0)));
println(hlog, "curvature at X3 = ", compute_curvature(zero + point3(.4, .3, 0)));
return {start, create_M_matrix(zero, unit_vector[0]), zero};
}
@ -338,14 +338,14 @@ void addTriangleV(rug::rugpoint *t1, rug::rugpoint *t2, rug::rugpoint *t3, ld le
}
hyperpoint kuen_cross(ld v, ld u) {
auto du = coord_derivative(hpxyz(v,u,0), 0);
auto dv = coord_derivative(hpxyz(v,u,0), 1);
auto du = coord_derivative(point3(v,u,0), 0);
auto dv = coord_derivative(point3(v,u,0), 1);
return du^dv;
}
ld kuen_hypot(ld v, ld u) {
auto du = coord_derivative(hpxyz(v,u,0), 0);
auto dv = coord_derivative(hpxyz(v,u,0), 1);
auto du = coord_derivative(point3(v,u,0), 0);
auto dv = coord_derivative(point3(v,u,0), 1);
auto n = hypot_d(3, du^dv);
return n;
}
@ -368,7 +368,7 @@ int dexp_comb_colors[16] = {
};
int coverage_style;
vector<pair<hyperpoint, int> > coverage;
EX vector<pair<hyperpoint, int> > coverage;
#ifndef CAP_KUEN_MAP
#define CAP_KUEN_MAP 0
@ -385,8 +385,8 @@ void draw_kuen_map() {
for(int h=0; h<512; h++) {
ld v = M_PI * (r+.5) / 512;
ld u = 2 * M_PI * (h+.5) / 512;
auto du = coord_derivative(hpxyz(v,u,0), 0);
auto dv = coord_derivative(hpxyz(v,u,0), 1);
auto du = coord_derivative(point3(v,u,0), 0);
auto dv = coord_derivative(point3(v,u,0), 1);
auto n = hypot_d(3, du^dv);
if(n > nmax) nmax = n;
@ -459,7 +459,8 @@ void run_hyperlike() {
int sgn = y > 0 ? 1 : -1;
ld phi = hyper_b * cosh(y);
int pt = y * precision * sgn / hyperlike_bound();
p->flat = hpxyz(phi * cos(x), phi * sin(x), sgn * integral_table[pt]);
USING_NATIVE_GEOMETRY;
p->native = point31(phi * cos(x), phi * sin(x), sgn * integral_table[pt]);
p->valid = true;
}
}
@ -520,7 +521,8 @@ void run_kuen() {
np->inqueue = false;
np->dist = 0;
np->h = p->h;
np->flat = coord(px.params);
USING_NATIVE_GEOMETRY;
np->native = coord(px.params);
np->surface_point = px;
np->dexp_id = p->dexp_id;
coverages[p->dexp_id] |= pid[part];
@ -534,9 +536,11 @@ void run_kuen() {
for(int i=0; i<3; i++)
if(!r[i]) looks_good = false;
if(!looks_good) continue;
for(int i=0; i<3; i++)
if(hypot_d(3, r[i]->flat - r[(i+1)%3]->flat) > .2)
for(int i=0; i<3; i++) {
USING_NATIVE_GEOMETRY;
if(hypot_d(3, r[i]->native - r[(i+1)%3]->native) > .2)
looks_good = false;
}
if(looks_good)
addTriangleV(r[0], r[1], r[2]);
}
@ -557,9 +561,11 @@ void run_kuen() {
template<class T> void run_function(T f) {
full_mesh();
for(auto p: rug::points)
p->flat = f(p->h),
for(auto p: rug::points) {
USING_NATIVE_GEOMETRY;
p->native = f(p->h),
p->valid = true;
}
}
void run_other() {
@ -572,7 +578,10 @@ void run_other() {
auto h = p->h;
p->surface_point = map_to_surface(h, dp);
p->flat = coord(p->surface_point.params);
if(1) {
USING_NATIVE_GEOMETRY;
p->native = coord(p->surface_point.params);
}
history::progress(XLAT("solving the geodesics on: %1, %2/%3", XLAT(shape_name[sh]), its(it), its(isize(rug::points))));
if(p->surface_point.remaining_distance == 0)
coverage.emplace_back(h, rchar(it) + 256 * 7);
@ -594,10 +603,9 @@ EX void run_shape(eShape s) {
coverage.clear();
need_mouseh = true;
sh = s;
transmatrix crot = rug::rugged ? rug::currentrot : Id;
rug::apply_rotation(inverse(crot));
if(rug::rugged) rug::close();
rug::gwhere = rug::rgEuclid;
rug::init();
// if(!rug::rugged) rug::reopen();
@ -641,8 +649,8 @@ EX void run_shape(eShape s) {
ld minx = 1e9, maxx = -1e9;
for(auto p: rug::points) if(p->valid) {
minx = min(p->flat[2], minx);
maxx = max(p->flat[2], maxx);
minx = min(p->native[2], minx);
maxx = max(p->native[2], maxx);
rug::qvalid++;
}
@ -650,12 +658,8 @@ EX void run_shape(eShape s) {
ld shift = -(minx + maxx) / 2;
for(auto p: rug::points) if(p->valid)
p->flat[2] += shift;
p->native[2] += shift;
}
rug::apply_rotation(crot);
if(rug::rug_perspective)
rug::push_all_points(2, -rug::model_distance);
}
void cancel_shape() {
@ -703,6 +707,13 @@ EX void show_surfaces() {
if((rug::rugged && sh && sh != dsHyperboloid && sh != dsHemisphere) || coverage_style)
dialog::addSelItem(XLAT("display coverage"), cstyles[coverage_style], 'c');
else dialog::addBreak(100);
#if CAP_FILES
if(rug::rugged)
dialog::addItem(XLAT("save the current embedding"), 's');
else
dialog::addItem(XLAT("load a saved embedding"), 's');
#endif
dialog::addHelp();
dialog::addBack();
@ -722,6 +733,15 @@ EX void show_surfaces() {
"For convenience, you can also choose other 3D models from this menu."
));
#if CAP_FILES
else if(uni == 's') {
static string rugname = "saved.rug";
if(rug::rugged)
dialog::openFileDialog(rugname, XLAT("save embedding to:"), ".rug", [] () { rug::rug_save(rugname); return true; });
else
dialog::openFileDialog(rugname, XLAT("load embedding from:"), ".rug", [] () { rug::init(); rug::rug_load(rugname); return true; });
}
#endif
else if(uni == '1')
run_shape(dsTractricoid);
else if(uni == '2')
@ -747,7 +767,7 @@ EX void show_surfaces() {
}
else if(uni == 'x')
for(auto p: rug::points)
p->flat = p->surface_point.params;
p->native = p->surface_point.params;
else if(uni == '#')
dialog::editNumber(dini_b, -1, 1, .05, .15, XLAT("parameter"),
XLAT("The larger the number, the more twisted it is.")

View File

@ -227,6 +227,10 @@
#define CAP_SVG (CAP_FILES && !ISMOBILE && !ISMINI)
#endif
#ifndef CAP_WRL
#define CAP_WRL (CAP_FILES && !ISMOBILE && !ISMINI && !ISWEB)
#endif
#ifndef CAP_POLY
#define CAP_POLY (CAP_SDLGFX || CAP_GL || CAP_SVG)
#endif

View File

@ -225,6 +225,9 @@ EX void initgame() {
items[itOrbWinter] = 30;
items[itOrbFlash] = 30;
}
if(firstland == laCA)
items[itOrbAether] = 2;
if(tactic::on && firstland == laCaribbean) {
if(hiitemsMax(itRedGem) >= 25) items[itRedGem] = min(hiitemsMax(itRedGem), 50);
@ -436,19 +439,19 @@ void applyBox(int& t) {
}
/** \brief the next box should contain tb */
void applyBoxBignum(bignum& tb) {
void applyBoxBignum(bignum& tb, string name) {
float tf;
int ti;
if(saving) tf = tb.approx_ld();
if(saving) memcpy(&ti, &tf, 4);
applyBox(ti);
applyBoxNum(ti, name);
if(loading) memcpy(&tf, &ti, 4);
if(loading) tb = bignum(tf);
}
/** \brief the next box should contain i, and possibly be named name */
EX void applyBoxNum(int& i, string name IS("")) {
fakebox[boxid] = (name == "");
fakebox[boxid] = (name == "" || name[0] == '@');
boxname[boxid] = name;
monsbox[boxid] = false;
applyBox(i);
@ -572,8 +575,8 @@ EX void applyBoxes() {
applyBoxNum(cheater, "number of cheats");
fakebox[boxid] = true;
if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.at->land, "");
else if(loading) firstland = safetyland = eLand(applyBoxLoad());
if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.at->land, "@safetyland");
else if(loading) firstland = safetyland = eLand(applyBoxLoad("@safetyland"));
else lostin = eLand(save.box[boxid++]);
for(int i=itOrbLightning; i<25; i++) applyBoxOrb(eItem(i));
@ -620,9 +623,9 @@ EX void applyBoxes() {
applyBoxOrb(itOrbSpace);
int geo = geometry;
applyBoxNum(geo, ""); geometry = eGeometry(geo);
applyBoxNum(geo, "@geometry"); geometry = eGeometry(geo);
applyBoxBool(hardcore, "hardcore");
applyBoxNum(hardcoreAt, "");
applyBoxNum(hardcoreAt, "@hardcoreAt");
applyBoxBool(shmup::on, "shmup");
if(saving) applyBoxSave(specialland, "euclid land");
else if(loading) specialland = eLand(applyBoxLoad("euclid land"));
@ -656,8 +659,8 @@ EX void applyBoxes() {
applyBoxM(moPrincessMoved, false); // live Princess for Safety
applyBoxM(moPrincessArmedMoved, false); // live Princess for Safety
applyBoxM(moMouse);
applyBoxNum(princess::saveArmedHP, "");
applyBoxNum(princess::saveHP, "");
applyBoxNum(princess::saveArmedHP, "@saveArmedHP");
applyBoxNum(princess::saveHP, "@saveHP");
applyBoxI(itIvory);
applyBoxI(itElemental);
@ -687,8 +690,8 @@ EX void applyBoxes() {
applyBoxOrb(itOrbLuck);
applyBoxOrb(itOrbStunning);
applyBoxBool(tactic::on, "");
applyBoxNum(elec::lightningfast, "");
applyBoxBool(tactic::on, "@tactic");
applyBoxNum(elec::lightningfast, "@lightningfast");
// if(save.box[boxid]) printf("lotus = %d (lost = %d)\n", save.box[boxid], isHaunted(lostin));
if(loadingHi && isHaunted(lostin)) boxid++;
@ -700,7 +703,7 @@ EX void applyBoxes() {
applyBoxOrb(itMutant2);
applyBoxOrb(itOrbFreedom);
applyBoxM(moRedFox);
applyBoxBool(survivalist);
applyBoxBool(survivalist, "@survivalist");
if(loadingHi) applyBoxI(itLotus);
else applyBoxNum(truelotus, "lotus/escape");
@ -730,17 +733,17 @@ EX void applyBoxes() {
applyBoxM(moDragonHead);
applyBoxOrb(itOrbDomination);
applyBoxI(itBabyTortoise);
applyBoxNum(tortoise::seekbits, "");
applyBoxNum(tortoise::seekbits, "@seekbits");
applyBoxM(moTortoise);
applyBoxOrb(itOrbShell);
applyBoxNum(safetyseed);
applyBoxNum(safetyseed, "@safetyseed");
// (+18)
for(int i=0; i<6; i++) {
applyBoxNum(multi::treasures[i]);
applyBoxNum(multi::kills[i]);
applyBoxNum(multi::deaths[i]);
applyBoxNum(multi::treasures[i], "@multi-treasures" + its(i));
applyBoxNum(multi::kills[i], "@multi-kills" + its(i));
applyBoxNum(multi::deaths[i], "@multi-deaths" + its(i));
}
// (+8)
applyBoxM(moDragonTail);
@ -757,7 +760,7 @@ EX void applyBoxes() {
bool sph;
sph = false; applyBoxBool(sph, "sphere"); if(sph) geometry = gSphere;
sph = false; applyBoxBool(sph, "elliptic"); if(sph) geometry = gElliptic;
applyBox(princess::reviveAt);
applyBoxNum(princess::reviveAt, "@reviveAt");
applyBoxI(itDodeca);
applyBoxI(itAmethyst);
@ -780,13 +783,13 @@ EX void applyBoxes() {
applyBoxM(moGadfly);
// 10.0:
applyBoxNum(hinttoshow); // 258
applyBoxNum(hinttoshow, "@hinttoshow"); // 258
addinv(itOrbMirror);
addinv(itGreenStone);
list_invorb();
applyBoxBool(inv::on, "inventory"); // 306
#if CAP_INV
applyBoxNum(inv::rseed);
applyBoxNum(inv::rseed, "@inv-rseed");
#else
{ int u; applyBoxNum(u); }
#endif
@ -828,16 +831,16 @@ EX void applyBoxes() {
applyBoxM(moMonk);
bool v2 = false;
applyBoxBool(v2); if(loading && v2) variation = eVariation::goldberg;
applyBox(gp::param.first);
applyBox(gp::param.second);
applyBoxBool(v2, "@variation"); if(loading && v2) variation = eVariation::goldberg;
applyBoxNum(gp::param.first, "@gp-first");
applyBoxNum(gp::param.second, "@gp-second");
v2 = false; applyBoxBool(v2); if(loading && v2) variation = eVariation::irregular;
applyBox(irr::cellcount);
applyBoxNum(irr::cellcount, "@irr-cellcount");
list_invorb();
applyBox(irr::bitruncations_performed);
applyBoxNum(irr::bitruncations_performed, "@irr-bitruncations");
applyBoxI(itVarTreasure);
applyBoxI(itBrownian);
@ -855,8 +858,8 @@ EX void applyBoxes() {
applyBoxM(moNarciss);
applyBoxM(moMirrorSpirit);
applyBox(clearing::direct);
applyBoxBignum(clearing::imputed);
applyBoxNum(clearing::direct, "@clearing-direct");
applyBoxBignum(clearing::imputed, "@clearing-imputed");
applyBoxOrb(itOrbImpact);
applyBoxOrb(itOrbChaos);

View File

@ -65,7 +65,6 @@ struct texture_data {
struct texture_config {
string texturename;
string configname;
color_t paint_color;
eTextureState tstate;
eTextureState tstate_max;
@ -121,7 +120,6 @@ struct texture_config {
texturename = "textures/hyperrogue-texture.png";
configname = "textures/hyperrogue.txc";
itt = Id;
paint_color = 0x000000FF;
grid_color = 0;
mesh_color = 0;
master_color = 0xFFFFFF30;
@ -355,7 +353,7 @@ hyperpoint texture_config::texture_coordinates(hyperpoint h) {
hyperpoint inmodel;
applymodel(h, inmodel);
inmodel[0] *= current_display->radius * 1. / current_display->scrsize;
inmodel[1] *= current_display->radius * vid.stretch / current_display->scrsize;
inmodel[1] *= current_display->radius * pconf.stretch / current_display->scrsize;
inmodel[2] = 1;
inmodel = itt * inmodel;
inmodel[0] = (inmodel[0] + 1) / 2;
@ -531,7 +529,7 @@ void texture_config::mark_triangles() {
}
}
static const auto current_texture_parameters = tie(geometry, variation, patterns::whichPattern, patterns::subpattern_flags, pmodel, vid.scale, vid.alpha);
static const auto current_texture_parameters = tie(geometry, variation, patterns::whichPattern, patterns::subpattern_flags, pmodel, pconf.scale, pconf.alpha);
void texture_config::clear_texture_map() {
texture_map.clear();
@ -624,9 +622,9 @@ void texture_config::saveFullTexture(string tn) {
addMessage(XLAT("Saving full texture to %1...", tn));
dynamicval<color_t> dd(grid_color, 0);
dynamicval<color_t> dm(mesh_color, 0);
dynamicval<ld> dx(vid.xposition, 0);
dynamicval<ld> dy(vid.yposition, 0);
dynamicval<ld> dvs(vid.scale, (pmodel == mdDisk && !euclid) ? 1 : vid.scale);
dynamicval<ld> dx(pconf.xposition, 0);
dynamicval<ld> dy(pconf.yposition, 0);
dynamicval<ld> dvs(pconf.scale, (pmodel == mdDisk && !euclid) ? 1 : pconf.scale);
dynamicval<bool> dro(rug::rugged, false);
dynamicval<bool> dnh(nohud, true);
texture::saving = true;
@ -741,9 +739,9 @@ struct magic_param {
void apply(ld delta) {
if(have_mp(mpProjection))
vid.alpha *= exp(delta * proj);
pconf.alpha *= exp(delta * proj);
if(have_mp(mpScale))
vid.scale *= exp(delta * scale);
pconf.scale *= exp(delta * scale);
if(do_spin) {
if(have_mp(mpRotate))
@ -853,7 +851,7 @@ void mousemovement() {
// do not zoom in portrait!
if(nonzero && !newmove) {
View = inverse(spintox(mouseeu)) * spintox(lastmouse) * View;
vid.scale = vid.scale * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
pconf.scale = pconf.scale * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
config.perform_mapping();
}
if(nonzero) lastmouse = mouseeu;
@ -863,7 +861,7 @@ void mousemovement() {
case tpsProjection: {
if(nonzero && !newmove) {
vid.alpha = vid.alpha * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
pconf.alpha = pconf.alpha * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
}
if(nonzero) lastmouse = mouseeu;
newmove = false;
@ -958,9 +956,9 @@ void init_textureconfig() {
addsaverenum(targetgeometry, "geometry", gNormal);
addsaverenum(pmodel, "used model", mdDisk);
addsaver(vid.yshift, "Y shift", 0);
addsaver(vid.yposition, "Y position", 0);
addsaver(vid.xposition, "X position", 0);
addsaver(vid.camera_angle, "camera angle", 0);
addsaver(pconf.yposition, "Y position", 0);
addsaver(pconf.xposition, "X position", 0);
addsaver(pconf.camera_angle, "camera angle", 0);
addsaverenum(targetvariation, "bitruncated", eVariation::bitruncated);
// ... geometry parameters
@ -977,9 +975,9 @@ void init_textureconfig() {
addsaver(config.color_alpha, "alpha color", 0);
addsaver(config.mesh_color, "mesh color", 0);
addsaver(vid.alpha, "projection", 1);
addsaver(vid.scale, "scale", 1);
addsaver(vid.stretch, "stretch", 1);
addsaver(pconf.alpha, "projection", 1);
addsaver(pconf.scale, "scale", 1);
addsaver(pconf.stretch, "stretch", 1);
addsaver(vid.binary_width, "binary-tiling-width", 1);
addsaver(config.texturename, "texture filename", "");
@ -1449,8 +1447,6 @@ array<point, 3> ptc(const array<hyperpoint, 3>& h) {
return make_array(ptc(h[0]), ptc(h[1]), ptc(h[2]));
}
EX ld penwidth = .02;
int texture_distance(pair<int, int> p1, pair<int, int> p2) {
return max(abs(p1.first-p2.first), abs(p1.second - p2.second));
}
@ -1521,7 +1517,7 @@ void filltriangle(const array<hyperpoint, 3>& v, const array<point, 3>& p, color
void splitseg(const transmatrix& A, const array<ld, 2>& angles, const array<hyperpoint, 2>& h, const array<point, 2>& p, color_t col, int lev) {
ld newangle = (angles[0] + angles[1]) / 2;
hyperpoint nh = A * xspinpush0(newangle, penwidth);
hyperpoint nh = A * xspinpush0(newangle, mapeditor::dtwidth);
auto np = ptc(nh);
filltriangle(make_array(h[0],h[1],nh), make_array(p[0],p[1],np), col, lev);
@ -1538,7 +1534,7 @@ void fillcircle(hyperpoint h, color_t col) {
ld step = M_PI * 2/3;
array<hyperpoint, 3> mh = make_array(A * xpush0(penwidth), A * xspinpush0(step, penwidth), A * xspinpush0(-step, penwidth));
array<hyperpoint, 3> mh = make_array(A * xpush0(mapeditor::dtwidth), A * xspinpush0(step, mapeditor::dtwidth), A * xspinpush0(-step, mapeditor::dtwidth));
auto mp = ptc(mh);
filltriangle(mh, mp, col, 0);
@ -1596,7 +1592,7 @@ EX void drawPixel(hyperpoint h, color_t col) {
}
EX void drawLine(hyperpoint h1, hyperpoint h2, color_t col, int steps IS(10)) {
if(steps > 0 && hdist(h1, h2) > penwidth / 3) {
if(steps > 0 && hdist(h1, h2) > mapeditor::dtwidth / 3) {
hyperpoint h3 = mid(h1, h2);
drawLine(h1, h3, col, steps-1);
drawLine(h3, h2, col, steps-1);

View File

@ -175,7 +175,7 @@ EX void slidehelp() {
/** \brief return from a subgame launched while in presentation */
void return_geometry() {
gamestack::pop();
vid.scale = 1; vid.alpha = 1;
pconf.scale = 1; pconf.alpha = 1;
presentation(pmGeometryReset);
addMessage(XLAT("Returned to your game."));
}
@ -262,11 +262,11 @@ bool handleKeyTour(int sym, int uni) {
break;
case '1':
set_geometry(gSphere);
vid.alpha = 1, vid.scale = .5;
pconf.alpha = 1, pconf.scale = .5;
break;
case '2':
set_geometry(gEuclid);
vid.alpha = 1, vid.scale = .5;
pconf.alpha = 1, pconf.scale = .5;
break;
}
start_game();
@ -288,7 +288,7 @@ bool handleKeyTour(int sym, int uni) {
return true;
}
if(NUMBERKEY == '3' && sphere) {
if(vid.alpha < 2) vid.scale = 400, vid.alpha = 400; else vid.scale = .5, vid.alpha = 1;
if(pconf.alpha < 2) pconf.scale = 400, pconf.alpha = 400; else pconf.scale = .5, pconf.alpha = 1;
addMessage(XLAT("Changed the projection."));
return true;
}
@ -450,8 +450,8 @@ EX namespace ss {
EX void start() {
currentslide = 0;
vid.scale = 1;
vid.alpha = 1;
pconf.scale = 1;
pconf.alpha = 1;
pmodel = mdDisk;
if(!tour::on) presentation(pmStartAll);
else {
@ -839,8 +839,8 @@ EX slide default_slides[] = {
{models+"Beltrami-Klein model", 43, LEGAL::ANY | USE_SLIDE_NAME,
"This model renders straight lines as straight, but it distorts angles.",
[] (presmode mode) {
if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) vid.alpha = 0;
if(mode == 3) vid.alpha = 1;
if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) pconf.alpha = 0;
if(mode == 3) pconf.alpha = 1;
}
},
{models+"Gans model", 44, LEGAL::ANY | USE_SLIDE_NAME,
@ -849,8 +849,8 @@ EX slide default_slides[] = {
"model are all obtained by looking at either the hyperboloid model or an "
"equidistant surface from various distances.",
[] (presmode mode) {
if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) vid.alpha = 400, vid.scale = 150;
if(mode == 3) vid.alpha = vid.scale = 1;
if(mode == 1 || mode == pmGeometryReset || mode == pmGeometry) pconf.alpha = 400, pconf.scale = 150;
if(mode == 3) pconf.alpha = pconf.scale = 1;
}
},
{models+"Band model", 45, LEGAL::NONEUC | USE_SLIDE_NAME,

View File

@ -134,7 +134,13 @@ struct hr_parse_exception : hr_exception {
struct exp_parser {
string s;
int at;
exp_parser() { at = 0; }
int line_number, last_line;
exp_parser() { at = 0; line_number = 1; last_line = 0; }
string where() {
if(s.find('\n')) return "(line " + its(line_number) + ", pos " + its(at-last_line) + ")";
else return "(pos " + its(at) + ")";
}
map<string, cld> extra_params;
@ -168,14 +174,21 @@ struct exp_parser {
void force_eat(const char *c) {
skip_white();
if(!eat(c)) throw hr_parse_exception("expected: " + string(c));
if(!eat(c)) throw hr_parse_exception("expected: " + string(c) + " at " + where());
}
};
#endif
void exp_parser::skip_white() {
while(next() == ' ' || next() == '\n' || next() == '\r' || next() == '\t') at++;
while(next() == ' ' || next() == '\n' || next() == '\r' || next() == '\t') {
if(next() == '\r') last_line++;
if(next() == '\n') {
println(hlog, "new line at ", at);
line_number++, last_line = at;
}
at++;
}
}
string exp_parser::next_token() {