Five new projections: Poor Man (hyperbolic only), Panini, retroazimuthal: Craig, Hammer, Littrow (retro-Hammer buggy on sphere)

This commit is contained in:
Zeno Rogue 2020-10-08 18:22:28 +02:00
parent 34dc8fcc6e
commit b74509c95a
5 changed files with 99 additions and 6 deletions

View File

@ -980,6 +980,8 @@ enum eModel : int {
// 32..38
mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdMiller, mdGallStereographic, mdWinkelTripel,
// 39..
mdPoorMan, mdPanini, mdRetroCraig, mdRetroLittrow, mdRetroHammer,
// 44..
mdGUARD, mdPixel, mdHyperboloidFlat, mdPolynomial, mdManual
};
#endif
@ -1031,6 +1033,11 @@ EX vector<modelinfo> mdinf = {
{X3("Miller projection"), mf::euc_boring | mf::band, DEFAULTS}, // scale latitude 4/5 -> Mercator -> 5/4
{X3("Gall stereographic"), mf::euc_boring | mf::band, DEFAULTS}, // like central cylindrical but stereographic
{X3("Winkel tripel"), mf::euc_boring | mf::broken, DEFAULTS}, // mean of equirec and Aitoff
{X3("Poor man's square"), mf::euc_boring, DEFAULTS}, //
{X3("Panini projection"), mf::euc_boring, DEFAULTS}, //
{X3("Craig retroazimuthal"), mf::euc_boring | mf::broken, DEFAULTS}, // retroazimuthal cylindrical
{X3("Littrow retroazimuthal"), mf::euc_boring | mf::broken, DEFAULTS}, // retroazimuthal conformal
{X3("Hammer retroazimuthal"), mf::euc_boring, DEFAULTS}, // retroazimuthal equidistant
{X3("guard"), 0, DEFAULTS},
{X3("polynomial"), mf::conformal, DEFAULTS},
};

View File

@ -293,6 +293,8 @@ EX bool two_sided_model() {
if(pmodel == mdHyperboloid) return !euclid;
// if(pmodel == mdHemisphere) return true;
if(pmodel == mdDisk) return sphere;
if(pmodel == mdRetroLittrow) return sphere;
if(pmodel == mdRetroHammer) return sphere;
if(pmodel == mdHemisphere) return true;
if(pmodel == mdRotatedHyperboles) return true;
if(pmodel == mdSpiral && pconf.spiral_cone < 360) return true;
@ -305,6 +307,12 @@ EX int get_side(const hyperpoint& H) {
double horizon = curnorm / pconf.alpha;
return (H[2] <= -horizon) ? -1 : 1;
}
if(pmodel == mdRetroLittrow && sphere) {
return H[2] >= 0 ? 1 : -1;
}
if(pmodel == mdRetroHammer && sphere) {
return H[2] >= 0 ? 1 : -1;
}
if(pmodel == mdRotatedHyperboles)
return H[1] > 0 ? -1 : 1;
if(pmodel == mdHyperboloid && hyperbolic)

View File

@ -384,7 +384,9 @@ void ge_select_tiling() {
EX string current_proj_name() {
bool h = hyperbolic || sn::in();
if(vpconf.model != mdDisk)
if(vpconf.model == mdPanini && vpconf.alpha == 1)
return XLAT("stereographic Panini");
else if(vpconf.model != mdDisk)
return models::get_model_name(vpconf.model);
else if(h && vpconf.alpha == 1)
return XLAT("Poincaré model");

View File

@ -1074,6 +1074,76 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) {
}
}
case mdRetroCraig: {
makeband(H_orig, ret, [] (ld& x, ld& y) {
if(x)
y = x / sin_auto(x) * (sin_auto(y) * cos_auto(x) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y));
else
y = sin_auto(y) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y);
});
break;
}
case mdRetroLittrow: {
makeband(H_orig, ret, [] (ld& x, ld& y) {
tie(x, y) = make_pair(
sin_auto(x) / cos_auto(y),
cos_auto(x) * tan_auto(y)
);
});
break;
}
case mdRetroHammer: {
ld d = hdist(H, ypush0(pconf.loximuthal_parameter));
makeband(H_orig, ret, [d,H] (ld& x, ld& y) {
if(x == 0 && y == 0) return;
if(x)
y = x / sin_auto(x) * (sin_auto(y) * cos_auto(x) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y));
else
y = sin_auto(y) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y);
ld scale = d / hypot(x, y);
if(H[2] < 0) scale = -scale;
x *= scale;
y *= scale;
});
break;
}
case mdPanini: {
ld proh = sqrt(H[2]*H[2] + curvature() * H[0] * H[0]);
H /= proh;
H /= (H[2] + pconf.alpha);
ret = H;
ret[2] = 0; ret[3] = 1;
break;
}
case mdPoorMan: {
find_zlev(H);
H = space_to_perspective(H);
models::apply_orientation_yz(H[1], H[2]);
models::apply_orientation(H[0], H[1]);
ld u = H[0], v = H[1];
if(abs(u) > 1e-3 && abs(v) > 1e-3) {
ld r2 = u*u+v*v;
ld scale = sqrt((-r2+sqrt(r2*(r2+4*u*u*v*v*(r2-2))))/(2*(r2-2))) / u / v;
if(u*v<0) scale = -scale;
H = scale * H;
}
ret = H;
ret[2] = 0;
ret[3] = 1;
models::apply_orientation(ret[1], ret[0]);
models::apply_orientation_yz(ret[2], ret[1]);
break;
}
case mdGUARD: case mdManual: break;
}

