diff --git a/classes.cpp b/classes.cpp index d2cfcf50..b9516975 100644 --- a/classes.cpp +++ b/classes.cpp @@ -1030,7 +1030,7 @@ EX vector mdinf = { {X3("azimuthal equi-area"), mf::azimuthal | mf::equiarea | mf::euc_boring, DEFAULTS}, {X3("ball model"), mf::conformal | mf::azimuthal | mf::space, DEFAULTS}, {"Minkowski hyperboloid", "plane", "sphere", mf::conformal | mf::space | mf::euc_boring, DEFAULTS}, - {"hemisphere", "sphere", "sphere", mf::conformal | mf::space, DEFAULTS}, + {"hemisphere", "sphere", "Minkowski hyperboloid", mf::conformal | mf::space, DEFAULTS}, {X3("band equidistant"), mf::band | mf::equidistant | mf::euc_boring, DEFAULTS}, {X3("band equi-area"), mf::band | mf::equiarea | mf::euc_boring, DEFAULTS}, {X3("sinusoidal"), mf::pseudoband | mf::equiarea | mf::euc_boring, DEFAULTS}, diff --git a/drawing.cpp b/drawing.cpp index acc848e7..2300318a 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -304,7 +304,7 @@ EX bool two_sided_model() { #endif if(GDIM == 3) return false; if(in_vr_sphere) return true; - if(pmodel == mdHyperboloid) return !euclid && !in_vr; + if(models::is_hyperboloid(pmodel)) return !euclid && !in_vr; // if(pmodel == mdHemisphere) return true; if(pmodel == mdDisk) return sphere || (hyperbolic && pconf.alpha < 0 && pconf.alpha > -1); if(pmodel == mdRetroLittrow) return sphere; @@ -348,11 +348,21 @@ EX int get_side(const hyperpoint& H) { return (models::sin_ball * H[2] > -models::cos_ball * H[1]) ? -1 : 1; if(pmodel == mdHyperboloid && sphere) return (models::sin_ball * H[2] > models::cos_ball * H[1]) ? -1 : 1; - if(pmodel == mdHemisphere) { + if(pmodel == mdHyperboloidFlat && sphere) + return H[2] >= 0 ? 1 : -1; + if(pmodel == mdHemisphere && hyperbolic) { hyperpoint res; applymodel(shiftless(H), res); return res[2] < 0 ? -1 : 1; } + if(pmodel == mdHemisphere && sphere) { + auto H1 = H; + int s = H1[2] > 0 ? 1 : -1; + if(hemi_side && s != hemi_side) return -spherespecial; + H1[0] /= H1[2]; H1[1] /= H1[2]; + H1[2] = s * sqrt(1 + H1[0]*H1[0] + H1[1] * H1[1]); + return (models::sin_ball * H1[2] > models::cos_ball * H1[1]) ? 1 : -1; + } if(pmodel == mdSpiral && pconf.spiral_cone < 360) { return cone_side(shiftless(H)); } @@ -2283,6 +2293,8 @@ EX void set_vr_sphere() { #endif } +EX int hemi_side = 0; + EX void draw_main() { DEBBI(DF_GRAPH, ("draw_main")); @@ -2296,6 +2308,24 @@ EX void draw_main() { return; } + if(pmodel == mdHemisphere && sphere && hemi_side == 0 && !vrhr::rendering()) { + hemi_side = models::sin_ball > 0 ? 1 : -1; + draw_main(); + + if(pconf.show_hyperboloid_flat) { + dynamicval dv (pmodel, mdHyperboloidFlat); + dynamicval ds (spherespecial, 1); + for(auto& ptd: ptds) + if(!among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE)) + ptd->draw(); + } + + hemi_side *= -1; + draw_main(); + hemi_side = 0; + return; + } + set_vr_sphere(); if(sphere && GDIM == 3 && pmodel == mdPerspective && !stretch::in() && !ray::in_use) { diff --git a/hypgraph.cpp b/hypgraph.cpp index f8f69fa6..55a35f2c 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -778,11 +778,23 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { case gcSphere: { if(vrhr::rendering()) { vr_sphere(ret, H, md); return; } + ld z = sqhypot_d(3, H); + int s = H[2] > 0 ? 1 : -1; ret = H; + ret /= ret[2]; + ret[2] = sqrt(1 + ret[0]*ret[0] + ret[1]*ret[1]) * s; + ret *= z; + ld& topz = pconf.top_z; + if(abs(ret[2]) > topz || (hemi_side && s != hemi_side)) { + ld scale = sqrt(topz*topz-1) / hypot_d(2, ret); + ret *= scale; + ret[2] = topz * s; + } if(pconf.depth_scaling != 1) { ld v = intval(H, Hypc); ret *= pow(v, (dir * pconf.depth_scaling-1) / 2); } + ret /= 3; break; } } @@ -823,27 +835,35 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) { } #endif + ret = H; + + if(sphere && pmodel == mdHyperboloidFlat) { + int s = H[2] > 0 ? 1 : -1; + ret /= ret[2]; + ret[2] = sqrt(1 + ret[0]*ret[0] + ret[1]*ret[1]) * s; + } + if(pconf.depth_scaling != 1) { - ld v = intval(H, Hypc); - H *= pow(v, (pconf.depth_scaling-1) / 2); + ld v = intval(ret, Hypc); + ret *= pow(v, (pconf.depth_scaling-1) / 2); } if(pmodel == mdHyperboloid) { ld& topz = pconf.top_z; - if(H[2] > topz) { - ld scale = sqrt(topz*topz-1) / hypot_d(2, H); - H *= scale; - H[2] = topz; + if(ret[2] > topz) { + ld scale = sqrt(topz*topz-1) / hypot_d(2, ret); + ret *= scale; + ret[2] = topz; } } else { - H = space_to_perspective(H, pconf.alpha); - H[2] = 1 - pconf.alpha; + ret = space_to_perspective(ret, pconf.alpha); + ret[2] = 1 - pconf.alpha; + if(sphere) ret[2] = -ret[2]; } - ret[0] = H[0] / 3; - ret[1] = (1 - H[2]) / 3; - ret[2] = H[1] / 3; + ret[0] = ret[0] / 3; + tie(ret[1], ret[2]) = make_pair(((sphere?0:1) - ret[2]) / 3, ret[1] / 3); models::apply_ball(ret[2], ret[1]); break; @@ -2276,7 +2296,7 @@ EX color_t modelcolor = 0; EX void draw_model_elements() { #if CAP_VR - if(vrhr::active() && pmodel == mdHyperboloid) return; + if(vrhr::active() && is_hyperboloid(pmodel)) return; #endif dynamicval lw(vid.linewidth, vid.linewidth * vid.multiplier_ring); @@ -2339,9 +2359,10 @@ EX void draw_model_elements() { return; } - case mdHyperboloid: { + case mdHyperboloid: + case mdHemisphere: { if(!pconf.show_hyperboloid_flat) return; - if(hyperbolic) { + if(models::is_hyperboloid(pmodel)) { #if CAP_QUEUE curvepoint(point3(0,0,1)); curvepoint(point3(0,0,-pconf.alpha)); @@ -2550,7 +2571,7 @@ EX void draw_boundary(int w) { queuecurve(shiftless(Id), lc, fc, p); queuereset(pmodel, p); } - if(euclid || sphere) { + if(euclid) { queuereset(mdPixel, p); for(int i=0; i<=360; i++) { curvepoint(point3(current_display->radius * cos(i * degree), current_display->radius * sin(i * degree), 0)); @@ -2558,13 +2579,15 @@ EX void draw_boundary(int w) { queuecurve(shiftless(Id), lc, fc, p); queuereset(pmodel, p); } + if(sphere) goto as_hyperboloid; return; } case mdHyperboloid: { if(hyperbolic) { + as_hyperboloid: ld& tz = pconf.top_z; - ld mz = acosh(tz); + ld mz = sphere ? atan(sqrt(tz*tz-1)) : acosh(tz); ld cb = models::cos_ball; ld sb = models::sin_ball; @@ -2609,6 +2632,13 @@ EX void draw_boundary(int w) { curvepoint(xspinpush0(t * degree, mz)); queuecurve(shiftless(Id), lc, fc, p); + + if(sphere) { + for(ld t=0; t<=360; t ++) + curvepoint(xspinpush0(t * degree, M_PI-mz)); + + queuecurve(shiftless(Id), lc, fc, p); + } } return; } diff --git a/models.cpp b/models.cpp index 099afbbf..fd843e6c 100644 --- a/models.cpp +++ b/models.cpp @@ -222,6 +222,10 @@ EX namespace models { if(sphere) return (mdinf[m].flags & mf::broken) ? 2 : 0; return 0; } + + EX bool is_hyperboloid(eModel m) { + return m == (sphere ? mdHemisphere : mdHyperboloid); + } EX bool is_perspective(eModel m) { return among(m, mdPerspective, mdGeodesic); @@ -592,7 +596,7 @@ EX namespace models { }); } - if(vpmodel == mdHyperboloid) + if(is_hyperboloid(vpmodel)) add_edit(vpconf.top_z); if(has_transition(vpmodel)) @@ -613,7 +617,7 @@ EX namespace models { if(vpmodel == mdFisheye) add_edit(vpconf.fisheye_param); - if(vpmodel == mdHyperboloid) + if(is_hyperboloid(vpmodel)) add_edit(pconf.show_hyperboloid_flat); if(vpmodel == mdCollignon)