mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2025-02-02 12:19:18 +00:00
new projections based on cartography
This commit is contained in:
parent
a6482d00ab
commit
2e47adef1c
18
classes.cpp
18
classes.cpp
@ -940,6 +940,7 @@ namespace mf {
|
|||||||
static const flagtype equivolume = 1024;
|
static const flagtype equivolume = 1024;
|
||||||
static const flagtype twopoint = 2048;
|
static const flagtype twopoint = 2048;
|
||||||
static const flagtype uses_bandshift = 4096;
|
static const flagtype uses_bandshift = 4096;
|
||||||
|
static const flagtype broken = 8192; /* in spherical case, these are broken along the meridian 180 deg */
|
||||||
|
|
||||||
static const flagtype band = (cylindrical | pseudocylindrical | uses_bandshift);
|
static const flagtype band = (cylindrical | pseudocylindrical | uses_bandshift);
|
||||||
static const flagtype pseudoband = (pseudocylindrical | uses_bandshift);
|
static const flagtype pseudoband = (pseudocylindrical | uses_bandshift);
|
||||||
@ -972,9 +973,13 @@ enum eModel : int {
|
|||||||
mdRotatedHyperboles, mdSpiral, mdPerspective,
|
mdRotatedHyperboles, mdSpiral, mdPerspective,
|
||||||
// 20..24
|
// 20..24
|
||||||
mdEquivolume, mdCentralInversion, mdSimulatedPerspective, mdTwoHybrid, mdGeodesic,
|
mdEquivolume, mdCentralInversion, mdSimulatedPerspective, mdTwoHybrid, mdGeodesic,
|
||||||
// 25
|
// 25..27
|
||||||
mdMollweide, mdCentralCyl, mdCollignon, mdHorocyclic, mdQuadrant, mdAxial, mdAntiAxial,
|
mdMollweide, mdCentralCyl, mdCollignon,
|
||||||
// 32..
|
// 28..31
|
||||||
|
mdHorocyclic, mdQuadrant, mdAxial, mdAntiAxial,
|
||||||
|
// 32..38
|
||||||
|
mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdMiller, mdGallStereographic, mdWinkelTripel,
|
||||||
|
// 39..
|
||||||
mdGUARD, mdPixel, mdHyperboloidFlat, mdPolynomial, mdManual
|
mdGUARD, mdPixel, mdHyperboloidFlat, mdPolynomial, mdManual
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -1019,6 +1024,13 @@ EX vector<modelinfo> mdinf = {
|
|||||||
{X3("quadrant coordinates"), mf::euc_boring, DEFAULTS},
|
{X3("quadrant coordinates"), mf::euc_boring, DEFAULTS},
|
||||||
{X3("axial coordinates"), mf::euc_boring, DEFAULTS},
|
{X3("axial coordinates"), mf::euc_boring, DEFAULTS},
|
||||||
{X3("anti-axial coordinates"), mf::euc_boring, DEFAULTS},
|
{X3("anti-axial coordinates"), mf::euc_boring, DEFAULTS},
|
||||||
|
{X3("Werner projection"), mf::euc_boring | mf::broken, DEFAULTS}, // keep distances from pole, and distances along parallels
|
||||||
|
{X3("Aitoff projection"), mf::euc_boring | mf::broken, DEFAULTS}, // halve longitudes, do azequid, double x
|
||||||
|
{X3("Hammer projection"), mf::euc_boring | mf::broken, DEFAULTS}, // halve longitudes, do azequia, double x
|
||||||
|
{X3("loximuthal projection"), mf::euc_boring | mf::broken, DEFAULTS}, // map loxodromes azimuthally and equidistantly
|
||||||
|
{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("guard"), 0, DEFAULTS},
|
{X3("guard"), 0, DEFAULTS},
|
||||||
{X3("polynomial"), mf::conformal, DEFAULTS},
|
{X3("polynomial"), mf::conformal, DEFAULTS},
|
||||||
};
|
};
|
||||||
|
69
drawing.cpp
69
drawing.cpp
@ -777,6 +777,7 @@ ld period_at(ld y) {
|
|||||||
|
|
||||||
switch(pmodel) {
|
switch(pmodel) {
|
||||||
case mdBand:
|
case mdBand:
|
||||||
|
case mdMiller:
|
||||||
return m * 4;
|
return m * 4;
|
||||||
case mdSinusoidal:
|
case mdSinusoidal:
|
||||||
return m * 2 * cos(y * M_PI);
|
return m * 2 * cos(y * M_PI);
|
||||||
@ -1544,7 +1545,75 @@ void dqi_poly::draw() {
|
|||||||
cnt = i;
|
cnt = i;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int broken_coord = models::get_broken_coord(pmodel);
|
||||||
|
static bool in_broken = false;
|
||||||
|
if(broken_coord && !in_broken) {
|
||||||
|
|
||||||
|
int zcoord = broken_coord;
|
||||||
|
int ycoord = 3 - zcoord;
|
||||||
|
|
||||||
|
vector<hyperpoint> all;
|
||||||
|
for(int i=0; i<cnt; i++)
|
||||||
|
all.push_back(V.T * glhr::gltopoint((*tab)[offset+i]));
|
||||||
|
int fail = 0;
|
||||||
|
int last_fail;
|
||||||
|
for(int i=0; i<cnt-1; i++)
|
||||||
|
if(all[i][0] * all[i+1][0] <= 0 && (all[i][0] * all[i+1][zcoord] - all[i+1][0] * all[i][zcoord]) * (all[i][0] - all[i+1][0]) < 0)
|
||||||
|
last_fail = i, fail++;
|
||||||
|
vector<glvertex> v;
|
||||||
|
dqi_poly p = *this;
|
||||||
|
p.tab = &v;
|
||||||
|
p.offset = 0;
|
||||||
|
p.V.T = Id;
|
||||||
|
if(fail) {
|
||||||
|
dynamicval<bool> ib(in_broken, true);
|
||||||
|
ld part = ilerp(all[last_fail][0], all[last_fail+1][0], 0);
|
||||||
|
hyperpoint initial = normalize(lerp(all[last_fail], all[last_fail+1], 1 - (1-part) * .99));
|
||||||
|
bool have_initial = true;
|
||||||
|
v.push_back(glhr::pointtogl(initial));
|
||||||
|
last_fail++;
|
||||||
|
int at = last_fail;
|
||||||
|
do {
|
||||||
|
v.push_back(glhr::pointtogl(all[at]));
|
||||||
|
if(at == cnt-1 && all[at] != all[0]) {
|
||||||
|
p.cnt = isize(v); p.draw(); v.clear(); at = 0;
|
||||||
|
have_initial = false;
|
||||||
|
}
|
||||||
|
int next = at+1;
|
||||||
|
if(next == cnt) next = 0;
|
||||||
|
if(all[at][0] * all[next][0] <= 0 && (all[at][0] * all[next][zcoord] - all[next][0] * all[at][zcoord]) * (all[at][0] - all[next][0]) < 0) {
|
||||||
|
ld part = ilerp(all[at][0], all[next][0], 0);
|
||||||
|
if(part > .999 || part < .001) { in_broken = false; return; }
|
||||||
|
hyperpoint final = normalize(lerp(all[at], all[next], part * .99));
|
||||||
|
v.push_back(glhr::pointtogl(final));
|
||||||
|
if(have_initial) {
|
||||||
|
int max = 4 << vid.linequality;
|
||||||
|
if(final[0] * initial[0] > 0) {
|
||||||
|
for(int i=1; i<=max; i++)
|
||||||
|
v.push_back(glhr::pointtogl(lerp(final, initial, i * 1. / max)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hyperpoint end = Hypc;
|
||||||
|
end[ycoord] = final[ycoord] > 0 ? 1 : -1;
|
||||||
|
for(int i=1; i<=max; i++)
|
||||||
|
v.push_back(glhr::pointtogl(lerp(final, end, i * 1. / max)));
|
||||||
|
for(int i=1; i<=max; i++)
|
||||||
|
v.push_back(glhr::pointtogl(lerp(end, initial, i * 1. / max)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.cnt = isize(v); p.draw(); v.clear();
|
||||||
|
initial = normalize(lerp(all[at], all[next], 1 - (1-part) * .99));
|
||||||
|
have_initial = true;
|
||||||
|
v.push_back(glhr::pointtogl(initial));
|
||||||
|
}
|
||||||
|
at = next;
|
||||||
|
}
|
||||||
|
while(at != last_fail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(sphere && pmodel == mdTwoPoint && !in_twopoint) {
|
if(sphere && pmodel == mdTwoPoint && !in_twopoint) {
|
||||||
#define MAX_PHASE 4
|
#define MAX_PHASE 4
|
||||||
vector<glvertex> phases[MAX_PHASE];
|
vector<glvertex> phases[MAX_PHASE];
|
||||||
|
5
hyper.h
5
hyper.h
@ -241,6 +241,7 @@ struct projection_configuration {
|
|||||||
ld clip_min, clip_max;
|
ld clip_min, clip_max;
|
||||||
ld model_orientation, halfplane_scale, model_orientation_yz;
|
ld model_orientation, halfplane_scale, model_orientation_yz;
|
||||||
ld collignon_parameter;
|
ld collignon_parameter;
|
||||||
|
ld aitoff_parameter, miller_parameter, loximuthal_parameter, winkel_parameter;
|
||||||
bool collignon_reflected;
|
bool collignon_reflected;
|
||||||
string formula;
|
string formula;
|
||||||
eModel basic_model;
|
eModel basic_model;
|
||||||
@ -267,6 +268,10 @@ struct projection_configuration {
|
|||||||
spiral_cone = 360;
|
spiral_cone = 360;
|
||||||
use_atan = false;
|
use_atan = false;
|
||||||
product_z_scale = 1;
|
product_z_scale = 1;
|
||||||
|
aitoff_parameter = .5;
|
||||||
|
miller_parameter = .8;
|
||||||
|
loximuthal_parameter = 0;
|
||||||
|
winkel_parameter = .5;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
103
hypgraph.cpp
103
hypgraph.cpp
@ -364,10 +364,14 @@ EX void apply_nil_rotation(hyperpoint& H) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EX void applymodel(shiftpoint H_orig, hyperpoint& ret) {
|
EX void applymodel(shiftpoint H_orig, hyperpoint& ret) {
|
||||||
|
apply_other_model(H_orig, ret, pmodel);
|
||||||
|
}
|
||||||
|
|
||||||
|
EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) {
|
||||||
|
|
||||||
hyperpoint H = H_orig.h;
|
hyperpoint H = H_orig.h;
|
||||||
|
|
||||||
if(models::product_model(pmodel)) {
|
if(models::product_model(md)) {
|
||||||
ld zlev = zlevel(H_orig.h);
|
ld zlev = zlevel(H_orig.h);
|
||||||
H_orig.h /= exp(zlev);
|
H_orig.h /= exp(zlev);
|
||||||
hybrid::in_underlying_geometry([&] { applymodel(H_orig, ret); });
|
hybrid::in_underlying_geometry([&] { applymodel(H_orig, ret); });
|
||||||
@ -376,7 +380,7 @@ EX void applymodel(shiftpoint H_orig, hyperpoint& ret) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(pmodel) {
|
switch(md) {
|
||||||
case mdPerspective: {
|
case mdPerspective: {
|
||||||
if(prod) H = product::inverse_exp(H);
|
if(prod) H = product::inverse_exp(H);
|
||||||
apply_nil_rotation(H);
|
apply_nil_rotation(H);
|
||||||
@ -832,6 +836,25 @@ EX void applymodel(shiftpoint H_orig, hyperpoint& ret) {
|
|||||||
else
|
else
|
||||||
makeband(H_orig, ret, band_conformal);
|
makeband(H_orig, ret, band_conformal);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case mdMiller:
|
||||||
|
makeband(H_orig, ret, [] (ld& x, ld& y) {
|
||||||
|
// y *= pconf.miller_parameter;
|
||||||
|
band_conformal(x, y);
|
||||||
|
// y /= pconf.miller_parameter;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mdLoximuthal:
|
||||||
|
makeband(H_orig, ret, [] (ld&x, ld &y) {
|
||||||
|
ld orig_y = y;
|
||||||
|
band_conformal(x, y);
|
||||||
|
y -= pconf.loximuthal_parameter;
|
||||||
|
orig_y -= pconf.loximuthal_parameter;
|
||||||
|
if(y) x = x * orig_y / y;
|
||||||
|
y = orig_y;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case mdTwoPoint:
|
case mdTwoPoint:
|
||||||
makeband(H_orig, ret, make_twopoint);
|
makeband(H_orig, ret, make_twopoint);
|
||||||
@ -861,6 +884,59 @@ EX void applymodel(shiftpoint H_orig, hyperpoint& ret) {
|
|||||||
makeband(H_orig, ret, [] (ld& x, ld& y) { y = tan_auto(y); ld top = vid.yres * M_PI / current_display->radius; if(y>top) y=top; if(y<-top) y=-top; });
|
makeband(H_orig, ret, [] (ld& x, ld& y) { y = tan_auto(y); ld top = vid.yres * M_PI / current_display->radius; if(y>top) y=top; if(y<-top) y=-top; });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case mdGallStereographic:
|
||||||
|
makeband(H_orig, ret, [] (ld& x, ld& y) {
|
||||||
|
y = 2 * sin_auto(y) / (1 + cos_auto(y));
|
||||||
|
ld top = vid.yres * M_PI / current_display->radius; if(y>top) y=top; if(y<-top) y=-top;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mdAitoff: case mdHammer: case mdWinkelTripel:
|
||||||
|
makeband(H_orig, ret, [&] (ld& x, ld& y) {
|
||||||
|
ld ox = x, oy = y;
|
||||||
|
x *= pconf.aitoff_parameter;
|
||||||
|
|
||||||
|
ld x0 = sin_auto(x) * cos_auto(y);
|
||||||
|
ld y0 = cos_auto(x) * cos_auto(y);
|
||||||
|
ld z0 = sin_auto(y);
|
||||||
|
|
||||||
|
ld d = acos_auto(y0);
|
||||||
|
ld d0 = hypot(x0, z0);
|
||||||
|
|
||||||
|
if(md == mdAitoff || md == mdWinkelTripel) ;
|
||||||
|
else if(sphere) d = sqrt(2*(1 - cos(d))) * M_PI / 2;
|
||||||
|
else d = sqrt(2*(cosh(d) - 1)) / 1.5;
|
||||||
|
|
||||||
|
x = x0 * d / d0 / pconf.aitoff_parameter, y = z0 * d / d0;
|
||||||
|
if(md == mdWinkelTripel)
|
||||||
|
x = lerp(x, ox, pconf.model_transition),
|
||||||
|
y = lerp(y, oy, pconf.model_transition);
|
||||||
|
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mdWerner: {
|
||||||
|
|
||||||
|
models::apply_orientation_yz(H[1], H[2]);
|
||||||
|
models::apply_orientation(H[0], H[1]);
|
||||||
|
|
||||||
|
find_zlev(H); // ignored for now
|
||||||
|
|
||||||
|
ld r = hdist0(H);
|
||||||
|
if(r == 0) { ret = H; return; }
|
||||||
|
ld angle = atan2(H[0], H[1]);
|
||||||
|
angle *= sin_auto(r) / r;
|
||||||
|
|
||||||
|
ret[0] = sin(angle) * r;
|
||||||
|
ret[1] = cos(angle) * r;
|
||||||
|
ret[2] = 0;
|
||||||
|
ret[3] = 1;
|
||||||
|
|
||||||
|
models::apply_orientation(ret[1], ret[0]);
|
||||||
|
models::apply_orientation_yz(ret[2], ret[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case mdCollignon:
|
case mdCollignon:
|
||||||
find_zlev(H_orig.h);
|
find_zlev(H_orig.h);
|
||||||
makeband(H_orig, ret, [] (ld& x, ld& y) {
|
makeband(H_orig, ret, [] (ld& x, ld& y) {
|
||||||
@ -1953,6 +2029,23 @@ EX void draw_boundary(int w) {
|
|||||||
if(elliptic && !among(pmodel, mdBand, mdBandEquidistant, mdBandEquiarea, mdSinusoidal, mdMollweide, mdCollignon))
|
if(elliptic && !among(pmodel, mdBand, mdBandEquidistant, mdBandEquiarea, mdSinusoidal, mdMollweide, mdCollignon))
|
||||||
circle_around_center(M_PI/2, periodcolor, 0, PPR::CIRCLE);
|
circle_around_center(M_PI/2, periodcolor, 0, PPR::CIRCLE);
|
||||||
|
|
||||||
|
int broken_coord = models::get_broken_coord(pmodel);
|
||||||
|
if(broken_coord) {
|
||||||
|
int unbroken_coord = 3 - broken_coord;
|
||||||
|
const ld eps = 1e-3;
|
||||||
|
const ld rem = sqrt(1-eps*eps);
|
||||||
|
for(int s: {-1, 1}) {
|
||||||
|
for(int a=1; a<180; a++) {
|
||||||
|
hyperpoint h = Hypc;
|
||||||
|
h[broken_coord] = -sin_auto(a*degree) * rem;
|
||||||
|
h[0] = sin_auto(a*degree) * eps * s;
|
||||||
|
h[unbroken_coord] = cos_auto(a*degree);
|
||||||
|
curvepoint(h);
|
||||||
|
}
|
||||||
|
queuecurve(shiftless(Id), periodcolor, 0, PPR::CIRCLE).flags |= POLY_FORCEWIDE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch(pmodel) {
|
switch(pmodel) {
|
||||||
|
|
||||||
case mdTwoPoint: {
|
case mdTwoPoint: {
|
||||||
@ -1979,10 +2072,12 @@ EX void draw_boundary(int w) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case mdBand: case mdBandEquidistant: case mdBandEquiarea: case mdSinusoidal: case mdMollweide: case mdCentralCyl: case mdCollignon: {
|
case mdBand: case mdBandEquidistant: case mdBandEquiarea: case mdSinusoidal: case mdMollweide: case mdCentralCyl: case mdCollignon:
|
||||||
|
case mdGallStereographic: case mdMiller:
|
||||||
|
{
|
||||||
if(GDIM == 3) return;
|
if(GDIM == 3) return;
|
||||||
if(pmodel == mdBand && pconf.model_transition != 1) return;
|
if(pmodel == mdBand && pconf.model_transition != 1) return;
|
||||||
bool bndband = (among(pmodel, mdBand, mdCentralCyl) ? hyperbolic : sphere);
|
bool bndband = (among(pmodel, mdBand, mdMiller, mdGallStereographic, mdCentralCyl) ? hyperbolic : sphere);
|
||||||
transmatrix T = spin(-pconf.model_orientation * degree);
|
transmatrix T = spin(-pconf.model_orientation * degree);
|
||||||
ld right = M_PI/2 - 1e-5;
|
ld right = M_PI/2 - 1e-5;
|
||||||
if(bndband)
|
if(bndband)
|
||||||
|
10
models.cpp
10
models.cpp
@ -207,7 +207,15 @@ EX namespace models {
|
|||||||
if(m == mdHorocyclic)
|
if(m == mdHorocyclic)
|
||||||
return hyperbolic;
|
return hyperbolic;
|
||||||
return
|
return
|
||||||
among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic, mdAxial, mdAntiAxial, mdQuadrant) || mdBandAny();
|
among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic, mdAxial, mdAntiAxial, mdQuadrant,
|
||||||
|
mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdWinkelTripel) || mdBandAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief returns the broken coordinate, or zero */
|
||||||
|
EX int get_broken_coord(eModel m) {
|
||||||
|
if(m == mdWerner) return 1;
|
||||||
|
if(sphere) return (mdinf[m].flags & mf::broken) ? 2 : 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EX bool is_perspective(eModel m) {
|
EX bool is_perspective(eModel m) {
|
||||||
|
Loading…
Reference in New Issue
Block a user