View File

@ -200,6 +200,8 @@ EX namespace models {
if(GDIM == 2 && pm == mdEquivolume) return false;
if(GDIM == 3 && among(pm, mdBall, mdHyperboloid, mdFormula, mdPolygonal, mdRotatedHyperboles, mdSpiral, mdHemisphere)) return false;
if(pm == mdCentralInversion && !euclid) return false;
if(pm == mdPoorMan) return hyperbolic;
if(pm == mdRetroHammer) return hyperbolic;
return true;
}
@ -442,7 +444,7 @@ EX namespace models {
dialog::addBreak(50);
}
if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) {
if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles, mdPanini)) {
dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", 'p');
dialog::add_action(projectionDialog);
}
@ -636,13 +638,17 @@ EX namespace models {
});
}
if(vpmodel == mdLoximuthal) {
if(among(vpmodel, mdLoximuthal, mdRetroHammer, mdRetroCraig)) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.loximuthal_parameter), 'b');
dialog::add_action([](){
dialog::add_action([vpmodel](){
dialog::editNumber(vpconf.loximuthal_parameter, -M_PI/2, M_PI/2, .1, 0, XLAT("parameter"),
(vpmodel == mdLoximuthal ?
"This model is similar to azimuthal equidistant, but based on loxodromes (lines of constant geographic direction) rather than geodesics. "
"The loximuthal projection maps (the shortest) loxodromes to straight lines of the same length, going through the starting point. "
"This setting changes the latitude of the starting point."
"This setting changes the latitude of the starting point." :
"In retroazimuthal projections, a point is drawn at such a point that the azimuth *from* that point to the chosen central point is correct. "
"For example, if you should move east, the point is drawn to the right. This parameter is the latitude of the central point.")
+ string(hyperbolic ? "\n\n(In hyperbolic geometry directions are assigned according to the Lobachevsky coordinates.)" : "")
);
});
}
@ -827,7 +833,7 @@ EX namespace models {
PHASEFROM(2);
if(pmodel == mdCollignon) shift_arg_formula(vpconf.collignon_parameter);
else if(pmodel == mdMiller) shift_arg_formula(vpconf.miller_parameter);
else if(pmodel == mdLoximuthal) shift_arg_formula(vpconf.loximuthal_parameter);
else if(among(pmodel, mdLoximuthal, mdRetroCraig, mdRetroHammer)) shift_arg_formula(vpconf.loximuthal_parameter);
else if(among(pmodel, mdAitoff, mdHammer, mdWinkelTripel)) shift_arg_formula(vpconf.aitoff_parameter);
if(pmodel == mdWinkelTripel) shift_arg_formula(vpconf.winkel_parameter);
}