From 7c84280b737b1b22274fd185fc9da1f0c3cfa531 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Mon, 26 Mar 2018 19:06:47 +0200 Subject: [PATCH] four new models, Mercator improved --- config.cpp | 1 + conformal.cpp | 32 ++++++++++-- graph.cpp | 64 ++++++++++++++++++++++-- hyper.h | 8 +-- hypgraph.cpp | 130 +++++++++++++++++++++++++++++++----------------- language-pl.cpp | 17 +++++++ polygons.cpp | 91 +++++++++++++++++++++++++-------- 7 files changed, 265 insertions(+), 78 deletions(-) diff --git a/config.cpp b/config.cpp index dc9c7d5e..1efb411a 100644 --- a/config.cpp +++ b/config.cpp @@ -340,6 +340,7 @@ void initConfig() { addsaver(stereo::fov, "field-of-vision", 90); addsaverenum(stereo::mode, "stereo-mode"); addsaver(vid.euclid_to_sphere, "euclid to sphere projection", 1.5); + addsaver(vid.twopoint_param, "twopoint parameter", 1); #if CAP_SHMUP shmup::initConfig(); diff --git a/conformal.cpp b/conformal.cpp index e1c90c61..80c5413c 100644 --- a/conformal.cpp +++ b/conformal.cpp @@ -533,7 +533,8 @@ namespace conformal { const char *modelnames[MODELCOUNT] = { "disk", "half-plane", "band", "polygonal", "polynomial", "azimuthal equidistant", "azimuthal equi-area", - "ball model", "Minkowski hyperboloid", "hemisphere" + "ball model", "Minkowski hyperboloid", "hemisphere", + "band equidistant", "band equi-area", "sinusoidal", "two-point equidistant" }; string get_model_name(eModel pm) { @@ -547,7 +548,8 @@ namespace conformal { } bool model_available(eModel pm) { - if(mdEqui() || pm == mdDisk || pm == mdPolynomial || pm == mdHyperboloid || pm == mdHemisphere) + if(mdAzimuthalEqui() || pm == mdDisk || pm == mdPolynomial || pm == mdHyperboloid || pm == mdHemisphere || + pm == mdBandEquidistant || pm == mdBandEquiarea || pm == mdSinusoidal || pm == mdTwoPoint) return true; if(sphere && pm == mdBand) return true; @@ -565,7 +567,7 @@ namespace conformal { for(int i=0; i,AURA+1> aurac; bool haveaura() { if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return false; - if(sphere && mdEqui()) return true; + if(sphere && mdAzimuthalEqui()) return true; return pmodel == mdDisk && (!sphere || vid.alpha > 10) && !euclid; } @@ -2213,7 +2213,7 @@ void drawaura() { if(!haveaura()) return; if(stereo::mode) return; double rad = vid.radius; - if(sphere && !mdEqui()) rad /= sqrt(vid.alpha*vid.alpha - 1); + if(sphere && !mdAzimuthalEqui()) rad /= sqrt(vid.alpha*vid.alpha - 1); for(int v=0; v<4; v++) sumaura(v); for(auto& p: auraspecials) { @@ -4965,6 +4965,11 @@ void queuecircleat(cell *c, double rad, int col) { void drawMarkers() { + if(pmodel == mdTwoPoint) { + queuechr(xpush(+vid.twopoint_param) * C0, 8, 'X', 0xFFFF00); + queuechr(xpush(-vid.twopoint_param) * C0, 8, 'X', 0xFFFF00); + } + if(!(cmode & sm::NORMAL)) return; for(cell *c1: crush_now) @@ -5426,11 +5431,62 @@ void drawfullmap() { ptds.clear(); - if(!stereo::active() && !euclid && (pmodel == mdDisk || pmodel == mdBall || (sphere && mdEqui()))) { + if(!stereo::active() && sphere && pmodel == mdTwoPoint) { + queuereset(vid.usingGL ? mdDisk : mdUnchanged, PPR_CIRCLE); + + for(int a=0; a<=180; a++) { + using namespace hyperpoint_vec; + ld x = cos(a * M_PI / 90); + ld y = 0; + ld z = -sqrt(1 - x*x); + hyperpoint h1; + applymodel(hpxyz(x,y,z), h1); + if(h1[1] < 0) h1[1] = -h1[1]; + if(a >= 90) h1[1] = -h1[1]; + curvepoint(h1 * vid.radius); + } + + queuecurve(ringcolor, 0, PPR_CIRCLE); + queuereset(pmodel, PPR_CIRCLE); + } + + if(!stereo::active() && sphere && pmodel == mdSinusoidal) { + queuereset(vid.usingGL ? mdDisk : mdUnchanged, PPR_CIRCLE); + + for(int a=-45; a<45; a++) { + curvepoint(hpxyz(cos(a * M_PI / 90) * vid.radius, a * vid.radius / 90, 0)); + } + for(int a=45; a>=-45; a--) { + curvepoint(hpxyz(-cos(a * M_PI / 90) * vid.radius, a * vid.radius / 90, 0)); + } + queuecurve(ringcolor, 0, PPR_CIRCLE); + + queuereset(pmodel, PPR_CIRCLE); + } + + if(!stereo::active() && vid.grid) { + ld rad = 0; + if(pmodel == mdBand && hyperbolic) rad = vid.radius; + if(pmodel == mdBandEquidistant && sphere) rad = vid.radius / 2; + if(pmodel == mdBandEquiarea && sphere) rad = vid.radius / M_PI; + + if(rad) { + queuereset(vid.usingGL ? mdDisk : mdUnchanged, PPR_CIRCLE); + curvepoint(hpxyz(-vid.xcenter, -rad, 0)); + curvepoint(hpxyz(vid.xres-vid.xcenter, -rad, 0)); + queuecurve(ringcolor, 0, PPR_CIRCLE); + curvepoint(hpxyz(-vid.xcenter, rad, 0)); + curvepoint(hpxyz(vid.xres-vid.xcenter, rad, 0)); + queuecurve(ringcolor, 0, PPR_CIRCLE); + queuereset(pmodel, PPR_CIRCLE); + } + } + + if(!stereo::active() && !euclid && (pmodel == mdDisk || pmodel == mdBall || (sphere && mdAzimuthalEqui()))) { double rad = vid.radius; bool isbnd = true; if(sphere) { - if(mdEqui()) + if(mdAzimuthalEqui()) ; else if(!vid.grid && !elliptic && !force_sphere_outline) rad = 0; diff --git a/hyper.h b/hyper.h index 5e3718c3..88ea904f 100644 --- a/hyper.h +++ b/hyper.h @@ -647,7 +647,7 @@ extern reaction_t help_delegate; struct videopar { ld scale, alpha, sspeed, mspeed, yshift, camera_angle; - ld ballangle, ballproj, euclid_to_sphere; + ld ballangle, ballproj, euclid_to_sphere, twopoint_param; int mobilecompasssize; int aurastr, aurasmoothen; @@ -888,7 +888,7 @@ namespace rug { enum eModel { mdDisk, mdHalfplane, mdBand, mdPolygonal, mdPolynomial, mdEquidistant, mdEquiarea, mdBall, mdHyperboloid, - mdHemisphere, + mdHemisphere, mdBandEquidistant, mdBandEquiarea, mdSinusoidal, mdTwoPoint, mdGUARD, mdUnchanged }; namespace conformal { @@ -1611,7 +1611,9 @@ void drawShape(pair* coords, int qty, int color); extern eModel pmodel; -inline bool mdEqui() { return pmodel == mdEquidistant || pmodel == mdEquiarea; } +inline bool mdAzimuthalEqui() { return pmodel == mdEquidistant || pmodel == mdEquiarea; } + +inline bool mdBandAny() { return pmodel == mdBand || pmodel == mdBandEquidistant || pmodel == mdBandEquiarea || pmodel == mdSinusoidal; } int darkena(int c, int lev, int a); diff --git a/hypgraph.cpp b/hypgraph.cpp index be09bcc6..b149a67d 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -203,8 +203,78 @@ void applymodel(hyperpoint H, hyperpoint& ret) { using namespace hyperpoint_vec; H = H / zlev; } + + if(pmodel == mdTwoPoint || mdBandAny() || pmodel == mdSinusoidal) { + // map to plane + if(pmodel == mdTwoPoint) { + auto p = vid.twopoint_param; + ld dleft = hdist(H, xpush(-p) * C0); + ld dright = hdist(H, xpush(p) * C0); + ld x = (dright*dright-dleft*dleft) / 4 / p; + ld y = sqrt(dleft * dleft - (x-p)*(x-p) + 1e-9); + x = -x; + if(H[1] < 0) y = -y; + ret = hpxyz(x/M_PI, y/M_PI, 0); + } + else { + ld x, y; + switch(cgclass) { + case gcHyperbolic: + y = asinh(H[1]), x = asinh(H[0] / cosh(y)); + break; + case gcSphere: + y = asin(H[1]), x = asin(H[0] / cos(y)); + if(H[2] < 0 && x > 0) x = M_PI - x; + else if(H[2] < 0 && x <= 0) x = -M_PI - x; + break; + case gcEuclid: + y = H[1], x = H[0]; + break; + } + if(pmodel == mdBand) switch(cgclass) { + case gcSphere: + y = atanh(sin(y) * zlev); + x *= 2; y *= 2; + break; + case gcHyperbolic: + y = 2 * atan(tanh(y/2) * zlev); + x *= 2; y *= 2; + break; + case gcEuclid: + y = y; + y *= 2; x *= 2; + break; + } + if(pmodel == mdBandEquiarea) switch(cgclass) { + case gcHyperbolic: + y = sinh(y); + break; + case gcSphere: + y = sin(y); + break; + default: + y = y; + break; + } + if(pmodel == mdSinusoidal) switch(cgclass) { + case gcHyperbolic: + x *= cosh(y); + break; + case gcSphere: + x *= cos(y); + break; + default: + x *= 1; + break; + } + ret = hpxyz(x / M_PI, y / M_PI, 0); + } + apply_depth(ret, -geom3::factor_to_lev(zlev)); + ghcheck(ret, H); + return; + } - if(mdEqui()) { + if(mdAzimuthalEqui()) { ld rad = sqrt(H[0] * H[0] + H[1] * H[1]); if(rad == 0) rad = 1; ld d = hdist0(H); @@ -236,21 +306,21 @@ void applymodel(hyperpoint H, hyperpoint& ret) { return; } - // Poincare to half-plane - - ld x0, y0; - x0 = H[0] / tz; - y0 = H[1] / tz; - if(conformal::lower_halfplane) x0 = -x0, y0 = -y0; - y0 += 1; - double rad = x0*x0 + y0*y0; - y0 /= rad; - x0 /= rad; - y0 -= .5; - - if(conformal::lower_halfplane) x0 = -x0, y0 = -y0; - if(pmodel == mdHalfplane) { + // Poincare to half-plane + + ld x0, y0; + x0 = H[0] / tz; + y0 = H[1] / tz; + if(conformal::lower_halfplane) x0 = -x0, y0 = -y0; + y0 += 1; + double rad = x0*x0 + y0*y0; + y0 /= rad; + x0 /= rad; + y0 -= .5; + + if(conformal::lower_halfplane) x0 = -x0, y0 = -y0; + ret[0] = x0; if(wmspatial || mmspatial) { if(conformal::lower_halfplane) y0 /= zlev; @@ -263,36 +333,6 @@ void applymodel(hyperpoint H, hyperpoint& ret) { ghcheck(ret,H); return; } - - // center - x0 *= 2; y0 *= 2; - - // half-plane to band - double tau = (log((x0+1)*(x0+1) + y0*y0) - log((x0-1)*(x0-1) + y0*y0)) / 2; - double u=(1-x0*x0-y0*y0); - u = (1 - x0*x0 - y0*y0 + sqrt(u*u+4*y0*y0)); - double yv = 2*y0 / u; - double sigma = 2 * atan(yv * zlev) - M_PI/2; - - x0 = tau; y0 = sigma; - - /* if(zlev != 1) { - double alp = (y0 * y0) / (1-y0*y0); - double gx = alp + sqrt(alp*alp-1); - double gy = y0 * (gx+1); - double yr = zlev * gy / (zlev * gx + 1); - printf("zlev = %10.5lf y0 = %20.10lf yr = %20.10lf\n", double(zlev), (double)y0, yr); - y0 = yr; - } */ - - ret[0] = x0/M_PI*2; - ret[1] = -y0/M_PI*2; - ret[2] = 0; - - if(zlev != 1 && stereo::active()) - apply_depth(ret, -geom3::factor_to_lev(zlev) / (1 + yv * yv)); - - ghcheck(ret,H); } // game-related graphics diff --git a/language-pl.cpp b/language-pl.cpp index 1cd87472..1395430d 100644 --- a/language-pl.cpp +++ b/language-pl.cpp @@ -6848,3 +6848,20 @@ S( "opcja ta działa tylko w trybie oszusta." ); S("NEVER", "NIGDY") + +S("Mercator", "odwzorowanie Merkatora") +S("band equidistant", "wstęga ekwidystantna") +S("band equi-area", "wstęga ekwiwalentna") +S("sinusoidal", "sinusoida") +S("two-point equidistant", "rzut dwupunktowy ekwidystantny") + +S( + "This model maps the world so that the distances from two points " + "are kept. This parameter gives the distance from the two points to " + "the center.", + + "Ten model przekształca świat tak, że odległości od dwóch punktów " + "są zachowane. Ten parametr zadaje odległość tych dwóch punktów " + "od środka.") + + diff --git a/polygons.cpp b/polygons.cpp index b9b0c850..e925de91 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -366,20 +366,33 @@ double linewidthat(const hyperpoint& h, double minwidth) { int mercator_coord; int mercator_loop_min = 0, mercator_loop_max = 0; +ld mercator_period; void fixMercator(bool tinf) { - ld period = 4 * vid.radius; - ld hperiod = period / 2; + if(pmodel == mdBand) + mercator_period = 4 * vid.radius; + else + mercator_period = 2 * vid.radius; - mercator_coord = 1; + if(pmodel == mdSinusoidal) + for(int i = 0; i hperiod) glcoords[0][mercator_coord] -= period; + while(glcoords[0][mercator_coord] < hperiod) glcoords[0][mercator_coord] += mercator_period; + while(glcoords[0][mercator_coord] > hperiod) glcoords[0][mercator_coord] -= mercator_period; } ld first = glcoords[0][mercator_coord]; @@ -389,9 +402,9 @@ void fixMercator(bool tinf) { for(int i = 0; i next + hperiod) - glcoords[i][mercator_coord] -= period; + glcoords[i][mercator_coord] -= mercator_period; next = glcoords[i][mercator_coord]; mincoord = min(mincoord, glcoords[i][mercator_coord]); maxcoord = max(maxcoord, glcoords[i][mercator_coord]); @@ -403,14 +416,17 @@ void fixMercator(bool tinf) { } ld last = first; - while(last < next - hperiod) last += period; - while(last > next + hperiod) last -= period; + while(last < next - hperiod) last += mercator_period; + while(last > next + hperiod) last -= mercator_period; if(first == last) { while(mincoord > cmin) - mercator_loop_min--, mincoord -= period; + mercator_loop_min--, mincoord -= mercator_period; while(maxcoord < cmax) - mercator_loop_max++, maxcoord += period; + mercator_loop_max++, maxcoord += mercator_period; + if(pmodel == mdSinusoidal) + for(int i = 0; i cmin) { - for(int i=0; i v0, v1; + if(sphere && pmodel == mdTwoPoint) { + v0.clear(); v1.clear(); + for(int i=0; i= 0) + v0.push_back(h), v1.push_back(h); + else if(h[1] < 0) + v0.push_back(h); + else if(h[1] > 0) + v1.push_back(h); + } + pp.V = Id; pp.offset = 0; + if(size(v0) == pp.cnt) { + pp.tab = &v0; + } + else if(size(v1) == pp.cnt) { + pp.tab = &v1; + } + else { + if(size(v0) >= 3) { pp.tab = &v0; pp.cnt = size(v0); drawpolyline(p); } + if(size(v1) >= 3) { pp.tab = &v1; pp.cnt = size(v1); drawpolyline(p); } + } + } if(spherespecial && p.prio == PPR_MOBILE_ARROW) { if(spherephase == 0) return; @@ -496,7 +542,7 @@ void drawpolyline(polytodraw& p) { addpoly(pp.V, *pp.tab, pp.offset, pp.cnt); mercator_loop_min = mercator_loop_max = 0; - if(sphere && pmodel == mdBand) + if(sphere && mdBandAny()) fixMercator(pp.tinf); int poly_limit = max(vid.xres, vid.yres) * 2; @@ -508,7 +554,7 @@ void drawpolyline(polytodraw& p) { return; // too large! } - if((spherespecial > 0 || (sphere && mdEqui())) && !(poly_flags & POLY_ISSIDE)) { + if((spherespecial > 0 || (sphere && mdAzimuthalEqui())) && !(poly_flags & POLY_ISSIDE)) { double rarea = 0; for(int i=0; i