diff --git a/conformal.cpp b/conformal.cpp index acfd3a34..ad8cda37 100644 --- a/conformal.cpp +++ b/conformal.cpp @@ -391,6 +391,10 @@ namespace conformal { int spiral_id = 7; cld spiral_multiplier; + ld right_spiral_multiplier = 1; + ld spiral_cone = 360; + ld spiral_cone_rad; + bool ring_not_spiral; void configure() { ld ball = -vid.ballangle * degree; @@ -404,7 +408,15 @@ namespace conformal { ld b = spiral_angle * degree; ld cos_spiral = cos(b); ld sin_spiral = sin(b); - spiral_multiplier = cld(cos_spiral, sin_spiral) * M_PI * cos_spiral; + spiral_cone_rad = spiral_cone * degree; + ring_not_spiral = abs(cos_spiral) < 1e-3; + if(ring_not_spiral) { + cos_spiral = 0; + sin_spiral = 1; + spiral_multiplier = cld(0, right_spiral_multiplier * spiral_cone_rad / 2); + } + else + spiral_multiplier = cld(cos_spiral, sin_spiral) * cld(spiral_cone_rad * cos_spiral / 2., 0); } if(euclid) { hyperpoint h = tC0(eumove(spiral_x, spiral_y)); @@ -756,7 +768,7 @@ namespace conformal { }); } - if(pmodel == mdBall || pmodel == mdHyperboloid || pmodel == mdHemisphere) { + if(pmodel == mdBall || pmodel == mdHyperboloid || pmodel == mdHemisphere || (pmodel == mdSpiral && spiral_cone != 360)) { dialog::addSelItem(XLAT("camera rotation in 3D models"), fts3(vid.ballangle), 'b'); dialog::add_action(config_camera_rotation); } @@ -811,6 +823,18 @@ namespace conformal { dialog::add_action([](){ dialog::editNumber(spiral_angle, 0, 360, 15, 0, XLAT("spiral angle"), ""); }); + + if(ring_not_spiral) { + dialog::addSelItem(XLAT("spiral multiplier"), fts(right_spiral_multiplier), 'M'); + dialog::add_action([](){ + dialog::editNumber(right_spiral_multiplier, 0, 10, -.1, 1, XLAT("spiral multiplier"), ""); + }); + } + + dialog::addSelItem(XLAT("spiral cone"), fts(spiral_cone), 'C'); + dialog::add_action([](){ + dialog::editNumber(spiral_cone, 0, 360, -45, 360, XLAT("spiral cone"), ""); + }); } if(pmodel == mdSpiral && euclid) { @@ -1097,6 +1121,13 @@ namespace conformal { else if(argis("-sang")) { PHASEFROM(2); shift_arg_formula(conformal::spiral_angle); + if(conformal::spiral_angle == 90) { + shift_arg_formula(conformal::right_spiral_multiplier); + } + } + else if(argis("-scone")) { + PHASEFROM(2); + shift_arg_formula(conformal::spiral_cone); } else if(argis("-sxy")) { PHASEFROM(2); diff --git a/hyper.h b/hyper.h index 7b122325..33297b8c 100644 --- a/hyper.h +++ b/hyper.h @@ -1331,6 +1331,7 @@ namespace conformal { extern ld model_transition; extern ld top_z; extern ld spiral_angle, spiral_x, spiral_y; + extern ld spiral_cone; // screen coordinates to logical coordinates: apply_orientation(x,y) // logical coordinates back to screen coordinates: apply_orientation(y,x) @@ -4620,6 +4621,8 @@ void show_color_dialog(); extern ld band_shift; +int cone_side(const hyperpoint H); + void fix_the_band(transmatrix& T); struct bandfixer { diff --git a/hypgraph.cpp b/hypgraph.cpp index 3053b239..ab01775c 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -569,12 +569,26 @@ void applymodel(hyperpoint H, hyperpoint& ret) { if(hyperbolic) makeband(H, ret, band_conformal); else ret = H; z = cld(ret[0], ret[1]) * conformal::spiral_multiplier; - z = exp(z); - ret[0] = real(z); - ret[1] = imag(z); + + if(conformal::spiral_cone < 360) { + ld alpha = imag(z) * 360 / conformal::spiral_cone; + ld r = real(z); + r = exp(r); + + ret[0] = -sin(alpha) * r; + ret[1] = cos(alpha) * r; + ret[2] = (r-1) * sqrt( pow(360/conformal::spiral_cone, 2) - 1); + + conformal::apply_ball(ret[2], ret[1]); + } + else { + z = exp(z); + ret[0] = real(z); + ret[1] = imag(z); - if(vid.skiprope) - ret = mobius(ret, vid.skiprope, 1); + if(vid.skiprope) + ret = mobius(ret, vid.skiprope, 1); + } } case mdGUARD: break; @@ -1502,6 +1516,14 @@ bool do_draw(cell *c, const transmatrix& T) { ld iz = imag(z) + 1.14279e-2; // make it never fall exactly on PI if(iz < -M_PI || iz >= M_PI) return false; } + if(hyperbolic && pmodel == mdSpiral && conformal::ring_not_spiral) { + cld z; + hyperpoint H = tC0(T); + hyperpoint ret; + makeband(H, ret, band_conformal); + z = cld(ret[0], ret[1]) * conformal::spiral_multiplier; + if(imag(z) < -conformal::spiral_cone_rad/2-1e-5 || imag(z) >= conformal::spiral_cone_rad/2-1e-5) return false; + } if(cells_drawn > vid.cells_drawn_limit) return false; bool usr = vid.use_smart_range || quotient || euwrap; if(usr && cells_drawn >= 50 && !in_smart_range(T)) return false; @@ -1509,4 +1531,32 @@ bool do_draw(cell *c, const transmatrix& T) { return true; } +int cone_side(const hyperpoint H) { + hyperpoint ret; + if(hyperbolic) makeband(H, ret, band_conformal); + else ret = H; + cld z = cld(ret[0], ret[1]) * conformal::spiral_multiplier; + + auto zth = [&] (cld z) { + ld alpha = imag(z) * 360 / conformal::spiral_cone; + ld r = real(z); + r = exp(r); + + hyperpoint ret; + + ret[0] = -sin(alpha) * r; + ret[1] = cos(alpha) * r; + ret[2] = (r-1) * sqrt( pow(360/conformal::spiral_cone, 2) - 1); + + conformal::apply_ball(ret[2], ret[1]); + return ret; + }; + + hyperpoint ret0 = zth(z); + hyperpoint ret1 = zth(z + cld(1e-3, 0)); + hyperpoint ret2 = zth(z + cld(0, 1e-3)); + + return (ret1[1] - ret0[1]) * (ret2[0] - ret0[0]) < (ret2[1] - ret0[1]) * (ret1[0] - ret0[0]) ? 1 : -1; + } + } diff --git a/polygons.cpp b/polygons.cpp index eddb2272..ca94f6a8 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -198,6 +198,7 @@ bool two_sided_model() { if(pmodel == mdDisk) return sphere; if(pmodel == mdHemisphere) return true; if(pmodel == mdRotatedHyperboles) return true; + if(pmodel == mdSpiral && conformal::spiral_cone < 360) return true; return false; } @@ -219,6 +220,9 @@ int get_side(const hyperpoint& H) { applymodel(H, res); return res[2] < 0 ? -1 : 1; } + if(pmodel == mdSpiral && conformal::spiral_cone < 360) { + return cone_side(H); + } return 0; }