four new models, Mercator improved

This commit is contained in:
Zeno Rogue 2018-03-26 19:06:47 +02:00
parent fa49fc550f
commit 7c84280b73
7 changed files with 265 additions and 78 deletions

View File

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

View File

@ -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<mdGUARD; i++) {
eModel m = eModel(i);
if(model_available(m))
dialog::addBoolItem(get_model_name(m), pmodel == m, '0' + i);
dialog::addBoolItem(get_model_name(m), pmodel == m, "0123456789!@#$%^&*()" [m]);
}
dialog::addBreak(100);
@ -611,10 +613,14 @@ namespace conformal {
dialog::addSelItem(XLAT("camera rotation in 3D models"), fts3(vid.ballangle), 'b');
}
if(pmodel == mdHemisphere) {
if(pmodel == mdHemisphere && euclid) {
dialog::addSelItem(XLAT("parameter"), fts3(vid.euclid_to_sphere), 'l');
}
if(pmodel == mdTwoPoint) {
dialog::addSelItem(XLAT("parameter"), fts3(vid.twopoint_param), 'l');
}
dialog::addBreak(100);
dialog::addItem(XLAT("history mode"), 'a');
dialog::addItem(XLAT("hypersian rug mode"), 'u');
@ -633,6 +639,14 @@ namespace conformal {
if(pmodel == mdDisk && sphere)
vid.scale = .4;
}
else if(uni == '!')
pmodel = eModel(10);
else if(uni == '@')
pmodel = eModel(11);
else if(uni == '#')
pmodel = eModel(12);
else if(uni == '$')
pmodel = eModel(13);
else if(uni == '6')
vid.alpha = 1, vid.scale = 1;
else if(uni == 'z')
@ -647,12 +661,20 @@ namespace conformal {
lower_halfplane = !lower_halfplane;
else if(uni == 'a')
pushScreen(history_menu);
else if(uni == 'l') {
else if(uni == 'l' && pmodel == mdHemisphere && euclid) {
dialog::editNumber(vid.euclid_to_sphere, 0, 10, .1, 1, XLAT("parameter"),
"Stereographic projection to a sphere. Choose the radius of the sphere."
);
dialog::scaleLog();
}
else if(uni == 'l' && pmodel == mdTwoPoint) {
dialog::editNumber(vid.twopoint_param, 0, 10, .1, 1, XLAT("parameter"),
"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."
);
dialog::scaleLog();
}
else if(uni == 'x' && pmodel == mdBall)
dialog::editNumber(vid.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.");

View File

@ -2158,7 +2158,7 @@ array<array<int,4>,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;

View File

@ -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<ld,ld>* 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);

View File

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

View File

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

View File

@ -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<size(glcoords); i++)
glcoords[i][mercator_coord] /= cos(glcoords[i][1] / vid.radius * M_PI);
ld hperiod = mercator_period / 2;
mercator_coord = 0;
ld cmin = -vid.xcenter, cmax = vid.xres - vid.xcenter, dmin = -vid.ycenter, dmax = vid.yres - vid.ycenter;
if(mercator_coord)
swap(cmin, dmin), swap(cmax, dmax);
if(pmodel == mdSinusoidal || pmodel == mdBandEquidistant)
dmin = -vid.radius / 2, dmax = vid.radius / 2;
if(pmodel == mdBandEquiarea)
dmin = -vid.radius / M_PI, dmax = vid.radius / M_PI;
for(int i = 0; i<size(glcoords); i++) {
while(glcoords[0][mercator_coord] < hperiod) glcoords[0][mercator_coord] += period;
while(glcoords[0][mercator_coord] > 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<size(glcoords); i++) {
while(glcoords[i][mercator_coord] < next - hperiod)
glcoords[i][mercator_coord] += period;
glcoords[i][mercator_coord] += mercator_period;
while(glcoords[i][mercator_coord] > next + hperiod)
glcoords[i][mercator_coord] -= period;
glcoords[i][mercator_coord] -= mercator_period;
next = glcoords[i][mercator_coord];
mincoord = min<ld>(mincoord, glcoords[i][mercator_coord]);
maxcoord = max<ld>(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<size(glcoords); i++)
glcoords[i][mercator_coord] *= cos(glcoords[i][1] / vid.radius * M_PI);
}
else {
if(tinf) {
@ -422,19 +438,22 @@ void fixMercator(bool tinf) {
swap(first, last);
}
while(maxcoord > cmin) {
for(int i=0; i<size(glcoords); i++) glcoords[i][mercator_coord] -= period;
first -= period; last -= period;
mincoord -= period; maxcoord -= period;
for(int i=0; i<size(glcoords); i++) glcoords[i][mercator_coord] -= mercator_period;
first -= mercator_period; last -= mercator_period;
mincoord -= mercator_period; maxcoord -= mercator_period;
}
int base = size(glcoords);
int minto = mincoord;
while(minto < cmax) {
for(int i=0; i<base; i++) {
glcoords.push_back(glcoords[size(glcoords)-base]);
glcoords.back()[mercator_coord] += period;
glcoords.back()[mercator_coord] += mercator_period;
}
minto += period;
minto += mercator_period;
}
if(pmodel == mdSinusoidal)
for(int i = 0; i<size(glcoords); i++)
glcoords[i][mercator_coord] *= cos(glcoords[i][1] / vid.radius * M_PI);
glcoords.push_back(glcoords.back());
glcoords.push_back(glcoords[0]);
for(int u=1; u<=2; u++) {
@ -445,6 +464,8 @@ void fixMercator(bool tinf) {
for(int a=0; a<qglcoords; a++)
printf("[%3d] %10.5lf %10.5lf\n", a, glcoords[a][0], glcoords[a][1]); */
}
}
unsigned char& part(int& col, int i) {
@ -465,6 +486,31 @@ void drawpolyline(polytodraw& p) {
pp.offset = 0;
return;
}
static vector<glvertex> v0, v1;
if(sphere && pmodel == mdTwoPoint) {
v0.clear(); v1.clear();
for(int i=0; i<pp.cnt; i++) {
glvertex h = glhr::pointtogl(pp.V * glhr::gltopoint((*pp.tab)[pp.offset+i]));
if(h[2] >= 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<size(glcoords)-1; i++)
rarea += glcoords[i][0] * glcoords[i+1][1] - glcoords[i][1] * glcoords[i+1][0];
@ -534,12 +580,15 @@ void drawpolyline(polytodraw& p) {
for(int l=mercator_loop_min; l <= mercator_loop_max; l++) {
if(l || lastl) {
for(int i=0; i<size(glcoords); i++)
glcoords[i][mercator_coord] += vid.radius * 4 * (l - lastl);
for(int i=0; i<size(glcoords); i++) {
if(pmodel == mdSinusoidal)
mercator_period = 2 * vid.radius * cos(glcoords[i][1] / vid.radius * M_PI);
glcoords[i][mercator_coord] += mercator_period * (l - lastl);
}
lastl = l;
}
if(mdEqui() && (poly_flags & POLY_INVERSE)) {
if(mdAzimuthalEqui() && (poly_flags & POLY_INVERSE)) {
ld h = atan2(glcoords[0][0], glcoords[0][1]);
for(int i=0; i<=360; i++) {
ld a = i * M_PI / 180 + h;