diff --git a/basegraph.cpp b/basegraph.cpp index f06a2442..7836c3e7 100644 --- a/basegraph.cpp +++ b/basegraph.cpp @@ -255,7 +255,7 @@ void stereo::set_viewport(int ed) { } bool model_needs_depth() { - return pmodel == mdBall || pmodel == mdHyperboloid || pmodel == mdHemisphere; + return pmodel == mdBall || pmodel == mdHemisphere; } void setGLProjection(color_t col) { diff --git a/conformal.cpp b/conformal.cpp index ee9e6499..ea5b5d3f 100644 --- a/conformal.cpp +++ b/conformal.cpp @@ -297,6 +297,7 @@ namespace conformal { ld model_orientation, halfplane_scale; ld ocos, osin; bool model_straight; + ld top_z = 5; bool autoband = false; bool autobandhistory = false; @@ -658,6 +659,9 @@ namespace conformal { dialog::addSelItem(XLAT("camera rotation in 3D models"), fts3(vid.ballangle), 'b'); } + if(pmodel == mdHyperboloid) + dialog::addSelItem(XLAT("topz"), fts3(top_z), 'l'); + if(pmodel == mdHemisphere && euclid) { dialog::addSelItem(XLAT("parameter"), fts3(vid.euclid_to_sphere), 'l'); } @@ -710,6 +714,8 @@ namespace conformal { #endif else if(uni == 'l' && model_has_orientation()) dialog::editNumber(model_orientation, 0, 360, 90, 0, XLAT("model orientation"), ""); + else if(uni == 'l' && pmodel == mdHyperboloid) + dialog::editNumber(top_z, 1, 20, 0.25, 4, XLAT("topz"), ""); else if(uni == 'b' && pmodel == mdHalfplane) dialog::editNumber(model_orientation, 0, 2, 0.25, 1, XLAT("halfplane scale"), ""); else if(uni == 's') { diff --git a/graph.cpp b/graph.cpp index 8789c4f4..f575a838 100644 --- a/graph.cpp +++ b/graph.cpp @@ -5562,15 +5562,43 @@ void drawfullmap() { if(pmodel == mdBall) ballgeometry(); } - if(pmodel == mdHyperboloid) { + if(pmodel == mdHyperboloid && hyperbolic) { #if CAP_QUEUE - color_t col = darkena(0x80, 0, 0x80); - queueline(hpxyz(0,0,1), hpxyz(0,0,-vid.alpha), col, 0, PPR::CIRCLE); - queueline(xpush0(+4), hpxyz(0,0,0), col, 0, PPR::CIRCLE); - queueline(xpush0(+4), hpxyz(0,0,-vid.alpha), col, 0, PPR::CIRCLE); - queueline(xpush0(-4), hpxyz(0,0,0), col, 0, PPR::CIRCLE); - queueline(xpush0(-4), hpxyz(0,0,-vid.alpha), col, 0, PPR::CIRCLE); - queueline(hpxyz(-1,0,0), hpxyz(1,0,0), col, 0, PPR::CIRCLE); + curvepoint(hpxyz(0,0,1)); + curvepoint(hpxyz(0,0,-vid.alpha)); + queuecurve(ringcolor, 0, PPR::CIRCLE); + + ld& tz = conformal::top_z; + ld z = acosh(tz); + + hyperpoint a = xpush0(z); + ld ball = -vid.ballangle * M_PI / 180; + ld cb = cos(ball), sb = sin(ball); + + a[1] = sb * a[2] / -cb; + a[0] = sqrt(-1 + a[2] * a[2] - a[1] * a[1]); + + curvepoint(hpxyz(0,0,-vid.alpha)); + curvepoint(a); + curvepoint(hpxyz(0,0,0)); + a[0] = -a[0]; + curvepoint(a); + curvepoint(hpxyz(0,0,-vid.alpha)); + queuecurve(ringcolor, 0, PPR::CIRCLE); + + curvepoint(hpxyz(-1,0,0)); + curvepoint(hpxyz(1,0,0)); + queuecurve(ringcolor, 0, PPR::CIRCLE); + + a[1] = sb * tz / -cb; + a[0] = sqrt(tz * tz - a[1] * a[1]); + a[2] = tz - vid.alpha; + + curvepoint(a); + curvepoint(hpxyz(0,0,-vid.alpha)); + a[0] = -a[0]; + curvepoint(a); + queuecurve(ringcolor, 0, PPR::CIRCLE); #endif } diff --git a/hyper.h b/hyper.h index f30ffe9c..12be8514 100644 --- a/hyper.h +++ b/hyper.h @@ -1265,7 +1265,7 @@ enum eModel { mdEquidistant, mdEquiarea, mdBall, mdHyperboloid, mdHemisphere, mdBandEquidistant, mdBandEquiarea, mdSinusoidal, mdTwoPoint, mdFisheye, - mdGUARD, mdUnchanged }; + mdGUARD, mdUnchanged, mdHyperboloidFlat }; namespace conformal { extern bool on; @@ -1280,6 +1280,7 @@ namespace conformal { extern ld halfplane_scale; extern ld ocos, osin; extern bool model_straight; + extern ld top_z; // screen coordinates to logical coordinates: apply_orientation(x,y) // logical coordinates back to screen coordinates: apply_orientation(y,x) diff --git a/hypgraph.cpp b/hypgraph.cpp index f8032616..280d0e2d 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -192,7 +192,31 @@ void applymodel(hyperpoint H, hyperpoint& ret) { return; } + if(pmodel == mdHyperboloidFlat) { + H[2] += vid.alpha; + H[0] /= H[2]; + H[1] /= H[2]; + H[2] = 1 - vid.alpha; + + ld ball = -vid.ballangle * M_PI / 180; + ld cb = cos(ball), sb = sin(ball); + + ret[0] = H[0] / 3; + ret[1] = (1 - H[2]) / 3 * cb - H[1] / 3 * sb; + ret[2] = -(-H[1] / 3 * cb - (1 - H[2]) / 3 * sb); + + ghcheck(ret,H); + return; + } + if(pmodel == mdHyperboloid) { + ld& tz = conformal::top_z; + if(H[2] > tz) { + ld scale = sqrt(tz*tz-1) / hypot(H[0], H[1]); + H[0] *= scale; + H[1] *= scale; + H[2] = tz; + } ld ball = -vid.ballangle * M_PI / 180; ld cb = cos(ball), sb = sin(ball); diff --git a/polygons.cpp b/polygons.cpp index ef1e9b40..d69cd9a2 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -183,10 +183,27 @@ bool knowgood; hyperpoint goodpoint; vector> tofix; +bool two_sided_model() { + if(pmodel == mdHyperboloid) return !euclid; + // if(pmodel == mdHemisphere) return true; + if(pmodel == mdDisk) return sphere; + return false; + } + bool correct_side(const hyperpoint& H) { - double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; - double horizon = curnorm / vid.alpha; - return (spherespecial>0) ^ (H[2] <= -horizon); + if(pmodel == mdDisk && sphere) { + double curnorm = H[0]*H[0]+H[1]*H[1]+H[2]*H[2]; + double horizon = curnorm / vid.alpha; + return (spherespecial>0) ^ (H[2] <= -horizon); + } + if(pmodel == mdHyperboloid && hyperbolic) { + ld ball = -vid.ballangle * M_PI / 180; + ld cb = cos(ball), sb = sin(ball); + return (spherespecial > 0) ^ ( + sb * H[2] > -cb * H[1] + ); + } + return true; } void fixpoint(array& hscr, hyperpoint H) { @@ -215,7 +232,7 @@ void addpoint(const hyperpoint& H) { poly_flags |= POLY_INFRONT, last_infront = false; if(!knowgood || (spherespecial > 0 ? H[2]>goodpoint[2] : H[2] ss(spherespecial, 0); draw(); return; - } + } */ #if CAP_GL if(vid.usingGL && using_perspective) { @@ -867,7 +884,7 @@ void dqi_poly::draw() { if(poly_flags & POLY_NIF_ERROR) return; - if(spherespecial == 1 && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && vid.alpha <= 1) { + if(spherespecial == 1 && sphere && (poly_flags & POLY_INFRONT) && (poly_flags & POLY_NOTINFRONT) && vid.alpha <= 1) { bool around_center = false; for(int i=0; i 0 || (sphere && equi)) && !(poly_flags & POLY_ISSIDE)) { + if(sphere && (spherespecial > 0 || equi) && !(poly_flags & POLY_ISSIDE)) { if(!tinf) { @@ -1314,7 +1331,14 @@ void drawqueue() { stereo::set_projection(0); // on the sphere, parts on the back are drawn first - if(sphere && pmodel == 0) { + if(two_sided_model()) { + + if(pmodel == mdHyperboloid) { + dynamicval dv(pmodel, mdHyperboloidFlat); + for(auto& ptd: ptds) + if(!among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE)) + ptd->draw(); + } // in SVG, draw boundary circle first if(svg::in) for(auto& ptd: ptds) ptd->draw_pre(); @@ -1324,7 +1348,9 @@ void drawqueue() { reverse_side_priorities(); for(int i=ptds.size()-1; i>=0; i--) - ptds[i]->draw_back(); + if(!among(ptds[i]->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE)) + ptds[i]->draw_back(); + glflush(); reverse_side_priorities(); spherespecial *= -1; @@ -1332,9 +1358,12 @@ void drawqueue() { stereo::set_projection(0); } - for(auto& ptd: ptds) ptd->draw(); + for(auto& ptd: ptds) { + dynamicval ss(spherespecial, among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE) ? 0 : spherespecial); + ptd->draw(); + } glflush(); - + #if CAP_GL if(vid.usingGL) stereo::set_projection(0), stereo::set_mask(0), stereo::set_viewport(0);