1 #ifdef NOTKNOT
2 #include "../hyper.cpp"
3 #endif
4
5 #include "rogueviz.h"
6
7 #if CAP_RAY
8
9 /**
10
11 * mg 2 in old video
12
13 Blocky Knot Portal:
14
15 compile with: mymake rogueviz/notknot
16
17 the video has been created with the following options:
18
19 older Euclidean
20
21 https://youtu.be/1TMY2U4_9Qg
22 nk_margin=2 -noplayer -canvas-random 20 -geo notknot -sight3 0.5 -ray-cells 600000 smooth_scrolling=1 camspd=10 panini_alpha=1 fov=150 -shot-hd ray_exp_decay_poly=30 ray_fixed_map=1
23
24 better Euclidean
25
26 https://youtu.be/eb2DhCcGH7U
27 nk_margin=4 -noplayer -canvas-random 20 -geo notknot -sight3 0.5 -ray-cells 600000 smooth_scrolling=1 camspd=10 panini_alpha=1 fov=150 -shot-hd ray_exp_decay_poly=30 ray_fixed_map=1 -ray-iter 100 ray_reflect_val=0.30
28
29 self-hiding Euclidean
30
31 https://youtu.be/vFLZ2NGtuGw
32 selfhide=1 nk_loop=4 nk_margin=4 -noplayer -canvas-random 20 -geo notknot -sight3 0.5 -ray-cells 600000 smooth_scrolling=1 camspd=10 panini_alpha=1 fov=150 -shot-hd ray_exp_decay_poly=30 ray_fixed_map=1 -ray-iter 100 ray_reflect_val=0.30
33
34 penrose staircase in Nil
35
36 -noplayer nk_loop=6 nk_secondary=0 nk_terminate=9999999 -geo Nil -nilperiod 8 8 8 -nilwidth .25 -canvas-random 20 -nkbase -geo notknot ray_fixed_map=1 -ray-cells 600000 -sight3 1 -ray-range 1 3 -ray-random 1 -shot-1000
37
38 self-hiding knotted portal in S3
39
40 selfhide=1 -run nk_secondary=0 nk_terminate=9999999 nk_loop=4 -canvas-random 10 -load spherknot.lev -nkbase -nkbasemap spherknot.lev -PM nativ -fov 270 panini_alpha=1 -nk-volumetric -noplayer ray_fixed_map=1 -ray-cells 600000 -ray-iter 600
41
42 basic portal in S3:
43
44 nk_secondary=0 nk_terminate=99999 nk_loop=0 -nk-unloop 60 7 -canvas-random 10 -load spherring.lev -nkbase -nkbasemap spherring.lev -PM nativ -fov 270 panini_alpha=1 -nk-volumetric -noplayer
45
46
47
48 The algorithm here is as follows:
49
50 * create the map without portals (this is just the cube with the trifoil knot in it)
51
52 This is done with a program (function create_trifoil_knot).
53 The general algorithm should work with any map, and it would be possible to make it so that the user can create the map using HyperRogue map editor.
54
55 * the universal cover can be seen as the set of all paths, where two paths are identified if they are homotopic. So we generate all paths
56 from the chosen starting point; when we generate extensions from path ...A, we check if we have a situation where we could go ...A->B->D or ...A->C->D --
57 if so, the two paths are homotopic, so we identify them (that is, all the references to one of the paths are replaced with the references to the other path,
58 possibly propagating to all known extensions of these paths)
59
60 * since the universal cover of the trifoil knot complement is infinite, we also identify two paths if they differ by three loops around the knot (around any point of the knot).
61 (If we did not do that, the algorithm would never terminate, or we could terminate it at some point (terminate_at), but then we easily run into ugly unfinished areas.)
62
63 * this result in a relatively standard HyperRogue map representation (cubes connected via glued faces) which is rendered using HyperRogue's raycaster.
64
65 */
66
67 namespace hr {
68
69 namespace notknot {
70
71 void create_notknot();
72
73 /* how many times we need to loop around the portal frame to get back to the same space */
74 /* the number of the worlds is: 1 (loop=1), 6 (loop=2), 24, 96, 600, infinity (loop>5) */
75 int loop = 3;
76
77 /* any loop repeated loop_any times */
78 int loop_any = 0;
79
80 /* extra space around the knot */
81 int margin = 4;
82
83 /* the scale factor for the knot */
84 int knotsize = 3;
85
86 int secondary_percentage = 10;
87
88 int terminate_at = 500000000;
89
90 string base_map = "";
91
92 /* make a self-hiding knot */
93 bool self_hiding = false;
94
95 eGeometry gNotKnot(eGeometry(-1));
96
97 /** It was easier to generate a program to design the trifoil knot than to generate it manually.
98 * This function does generate the instructions for trifoil knot, although they have been adjusted manually
99 * The generated instructions is the weird array in create_trifoil_knot()
100 */
101
102 vector<vector<int> > to_unloop;
103
gen_trifoil()104 void gen_trifoil() {
105 for(int len=2; len<=12; len+=2) {
106 println(hlog, "trying len = ", len);
107 int pos = 1;
108 for(int l=0; l<len; l++) pos *= 6;
109
110 for(int p=0; p<pos; p++) {
111
112 vector<int> lst;
113 int a = p;
114 int bal = 0;
115 for(int p=0; p<len; p++) {
116 if(a%6 < 3) bal++; else bal--;
117 lst.push_back(a%6);
118 a /= 6;
119 }
120 if(bal) continue;
121
122 array<int, 3> start = {0, 0, 0};
123
124 array<int, 3> where = start;
125
126 set<array<int, 3> > ful;
127 map<array<int, 2>, int> proj;
128
129 int steps = 0;
130 vector<pair<int, int> > crosses;
131
132 for(int i=0; i<3; i++) {
133 for(auto d: lst) {
134 if(ful.count(where)) goto next;
135 ful.insert(where);
136 auto pco = array<int,2>{{where[0]-where[2], where[1]-where[2]}};
137 if(proj.count(pco)) crosses.emplace_back(proj[pco], steps);
138 else proj[pco] = steps;
139 where[(d+i)%3] += (d<3?1:-1);
140 steps++;
141 }
142 }
143
144 if(where != start) { println(hlog, "bad loop"); continue; }
145 if(isize(ful) != 3*len) continue;
146
147 if(isize(proj) != 3*len-3) continue;
148
149 println(hlog, "len=", len, " ful=", isize(ful), " proj=", isize(proj), " for ", lst);
150
151 println(hlog, "crosses = ", crosses);
152
153 if(1) {
154 set<int> crosval;
155 for(auto c: crosses) crosval.insert(c.first), crosval.insert(c.second);
156 vector<int> cvs;
157 for(auto s: crosval) cvs.push_back(s);
158
159 bool wrong = false;
160 for(auto c: crosses) if(c.first == cvs[0] && c.second == cvs[1]) wrong = true;
161 for(auto c: crosses) if(c.first == cvs[1] && c.second == cvs[2]) wrong = true;
162
163 if(wrong) continue;
164 }
165
166 println(hlog, "result: ", lst);
167
168 exit(3);
169
170 next: ;
171 }
172 }
173
174 exit(2);
175 }
176
177 eGeometry base = gCubeTiling;
178
179 const int arrsize = 12;
180
181 struct hrmap_notknot : hrmap {
182
183 /* represents a path (may be partially identified) */
184 struct ucover {
185 /* the heptagon of the underlying map */
186 heptagon *where;
187 /* the heptagon of the result map */
188 heptagon *result;
189 /* what is has been merged into */
190 ucover *merged_into;
191 /* connections in every direction */
192 ucover *ptr[arrsize];
193 /* used for painting walls in a single color */
194 ucover *wall_merge;
195 color_t wallcolor, wallcolor2;
196 /* 0 = live, 1 = wall, 2 = merged, 4 = overflow, 8 = to hide */
197 char state;
198 /* direction to the parent */
199 char parentdir;
200 /* index in the table `all` */
201 int index;
ucoverhr::notknot::hrmap_notknot::ucover202 ucover(heptagon *w, int s) { where = w; for(int i=0; i<arrsize; i++) ptr[i] = nullptr; state = s; merged_into = nullptr; result = nullptr; wall_merge = this; }
iswallhr::notknot::hrmap_notknot::ucover203 bool iswall() { return state & 1; }
nowallhr::notknot::hrmap_notknot::ucover204 bool nowall() { return !iswall(); }
ismergedhr::notknot::hrmap_notknot::ucover205 bool ismerged() { return state & 2; }
isoverhr::notknot::hrmap_notknot::ucover206 bool isover() { return state & 4; }
tohidehr::notknot::hrmap_notknot::ucover207 bool tohide() { return state & 8; }
208 };
209
210 /* find-union algorithm for wall_merge */
ufindhr::notknot::hrmap_notknot211 ucover *ufind(ucover *at) {
212 if(at->wall_merge == at) return at;
213 return at->wall_merge = ufind(at->wall_merge);
214 }
215
funionhr::notknot::hrmap_notknot216 void funion(ucover *a, ucover *b) {
217 ufind(b)->wall_merge = ufind(a);
218 }
219
220 /* the vector of all paths */
221 vector<ucover*> all;
222
223 /* the stack of known unifications */
224 vector<pair<ucover*, ucover*>> unify;
225
226 /* the underlying map */
227 hrmap *euc;
228
getOriginhr::notknot::hrmap_notknot229 heptagon *getOrigin() override {
230 // return hepts[0];
231 return all[0]->result;
232 }
233
athr::notknot::hrmap_notknot234 heptagon* at(int x, int y, int z) {
235 dynamicval<eGeometry> g(geometry, base);
236 dynamicval<hrmap*> m(currentmap, euc);
237 euc::coord co = euc::basic_canonicalize({x, y, z});
238
239 return euc::get_at(co);
240 }
241
242 /* make sure that where->move(d) and where->c.spin(d) are known */
cmovhr::notknot::hrmap_notknot243 void cmov(heptagon *where, int d) {
244 if(where->move(d)) return;
245 dynamicval<eGeometry> g(geometry, base);
246 dynamicval<hrmap*> m(currentmap, euc);
247 createStep(where, d);
248 }
249
create_trifoil_knothr::notknot::hrmap_notknot250 heptagon *create_trifoil_knot() {
251 euc::coord cmin{99,99,99}, cmax{-99,-99,-99}, cat{0,0,0};
252 heptagon *h = at(0, 0, 0);
253
254 vector<heptagon*> trifoil;
255
256 int step = knotsize;
257
258 for(int i=0; i<3; i++) {
259 for(auto m: {3, 3, 3, 3, 5, 5, 1, 0, 0, 0, 0, 0}) for(int rep=0; rep<step; rep++) {
260 trifoil.push_back(h);
261 int d = (i+m)%3 + (m<3?0:3);
262 cmov(h, d);
263 h = h->move(d);
264 if(m<3) cat[(i+m)%3]++; else cat[(i+m)%3]--;
265 for(int k=0; k<3; k++) cmin[k] = min(cmin[k], cat[k]), cmax[k] = max(cmax[k], cat[k]);
266 }
267 }
268
269 int& mg = margin;
270
271 for(int i=cmin[0]-mg; i<=cmax[0]+mg; i++)
272 for(int j=cmin[1]-mg; j<=cmax[1]+mg; j++)
273 for(int k=cmin[2]-mg; k<=cmax[2]+mg; k++)
274 if(among(i,cmin[0]-mg,cmax[0]+mg) || among(j,cmin[1]-mg,cmax[1]+mg) || among(k,cmin[2]-mg,cmax[2]+mg))
275 at(i,j,k)->zebraval = 1;
276 else
277 at(i,j,k)->zebraval = 0;
278
279 for(auto h: trifoil)
280 h->zebraval = 9;
281
282 return at(cmax[0], cmax[1], cmax[2]);
283 }
284
create_nil_knothr::notknot::hrmap_notknot285 heptagon *create_nil_knot() {
286 dynamicval<eGeometry> g(geometry, base);
287 dynamicval<hrmap*> m(currentmap, euc);
288 auto ac = currentmap->allcells();
289 for(cell *c: ac) c->master->zebraval = 0;
290
291 auto hept = [&] (int x, int y, int z) {
292 x = zgmod(x, nilv::nilperiod[0]);
293 y = zgmod(y, nilv::nilperiod[1]);
294 z = zgmod(z, nilv::nilperiod[2]);
295 return nilv::get_heptagon_at(nilv::mvec(x,y,z));
296 };
297
298 hept(-3, -3, -4)->zebraval |= 16;
299
300 heptagon* h0 = hept(-2, -2, 0);
301 auto h = h0;
302
303 for(int d: {4, 3, 1, 0})
304 for(int i=0; i<4; i++) {
305 h->zebraval |= 1;
306 h = h->cmove(d);
307 h->zebraval |= 1;
308 h = h->cmove(5);
309 }
310
311 if(h != h0) { println(hlog, "not looped"); exit(1); }
312
313 hept(0, 0, 2)->zebraval |= 1;
314 hept(1, 0, 2)->zebraval |= 1;
315 hept(-1, 0, 2)->zebraval |= 1;
316
317 for(int z=0; z<8; z++)
318 for(int x=-3; x<4; x++)
319 for(int y=-3; y<4; y++)
320 if(!(x>=-1 && x<=1 && y>=-1 && y<=1))
321 if(!(hept(x,y,z)->zebraval & 1))
322 hept(x, y, z)->zebraval |= 32;
323
324 return ac[0]->master;
325 }
326
327 #if CAP_SOLV
create_solv_knothr::notknot::hrmap_notknot328 heptagon *create_solv_knot() {
329 dynamicval<eGeometry> g(geometry, base);
330 dynamicval<hrmap*> m(currentmap, euc);
331 auto ac = currentmap->allcells();
332 for(cell *c: ac) c->master->zebraval = 0;
333
334 auto hept = [&] (int x, int y, int z) {
335 asonov::coord co(x, y, z);
336 return asonov::get_at(co);
337 };
338
339 auto h = hept(0, 0, 0);
340
341 h->zebraval |= 16;
342
343 int top = asonov::period_xy;
344 top += 2 - margin;
345
346 for(int x=1; x<top-1; x++)
347 for(int y=1; y<top-1; y++)
348 if(x==1 || y==1 || x==top-2 || y==top-2)
349 hept(x, y, 0)->zebraval |= 9;
350
351 hept(0, 0, (asonov::period_z+1)/2)->c7->wall = waFloorA;
352
353 hept(2, 2, 0)->zebraval |= 128;
354
355 // if(top > 4) hept(3, 3, 1)->zebraval |= 9;
356
357 return ac[0]->master;
358 }
359 #endif
360
interpret_basemaphr::notknot::hrmap_notknot361 heptagon *interpret_basemap() {
362 dynamicval<eGeometry> g(geometry, base);
363 dynamicval<hrmap*> m(currentmap, euc);
364 auto ac = currentmap->allcells();
365
366 for(cell *c: ac) {
367 auto& m = c->master->zebraval;
368 m = 0;
369 if(c->wall == waPlatform)
370 m |= 9;
371 }
372
373 return ac[0]->master;
374 }
375
create_underhr::notknot::hrmap_notknot376 heptagon *create_under() {
377 if(base_map != "")
378 return interpret_basemap();
379 if(base == gCubeTiling)
380 return create_trifoil_knot();
381 else if(base == gNil)
382 return create_nil_knot();
383 #if CAP_SOLV
384 else if(base == gArnoldCat)
385 return create_solv_knot();
386 #endif
387 throw hr_exception();
388 }
389
390 bool remove_marked_walls;
391
gen_adjhr::notknot::hrmap_notknot392 ucover *gen_adj(ucover *u, int d) {
393 if(u->ptr[d]) return u->ptr[d];
394 cmov(u->where, d);
395 auto x = u->where->move(d);
396 auto d1 = u->where->c.spin(d);
397 auto z = x->zebraval;
398 if(z & 6) {
399 throw hr_exception("zebraval failure!");
400 exit(3);
401 x->zebraval = 0;
402 }
403 if(remove_marked_walls && (z & 8))
404 z &=~ 9;
405 u->ptr[d] = new ucover(x, z & 15);
406 u->ptr[d]->ptr[d1] = u;
407 u->ptr[d]->index = isize(all);
408 u->ptr[d]->parentdir = d1;
409 all.push_back(u->ptr[d]);
410 return u->ptr[d];
411 };
412
add_to_unifyhr::notknot::hrmap_notknot413 void add_to_unify(ucover *a, ucover *b) {
414 if(a->where != b->where)
415 throw hr_exception("unification error");
416 unify.emplace_back(a, b);
417 };
418
419 map<heptagon*, vector<vector<int> > > uloops;
420
collapse_loophr::notknot::hrmap_notknot421 bool collapse_loop(ucover* u, int loopcount, const vector<int>& looplist) {
422 auto ux = u;
423 for(int iter=0; iter<loopcount; iter++)
424 for(int w: looplist) {
425 ux = gen_adj(ux, w);
426 if(ux->iswall()) return false;
427 }
428 add_to_unify(u, ux);
429 return true;
430 };
431
verify_loophr::notknot::hrmap_notknot432 bool verify_loop(ucover* u, int loopcount, const vector<int>& looplist) {
433 auto ux = u;
434 for(int iter=0; iter<loopcount; iter++)
435 for(int w: looplist) {
436 ux = gen_adj(ux, w);
437 if(ux->iswall()) return false;
438 }
439 return true;
440 }
441
record_loophr::notknot::hrmap_notknot442 void record_loop(ucover* u, int loopcount, const vector<int>& looplist) {
443 vector<int> repeated;
444 for(int l=0; l<loopcount; l++)
445 for(auto v: looplist) repeated.push_back(v);
446 uloops[u->where].push_back(repeated);
447 };
448
record_loop_if_nowallhr::notknot::hrmap_notknot449 void record_loop_if_nowall(ucover* u, int loopcount, const vector<int>& looplist) {
450 if(verify_loop(u, loopcount, looplist))
451 record_loop(u, loopcount, looplist);
452 }
453
record_loop_verifyhr::notknot::hrmap_notknot454 void record_loop_verify(ucover* u, int loopcount, const vector<int>& looplist, const hr_exception& ex) {
455 if(!verify_loop(u, loopcount, looplist)) throw ex;
456 record_loop(u, loopcount, looplist);
457 }
458
adjhr::notknot::hrmap_notknot459 transmatrix adj(ucover *u, int k) {
460 dynamicval<eGeometry> g(geometry, base);
461 dynamicval<hrmap*> m(currentmap, euc);
462 return currentmap->adj(u->where, k);
463 }
464
adjacent_matrixhr::notknot::hrmap_notknot465 bool adjacent_matrix(const transmatrix& Tk, const transmatrix& Tl) {
466 for(auto vk: cgi.heptshape->vertices_only)
467 for(auto vl: cgi.heptshape->vertices_only)
468 if(hdist(Tk * vk, Tl * vl) < .01)
469 return true;
470
471 return false;
472 }
473
adjacent_facehr::notknot::hrmap_notknot474 bool adjacent_face(ucover *u, int k, int l) {
475 if(base == gCubeTiling) return abs(k-l) != 3;
476 if(base == gNil) return abs(k-l) != 3;
477 return adjacent_matrix(adj(u, k), adj(u, l));
478 }
479
unify_homotopieshr::notknot::hrmap_notknot480 void unify_homotopies(ucover *u) {
481 /* unify homotopies */
482 if(base == gNil) {
483 collapse_loop(u, 1, {0, 2, 3, 5});
484 collapse_loop(u, 1, {1, 2, 4, 5});
485 collapse_loop(u, 1, {0, 1, 3, 4, 2});
486 return;
487 }
488 if(base == gCubeTiling) {
489 collapse_loop(u, 1, {0, 1, 3, 4});
490 collapse_loop(u, 1, {0, 2, 3, 5});
491 collapse_loop(u, 1, {1, 2, 4, 5});
492 return;
493 }
494
495 for(auto& v: cgi.heptshape->vertices_only) {
496 map<heptagon*, ucover*> visited;
497 vector<pair<ucover *, transmatrix>> q;
498
499 auto visit = [&] (ucover *u, const transmatrix& T) {
500 if(visited.count(u->where)) {
501 add_to_unify(u, visited[u->where]);
502 return;
503 }
504 visited[u->where] = u;
505 q.emplace_back(u, T);
506 };
507
508 hyperpoint h = v;
509 visit(u, Id);
510 for(int i=0; i<isize(q); i++) {
511 auto u1 = q[i].first;
512 transmatrix T0 = q[i].second;
513 for(int i=0; i<u1->where->type; i++) {
514 auto u2 = gen_adj(u1, i);
515 if(u2->state != 0) continue;
516 auto T1 = T0 * adj(u1, i);
517 bool adjacent = false;
518 for(auto& v2: cgi.heptshape->vertices_only)
519 if(hdist(T1 * v2, h) < 1e-5)
520 adjacent = true;
521 if(adjacent)
522 visit(u2, T1);
523 }
524 }
525 }
526 }
527
unify_loops_generalhr::notknot::hrmap_notknot528 void unify_loops_general(ucover *u) {
529 int t = u->where->type;
530 for(int i=0; i<t; i++) {
531 auto u1 = gen_adj(u, i);
532 if(u1->nowall()) continue;
533 if(u1->where->zebraval != 9) continue;
534 transmatrix M = adj(u, i);
535
536 map<heptagon*, int> camefrom;
537 vector<pair<ucover*, transmatrix>> visited;
538
539 auto visit = [&] (ucover *from, ucover *at, int ldir, const transmatrix& T) {
540 // println(hlog, from ? from->where : (heptagon*)nullptr, " -> ", at->where, " (", i, ")", " (reverse ", at->where->c.spin(i), ")");
541 if(camefrom.count(at->where)) {
542 vector<int> path;
543 vector<int> rpath;
544
545 while(at->where != u->where) {
546 int d = camefrom[at->where];
547 // println(hlog, "from ", at->where, " going back ", d);
548 rpath.push_back(at->where->c.spin(d));
549 at = gen_adj(at, d);
550 }
551 while(!rpath.empty()) { path.push_back(rpath.back()); rpath.pop_back(); }
552 path.push_back(ldir);
553
554 int st = 0;
555
556 while(from->where != u->where) {
557 int d = camefrom[from->where];
558 // println(hlog, "from ", from->where, " going ", d);
559 st++; if(st == 10) exit(1);
560 path.push_back(d);
561 from = gen_adj(from, d);
562 }
563
564 if(false) {
565 println(hlog, "path = ", path);
566 }
567
568 record_loop_verify(u, loop, path, hr_exception("wall in loops_general"));
569 }
570 else {
571 camefrom[at->where] = ldir;
572 visited.emplace_back(at, T);
573 }
574 };
575
576 visit(nullptr, u, -1, Id);
577 for(int vi=0; vi<isize(visited); vi++) {
578 auto at = visited[vi].first;
579 auto Ti = visited[vi].second;
580
581 for(int j=0; j<at->where->type; j++) {
582 if(j == camefrom[at->where]) continue;
583 auto u2 = gen_adj(at, j);
584 if(u2->iswall()) continue;
585 transmatrix Tj = Ti * adj(at, j);
586 bool adj = false;
587 for(auto v: cgi.heptshape->vertices_only) for(auto v1: cgi.heptshape->vertices_only)
588 if(hdist(M * v1, Tj * v) < 1e-3) adj = true;
589
590 if(adj) visit(at, u2, at->where->c.spin(j), Tj);
591 }
592 }
593 }
594 }
595
unify_loopshr::notknot::hrmap_notknot596 void unify_loops(ucover *u) {
597 /* try to make it finite */
598 bool special = false;
599
600 if(base_map == "" && base == gNil) {
601 special = true;
602 record_loop_if_nowall(u, loop, {0, 0, 2, 2, 2, 3, 3, 5, 5, 5});
603
604 if(u->where->zebraval & 16) {
605 for(int dir: {0,1,2})
606 record_loop_verify(u, nilv::nilperiod[dir], {dir}, hr_exception("16 failed"));
607 }
608 }
609
610 #if CAP_SOLV
611 if(base_map == "" && base == gArnoldCat) {
612
613 if(u->where->zebraval & 16) {
614 for(int dir: {0,4,5}) {
615 int steps = dir ? asonov::period_xy : asonov::period_z;
616 if(dir) steps *= 2;
617 record_loop_verify(u, steps, {dir}, hr_exception("16 failed"));
618 }
619 }
620
621 if(u->where->zebraval & 128) {
622 for(int a=0; a<2; a++) {
623 vector<int> myloop;
624 myloop.push_back(0);
625
626 auto add_shift = [&] (int x, int y, int lev) {
627 if(a) swap(x, y);
628 while(lev>0) lev--, tie(x,y) = make_pair(x*2-y, y-x);
629 while(lev<0) lev++, tie(x,y) = make_pair(x+y, x+2*y);
630 while(x>0) x--, myloop.push_back(4);
631 while(y>0) y--, myloop.push_back(5);
632 while(x<0) x++, myloop.push_back(10);
633 while(y<0) y++, myloop.push_back(11);
634 };
635
636 auto p = asonov::period_xy;
637
638 add_shift(p-2, 0, 1);
639 myloop.push_back(6);
640 myloop.push_back(6);
641 add_shift(2, 0, -1);
642 myloop.push_back(0);
643 myloop.push_back(0);
644 add_shift(-2, 0, 1);
645 myloop.push_back(6);
646 myloop.push_back(6);
647 add_shift(2-p, 0, -1);
648 myloop.push_back(0);
649
650 record_loop_verify(u, 1, myloop, hr_exception("128 failed"));
651 }
652 }
653 }
654 #endif
655
656 if(base == gCubeTiling) {
657 special = true;
658 record_loop_if_nowall(u, loop, {0, 0, 1, 1, 3, 3, 4, 4});
659 }
660
661 if(base == gCell120) {
662 special = true;
663 int t = u->where->type;
664 for(int i=0; i<t; i++)
665 for(int j=0; j<i; j++) {
666 auto u1 = u->ptr[i];
667 auto u2 = u->ptr[j];
668 if(u1->nowall()) continue;
669 if(u2->nowall()) continue;
670 auto ucur = u;
671 auto ulast = (ucover*) nullptr;
672
673 vector<int> myloop;
674
675 for(int step=0; step<5; step++) {
676 for(int i=0; i<t; i++) {
677 auto ucand = ucur->ptr[i];
678 if(ucand && isNeighbor(ucand->where->c7, u1->where->c7) && isNeighbor(ucand->where->c7, u2->where->c7) && ucand != ulast) {
679 myloop.push_back(i);
680 ulast = ucur, ucur = ucand;
681 goto next_step;
682 }
683 }
684 goto fail;
685 next_step: ;
686 }
687
688 record_loop(u, loop, myloop);
689 fail: ;
690 }
691 }
692
693 if(loop && !special)
694 unify_loops_general(u);
695
696 if(u->where == all[0]->where)
697 for(auto& lo: to_unloop) {
698 record_loop_verify(u, 1, lo, hr_exception("loop-to-unloop goes through a wall"));
699 }
700 }
701
702 map<heptagon*, int> indices;
703
hrmap_notknothr::notknot::hrmap_notknot704 hrmap_notknot() {
705
706 try {
707 if(base_map != "") {
708 dynamicval<eGeometry> dg(geometry, geometry);
709 mapstream::loadMap(base_map);
710 base = geometry;
711 create_notknot();
712 euc = currentmap;
713 for(hrmap*& m: allmaps) if(m == euc) m = NULL;
714 }
715 else {
716 dynamicval<eGeometry> dg(geometry, base);
717 initcells(); euc = currentmap;
718 for(hrmap*& m: allmaps) if(m == euc) m = NULL;
719 }
720
721 int i = 0;
722
723 all.emplace_back(new ucover(create_under(), 0));
724 all[0]->index = 0;
725 all[0]->parentdir = -1;
726
727 if(all[0]->where->zebraval & 1)
728 throw hr_exception("error: starting inside a wall");
729
730 remove_marked_walls = false;
731 bool first = true;
732
733 back:
734
735 while(true) {
736
737 /* handle all known unifications */
738 if(!unify.empty()) {
739 ucover *uf, *ut;
740 tie(uf, ut) = unify.back();
741 unify.pop_back();
742 while(uf->merged_into) uf = uf->merged_into;
743 while(ut->merged_into) ut = ut->merged_into;
744 if(uf == ut) continue;
745 if(!uf || !ut) println(hlog, "null unified");
746 /* we always keep the one with the lower index */
747 if(uf->index < ut->index) swap(uf, ut);
748
749 /* if a knot is removed, remove the other copy */
750 if(uf->iswall() && ut->nowall())
751 uf->state &=~ 1;
752
753 uf->state |= 2; uf->merged_into = ut;
754 if(uf->where != ut->where)
755 throw hr_exception("where confusion");
756 for(int d=0; d<uf->where->type; d++) {
757 cmov(uf->where, d);
758 auto d1 = uf->where->c.spin(d);
759 if(uf->ptr[d]) {
760 if(!ut->ptr[d]) {
761 /* was a known connection in uf, but not in ut -- reconnect it to ut */
762 uf->ptr[d]->ptr[d1] = ut;
763 ut->ptr[d] = uf->ptr[d];
764 uf->ptr[d] = nullptr;
765 }
766 else {
767 /* in some direction, connections for both uf and ut are already known, so unify them too */
768 add_to_unify(uf->ptr[d], ut->ptr[d]);
769 uf->ptr[d]->ptr[d1] = nullptr;
770 uf->ptr[d] = nullptr;
771 }
772 }
773 }
774 continue;
775 }
776
777 /* handle creation and loops */
778
779 if(i >= isize(all)) break;
780 auto u = all[i++];
781 if(u->state != 0) continue;
782 if(i > terminate_at) { u->state |= 4; continue; }
783
784 for(int k=0; k<u->where->type; k++) gen_adj(u, k);
785
786 unify_homotopies(u);
787
788 if(!uloops.count(u->where)) {
789 uloops[u->where] = {};
790 unify_loops(u);
791 // println(hlog, "loops recorded for ", u->where, ": ", isize(uloops[u->where]));
792 }
793
794 for(auto& myloop: uloops[u->where])
795 if(!collapse_loop(u, 1, myloop))
796 throw hr_exception("invalid loop recorded");
797
798 if(u->where == all[0]->where) {
799 vector<int> pathback;
800 auto uc = u;
801 while(uc->parentdir != -1) {
802 pathback.push_back(uc->parentdir);
803 uc = uc->ptr[(int) uc->parentdir];
804 }
805 // println(hlog, "pathback = ", pathback);
806
807 if(loop_any) {
808 auto us = all[0];
809 for(int it=1; it<loop_any; it++) {
810 uc = u;
811 while(uc->parentdir != -1) {
812 us = gen_adj(us, uc->parentdir);
813 uc = uc->ptr[(int) uc->parentdir];
814 }
815 }
816 add_to_unify(us, u);
817 }
818 }
819 }
820
821 /* make the walls single-colored */
822
823 println(hlog, "single-colored");
824
825 for(int i=0; i<isize(all); i++) {
826 auto u = all[i];
827 if(u->state != 0) continue;
828
829 if(u->where->zebraval & 32) {
830 for(int k=0; k<u->where->type; k++) {
831 auto uk = gen_adj(u, k);
832 if(uk->state != 0) continue;
833 if(uk->where->zebraval & 32)
834 funion(u, uk);
835 }
836 }
837
838 /* convex corners */
839 for(int k=0; k<u->where->type; k++)
840 for(int l=0; l<u->where->type; l++) {
841 auto uk = gen_adj(u, k);
842 if(uk->state != 0) continue;
843 auto ul = gen_adj(u, l);
844 if(ul->state != 0) continue;
845 if(base == gCubeTiling || base == gNil) {
846 auto ukl = gen_adj(uk, l);
847 auto ulk = gen_adj(ul, k);
848 if(ukl->where == ulk->where && ukl->state != 0)
849 funion(ukl, ulk);
850 }
851 else {
852 for(int k1=0; k1<u->where->type; k1++)
853 for(int l1=0; l1<u->where->type; l1++) {
854 auto ukl = gen_adj(uk, l1);
855 auto ulk = gen_adj(ul, k1);
856 if(ukl->where == ulk->where && ukl->state != 0 &&
857 eqmatrix(adj(u, k) * adj(uk, l1), adj(u, l) * adj(ul, k1))
858 )
859 funion(ukl, ulk);
860 if(base == gCell600 && isNeighbor(ukl->where->c7, ulk->where->c7) && ukl->state != 0 && ulk->state != 0)
861 funion(ukl, ulk);
862
863 if(base == gCell600 && ulk->nowall() && ukl->iswall()) {
864 for(int m1=0; m1<u->where->type; m1++) {
865 auto ulkm = gen_adj(ulk, m1);
866 if(ulkm->where == ukl->where) funion(ulkm, ukl);
867 }
868 }
869 }
870 }
871 }
872
873 /* flat areas */
874 #if CAP_SOLV
875 if(!asonov::in())
876 #endif
877 for(int k=0; k<u->where->type; k++)
878 for(int l=0; l<u->where->type; l++) {
879 auto uk = gen_adj(u, k);
880 if(uk->state != 0) continue;
881 auto ul = gen_adj(u, l);
882 if(ul->nowall()) continue;
883 auto ukl = gen_adj(uk, l);
884 if(ukl->nowall()) continue;
885 funion(ul, ukl);
886 }
887
888 /* concave corners */
889 for(int k=0; k<u->where->type; k++)
890 for(int l=0; l<u->where->type; l++)
891 if(adjacent_face(u, k, l)) {
892 auto uk = gen_adj(u, k);
893 if(uk->nowall()) continue;
894 auto ul = gen_adj(u, l);
895 if(ul->nowall()) continue;
896 funion(ul, uk);
897 }
898
899 if(base == gCell600)
900 for(int k=0; k<u->where->type; k++)
901 for(int l=0; l<u->where->type; l++)
902 if(adjacent_face(u, k, l)) {
903 auto uk = gen_adj(u, k);
904 if(uk->nowall()) continue;
905 auto ul = gen_adj(u, l);
906 if(ul->iswall()) continue;
907
908 for(int m=0; m<u->where->type; m++)
909 if(adjacent_matrix(adj(u, k), adj(u, l) * adj(ul, m))) {
910 auto um = gen_adj(ul, m);
911 if(um->nowall()) continue;
912 funion(um, uk);
913 }
914 }
915 }
916
917 /* statistics */
918 int lives = 0, walls = 0, overflow = 0, merged = 0;
919
920 for(auto v: all) {
921 if(v->state == 0) lives++;
922 if(v->iswall()) walls++;
923 if(v->ismerged()) merged++;
924 if(v->isover()) overflow++;
925 }
926
927 set<heptagon*> wheres;
928
929 println(hlog, "lives = ", lives);
930 println(hlog, "walls = ", walls);
931 println(hlog, "merged = ", merged);
932 println(hlog, "overflow = ", overflow);
933 println(hlog, "total = ", isize(all));
934
935 /* create the result map */
936 for(int i=0; i<isize(all); i++) {
937 auto u = all[i];
938 if(u->ismerged()) continue;
939 if(u->state == 0) wheres.insert(all[i]->where);
940 u->result = tailored_alloc<heptagon> (S7);
941 u->result->c7 = newCell(S7, u->result);
942 indices[u->result] = i;
943 }
944
945 println(hlog, "wheres = ", isize(wheres), " : ", lives * 1. / isize(wheres));
946
947 for(int i=0; i<isize(all); i++) {
948 auto u = all[i];
949 if(u->ismerged()) continue;
950
951 for(int d=0; d<S7; d++) {
952 cmov(u->where, d);
953 auto d1 = u->where->c.spin(d);
954 if(u->ptr[d] && u->ptr[d]->result == nullptr)
955 throw hr_exception(lalign(0, "connection to null in state ", u->ptr[d]->state, " from state ", u->state, " i=", i, " .. ", u->ptr[d]->index));
956 if(u->ptr[d] && u->ptr[d]->ptr[d1] != u)
957 throw hr_exception("wrong connection");
958 if(u->ptr[d])
959 u->result->c.connect(d, u->ptr[d]->result, d1, false);
960 else
961 u->result->c.connect(d, u->result, d, false);
962 }
963 }
964
965 for(int k=0; k<23; k++) hrand(5);
966
967 int colors_used = 0;
968
969 for(int i=0; i<isize(all); i++)
970 all[i]->wallcolor = 0;
971
972 for(int i=0; i<isize(all); i++)
973 if(all[i]->iswall() && !all[i]->ismerged())
974 ufind(all[i])->wallcolor++;
975
976 map<int, int> sizes;
977
978 for(int i=0; i<isize(all); i++)
979 if(all[i]->iswall() && ufind(all[i]) == all[i] && all[i]->wallcolor)
980 colors_used++,
981 sizes[all[i]->wallcolor]++;
982
983 for(auto p: sizes)
984 println(hlog, "size = ", p.first, " times ", p.second);
985
986 println(hlog, "colors_used = ", colors_used);
987
988 if(first && self_hiding) {
989 ucover *what = nullptr;
990 for(int i=0; i<isize(all); i++)
991 if(all[i]->iswall() && all[i]->tohide() && !all[i]->ismerged())
992 what = ufind(all[i]);
993
994 for(int i=0; i<isize(all); i++)
995 if(all[i]->iswall() && ufind(all[i]) == what)
996 all[i]->state &=~ 9;
997
998 println(hlog, "removed one knot!");
999
1000 first = false; i = 0; remove_marked_walls = true;
1001 goto back;
1002 }
1003
1004 for(int i=0; i<isize(all); i++)
1005 if(all[i]->iswall() && ufind(all[i]) == all[i] && all[i]->wallcolor) {
1006 all[i]->wallcolor = hrand(0x1000000) | 0x404040,
1007 all[i]->wallcolor2 = hrand(0x1000000) | 0x404040;
1008 }
1009
1010 for(int i=0; i<isize(all); i++)
1011 if((all[i]->where->zebraval & 32) && ufind(all[i]) == all[i]) {
1012 auto& w = all[i]->wallcolor;
1013 all[i]->wallcolor = (hrand(0x1000000) << 8) | 0x01;
1014 switch(hrand(6)) {
1015 case 0: w |= 0xFF000000; break;
1016 case 1: w |= 0x00FF0000; break;
1017 case 2: w |= 0x0000FF00; break;
1018 case 3: w |= 0xC0C00000; break;
1019 case 4: w |= 0x00C0C000; break;
1020 case 5: w |= 0xC000C000; break;
1021 }
1022 }
1023
1024 for(int i=0; i<isize(all); i++) {
1025 auto u = all[i];
1026 if(!u->result) continue;
1027 cell *c = u->result->c7;
1028 setdist(c, 7, c);
1029 c->land = laCanvas;
1030 if(u->iswall()) {
1031 c->wall = waWaxWall;
1032 c->landparam = hrand(100) < secondary_percentage ? ufind(u)->wallcolor2 : ufind(u)->wallcolor;
1033 if(ufind(u)->nowall()) println(hlog, "connected to state ", ufind(u)->state);
1034 // if(!(c->landparam & 0x404040)) println(hlog, "color found ", c->landparam);
1035 }
1036 else if(u->isover())
1037 c->wall = waBigTree;
1038 else
1039 c->wall = waNone;
1040
1041 if(all[i]->where->zebraval & 32)
1042 ray::volumetric::vmap[c] = ufind(u)->wallcolor ^ ((hrand(0x1000000) & 0x3F3F3F) << 8);
1043 else
1044 ray::volumetric::vmap[c] = 0x00000001;
1045 }
1046
1047 } catch(const hr_exception& s) {
1048 println(hlog, "exception: ", s.what());
1049 throw;
1050 }
1051 }
1052
add_foghr::notknot::hrmap_notknot1053 void add_fog() {
1054 vector<color_t> cols = {0xFF000001, 0xC0C00001, 0x00FF0001, 0x00C0C001, 0x0000FF01, 0xC000C001};
1055 int id = 0;
1056 map<cell*, pair<int, int> > dist;
1057 vector<cell*> lst;
1058
1059 auto color = [&] (cell *c, color_t col, int d) {
1060 if(!dist.count(c)) dist[c] = {d, 0};
1061 auto& p = dist[c];
1062 if(p.first == d) {
1063 if(!p.second) lst.push_back(c);
1064 p.second++;
1065 auto& vm = ray::volumetric::vmap[c];
1066 if(p.second == 1) vm = col;
1067 else vm = gradient(vm, col, 0, 1, p.second);
1068 }
1069 };
1070
1071 int qty = 0;
1072 for(int i=0; i<isize(all); i++)
1073 if(all[i]->result)
1074 if(all[i]->where->c7->wall == waFloorA)
1075 qty++;
1076
1077 for(int i=0; i<isize(all); i++)
1078 if(all[i]->result)
1079 if(all[i]->where->c7->wall == waFloorA) {
1080 cell *c = all[i]->result->c7;
1081 if(dist.count(c)) continue;
1082 int idd = (id++) % isize(cols);
1083 color_t col = rainbow_color(1, idd * 1. / qty);
1084 col <<= 8; col |= 1;
1085
1086 color(c, cols[idd], 0);
1087 }
1088
1089 for(int i=0; i<isize(lst); i++) {
1090 auto c = lst[i];
1091 auto col = ray::volumetric::vmap[c];
1092 int d = dist[c].first;
1093 forCellCM(c1, c)
1094 if(c1->wall == waNone)
1095 color(c1, col, d+1);
1096 }
1097
1098 for(int i=0; i<isize(lst); i++) {
1099 auto c = lst[i];
1100 ray::volumetric::vmap[c] ^= ((hrand(0x1000000) & 0x3F3F3F) << 8);
1101 }
1102
1103 ray::volumetric::enable();
1104 }
1105
relative_matrixhhr::notknot::hrmap_notknot1106 transmatrix relative_matrixh(heptagon *h2, heptagon *h1, const hyperpoint& hint) override {
1107 return Id;
1108 }
1109
adjhr::notknot::hrmap_notknot1110 transmatrix adj(heptagon *h, int i) override {
1111 return adj(all[indices[h]], i);
1112 }
1113
adjhr::notknot::hrmap_notknot1114 transmatrix adj(cell *c, int i) override {
1115 return adj(c->master, i);
1116 }
1117
~hrmap_notknothr::notknot::hrmap_notknot1118 ~hrmap_notknot() {
1119 for(auto uc: all) {
1120 if(uc && uc->result) {
1121 tailored_delete(uc->result->c7);
1122 tailored_delete(uc->result);
1123 }
1124 if(uc) delete uc;
1125 }
1126 delete euc;
1127 }
1128
1129 };
1130
__anon07a57b340702null1131 auto h = addHook(hooks_newmap, 0, [] {
1132 // gen_trifoil();
1133 if(geometry == gNotKnot) {
1134 return (hrmap*) new hrmap_notknot;
1135 }
1136 return (hrmap*) nullptr;
1137 });
1138
create_notknot()1139 void create_notknot() {
1140 if(true) {
1141 dynamicval<eGeometry> b(geometry, base);
1142 check_cgi();
1143 cgi.require_basics();
1144 cgi.require_shapes();
1145 cgi.require_usershapes();
1146 }
1147 if(gNotKnot == eGeometry(-1)) {
1148 ginf.push_back(ginf[base]);
1149 gNotKnot = eGeometry(isize(ginf) - 1);
1150 }
1151 else ginf[gNotKnot] = ginf[base];
1152 auto& gi = ginf.back();
1153 gi.flags |= qANYQ | qBOUNDED | qEXPERIMENTAL | qPORTALSPACE;
1154 gi.quotient_name = "notknot";
1155 gi.shortname = "notknot";
1156 gi.menu_displayed_name = "notknot";
1157 }
1158
regenerate()1159 void regenerate() {
1160 if(geometry == gNotKnot && game_active) {
1161 stop_game();
1162 start_game();
1163 }
1164 }
1165
1166 bool show_selfhiding = true;
1167
launch_euc()1168 void launch_euc() {
1169 stop_game();
1170 set_geometry(gCubeTiling);
1171 base = gCubeTiling;
1172 base_map = "";
1173 to_unloop.clear();
1174 create_notknot();
1175 loop = 3;
1176 secondary_percentage = 10;
1177 show_selfhiding = true;
1178 set_geometry(gNotKnot);
1179 start_game();
1180 ray::reset_raycaster();
1181 ray::volumetric::on = false;
1182 ray::exp_decay_poly = 30;
1183 pmodel = mdPerspective;
1184 }
1185
launch_nil()1186 void launch_nil() {
1187 stop_game();
1188 set_geometry(gNil);
1189 base_map = "";
1190 base = geometry;
1191 to_unloop.clear();
1192 secondary_percentage = 0;
1193 nilv::nilwidth = .25;
1194 nilv::nilperiod = make_array(8, 8, 8);
1195 nilv::set_flags();
1196 create_notknot();
1197 show_selfhiding = false;
1198 loop = 6;
1199 set_geometry(gNotKnot);
1200 start_game();
1201 ray::reset_raycaster();
1202 ray::volumetric::on = true;
1203 ray::exp_decay_poly = 3;
1204 camera_speed = 1;
1205 pmodel = mdGeodesic;
1206 }
1207
launch_sphere()1208 void launch_sphere() {
1209 stop_game();
1210 set_geometry(gCell120);
1211 base_map = "spherring.lev";
1212 to_unloop.clear();
1213 vector<int> v; for(int i=0; i<60; i++) v.push_back(7);
1214 to_unloop.emplace_back(v);
1215 show_selfhiding = true;
1216 secondary_percentage = 0;
1217 loop = 0;
1218 set_geometry(gNotKnot);
1219 start_game();
1220 ray::reset_raycaster();
1221 ray::volumetric::on = false;
1222 ray::exp_decay_poly = 10;
1223 camera_speed = 1;
1224 mapeditor::drawplayer = false;
1225 pmodel = mdPerspective;
1226 ((hrmap_notknot*)currentmap)->add_fog();
1227 }
1228
launch_sphereknot()1229 void launch_sphereknot() {
1230 stop_game();
1231 set_geometry(gCell600);
1232 base_map = "spherknot.lev";
1233 to_unloop.clear();
1234 secondary_percentage = 0;
1235 show_selfhiding = true;
1236 loop = 3;
1237 set_geometry(gNotKnot);
1238 start_game();
1239 ray::reset_raycaster();
1240 ray::volumetric::on = false;
1241 ray::exp_decay_poly = 10;
1242 camera_speed = 1;
1243 mapeditor::drawplayer = false;
1244 pmodel = mdPerspective;
1245 ((hrmap_notknot*)currentmap)->add_fog();
1246 }
1247
1248 #if CAP_SOLV
launch_solv()1249 void launch_solv() {
1250 stop_game();
1251 set_geometry(gArnoldCat);
1252 base_map = "";
1253 base = geometry;
1254 to_unloop.clear();
1255 secondary_percentage = 0;
1256 vid.binary_width = .25;
1257 margin = 3;
1258 asonov::period_xy = 8;
1259 asonov::period_z = 3;
1260 loop = 6;
1261 asonov::set_flags();
1262 create_notknot();
1263 show_selfhiding = false;
1264 set_geometry(gNotKnot);
1265 start_game();
1266 ray::reset_raycaster();
1267 ray::exp_decay_poly = 10;
1268 camera_speed = 1;
1269 pmodel = mdGeodesic;
1270 ((hrmap_notknot*)currentmap)->add_fog();
1271 }
1272 #endif
1273
show()1274 void show() {
1275 cmode = sm::SIDE | sm::MAYDARK;
1276 gamescreen(0);
1277 dialog::init(XLAT("notknot"), 0xFFFFFFFF, 150, 0);
1278
1279 dialog::addItem("available scenes", 'a');
1280 dialog::add_action_push([] {
1281 cmode = sm::SIDE | sm::MAYDARK;
1282 gamescreen(0);
1283
1284 dialog::init(XLAT("notknot scenes"), 0xFFFFFFFF, 150, 0);
1285
1286 dialog::addItem("knot portal in Euclidean geometry", 'a');
1287 dialog::add_action(launch_euc);
1288
1289 dialog::addItem("Penrose staircase portal in Nil geometry", 'b');
1290 dialog::add_action(launch_nil);
1291
1292 dialog::addItem("great circle portal in spherical geometry", 'c');
1293 dialog::add_action(launch_sphere);
1294
1295 dialog::addItem("knotted portal in spherical geometry", 'd');
1296 dialog::add_action(launch_sphereknot);
1297
1298 #if CAP_SOLV
1299 dialog::addItem("a portal in Solv geometry", 'e');
1300 dialog::add_action(launch_solv);
1301 #endif
1302
1303 dialog::display();
1304 });
1305
1306 if(loop) add_edit(loop);
1307 if(base == gCubeTiling && base_map == "") {
1308 add_edit(margin);
1309 add_edit(knotsize);
1310 }
1311 if(show_selfhiding) add_edit(self_hiding);
1312
1313 if(nil) menuitem_nilwidth('w');
1314 if(sol) menuitem_binary_width('w');
1315
1316 if(base != gCubeTiling) {
1317 dialog::addBoolItem("fog enabled", ray::volumetric::on, 'f');
1318 dialog::add_action([] {
1319 ray::volumetric::on = !ray::volumetric::on;
1320 ray::reset_raycaster();
1321 if(sphere && ray::volumetric::on)
1322 ((hrmap_notknot*)currentmap)->add_fog();
1323 });
1324 }
1325
1326 dialog::addBreak(100);
1327
1328 dialog::addItem(XLAT("configure raycasting"), 'A');
1329 dialog::add_action_push(ray::configure);
1330
1331 add_edit_fov('f');
1332
1333 #if CAP_VR
1334 dialog::addBoolItem(XLAT("VR settings"), vrhr::active(), 'v');
1335 dialog::add_action_push(vrhr::show_vr_settings);
1336 #endif
1337
1338 dialog::addBack();
1339 dialog::display();
1340 }
1341
o_key(o_funcs & v)1342 void o_key(o_funcs& v) {
1343 if(geometry == gNotKnot) v.push_back(named_dialog("notknot", show));
1344 }
1345
1346 bool do_check_cycle;
1347 cell *startcell, *current;
1348 vector<int> dirs;
1349
check_cycle()1350 void check_cycle() {
1351 if(!do_check_cycle) return;
1352 if(!current) {
1353 auto s = currentmap->allcells()[0];
1354 println(hlog, "starting the cycle, ", cwt.at == s);
1355 startcell = current = cwt.at = s;
1356 }
1357 if(cwt.at != current) {
1358 forCellIdEx(c1, i, current)
1359 if(c1 == cwt.at) {
1360 dirs.push_back(i);
1361 current = cwt.at;
1362 startcell->item = itGold;
1363 println(hlog, "dirs = ", dirs, " finished = ", startcell == current);
1364 string dirstr;
1365 for(int d: dirs)
1366 if(d < 10)
1367 dirstr += char('0' + d);
1368 else
1369 dirstr += char('a' + d-10);
1370 addMessage("this loop can be identified with identity using: -nk-unloop 1 " + dirstr);
1371 }
1372 }
1373 }
1374
gen_knot()1375 void gen_knot() {
1376 for(cell *c: currentmap->allcells())
1377 c->wall = waNone;
1378
1379 cell *last = nullptr;
1380
1381 for(int i=0; i<3600; i++) {
1382 ld alpha = i * degree / 10;
1383 ld q = sqrt(2)/2;
1384 hyperpoint h = hyperpoint(q*cos(alpha*2), q*sin(alpha*2), q*cos(alpha*3), q*sin(alpha*3));
1385 cell *b = currentmap->gamestart();
1386 virtualRebase(b, h);
1387 b->wall = waPlatform;
1388 if(b != last) {
1389 if(!last) println(hlog, "start at ", b);
1390 if(last) println(hlog, "i=", i, ": to ", b, " isN = ", isNeighbor(last, b));
1391 last = b;
1392 }
1393 }
1394 }
1395
nk_launch()1396 void nk_launch() {
1397 margin = 4;
1398 mapeditor::drawplayer = false;
1399 stop_game();
1400 firstland = specialland = laCanvas;
1401 set_geometry(gNotKnot);
1402 sightranges[geometry] = .5;
1403 ray::max_cells = 600000;
1404 smooth_scrolling = 1;
1405 camera_speed = 10;
1406 // panini_alpha = 1;
1407 // fov = 150;
1408 ray::exp_decay_poly = 30;
1409 ray::fixed_map = true;
1410 ray::max_iter_iso = 80;
1411 showstartmenu = false;
1412 #if CAP_VR
1413 vrhr::hsm = vrhr::eHeadset::holonomy;
1414 vrhr::eyes = vrhr::eEyes::truesim;
1415 vrhr::cscr = vrhr::eCompScreen::eyes;
1416 vrhr::absolute_unit_in_meters = 0.2;
1417 #endif
1418 }
1419
1420 auto shot_hooks = addHook(hooks_initialize, 100, create_notknot)
__anon07a57b340a02null1421 + addHook(hooks_welcome_message, 100, [] {
1422 if(geometry == gNotKnot) {
1423 addMessage("Welcome to Notknot! Press 'o' for options");
1424 return true;
1425 }
1426 return false;
1427 })
__anon07a57b340b02null1428 + addHook(hooks_args, 100, [] {
1429 using namespace arg;
1430
1431 if(0) ;
1432 else if(argis("-nkbase")) {
1433 base = geometry;
1434 create_notknot();
1435 }
1436 else if(argis("-nkbasemap")) {
1437 shift(); base_map = args();
1438 set_geometry(gNotKnot);
1439 }
1440 else if(argis("-nk-volumetric")) {
1441 start_game();
1442 ((hrmap_notknot*)currentmap)->add_fog();
1443 }
1444 else if(argis("-nk-genknot")) {
1445 start_game();
1446 gen_knot();
1447 }
1448 else if(argis("-nk-findloop")) {
1449 do_check_cycle = true;
1450 }
1451 else if(argis("-nk-unloop")) {
1452 shift();
1453 int copies = argi();
1454 shift();
1455 vector<int> v;
1456 for(int i=0; i<copies; i++)
1457 for(char c: args())
1458 if(c >= '0' && c <= '9') v.push_back(c - '0');
1459 else v.push_back(c - 'a' + 10);
1460 to_unloop.push_back(v);
1461 println(hlog, "pushed to to_unloop: ", v);
1462 }
1463 else if(argis("-nk-launch"))
1464 nk_launch();
1465 else return 1;
1466 return 0;
1467 })
1468
1469 + addHook(hooks_o_key, 80, o_key)
1470 + addHook(hooks_frame, 100, check_cycle)
__anon07a57b340c02(string& s) 1471 + addHook(hooks_cgi_string, 100, [] (string& s) {
1472 if(geometry == gNotKnot) {
1473 s += " base: ";
1474 dynamicval<eGeometry> b(geometry, base);
1475 s += cgi_string();
1476 }
1477 })
__anon07a57b340d02null1478 + addHook(hooks_configfile, 100, [] {
1479 param_i(loop, "nk_loop")
1480 ->editable(1, 5, 1, "notknot order", "How many times do we need to go around the knot to get back.", 'o')
1481 ->set_sets([] { dialog::bound_low(1); dialog::bound_up(5); })
1482 ->set_reaction(regenerate);
1483 param_i(margin, "nk_margin")
1484 ->editable(0, 10, 1, "notknot margins", "Empty space close to the walls.", 'm')
1485 ->set_sets([] { dialog::bound_low(0); dialog::bound_up(10); })
1486 ->set_reaction(regenerate);
1487 param_i(knotsize, "nk_knotsize")
1488 ->editable(0, 10, 1, "notknot size", "Size of the knot.", 's')
1489 ->set_sets([] { dialog::bound_low(2); dialog::bound_up(5); })
1490 ->set_reaction(regenerate);
1491 param_i(terminate_at, "nk_terminate")->set_reaction(regenerate);
1492 param_i(secondary_percentage, "nk_secondary");
1493 param_b(self_hiding, "selfhide")
1494 ->editable("self-hiding knot", 'h')
1495 ->set_reaction(regenerate);
1496 param_i(loop_any, "nk_loopany");
1497 })
1498 #ifndef NOTKNOT
__anon07a57b341102(string s, vector<tour::slide>& v) 1499 + addHook_rvslides(180, [] (string s, vector<tour::slide>& v) {
1500 if(s != "mixed") return;
1501 v.push_back(tour::slide{
1502 "weird portals", 10, tour::LEGAL::NONE | tour::QUICKSKIP,
1503 "Some experiments with weird portals. Press '5' to change between available experiments.\n"
1504 ,
1505 [] (tour::presmode mode) {
1506 slide_url(mode, 'k', "knotted portal (YouTube)", "https://www.youtube.com/watch?v=eb2DhCcGH7U");
1507 slide_url(mode, 'h', "self-hiding knot portal (YouTube)", "https://www.youtube.com/watch?v=vFLZ2NGtuGw");
1508 slide_url(mode, 'n', "non-Euclidean portal in Nil (YouTube)", "https://www.youtube.com/watch?v=2K-v8tK68AE");
1509 slide_url(mode, 's', "spherical portal (YouTube)", "https://www.youtube.com/watch?v=PerPeQFu5gw");
1510 slide_url(mode, 'c', "Cat Portal in Solv (YouTube)", "https://www.youtube.com/watch?v=CGiSxC9B6i0");
1511 setCanvas(mode, '0');
1512 using namespace tour;
1513 if(mode == pmStart) {
1514 slide_backup(margin);
1515 slide_backup(mapeditor::drawplayer);
1516 slide_backup(firstland);
1517 slide_backup(specialland);
1518 slide_backup(ray::max_cells);
1519 slide_backup(smooth_scrolling);
1520 slide_backup(camera_speed);
1521 slide_backup(ray::exp_decay_poly);
1522 slide_backup(ray::fixed_map);
1523 slide_backup(ray::max_iter_iso);
1524 #if CAP_VR
1525 slide_backup(vrhr::hsm);
1526 slide_backup(vrhr::eyes);
1527 slide_backup(vrhr::cscr);
1528 slide_backup(vrhr::absolute_unit_in_meters);
1529 #endif
1530
1531 on_restore([] { nilv::set_flags(); asonov::set_flags(); });
1532
1533 slide_backup(nilv::nilwidth);
1534 slide_backup(nilv::nilperiod);
1535
1536 slide_backup(vid.binary_width);
1537 slide_backup(asonov::period_xy);
1538 slide_backup(asonov::period_z);
1539
1540 nk_launch();
1541 start_game();
1542 loop = 2;
1543 }
1544 if(mode == tour::pmKey) {
1545 pushScreen(show);
1546 }
1547 }
1548 });
1549 })
1550 #endif
1551 ;
1552
1553 #ifdef NOTKNOT
1554 auto hook1=
__anon07a57b341402null1555 addHook(hooks_config, 100, [] {
1556 if(arg::curphase == 1)
1557 conffile = "notknot.ini";
1558 if(arg::curphase == 2)
1559 nk_launch();
1560 });
1561 #endif
1562
1563 }
1564
1565 }
1566 #endif
1567