diff --git a/devmods/hatter.cpp b/devmods/hatter.cpp new file mode 100644 index 00000000..028243fa --- /dev/null +++ b/devmods/hatter.cpp @@ -0,0 +1,516 @@ +/** + * This program was used to generate the rule tables in apeirodic-hat.cpp. + * + * Some data was generated by this program itself, based on manual keyboard+mouse control. + * Some of minor manual control tools have been removed, but the process was as follows: + * + * * Run with `-symbol "12,6,4" -dual -canvas 101010 -smart 1 stamplen=0` and draw the hat shape, the table output is hatcorners + * * Place hats into clusters as shown in the paper, obtaining the table hats[0] + * * Place clusters into superclusters as shown in the paper, obtaining the table hats[1] + * * Repeat for hats[2], hats[3], hats[4] and hats[5] (the paper does not specify precisely the coordinates to arrange the clusters; but we can multiply the previous hats by scaling factor + * * for an approximate, and fix manually so that it matches) + * * 'CON Lx' lines state the rules deduced; the rules should be the same for L1 and L2 (except the matrix codes returned by matcode), so we conjecture that this set of rules is complete + * * Fill the table `hatid` to declare the correspondence between L1 and L2 matrices + * * Run again, and we get rules (prefixed by RULE1 and RULE0) + + **/ + +#include "../rogueviz/rogueviz.h" + +namespace rogueviz { + +int toplev = 5; + +vector hatcorners_add; + +vector hatcorners[2]; + +vector hats[8]; + +vector hattype; + +hyperpoint pt(ld x, ld y) { return hpxy(x, y); } + +transmatrix rot; + +transmatrix sca; + +transmatrix U; + +transmatrix mt(ld a, ld b, ld c, ld d, ld e, ld f, ld g, ld h, ld i) { + transmatrix T = Id; + T[0][0] = a; + T[0][1] = b; + T[0][2] = c; + T[1][0] = d; + T[1][1] = e; + T[1][2] = f; + T[2][0] = g; + T[2][1] = h; + T[2][2] = i; + return T; + } + +map hatid; + +void init() { + rot = Id; + hatcorners[0] = { + pt(-1.1160254038,1.4330127019), + pt(-0.0915063509,2.0245190528), + pt(0.2500000000,1.4330127019), + pt(-0.0915063509,0.8415063509), + pt(0.9330127019,0.2500000000), + pt(0.9330127019,-0.9330127019), + pt(0.2500000000,-0.9330127019), + pt(-0.0915063509,-1.5245190528), + pt(-1.1160254038,-0.9330127019), + pt(-2.1405444566,-1.5245190528), + pt(-2.4820508076,-0.9330127019), + pt(0,0), + pt(-1.7990381057,0.2500000000), + pt(-1.1160254038,0.2500000000), + }; + hatcorners[0][11] = mid(hatcorners[0][10], hatcorners[0][12]); + hatcorners[1] = hatcorners[0]; + for(auto& h: hatcorners[1]) h = MirrorX * h; + reverse(hatcorners[1].begin(), hatcorners[1].end()); + + hats[0] = { + mt(0.5000000000,-0.8660254038,-1.3660254038, -0.8660254038,-0.5000000000,-0.0000000000, 0.0000000000,0.0000000000,1.0000000000) * MirrorX, + mt(0.5000000000,-0.8660254038,0.6830127019, 0.8660254038,0.5000000000,1.6830127019, 0.0000000000,0.0000000000,1.0000000000), + mt(-1.0000000000,0.0000000000,-2.2320508076, -0.0000000000,-1.0000000000,-1.8660254038, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-4.5310889132, 0.8660254038,-0.5000000000,-1.6160254038, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-3.4150635095, 0.8660254038,0.5000000000,1.6830127019, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,-0.0000000000,-2.0490381057, 0.0000000000,1.0000000000,3.5490381057, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,0.2500000000, -0.8660254038,0.5000000000,3.2990381057, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-5.4641016151, 0.8660254038,0.5000000000,0.5000000000, 0.0000000000,0.0000000000,1.0000000000), + }; + + hats[1] = { + mt(1.0000000000,0.0000000000,0.0000000000, 0.0000000000,1.0000000000,0.0000000000, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,0.8660254038,-0.8660254038, -0.8660254038,-0.5000000000,7.0980762114, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,-5.8971143170, -0.8660254038,0.5000000000,4.4820508076, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,-0.0000000000,-6.1471143170, 0.0000000000,1.0000000000,-1.1830127019, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-4.5310889132, 0.8660254038,-0.5000000000,-3.9820508076, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-9.5621778265, 0.8660254038,0.5000000000,-6.5980762114, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,-0.0000000000,-14.3432667397, 0.0000000000,1.0000000000,-3.5490381057, 0.0000000000,0.0000000000,1.0000000000), + }; + hattype = {7, 8, 8, 8, 8, 8, 8}; + + ld q7 = 1, q8 = 0; + ld val; + for(int a=0; a<100; a++) { + ld nq7 = q7 + q8; + ld nq8 = q7 * 5 + q8 * 6; + println(hlog, format("%.20f", val = (nq7 + nq8) / (q7 + q8))); + q7 = nq7; q8 = nq8; + } + val = sqrt(val); + println(hlog, "root: ", format("%.20f", val)); + for(int a=-50; a<50; a++) + for(int b=1; b<50; b++) + for(int c=-50; c<50; c++) + for(int d=1; d<50; d++) { + ld err = abs(a*1./b + c * sqrt(1./d) - val); + if(err < 1e-6) + println(hlog, tie(a,b,c,d), " : ", err); + } + val = (3 + sqrt(5)) / 2; // scaling each axis + + sca = Id; sca[0][0] = sca[1][1] = val; + + hats[2] = { + mt(1.0000000000,0.0000000000,0.0000000000, 0.0000000000,1.0000000000,0.0000000000, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,0.8660254038,1.1830127025, -0.8660254038,-0.5000000000,15.3791651251, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,-12.0442286339, -0.8660254038,0.5000000000,10.3971143173, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-14.3432667399, 0.0000000000,1.0000000000,-3.5490381057, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-12.7272413356, 0.8660254038,-0.5000000000,-13.4461524228, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-25.9544826718, 0.8660254038,0.5000000000,-18.4282032304, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-36.8826859024, 0.0000000000,1.0000000000,-9.4641016152, 0.0000000000,0.0000000000,1.0000000000), + }; + + hats[3] = { + mt(1.0000000000,0.0000000000,0.0000000000, 0.0000000000,1.0000000000,0.0000000000, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,0.8660254038,7.3301270200, -0.8660254038,-0.5000000000,37.8564064623, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,-28.4365334803, -0.8660254038,0.5000000000,26.9592921447, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-36.8826859027, 0.0000000000,1.0000000000,-9.4641016152, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-33.2176223915, 0.8660254038,-0.5000000000,-39.4724318658, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-68.9842828915, 0.8660254038,0.5000000000,-50.3695461828, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-96.3047909683, 0.0000000000,1.0000000000,-24.8432667403, 0.0000000000,0.0000000000,1.0000000000), + }; + + hats[4] = { + mt(1.0000000000,0.0000000000,0.0000000000, 0.0000000000,1.0000000000,0.0000000000, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,0.8660254038,23.7224318656, -0.8660254038,-0.5000000000,97.0070415601, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,-71.4663337016, -0.8660254038,0.5000000000,70.7307621167, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-96.3047909682, 0.0000000000,1.0000000000,-24.8432667399, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-86.4926131352, 0.8660254038,-0.5000000000,-108.0871685769, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-181.6813787030, 0.8660254038,0.5000000000,-134.3634480195, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-252.0316870025, 0.0000000000,1.0000000000,-65.0656986057, 0.0000000000,0.0000000000,1.0000000000), + }; + + hats[5] = { + mt(1.0000000000,0.0000000000,0.0000000000, 0.0000000000,1.0000000000,0.0000000000, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,0.8660254038,66.7522320948, -0.8660254038,-0.5000000000,251.9817055201, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,0.8660254038,-184.1634295166, -0.8660254038,0.5000000000,185.4829942043, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-252.0316870019, 0.0000000000,1.0000000000,-65.0656986045, 0.0000000000,0.0000000000,1.0000000000), + mt(-0.5000000000,-0.8660254038,-225.8272043260, 0.8660254038,-0.5000000000,-287.9050992716, 0.0000000000,0.0000000000,1.0000000000), + mt(0.5000000000,-0.8660254038,-476.7428659331, 0.8660254038,0.5000000000,-354.4038105811, 0.0000000000,0.0000000000,1.0000000000), + mt(1.0000000000,0.0000000000,-659.7902700392, 0.0000000000,1.0000000000,-170.3538290768, 0.0000000000,0.0000000000,1.0000000000), + }; + + hats[6] = hats[5]; + + auto acs = inverse(sca); + + println(hlog, "shifts:"); + indenter ind(2); + for(int i=0; i<7; i++) { + + transmatrix S = gpushxto0(hats[1][i] * C0) * hats[1][i]; + // println(hlog, "S = ", kz(S)); + transmatrix S0 = inverse(S); + // transmatrix S1 = S; + + auto& t = hats[6][i]; + auto& t3 = hats[5][i]; + auto& t2 = hats[4][i]; + auto& t1 = hats[3][i]; + + hyperpoint fix2 = t2 * C0 - sca * t1 * C0; + + hyperpoint cfix3 = acs * fix2; + + hyperpoint rfix3 = t3 * C0 - sca * t2 * C0; + + t = sca * t * acs * acs * rgpushxto0(rfix3) * sca; + + println(hlog, kz(rfix3-cfix3), " from ", fix2, " .. ", S0 * cfix3 - acs * fix2); + + // t = t * rgpushxto0(sca * (t3 * C0 - bt3 * C0)); + // t = t * + } + + hatid["R0A000L0.000"] = -1; + int nextid = 8; + + hatid["R0A011L6.147"] = hatid["R0A014L14.343"] = nextid++; + hatid["R0A016L8.196"] = hatid["R0A015L22.539"] = nextid++; + hatid["R0A016L8.196"] = hatid["R0A015L22.539"] = nextid++; + hatid["R0A191L6.147"] = hatid["R0A194L14.343"] = nextid++; + hatid["R0A196L8.196"] = hatid["R0A195L22.539"] = nextid++; + hatid["R1A023L6.903"] = hatid["R1A019L15.060"] = nextid++; + hatid["R1A126L5.555"] = hatid["R1A131L15.741"] = nextid++; + hatid["R1A185L7.579"] = hatid["R1A191L21.879"] = nextid++; + hatid["R1A238L3.558"] = hatid["R1A232L11.654"] = nextid++; + hatid["R1A327L4.885"] = hatid["R1A320L10.974"] = nextid++; + hatid["R2A037L6.054"] = hatid["R2A025L14.053"] = nextid++; + hatid["R2A092L2.046"] = hatid["R2A096L3.188"] = nextid++; + hatid["R2A138L8.858"] = hatid["R2A145L25.101"] = nextid++; + hatid["R2A221L4.953"] = hatid["R2A226L12.883"] = nextid++; + hatid["R2A300L2.571"] = hatid["R2A279L2.571"] = nextid++; + hatid["R3A226L7.005"] = hatid["R3A233L16.845"] = nextid++; + hatid["R4A000L5.143"] = hatid["R4A339L10.197"] = nextid++; + hatid["R4A097L4.093"] = hatid["R4A085L4.171"] = nextid++; + hatid["R4A152L9.906"] = hatid["R4A156L21.728"] = nextid++; + hatid["R4A198L11.809"] = hatid["R4A205L27.793"] = nextid++; + hatid["R4A281L4.171"] = hatid["R4A286L6.625"] = nextid++; + hatid["R5A087L5.006"] = hatid["R5A080L5.503"] = nextid++; + hatid["R5A143L7.731"] = hatid["R5A139L13.041"] = nextid++; + hatid["R5A246L6.309"] = hatid["R5A251L9.388"] = nextid++; + hatid["R5A305L6.626"] = hatid["R5A311L15.426"] = nextid++; + hatid["R5A358L8.119"] = hatid["R5A352L19.349"] = nextid++; + + println(hlog, "nextid = ", nextid); + }; + +void draw_cross(hyperpoint h) { + transmatrix T = rgpushxto0(h); + shiftmatrix sId = shiftless(Id); + for(int i=0; i<12; i++) + queueline(sId * T * C0, sId * T * xspinpush0(30._deg * i, 0.1), 0xFFFFFFFF); + } + +void draw_shape(transmatrix T, vector sh, color_t lc, color_t fc) { + for(hyperpoint h: sh) curvepoint(h); + curvepoint(sh[0]); + queuecurve(shiftless(Id) * T, lc, fc, PPR::LINE); + } + +/* +void draw_superhat(transmatrix T, const vector& ms, int q, color_t lc, color_t fc) { + for(auto& m: ms) + draw_shape(T * m, hatcorners, lc, fc); + } +*/ + +hyperpoint sh; + +color_t coltables[8] = { 0xFF000080, 0x00FF0080, 0x0000FF80, 0xFFFF0080, 0xFF00FF80, 0x00FFFF80, 0xFFFFFF80, 0x4080C080 }; + +vector curlabel; + +ld ldist = 9999; + +hyperpoint hfound, hfound1; + +int connection_mode = 0; + +int found_pairs, found_pairs_swap; + +using pthash = int; + +pthash makehash(hyperpoint h) { + return int(floor(h[0] * 10 + .31)) + int(floor(h[1] * 10 + .31)) * 10000; + } + +map, vector > seen_edges; + +string name(int x) { return s0 + char('A' + x); } + +string matcode(transmatrix T) { + vector res(3); + hyperpoint h = kz(T * C0); + + transmatrix S = gpushxto0(T * C0) * T; + ld alpha = atan2(S * xpush0(1)) / degree; + int ialpha = gmod(floor(alpha + .5), 360); + + int hangle = gmod(floor(atan2(h) / degree + .3), 360); + + h[2] = ialpha/60; + swap(h[1], h[2]); swap(h[0], h[1]); + // return lalign(0, h); + + return format("R%dA%03dL%.3f", ialpha/60, hangle, hypot_d(2, h)); + } + +int ghatid(string s) { + if(hatid.count(s)) return hatid[s]; + return -999; + } + +void edge_connect(vector l1, vector l2) { + transmatrix T1 = Id; + transmatrix T2 = Id; + transmatrix W = Id; + int idx = 0; + for(int i=toplev; i>0; i--) { + T1 = T1 * hats[i][l1[idx]]; + T2 = T2 * hats[i][l2[idx]]; + transmatrix W1 = inverse(T1) * T2; + if(!eqmatrix(W1, Id)) + println(hlog, "CON L", i, " ", matcode(W1), " :: ", matcode(W), " ", tie(l1[idx], l2[idx]), " REV ", matcode(inverse(W1))); + if(i == 1) println(hlog, "RULE1 {", l1[idx], ", ", l2[idx], ", ", ghatid(matcode(W1)), ", ", ghatid(matcode(W)), ", ", ghatid(matcode(inverse(W1))), "},"); + W = W1; + idx++; + } + + println(hlog, "CON L0 ", make_pair(make_pair(l1[idx], name(l1[idx+1])), make_pair(l2[idx], name(l2[idx+1]))), " :: ", matcode(W)); + println(hlog, "RULE0 {", l1[idx], ", ", l1[idx+1], ", ", l2[idx], ", ", l2[idx+1], ", ", ghatid(matcode(W)), "},"); + } + +vector > extedges; + +void edge_label(vector& lbl, hyperpoint a, hyperpoint b) { + auto ha = makehash(a); + auto hb = makehash(b); + + if(connection_mode >= 2) return; + + if(connection_mode == 1) { + if(seen_edges.count({ha, hb})) { + extedges.emplace_back(a, b); + /* vid.linewidth *= 10; + queueline(shiftless(Id) * a, shiftless(Id) * b, 0xFF00FF80); + vid.linewidth /= 10; + */ + } + return; + } + + if(seen_edges.count({hb, ha})) { + edge_connect(lbl, seen_edges[{hb, ha}]); + edge_connect(seen_edges[{hb, ha}], lbl); + seen_edges.erase({hb, ha}); + found_pairs++; + return; + } + if(seen_edges.count({ha, hb})) { + println(hlog, "CON ", lbl, " TO ", seen_edges[{hb, ha}], " SWAP"); + seen_edges.erase({ha, hb}); + found_pairs_swap++; + return; + } + seen_edges[{ha, hb}] = lbl; + } + +void point_label(vector& lbl, hyperpoint h) { + if(lbl == curlabel) draw_cross(h); + ld dist = hdist(unshift(mouseh), h); + if(dist < ldist) { + ldist = dist; + curlabel = lbl; + hfound = h; + println(hlog, "found: ", lbl, " at: ", dist); + } + } + +void draw_superhat_label(transmatrix T, const vector& ms, int q, color_t lc, color_t fc, vector& label) { + for(int i=0; i& label) { + if(levs == 0) { + draw_superhat_label(T, hats[0], t, 0xFFFFFFFF, col, label); + return; + } + + if(connection_mode == 2 && levs == toplev-1) { + int eid = 0; + for(auto e: extedges) { + queueline(shiftless(Id) * T * e.first, shiftless(Id) * T * e.second, col); + label.push_back(eid++); + point_label(label, T * e.first); + label.pop_back(); + } + return; + } + + transmatrix scap = Id; + for(int i=1; i= 2) draw_shape(Id, hatcorners_add, 0xFF0000FF, 0xFF000080); + + transmatrix B = rgpushxto0( unshift(ggmatrix(cwt.at)) * C0 ); + + vector glabel; + + draw_recurse_label(B, toplev, 8, 0, glabel); + if(connection_mode == 0) { + println(hlog, "CON found = ", found_pairs, " swap = ", found_pairs_swap, " not found = ", isize(seen_edges)); + // seen_edges.clear(); found_pairs = 0; + connection_mode = 1; + } + else if(connection_mode == 1) { + connection_mode = 2; + toplev++; + } + + draw_cross(C0); + } + +string writematrix(transmatrix T) { + return format("mt(%.10f,%.10f,%.10f, %.10f,%.10f,%.10f, %.10f,%.10f,%.10f)", + T[0][0], + T[0][1], + T[0][2], + T[1][0], + T[1][1], + T[1][2], + T[2][0], + T[2][1], + T[2][2] + ); + } + +void hatter() { + cmode = sm::NORMAL | sm::CENTER | sm::PANNING; + clearMessages(); + dialog::init(); + gamescreen(); + + shiftpoint s = mapeditor::full_mouseh(); + sh = unshift(s); + + dialog::add_key_action('a', [] { + hatcorners_add.push_back(sh); + println(hlog, "hatcorners = {"); + for(auto h: hatcorners_add) println(hlog, format(" pt(%.10f,%.10f),", h[0], h[1])); + println(hlog, " }"); + }); + + /* dialog::add_key_action('b', [] { + if(hats.empty()) return; + hats.pop_back(); + hattype.pop_back(); + }); */ + + dialog::add_key_action('[', [] { + // hyperpoint h = currentmap->get_corner(cwt.at, 0); + // rot = rot * gpushxto0(h); + rot = rot * spin(60._deg); + // rot = rot * rgpushxto0(h); + }); + + dialog::add_key_action(']', [] { + // hyperpoint h = currentmap->get_corner(cwt.at, 0); + // rot = rot * gpushxto0(h); + rot = MirrorX * rot; + // rot = rot * rgpushxto0(h); + }); + + dialog::add_key_action('7', [] { next_hattype = 7; }); + dialog::add_key_action('8', [] { next_hattype = 8; }); + + dialog::add_key_action('f', [] { hfound1 = hfound; }); + dialog::add_key_action('b', [] { + int id = curlabel[0]; + hats[toplev][id] = rgpushxto0(hfound1 - hfound) * hats[toplev][id]; + + println(hlog, "hats[", toplev, "] = {"); + for(auto h: hats[toplev]) println(hlog, " ", writematrix(h), ","); + println(hlog, " }"); + }); + + dialog::add_key_action('q', [] { + exit(0); + }); + + dialog::add_key_action('g', [] { + ldist = 9999; + }); + + keyhandler = [] (int sym, int uni) { dialog::handleNavigation(sym, uni); }; + } + +void enable_hatter() { + init(); + mapeditor::snapping = true; + rv_hook(hooks_frame, 100, hatframe); + pushScreen(hatter); + } + +auto hathook = arg::add3("-hatter", enable_hatter); + +}