diff --git a/arbitrile.cpp b/arbitrile.cpp index 9124d180..f843f15c 100644 --- a/arbitrile.cpp +++ b/arbitrile.cpp @@ -1213,9 +1213,8 @@ struct hrmap_arbi : hrmap { hyperpoint get_corner(cell *c, int cid, ld cf) override { auto& sh = arb::current_or_slided().shapes[arb::id_of(c->master)]; int id = gmod(cid, c->type); - if(sh.angles[id] == 0) { - return normalize(C0 + 999 * kleinize(sh.vertices[id])); - } + if(sh.angles[id] <= 0) + return sh.vertices[id]; return normalize(C0 + (sh.vertices[id] - C0) * 3 / cf); } @@ -1611,11 +1610,11 @@ EX void choose() { }); } -EX pair rep_ideal(ld e) { +EX pair rep_ideal(ld e, ld u IS(1)) { ld alpha = 2 * M_PI / e; - hyperpoint h1 = point3(cos(alpha), -sin(alpha), 1); - hyperpoint h2 = point3(1, 0, 1); - hyperpoint h3 = point3(cos(alpha), sin(alpha), 1); + hyperpoint h1 = point3(cos(alpha)*u, -sin(alpha)*u, 1); + hyperpoint h2 = point3(u, 0, 1); + hyperpoint h3 = point3(cos(alpha)*u, sin(alpha)*u, 1); hyperpoint h12 = mid(h1, h2); hyperpoint h23 = mid(h2, h3); ld len = hdist(h12, h23); diff --git a/floorshapes.cpp b/floorshapes.cpp index cf332c14..fa179c2f 100644 --- a/floorshapes.cpp +++ b/floorshapes.cpp @@ -244,7 +244,7 @@ void geometry_information::bshape2(hpcshape& sh, PPR prio, int shapeid, matrixli /* in case of apeirogonal shapes, we may need to cyclically rotate */ bool apeirogonal = false; - vector backup; + vector tail, head; for(int r=0; rs; is); - first = true; + apeirogonal = true; + for(auto h: head) tail.push_back(h); + head.clear(); } - if(!invalid) hpcpush(mid(nh, nh)); + if(!invalid) head.push_back(nh); } } - for(auto& b: backup) hpcpush(b); - if(!apeirogonal) hpcpush(hpc[last->s]); + for(auto& h: head) hpcpush(h); + for(auto& h: tail) hpcpush(h); + if(!apeirogonal) hpcpush(starting_point); } template void sizeto(T& t, int n) { @@ -353,29 +353,8 @@ namespace irr { void generate_floorshapes(); } void geometry_information::finish_apeirogon(hyperpoint center) { last->flags |= POLY_APEIROGONAL; last->she = isize(hpc); - hyperpoint b = hpc.back(); - if(material(-1e-9 * C0 + center) > 0) { - hpcpush(normalize(center)); - } - else { - for(int i=1; i<15; i++) hpcpush(towards_inf(b, center, i)); - hyperpoint h = 1e-9 * C0 + center; - if(material(h) > 0) - hpcpush(normalize(h)); - else { - ld left = -atan2(towards_inf(b, center, 15)); - ld right = -atan2(towards_inf(hpc[last->s], center, 15)); - if(right > left + 180*degree) right -= 360*degree; - if(right < left - 180*degree) right += 360*degree; - hyperpoint l1 = towards_inf(b, center, 15); l1 /= l1[2]; - hyperpoint l2 = xspinpush0(left, 15); l2 /= l2[2]; - println(hlog, "doing that ", left, "..", right); - /* call hpc.push_back directly to avoid adding points */ - for(int i=0; i<=10; i++) hpc.push_back(xspinpush0(lerp(left, right, i/10.), 15)); - } - for(int i=15; i>=1; i--) hpcpush(towards_inf(hpc[last->s], center, i)); - } - hpcpush(hpc[last->s]); + hpcpush(center); + hpcpush(starting_point); } // !siid equals pseudohept(c) @@ -648,11 +627,11 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i if(i != isize(m.v)) printf("warning: i=%d sm=%d\n", i, isize(m.v)); bshape2((ii?fsh.shadow:fsh.b)[id], fsh.prio, (fsh.shapeid2 && geosupport_football() < 2) ? fsh.shapeid2 : siid?fsh.shapeid0:fsh.shapeid1, m); - if(apeirogonal) { + if(apeirogonal && !first) { int id = arb::id_of(c->master); auto &ac = arb::current_or_slided(); auto& sh = ac.shapes[id]; - hpcpush(arb::get_adj(arb::current_or_slided(), id, cor-2, id, cor-1) * hpc[last->s]); + hpcpush(arb::get_adj(arb::current_or_slided(), id, cor-2, id, cor-1) * starting_point); finish_apeirogon(sh.vertices.back()); } } diff --git a/geometry.cpp b/geometry.cpp index e75d406d..0252fdfb 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -380,8 +380,20 @@ hpcshape ld dlow_table[SIDEPARS], dhi_table[SIDEPARS], dfloor_table[SIDEPARS]; int prehpc; + /** list of points in all shapes */ vector hpc; + /** what shape are we currently creating */ + hpcshape *last; + /** is the current shape already started? first = not yet */ bool first; + /** starting point of the current shape, can be ultraideal */ + hyperpoint starting_point; + /** first ideal point of the current shape */ + hyperpoint starting_ideal; + /** last added point of the current shape, can be ultraideal */ + hyperpoint last_point; + /** last ideal point of the current shape */ + hyperpoint last_ideal; bool validsidepar[SIDEPARS]; @@ -389,7 +401,6 @@ hpcshape #endif hpcshape shFullCross[2]; - hpcshape *last; int SD3, SD6, SD7, S12, S14, S21, S28, S42, S36, S84; @@ -422,6 +433,7 @@ hpcshape void prepare_usershapes(); void hpcpush(hyperpoint h); + void hpc_connect_ideal(hyperpoint a, hyperpoint b); void hpcsquare(hyperpoint h1, hyperpoint h2, hyperpoint h3, hyperpoint h4); void chasmifyPoly(double fac, double fac2, int k); void shift(hpcshape& sh, double dx, double dy, double dz); diff --git a/hyperpoint.cpp b/hyperpoint.cpp index 32177cad..0f4e671a 100644 --- a/hyperpoint.cpp +++ b/hyperpoint.cpp @@ -472,6 +472,40 @@ EX ld material(const hyperpoint& h) { else return h[LDIM]; } +EX int safe_classify_ideals(hyperpoint h) { + if(hyperbolic || in_h2xe()) { + h /= h[LDIM]; + ld x = MDIM == 3 ? 1 - (h[0] * h[0] + h[1] * h[1]) : 1 - (h[0] * h[0] + h[1] * h[1] + h[2] * h[2]); + if(x > 1e-6) return 1; + if(x < -1e-6) return -1; + return 0; + } + return 1; + } + +EX ld ideal_limit = 10; +EX ld ideal_each = degree; + +EX hyperpoint safe_approximation_of_ideal(hyperpoint h) { + return towards_inf(C0, h, ideal_limit); + } + +/** the point on the line ab which is closest to zero. Might not be normalized. Works even if a and b are (ultra)ideal */ +EX hyperpoint closest_to_zero(hyperpoint a, hyperpoint b) { + if(sqhypot_d(MDIM, a-b) < 1e-9) return a; + if(isnan(a[0])) return a; + a /= a[LDIM]; + b /= b[LDIM]; + ld mul_a = 0, mul_b = 0; + for(int i=0; i left + 180*degree) right -= 360*degree; + if(right < left - 180*degree) right += 360*degree; + /* call hpc.push_back directly to avoid adding points */ + ld qty = ceil(abs(right-left) / ideal_each); + for(int i=0; i<=qty; i++) hpc.push_back(xspinpush0(lerp(left, right, i/qty), ideal_limit)); + } + void geometry_information::hpcpush(hyperpoint h) { - if(sphere) h = mid(h,h); + + if(GDIM == 3 || (last->flags & POLY_TRIANGLES)) { + hpc.push_back(h); + return; + } + ld error_threshold = euclid ? 1000 : 1e10; - ld threshold = (GDIM == 3 || last->flags & POLY_TRIANGLES) ? error_threshold : (sphere ? (ISMOBWEB || NONSTDVAR ? .04 : .001) : 0.1) * pow(.25, vid.linequality); - if(/*vid.usingGL && */!first) { - ld i = intval(hpc.back(), h); - if(i > threshold && i < error_threshold) { - hyperpoint md = mid(hpc.back(), h); - hpcpush(md); - hpcpush(h); - return; + ld threshold = (sphere ? (ISMOBWEB || NONSTDVAR ? .04 : .001) : 0.1) * pow(.25, vid.linequality); + + if(first) { + starting_ideal = starting_point = last_point = last_ideal = h; + first = false; + int c = safe_classify_ideals(h); + if(c == 0) { + starting_ideal = safe_approximation_of_ideal(h); + hpc.push_back(starting_ideal); + } + else if(c > 0) { + hpc.push_back(normalize(h)); + } + } + else { + int c1 = safe_classify_ideals(last_point); + int c2 = safe_classify_ideals(h); + if(c1 > 0 && c2 > 0) { + h = normalize(h); + ld i = intval(last_point, h); + if(i > threshold && i < error_threshold) { + hyperpoint md = mid(hpc.back(), h); + hpcpush(md); + hpcpush(h); + return; + } + else { + hpc.push_back(h); + last_point = last_ideal = h; + } + } + else if(c1 > 0 && c2 <= 0) { + for(ld t = threshold; t < ideal_limit; t += threshold) hpc.push_back(last_ideal = towards_inf(last_point, h, t)); + last_point = h; + } + else if(c1 <= 0 && c2 > 0) { + hyperpoint next_ideal = towards_inf(h, last_point, ideal_limit); + hpc_connect_ideal(last_ideal, next_ideal); + ld t = threshold; while(t < ideal_limit) t += threshold; t -= threshold; + for(; t > threshold/2; t -= threshold) hpc.push_back(towards_inf(h, last_point, t)); + last_point = last_ideal = h; + } + else if(c1 <= 0 && c2 <= 0) { + hyperpoint p = closest_to_zero(last_point, h); + indenter ind(2); + int cp = safe_classify_ideals(p); + if(cp > 0) { + hpcpush(normalize(p)); + hpcpush(h); + return; + } + else { + last_ideal = last_point = h; + } } } - first = false; - hpc.push_back(h); } void geometry_information::chasmifyPoly(double fac, double fac2, int k) { @@ -119,6 +178,10 @@ hyperpoint geometry_information::turtlevertex(int u, double x, double y, double void geometry_information::finishshape() { if(!last) return; + + if(!first && safe_classify_ideals(starting_point) <= 0 && sqhypot_d(LDIM, starting_point - last_point) < 1e-9) + hpc_connect_ideal(last_ideal, starting_ideal); + last->e = isize(hpc); double area = 0; for(int i=last->s; ie-1; i++) diff --git a/util.cpp b/util.cpp index 11f6809e..0836b2da 100644 --- a/util.cpp +++ b/util.cpp @@ -275,13 +275,17 @@ cld exp_parser::parse(int prio) { #endif else if(eat("ideal_angle(")) { ld edges = rparse(0); + ld u = 1; + skip_white(); if(eat(",")) u = rparse(0); force_eat(")"); - return arb::rep_ideal(edges).second; + return arb::rep_ideal(edges, u).second; } else if(eat("ideal_edge(")) { ld edges = rparse(0); + ld u = 1; + skip_white(); if(eat(",")) u = rparse(0); force_eat(")"); - return arb::rep_ideal(edges).first; + return arb::rep_ideal(edges, u).first; } else if(eat("regangle(")) { cld edgelen = parse(0);