From ec82e5695b1325f76077169d1ab2df77923fe47c Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Fri, 12 Jul 2019 23:10:01 +0200 Subject: [PATCH] extra two-point projections --- classes.cpp | 4 +- classes.h | 9 ++++- conformal.cpp | 8 ++-- hypgraph.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 7 deletions(-) diff --git a/classes.cpp b/classes.cpp index ca7c9aeb..b2369b1a 100644 --- a/classes.cpp +++ b/classes.cpp @@ -581,7 +581,7 @@ const modelinfo models[int(mdPolynomial)+1] = { {X3("band equidistant"), mf::band | mf::equidistant | mf::euc_boring}, {X3("band equi-area"), mf::band | mf::equiarea | mf::euc_boring}, {X3("sinusoidal"), mf::quasiband | mf::equiarea | mf::euc_boring}, - {X3("two-point equidistant"), mf::equidistant | mf::euc_boring}, + {X3("two-point equidistant"), mf::equidistant | mf::euc_boring | mf::twopoint}, {X3("fisheye"), 0}, {X3("Joukowsky transform"), mf::hyper_only | mf::conformal}, {X3("Joukowsky+inversion"), mf::hyper_only | mf::conformal}, @@ -590,6 +590,8 @@ const modelinfo models[int(mdPolynomial)+1] = { {X3("native perspective"), 0}, {X3("azimuthal equi-volume"), mf::azimuthal | mf::equivolume | mf::euc_boring}, {X3("central inversion"), mf::azimuthal | mf::conformal}, + {X3("two-point azimuthal"), mf::euc_boring | mf::twopoint}, + {X3("two-point hybrid"), mf::euc_boring | mf::twopoint}, {X3(""), 0}, {X3(""), 0}, {X3(""), 0}, diff --git a/classes.h b/classes.h index a6031b01..ede7b6ae 100644 --- a/classes.h +++ b/classes.h @@ -256,11 +256,17 @@ struct landtacinfo { eLand l; int tries, multiplier; }; enum eModel { mdDisk, mdHalfplane, mdBand, mdPolygonal, mdFormula, + // 5..8. mdEquidistant, mdEquiarea, mdBall, mdHyperboloid, + // 9..13 mdHemisphere, mdBandEquidistant, mdBandEquiarea, mdSinusoidal, mdTwoPoint, + // 14..16 mdFisheye, mdJoukowsky, mdJoukowskyInverted, + // 17..19 mdRotatedHyperboles, mdSpiral, mdPerspective, - mdEquivolume, mdCentralInversion, + // 20..22 + mdEquivolume, mdCentralInversion, mdSimulatedPerspective, mdTwoHybrid, + // 24.. mdGUARD, mdUnchanged, mdHyperboloidFlat, mdPolynomial, mdRug, mdFlatten }; @@ -278,6 +284,7 @@ namespace mf { static const flagtype hyper_or_torus = 256; static const flagtype quasiband = 512; static const flagtype equivolume = 1024; + static const flagtype twopoint = 2048; }; struct modelinfo { diff --git a/conformal.cpp b/conformal.cpp index 50202814..309f6e04 100644 --- a/conformal.cpp +++ b/conformal.cpp @@ -641,7 +641,7 @@ namespace conformal { bool model_has_orientation() { return - among(pmodel, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral) || mdBandAny(); + among(pmodel, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid) || mdBandAny(); } bool model_has_transition() { @@ -752,7 +752,7 @@ namespace conformal { eModel m = eModel(i); if(m == mdFormula && ISMOBILE) continue; if(model_available(m)) { - dialog::addBoolItem(get_model_name(m), pmodel == m, "0123456789!@#$%^&*()]['" [m]); + dialog::addBoolItem(get_model_name(m), pmodel == m, "0123456789!@#$%^&*()][{}'" [m]); dialog::add_action([m] () { if(m == mdFormula) { edit_formula(); @@ -927,10 +927,10 @@ namespace conformal { }); } - if(pmodel == mdTwoPoint) { + if(among(pmodel, mdTwoPoint, mdSimulatedPerspective, mdTwoHybrid)) { dialog::addSelItem(XLAT("parameter"), fts(vid.twopoint_param), 'b'); dialog::add_action([](){ - dialog::editNumber(vid.twopoint_param, 0, 10, .1, 1, XLAT("parameter"), + dialog::editNumber(vid.twopoint_param, 1e-3, 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." diff --git a/hypgraph.cpp b/hypgraph.cpp index 14dd532a..cbfd3c3a 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -257,6 +257,52 @@ hyperpoint mobius(hyperpoint h, ld angle, ld scale = 1) { return space_to_perspective(h, 1) / scale; } +hyperpoint compute_hybrid(hyperpoint H, int rootid) { + auto& t = vid.twopoint_param; + hyperpoint Hl = xpush(+t) * H; + hyperpoint Hr = xpush(-t) * H; + ld g = (Hl[0] + 1e-7) / (Hl[1] + 1e-8); + ld d = hdist0(Hr); + + hyperpoint spinned = spintox(Hl) * xpush0(2*t); + if(Hl[0] < 0) spinned = pispin * spinned; + + ld y = asin_auto(spinned[1]); + ld x = asin_auto_clamp(spinned[0] / cos_auto(y)); + + int sign = (Hl[0] > 0 ? 1 : -1) * hdist0(Hl) < x ? -1 : 1; + + switch(rootid & 3) { + case 1: sign = -sign; break; + case 2: sign = 1; break; + case 3: sign = -1; break; + } + + // (x + t) / g = y + // yy + (x-t)(x-t) = dd + // (x+t)*(x+t)/g*g + x*x + t*t - 2*x*t = dd + + // x*x*(1+1/g*g) + t*t*(1+1/g*g) + 2xt (1/gg-1) = dd + // xx + 2xt (1/gg-1) / (1+1/gg) = dd / (1+1/gg) - tt + + ld b = t*(1/g/g - 1) / (1+1/g/g); + ld c = d*d / (1+1/g/g) - t*t; + + // xx + 2bx = c + // xx + 2bx + bb = c + bb + // (x+b)^2 = c+bb + // x = +/- sqrt(c+bb) - b + + ld a = c+b*b; + + hyperpoint ret; + ret[0] = (a > 0 ? sign * sqrt(a) : 0) - b; + ret[1] = (ret[0] + t) / g; + ret[2] = 0; + + return ret; + } + void applymodel(hyperpoint H, hyperpoint& ret) { using namespace hyperpoint_vec; @@ -436,6 +482,43 @@ void applymodel(hyperpoint H, hyperpoint& ret) { break; } + case mdSimulatedPerspective: { + conformal::apply_orientation_yz(H[1], H[2]); + conformal::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; + 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 + // r.x * lyx + t * lyx = r.x * ryx - t * ryx + // r.x * (lyx-ryx) = - t * (ryx + lyx) + // 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[2] = 0; + + move_y_to_z(ret, yz); + conformal::apply_orientation(ret[0], ret[1]); + conformal::apply_orientation_yz(ret[2], ret[1]); + break; + } + + case mdTwoHybrid: { + conformal::apply_orientation_yz(H[1], H[2]); + conformal::apply_orientation(H[0], H[1]); + auto yz = move_z_to_y(H); + + ret = compute_hybrid(H, whateveri[0]); + + move_y_to_z(ret, yz); + conformal::apply_orientation(ret[0], ret[1]); + conformal::apply_orientation_yz(ret[2], ret[1]); + break; + } + case mdJoukowsky: case mdJoukowskyInverted: { conformal::apply_orientation_yz(H[1], H[2]); @@ -1394,8 +1477,33 @@ void draw_model_elements() { queuechr(current_display->xcenter, current_display->ycenter + current_display->radius * vid.alpha, 0, vid.fsize, 'X', ringcolor, 1, 8); return; } + + case mdTwoHybrid: { + queuereset(mdUnchanged, PPR::CIRCLE); + + for(int mode=0; mode<4; mode++) { + for(int s=-199; s<=199; s += 2) { + ld p = s / 200.; + ld a = vid.twopoint_param * (1+p); + ld b = vid.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 res = compute_hybrid(H, 2 | mode); + conformal::apply_orientation(res[0], res[1]); + conformal::apply_orientation_yz(res[2], res[1]); + using namespace hyperpoint_vec; + curvepoint(res * current_display->radius); + } + queuecurve(ringcolor, 0, PPR::CIRCLE); + } - case mdTwoPoint: { + queuereset(pmodel, PPR::CIRCLE); + /* fallthrough */ + } + + case mdTwoPoint: case mdSimulatedPerspective: { ld a = -conformal::model_orientation * degree; queuechr(xspinpush0(a, +vid.twopoint_param), vid.xres / 100, 'X', ringcolor >> 8); queuechr(xspinpush0(a, -vid.twopoint_param), vid.xres / 100, 'X', ringcolor >> 8);