// Hyperbolic Rogue -- rule generator // Copyright (C) 2011-2021 Zeno Rogue, see 'hyper.cpp' for details /** \file rulegen3.cpp * \brief An algorithm to create strict tree rules for arb tessellations -- 3D parts */ #include "hyper.h" namespace hr { EX namespace rulegen { struct road_shortcut_trie_vertex { set> backpaths; map> children; }; EX map> road_shortcuts; int qroad; map qroad_for; map qroad_memo; EX void add_road_shortcut(tcell *s, tcell *t) { shared_ptr u; vector tpath; if(!road_shortcuts.count(s->id)) road_shortcuts[s->id] = make_shared(); u = road_shortcuts[s->id]; while(true) { // println(hlog, s, " dist=", s->dist, " parent = ", s->parent_dir, " vs ", t, " dist=", t->dist, " parent = ", t->parent_dir); if(s == t) { reverse(tpath.begin(), tpath.end()); auto& ba = u->backpaths; if(!ba.count(tpath)) qroad++, qroad_for[s->id]++; ba.insert(tpath); return; } if(s->dist >= t->dist) { twalker sw = s; get_parent_dir(sw); if(s->parent_dir == MYSTERY) throw hr_exception("unknown parent_dir (s) in add_road_shortcut"); if(!u->children.count(s->parent_dir)) u->children[s->parent_dir] = make_shared(); u = u->children[s->parent_dir]; s = s->move(s->parent_dir); } if(t->dist > s->dist) { twalker tw = t; get_parent_dir(tw); if(t->parent_dir == MYSTERY) throw hr_exception("unknown parent_dir (t) in add_road_shortcut"); tpath.push_back(t->c.spin(t->parent_dir)); t = t->move(t->parent_dir); } } } EX int newcon; EX void apply_road_shortcut(tcell *s) { auto& mem = qroad_memo[s]; if(mem == qroad_for[s->id]) return; mem = qroad_for[s->id]; shared_ptr u; if(!road_shortcuts.count(s->id)) return; u = road_shortcuts[s->id]; int q = tcellcount; while(true) { for(auto& v: u->backpaths) { auto s1 = s; for(auto x: v) { s1 = s1->cmove(x); be_solid(s1); } } twalker s0 = s; get_parent_dir(s0); if(!u->children.count(s->parent_dir)) break; u = u->children[s->parent_dir]; s = s->move(s->parent_dir); } static int qmax = 0; newcon += tcellcount - q; if(tcellcount > q + qmax) println(hlog, "road shortcuts created ", qmax = tcellcount-q, " new connections"); } /** next roadsign ID -- they start at -100 and go downwards */ int next_roadsign_id = -100; /** get the ID of a roadsign path */ EX map, int> roadsign_id; EX int get_roadsign(twalker what) { int dlimit = what.at->dist - 1; tcell *s = what.at, *t = what.peek(); apply_road_shortcut(s); vector result; while(s->dist > dlimit) { twalker s0 = s; get_parent_dir(s0); if(s->parent_dir == MYSTERY) throw hr_exception("parent_dir unknown"); result.push_back(s->parent_dir); s = s->move(s->parent_dir); result.push_back(s->dist - dlimit); } vector tail; while(t->dist > dlimit) { twalker t0 = t; get_parent_dir(t0); if(t->parent_dir == MYSTERY) throw hr_exception("parent_dir unknown"); tail.push_back(t->dist - dlimit); tail.push_back(t->c.spin(t->parent_dir)); t = t->move(t->parent_dir); } map visited; queue vqueue; auto visit = [&] (tcell *c, int dir) { if(visited.count(c)) return; visited[c] = dir; vqueue.push(c); }; visit(s, MYSTERY); while(true) { if(vqueue.empty()) throw hr_exception("vqueue empty"); tcell *c = vqueue.front(); if(c == t) break; vqueue.pop(); for(int i=0; itype; i++) if(c->move(i) && c->move(i)->dist <= dlimit) visit(c->move(i), c->c.spin(i)); } while(t != s) { add_road_shortcut(s, t); int d = visited.at(t); tail.push_back(t->dist - dlimit); tail.push_back(t->c.spin(d)); t = t->move(d); } reverse(tail.begin(), tail.end()); for(auto t: tail) result.push_back(t); if(roadsign_id.count(result)) return roadsign_id[result]; return roadsign_id[result] = next_roadsign_id--; } map, vector> > all_edges; EX vector>& check_all_edges(twalker cw, analyzer_state* a, int id) { auto& ae = all_edges[{cw.at->id, cw.spin}]; if(ae.empty()) { set seen; vector > visited; vector> ae1; auto visit = [&] (twalker tw, const transmatrix& T, int id, int dir) { if(seen.count(tw.at)) return; seen.insert(tw.at); auto& sh0 = currentmap->get_cellshape(tcell_to_cell[cw.at]); auto& sh1 = currentmap->get_cellshape(tcell_to_cell[tw.at]); int common = 0; vector kleinized; vector rotated; for(auto v: sh0.vertices_only) kleinized.push_back(kleinize(sh0.from_cellcenter * v)); for(auto w: sh1.vertices_only) rotated.push_back(kleinize(T*sh1.from_cellcenter * w)); for(auto v: kleinized) for(auto w: rotated) if(sqhypot_d(MDIM, v-w) < 1e-6) common++; if(flags & w_vertex_edges) { if(common < 1) { ae1.emplace_back(id, dir); return; } } else { if(common < 2) { ae1.emplace_back(id, dir); return; } } visited.emplace_back(tw, T); ae.emplace_back(id, dir); }; visit(cw, Id, -1, -1); for(int i=0; itype; j++) { visit(tw + j + wstep, visited[i].second * currentmap->adj(tcell_to_cell[tw.at], (tw+j).spin), i, j); } } if(flags & w_ae_extra_step) for(auto p: ae1) ae.push_back(p); println(hlog, "for ", tie(cw.at->id, cw.spin), " generated all_edges structure: ", ae, " of size ", isize(ae)); } return ae; } EX void cleanup3() { all_edges.clear(); roadsign_id.clear(); next_roadsign_id = -100; } int last_qroad; EX void check_road_shortcuts() { println(hlog, "road shortcuts = ", qroad, " treestates = ", isize(treestates), " roadsigns = ", next_roadsign_id); if(qroad > last_qroad) { println(hlog, "qroad_for = ", qroad_for); println(hlog, "newcon = ", newcon, " tcellcount = ", tcellcount); newcon = 0; clear_codes(); last_qroad = qroad; roadsign_id.clear(); next_roadsign_id = -100; throw rulegen_retry("new road shortcuts"); } } void genhoneycomb(string fname) { if(WDIM != 3) throw hr_exception("genhoneycomb not in honeycomb"); int qc = isize(t_origin); vector data; string side_data; map> rev_roadsign_id; for(auto& rs: roadsign_id) rev_roadsign_id[rs.second] = rs.first; int N = isize(treestates); using classdata = pair, int>; vector nclassify(N); for(int i=0; i= 0) print(hlog, " ", nclassify[r].first[0]); else print(hlog, " S", r); } println(hlog); } println(hlog); vector childpos; for(int i=0; i= 0) { data.push_back(nclassify[r].first[0]); } else { data.push_back(-1); auto& str = rev_roadsign_id[r]; bool next = true; for(auto ch: str) { if(next) side_data += ('a' + ch); next = !next; } side_data += ","; } } } childpos.push_back(isize(data)); shstream ss; ss.write(ss.get_vernum()); mapstream::save_geometry(ss); ss.write(fieldpattern::use_rule_fp); ss.write(fieldpattern::use_quotient_fp); ss.write(reg3::minimize_quotient_maps); auto& fp = currfp; hwrite_fpattern(ss, fp); vector root(qc, 0); for(int i=0; i