1 #include "hyper.h"
2 
3 // Fake non-Euclidean
4 
5 namespace hr {
6 
7 EX namespace fake {
8 
9   EX ld scale;
10 
11   EX bool multiple;
12 
13   EX bool multiple_special_draw = true;
14   EX bool recursive_draw = false;
15 
16   EX eGeometry underlying;
17   EX geometry_information *underlying_cgip;
18   EX hrmap *pmap;
19   EX geometry_information *pcgip;
20   EX eGeometry actual_geometry;
21 
22   EX int ordered_mode = 0;
23 
in()24   EX bool in() { return geometry == gFake; }
25 
on_dim_change()26   EX void on_dim_change() { pmap->on_dim_change(); }
27 
28   /** like in() but takes slided arb into account */
split()29   EX bool split() { return in() || arb::in_slided(); }
30 
available()31   EX bool available() {
32     if(in()) return true;
33     if(WDIM == 2 && standard_tiling() && (PURE || BITRUNCATED)) return true;
34     if(arcm::in() && PURE) return true;
35     if(WDIM == 2) return false;
36     if(among(geometry, gBitrunc3)) return false;
37     if(reg3::in() && !among(variation, eVariation::pure, eVariation::subcubes, eVariation::coxeter, eVariation::bch_oct)) return false;
38     return euc::in() || reg3::in();
39     }
40 
41   map<cell*, ld> random_order;
42 
43   // a dummy map that does nothing
44   struct hrmap_fake : hrmap {
45     hrmap *underlying_map;
46 
in_underlyinghr::fake::hrmap_fake47     template<class T> auto in_underlying(const T& t) -> decltype(t()) {
48       pcgip = cgip;
49       dynamicval<hrmap*> gpm(pmap, this);
50       dynamicval<eGeometry> gag(actual_geometry, geometry);
51       dynamicval<eGeometry> g(geometry, underlying);
52       dynamicval<geometry_information*> gc(cgip, underlying_cgip);
53       dynamicval<hrmap*> gu(currentmap, underlying_map);
54       return t();
55       }
56 
__anonb7ce6a140102null57     heptagon *getOrigin() override { return in_underlying([this] { return underlying_map->getOrigin(); }); }
58 
__anonb7ce6a140202null59     cell* gamestart() override { return in_underlying([this] { return underlying_map->gamestart(); }); }
60 
hrmap_fakehr::fake::hrmap_fake61     hrmap_fake(hrmap *u) {
62       underlying_map = u;
63       for(hrmap*& m: allmaps) if(m == underlying_map) m = this;
64       if(currentmap == u) currentmap = this;
65       }
66 
find_cell_connectionhr::fake::hrmap_fake67     void find_cell_connection(cell *c, int d) override {
68       FPIU(createMov(c, d));
69       }
70 
hrmap_fakehr::fake::hrmap_fake71     hrmap_fake() {
72       in_underlying([this] { initcells(); underlying_map = currentmap; });
73       for(hrmap*& m: allmaps) if(m == underlying_map) m = NULL;
74       }
75 
~hrmap_fakehr::fake::hrmap_fake76     ~hrmap_fake() {
77       in_underlying([this] {
78         delete underlying_map;
79         });
80       }
81 
create_stephr::fake::hrmap_fake82     heptagon *create_step(heptagon *parent, int d) override {
83       return FPIU(currentmap->create_step(parent, d));
84       }
85 
get_cornerhr::fake::hrmap_fake86     virtual hyperpoint get_corner(cell *c, int cid, ld cf=3) {
87 
88       if(arcm::in()) {
89         return underlying_map->get_corner(c, cid, cf);
90         }
91 
92       hyperpoint h;
93       h = FPIU(currentmap->get_corner(c, cid, cf));
94       return befake(h);
95       }
96 
adjhr::fake::hrmap_fake97     transmatrix adj(cell *c, int d) override {
98       if(variation == eVariation::coxeter) {
99         array<int, 3> which;
100         in_underlying([&which, c, d] {
101           auto T = currentmap->adj(c, d);
102           auto& f1 = currentmap->get_cellshape(c).faces_local[d];
103           auto& f2 = currentmap->get_cellshape(c->move(d)).faces_local[c->c.spin(d)];
104           for(int i=0; i<3; i++) {
105             which[i] = -1;
106             for(int j=0; j<isize(f2); j++)
107               if(hdist(T * f2[j], f1[i]) < 1e-6)
108                 which[i] = j;
109             }
110           });
111         auto& f1 = get_cellshape(c).faces_local[d];
112         auto& f2 = get_cellshape(c->move(d)).faces_local[c->c.spin(d)];
113         vector<ld> d1;
114         for(auto& v: f1) d1.push_back(hdist0(normalize(v)));
115         vector<hyperpoint> cf2(3);
116         for(int i=0; i<3; i++)
117           cf2[i] = f2[which[i]];
118         transmatrix F2, F1;
119         for(int i=0; i<3; i++) set_column(F2, i, cf2[i]);
120         for(int i=0; i<3; i++) set_column(F1, i, f1[i]);
121 
122         auto dtang = [] (vector<hyperpoint> v) {
123           if(euclid) return (v[1] - v[0]) ^ (v[2] - v[0]);
124           transmatrix T = gpushxto0(normalize(v[0]));
125           hyperpoint h = iso_inverse(T) * ((T*v[1]) ^ (T*v[2]));
126           return h;
127           };
128 
129         set_column(F2, 3, dtang(cf2));
130         set_column(F1, 3, dtang(f1));
131         transmatrix T = F1 * inverse(F2);
132         return T;
133         }
134       transmatrix S1, S2;
135       ld dist;
136       bool impure = reg3::in() && !PURE;
137       vector<int> mseq;
138       if(impure) {
139         mseq = FPIU ( currentmap->get_move_seq(c, d) );
140         if(mseq.empty()) {
141           auto& s1 = get_cellshape(c);
142           auto& s2 = get_cellshape(c->move(d));
143           return s1.from_cellcenter * s2.to_cellcenter;
144           }
145         if(isize(mseq) > 1)
146           throw hr_exception("fake adj not implemented for isize(mseq) > 1");
147         }
148       in_underlying([c, d, &S1, &S2, &dist, &impure, &mseq] {
149         #if CAP_ARCM
150         dynamicval<bool> u(arcm::use_gmatrix, false);
151         #endif
152         transmatrix T;
153         if(impure) {
154           T = currentmap->adj(c->master, mseq[0]);
155           }
156         else {
157           T = currentmap->adj(c, d);
158           }
159         S1 = rspintox(tC0(T));
160         transmatrix T1 = spintox(tC0(T)) * T;
161         dist = hdist0(tC0(T1));
162         S2 = xpush(-dist) * T1;
163         });
164 
165       if(impure) {
166         auto& s1 = get_cellshape(c);
167         auto& s2 = get_cellshape(c->move(d));
168         S1 = s1.from_cellcenter * S1;
169         S2 = S2 * s2.to_cellcenter;
170         }
171 
172       #if CAP_ARCM
173       if(arcm::in()) {
174         int t = arcm::id_of(c->master);
175         int t2 = arcm::id_of(c->move(d)->master);
176         auto& cof = arcm::current_or_fake();
177         cgi.adjcheck = cof.inradius[t/2] + cof.inradius[t2/2];
178         }
179       #else
180       if(0) ;
181       #endif
182 
183       else if(WDIM == 2) {
184 
185         ld dist;
186         in_underlying([c, d, &dist] {
187           dist = currentmap->spacedist(c, d);
188           });
189 
190         auto& u = *underlying_cgip;
191         if(dist == u.tessf) cgi.adjcheck = cgi.tessf;
192         else if(dist == u.crossf) cgi.adjcheck = cgi.crossf;
193         else if(dist == u.hexhexdist) cgi.adjcheck = cgi.hexhexdist;
194         else cgi.adjcheck = dist * scale;
195         }
196 
197       else if(underlying == gBitrunc3) {
198         ld x = (d % 7 < 3) ? 1 : sqrt(3)/2;
199         x *= scale;
200         cgi.adjcheck = 2 * atanh(x);
201         }
202 
203       return S1 * xpush(cgi.adjcheck) * S2;
204       }
205 
draw_recursivehr::fake::hrmap_fake206     void draw_recursive(cell *c, const shiftmatrix& V, ld a0, ld a1, cell *parent, int depth) {
207       if(!do_draw(c, V)) return;
208       drawcell(c, V);
209 
210       if(depth >= 15) return;
211 
212       // queuestr(V, .2, fts(a0)+":"+fts(a1), 0xFFFFFFFF, 1);
213 
214       ld d = hdist0(tC0(V));
215 
216       if(false) {
217         curvepoint(spin(-a0) * xpush0(d));
218         curvepoint(spin(-a0) * xpush0(d+.2));
219         curvepoint(spin(-a1) * xpush0(d+.2));
220         curvepoint(spin(-a1) * xpush0(d));
221         curvepoint(spin(-a0) * xpush0(d));
222         queuecurve(shiftless(Id), 0xFF0000FF, 0, PPR::LINE);
223         }
224 
225 
226       indenter id(2);
227       for(int i=0; i<c->type; i++) if(c->move(i) && c->move(i) != parent) {
228         auto h0 = V * befake(FPIU(get_corner_position(c, i)));
229         auto h1 = V * befake(FPIU(get_corner_position(c, (i+1) % c->type)));
230         ld b0 = atan2(unshift(h0));
231         ld b1 = atan2(unshift(h1));
232         while(b1 < b0) b1 += 2 * M_PI;
233         if(a0 == -1) {
234           draw_recursive(c->move(i), optimized_shift(V * adj(c, i)), b0, b1, c, depth+1);
235           }
236         else {
237           if(b1 - b0 > M_PI) continue;
238 
239           if(b0 < a0 - M_PI) b0 += 2 * M_PI;
240           if(b0 > a0 + M_PI) b0 -= 2 * M_PI;
241           if(b0 < a0) b0 = a0;
242 
243           if(b1 > a1 + M_PI) b1 -= 2 * M_PI;
244           if(b1 < a1 - M_PI) b1 += 2 * M_PI;
245           if(b1 > a1) b1 = a1;
246 
247           if(b0 > b1) continue;
248 
249           draw_recursive(c->move(i), optimized_shift(V * adj(c, i)), b0, b1, c, depth+1);
250           }
251         }
252       }
253 
relative_matrixchr::fake::hrmap_fake254     transmatrix relative_matrixc(cell *h2, cell *h1, const hyperpoint& hint) override {
255       if(arcm::in()) return underlying_map->relative_matrix(h2, h1, hint);
256       if(h1 == h2) return Id;
257 
258       for(int a=0; a<h1->type; a++) if(h1->move(a) == h2)
259         return adj(h1, a);
260 
261       return Id;
262       }
263 
relative_matrixhhr::fake::hrmap_fake264     transmatrix relative_matrixh(heptagon *h2, heptagon *h1, const hyperpoint& hint) override {
265       if(arcm::in()) return underlying_map->relative_matrix(h2, h1, hint);
266       return relative_matrix(h2->c7, h1->c7, hint);
267       }
268 
draw_athr::fake::hrmap_fake269     void draw_at(cell *at, const shiftmatrix& where) override {
270       sphereflip = Id;
271 
272       // for(int i=0; i<S6; i++) queuepoly(ggmatrix(cwt.at), shWall3D[i], 0xFF0000FF);
273 
274       if(pmodel == mdDisk && WDIM == 2 && recursive_draw) {
275         draw_recursive(at, where, -1, -1, nullptr, 0);
276         return;
277         }
278 
279       dq::clear_all();
280 
281       int id = 0;
282       int limit = 100 * pow(1.2, sightrange_bonus);
283       if(WDIM == 3 || vid.use_smart_range)
284         limit = INT_MAX;
285 
286       if(ordered_mode && !(multiple && multiple_special_draw)) {
287         using pct = pair<cell*, shiftmatrix>;
288         auto comparer = [] (pct& a1, pct& a2) {
289           if(ordered_mode > 2) {
290             auto val = [] (pct& a) {
291               if(!random_order.count(a.first))
292                 random_order[a.first] = randd() * 2;
293               return random_order[a.first] + hdist0(tC0(a.second));
294               };
295             return val(a1) > val(a2);
296             }
297           return a1.second[LDIM][LDIM] > a2.second[LDIM][LDIM];
298           };
299         std::priority_queue<pct, std::vector<pct>, decltype(comparer)> myqueue(comparer);
300 
301         auto enq = [&] (cell *c, const shiftmatrix& V) {
302           if(!c) return;
303           if(ordered_mode == 1 || ordered_mode == 3) {
304             if(dq::visited_c.count(c)) return;
305             dq::visited_c.insert(c);
306             }
307           myqueue.emplace(c, V);
308           };
309 
310         enq(centerover, cview());
311 
312         while(!myqueue.empty()) {
313           auto& p = myqueue.top();
314           id++;
315           cell *c = p.first;
316           shiftmatrix V = p.second;
317           myqueue.pop();
318 
319           if(ordered_mode == 2 || ordered_mode == 4) {
320             if(dq::visited_c.count(c)) continue;
321             dq::visited_c.insert(c);
322             }
323 
324           if(!do_draw(c, V)) continue;
325           drawcell(c, V);
326 
327           if(in_wallopt() && isWall3(c) && isize(dq::drawqueue_c) > 1000) continue;
328 
329           if(id > limit) continue;
330 
331           for(int i=0; i<c->type; i++) if(c->move(i)) {
332             enq(c->move(i), optimized_shift(V * adj(c, i)));
333             }
334           }
335 
336         return;
337         }
338 
339       auto enqueue = (multiple && multiple_special_draw ? dq::enqueue_by_matrix_c : dq::enqueue_c);
340       enqueue(at, where);
341 
342       while(!dq::drawqueue_c.empty()) {
343         auto& p = dq::drawqueue_c.front();
344         id++;
345         cell *c = p.first;
346         shiftmatrix V = p.second;
347         dq::drawqueue_c.pop();
348 
349         if(!do_draw(c, V)) continue;
350         drawcell(c, V);
351         if(in_wallopt() && isWall3(c) && isize(dq::drawqueue_c) > 1000) continue;
352 
353         if(id > limit) continue;
354 
355         for(int i=0; i<c->type; i++) if(c->move(i)) {
356           enqueue(c->move(i), optimized_shift(V * adj(c, i)));
357           }
358         }
359       }
360 
spin_anglehr::fake::hrmap_fake361     ld spin_angle(cell *c, int d) override {
362       return underlying_map->spin_angle(c,d);
363       }
364 
shvidhr::fake::hrmap_fake365     int shvid(cell *c) override {
366       return FPIU( currentmap->shvid(c) );
367       }
368 
get_cellshapehr::fake::hrmap_fake369     subcellshape& get_cellshape(cell *c) override {
370       return *FPIU( (cgip = pcgip, &(currentmap->get_cellshape(c))) );
371       }
372 
ray_iadjhr::fake::hrmap_fake373     transmatrix ray_iadj(cell *c, int i) override {
374       if(WDIM == 2)
375         return to_other_side(get_corner(c, i), get_corner(c, i+1));
376       if(PURE) return iadj(c, i);
377       auto& v = get_cellshape(c).faces_local[i];
378       hyperpoint h =
379         project_on_triangle(v[0], v[1], v[2]);
380       transmatrix T = rspintox(h);
381       return T * xpush(-2*hdist0(h)) * spintox(h);
382       }
383     };
384 
new_map()385   EX hrmap* new_map() { return new hrmap_fake; }
386 
get_umap()387   EX hrmap* get_umap() { if(!dynamic_cast<hrmap_fake*>(currentmap)) return nullptr; else return ((hrmap_fake*)currentmap)->underlying_map; }
388 
389   #if HDR
in_underlying_geometry(const T & f)390   template<class T> auto in_underlying_geometry(const T& f) -> decltype(f()) {
391     if(!fake::in()) return f();
392     pcgip = cgip;
393     dynamicval<eGeometry> g(geometry, underlying);
394     dynamicval<eGeometry> gag(actual_geometry, geometry);
395     dynamicval<geometry_information*> gc(cgip, underlying_cgip);
396     dynamicval<hrmap*> gpm(pmap, currentmap);
397     dynamicval<hrmap*> gm(currentmap, get_umap());
398     return f();
399     }
400 
401   #define FPIU(x) hr::fake::in_underlying_geometry([&] { return (x); })
402   #endif
403 
befake(hyperpoint h)404 EX hyperpoint befake(hyperpoint h) {
405   auto h1 = h / h[LDIM] * scale;
406   h1[LDIM] = 1;
407   if(material(h1) > 1e-3)
408     h1 = normalize(h1);
409   return h1;
410   }
411 
befake(const vector<hyperpoint> & v)412 EX vector<hyperpoint> befake(const vector<hyperpoint>& v) {
413   vector<hyperpoint> res;
414   for(auto& h: v) res.push_back(befake(h));
415   return res;
416   }
417 
befake(const vector<vector<hyperpoint>> & v)418 EX vector<vector<hyperpoint>> befake(const vector<vector<hyperpoint>>& v) {
419   vector<vector<hyperpoint>> res;
420   for(auto& h: v) res.push_back(befake(h));
421   return res;
422   }
423 
compute_around(bool setup)424 EX ld compute_around(bool setup) {
425   auto &ucgi = *underlying_cgip;
426 
427   auto fcs = befake(ucgi.heptshape->faces);
428 
429   if(setup) {
430     cgi.heptshape->faces = fcs;
431     cgi.heptshape->compute_hept();
432     }
433 
434   hyperpoint h = Hypc;
435   for(int i=0; i<ucgi.face; i++) h += fcs[0][i];
436   if(material(h) > 0)
437     h = normalize(h);
438 
439   if(setup)
440     cgi.adjcheck = 2 * hdist0(h);
441 
442   hyperpoint h2 = rspintox(h) * xpush0(2 * hdist0(h));
443 
444   auto kh= kleinize(h);
445   auto k0 = kleinize(fcs[0][0]);
446   auto k1 = kleinize(fcs[0][1]);
447 
448   auto vec = k1 - k0;
449 
450   // u = fcs[0] + vec * z
451 
452   // (f1-u) | (vec-u) = 0
453   // (f1 - f0 + vec*z) |
454 
455   // (vec | h2-vec*z)  == (vec | h2) - (vec | vec*z) == 0
456 
457   auto z = (vec|(kh-k0)) / (vec|vec);
458 
459   hyperpoint u = k0 + vec * z;
460 
461   if(material(u) <= 0)
462     return HUGE_VAL;
463 
464   u = normalize(u);
465 
466   h2 = spintox(u) * h2;
467   u = spintox(u) * u;
468 
469   h2 = gpushxto0(u) * h2;
470   u = gpushxto0(u) * u;
471 
472   ld x = hypot(h2[1], h2[2]);
473   ld y = h2[0];
474 
475   ld ans = 360 / (90 + atan(y/x) / degree);
476 
477   return ans;
478   }
479 
generate()480 EX void generate() {
481   FPIU( cgi.require_basics() );
482   #if MAXMDIM >= 4
483   auto &ucgi = *underlying_cgip;
484 
485   cgi.loop = ucgi.loop;
486   cgi.face = ucgi.face;
487   cgi.schmid = ucgi.schmid;
488 
489   auto& hsh = get_hsh();
490 
491   hsh = *ucgi.heptshape;
492 
493   for(int b=0; b<32; b++)
494     cgi.spins[b] = ucgi.spins[b];
495 
496   compute_around(true);
497   hsh.compute_hept();
498   reg3::compute_ultra();
499 
500   reg3::generate_subcells();
501   if(variation == eVariation::coxeter) {
502     for(int i=0; i<isize(cgi.subshapes); i++) {
503       auto& s = cgi.subshapes[i];
504       s.faces_local = ucgi.subshapes[i].faces_local;
505       for(auto &face: s.faces_local) for(auto& v: face) {
506         v = kleinize(v);
507         for(int i=0; i<3; i++) v[i] *= scale;
508         }
509       reg3::make_vertices_only(s.vertices_only, s.faces_local);
510       }
511     }
512   #endif
513   }
514 
get_middle()515 int get_middle() {
516   if(S7 == 20) return 5;
517   if(S7 == 8) return 4;
518   return 3;
519   }
520 
521 EX ld around;
522 
523 /** @brief the value of 'around' which makes the tiling Euclidean */
compute_euclidean()524 EX ld compute_euclidean() {
525   #if CAP_ARCM
526   if(arcm::in()) return arcm::current.N * 2 / arcm::current.euclidean_angle_sum;
527   #endif
528   if(WDIM == 2) return 4 / (S7-2.) + 2;
529 
530   if(underlying == gRhombic3) return 3;
531   if(underlying == gBitrunc3) return 2.55208;
532   int middle = get_middle();
533 
534   if(!fake::in()) underlying_cgip = cgip;
535 
536   return M_PI / asin(cos(M_PI/middle) / sin(M_PI/underlying_cgip->face));
537   }
538 
around_orig()539 EX ld around_orig() {
540   #if CAP_ARCM
541   if(arcm::in())
542     return arcm::current.N;
543   #endif
544   if(WDIM == 2)
545     return S3;
546   if(underlying == gRhombic3)
547     return 3;
548   if(underlying == gBitrunc3)
549     return 2.24259;
550   return
551     geometry == gFake ? underlying_cgip->loop : cgi.loop;
552   }
553 
geometry_of_curvature(ld curvature,int dim)554 EX geometryinfo1 geometry_of_curvature(ld curvature, int dim) {
555   if(curvature == 0)
556     return WDIM == 3 ? giEuclid3 : giEuclid2;
557 
558   if(curvature < 0)
559     return WDIM == 3 ? giHyperb3 : giHyperb2;
560 
561   return WDIM == 3 ? giSphere3 : giSphere2;
562   }
563 
compute_scale()564 EX void compute_scale() {
565 
566   ld good = compute_euclidean();
567 
568   if(around < 0) around = good;
569 
570   if(abs(good - around) < 1e-6) good = around;
571 
572   int s3 = around_orig();
573 
574   multiple = false;
575   int mcount = int(around / s3 + .5);
576   multiple = abs(around - mcount * s3) < 1e-6;
577 
578   ginf[gFake].g = geometry_of_curvature(good - around, WDIM);
579 
580   geom3::apply_always3();
581   ld around_ideal = 1/(1/2. - 1./get_middle());
582 
583   bool have_ideal = abs(around_ideal - around) < 1e-6;
584   if(underlying == gRhombic3 || underlying == gBitrunc3) have_ideal = false;
585 
586   if(arcm::in()) {
587     ginf[gFake].tiling_name = "(" + ginf[gArchimedean].tiling_name + ")^" + fts(around / around_orig());
588     return;
589     }
590   else if(WDIM == 2) {
591     ginf[gFake].tiling_name = lalign(0, "{", S7, ",", around, "}");
592     return;
593     }
594   else if(euclid) scale = 1;
595   else if(have_ideal) {
596     hyperpoint h0 = underlying_cgip->heptshape->faces[0][0];
597     auto s = kleinize(h0);
598     ld d = hypot_d(LDIM, s);
599     scale = 1/d;
600 
601     hyperpoint h = h0;
602     auto h1 = h / h[WDIM] * scale;
603     h1[WDIM] = 1;
604     set_flag(ginf[gFake].flags, qIDEAL, true);
605     set_flag(ginf[gFake].flags, qULTRA, false);
606     }
607   else {
608     set_flag(ginf[gFake].flags, qIDEAL, false);
609     set_flag(ginf[gFake].flags, qULTRA, around > around_ideal);
610     ld minscale = 0, maxscale = 10;
611     for(int it=0; it<100; it++) {
612       scale = (minscale + maxscale) /  2;
613       ld ar = compute_around(false);
614       if(sphere) {
615         if(ar < around) maxscale = scale;
616         else minscale = scale;
617         }
618       else {
619         if(ar > around) maxscale = scale;
620         else minscale = scale;
621         }
622       }
623 
624     /* ultra a bit earlier */
625     if(underlying == gRhombic3 || underlying == gBitrunc3) {
626       auto fcs = befake(underlying_cgip->heptshape->faces[0][0]);
627       set_flag(ginf[gFake].flags, qULTRA, material(fcs) < 0);
628       }
629     }
630 
631   auto& u = underlying_cgip;
632   ginf[gFake].tiling_name = lalign(0, "{", u->face, ",", get_middle(), ",", around, "}");
633   }
634 
set_gfake(ld _around)635 void set_gfake(ld _around) {
636   cgi.require_basics();
637   underlying = geometry;
638   underlying_cgip = cgip;
639   ginf[gFake] = ginf[underlying];
640 
641   geometry = gFake;
642 
643   around = _around;
644 
645   compute_scale();
646   check_cgi();
647   cgi.require_basics();
648 
649   if(currentmap) new hrmap_fake(currentmap);
650   }
651 
change_around()652 EX void change_around() {
653   if(around >= 0 && around <= 2) return;
654 
655   ld t = in() ? scale : 1;
656   hyperpoint h = inverse_exp(shiftless(tC0(View)));
657   transmatrix T = gpushxto0(tC0(View)) * View;
658 
659   ld range = sightranges[geometry];
660 
661   if(!fake::in()) {
662     underlying = geometry;
663     if(around == around_orig()) return; /* do nothing */
664     set_gfake(around);
665     }
666 
667   else {
668     compute_scale();
669     ray::reset_raycaster();
670 
671     /* to compute scale */
672     if(WDIM == 2)
673       cgi.prepare_basics();
674     }
675 
676   t = scale / t;
677   h *= t;
678   View = rgpushxto0(direct_exp(h)) * T;
679   fixmatrix(View);
680 
681   sightranges[gFake] = range * t;
682   #if CAP_TEXTURE
683   texture::config.remap();
684   #endif
685   geom3::apply_always3();
686   }
687 
configure()688 EX void configure() {
689   if(!in()) {
690     underlying_cgip = cgip;
691     around = around_orig();
692     }
693   dialog::editNumber(around, 2.01, 10, 1, around, "fake curvature",
694     "This feature lets you construct the same tiling, but "
695     "from shapes of different curvature.\n\n"
696     "The number you give here is (2D) vertex degree or (3D) "
697     "the number of cells around an edge.\n\n"
698     );
699   if(fake::in())
700     dialog::reaction = change_around;
701   else
702     dialog::reaction_final = change_around;
703   dialog::extra_options = [] {
704     ld e = compute_euclidean();
705     dialog::addSelItem("Euclidean", fts(e), 'E');
706     dialog::add_action([e] {
707       around = e;
708       popScreen();
709       change_around();
710       });
711 
712     dialog::addSelItem("original", fts(around_orig()), 'O');
713     dialog::add_action([] {
714       around = around_orig();
715       popScreen();
716       change_around();
717       });
718 
719     dialog::addSelItem("double original", fts(2 * around_orig()), 'D');
720     dialog::add_action([] {
721       around = 2 * around_orig();
722       popScreen();
723       change_around();
724       });
725 
726     dialog::addBoolItem_action("draw all if multiple of original", multiple_special_draw, 'M');
727     dialog::addBoolItem_action("draw copies (2D only)", recursive_draw, 'C');
728 
729     dialog::addBoolItem_choice("unordered", ordered_mode, 0, 'U');
730     dialog::addBoolItem_choice("pre-ordered", ordered_mode, 1, 'P');
731     dialog::addBoolItem_choice("post-ordered", ordered_mode, 2, 'Q');
732 
733     };
734   }
735 
736 #if CAP_COMMANDLINE
readArgs()737 int readArgs() {
738   using namespace arg;
739 
740   if(0) ;
741   else if(argis("-gfake-euc")) {
742     start_game();
743     around = compute_euclidean();
744     change_around();
745     }
746   else if(argis("-gfake")) {
747     start_game();
748     shift_arg_formula(around, change_around);
749     }
750   else if(argis("-gfake-order")) {
751     shift(); ordered_mode = argi();
752     }
753   else return 1;
754   return 0;
755   }
756 
757 auto fundamentalhook = addHook(hooks_args, 100, readArgs);
758 #endif
759 
760 EX }
761 
762 }
763 
764