1 #include "rogueviz.h"
2
3 namespace rogueviz {
4
5 #if CAP_ARCM
6 namespace pentagonal {
7 transmatrix ts[3];
8
facingdir(array<hyperpoint,3> & a)9 hyperpoint facingdir(array<hyperpoint,3>& a) {
10 hyperpoint tmp = (a[1]-a[0]) ^ (a[2]-a[0]);
11 tmp /= sqrt(tmp|tmp);
12 return tmp;
13 }
14
15 vector<pair<ld, transmatrix>> sideangles;
16
17 cell *p0, *t0, *t1, *t2, *cc;
18
19 hyperpoint cor;
20
kframe()21 void kframe() {
22 queuestr(gmatrix[p0], 0.6, "P0", 0xFFFFFF, 1);
23 queuestr(gmatrix[cc], 0.6, "C", 0xFFFFFF, 1);
24 queuestr(gmatrix[t0], 0.6, "T0", 0xFFFFFF, 1);
25 queuestr(gmatrix[t1], 0.6, "T1", 0xFFFFFF, 1);
26 queuestr(gmatrix[t2], 0.6, "T2", 0xFFFFFF, 1);
27 }
28
29 hyperpoint xts0;
30 array<hyperpoint, 3> mts;
31
pt(hyperpoint h,hyperpoint c,int id)32 rug::rugpoint *pt(hyperpoint h, hyperpoint c, int id) {
33 auto r = rug::addRugpoint(shiftless(C0), -1);
34 r->native = h;
35 r->native[3] = 1;
36 if(hyperbolic) r->native[2] = -r->native[2];
37 r->x1 = (1 + c[0]) / 16 + (id/8) / 8.;
38 r->y1 = (1 + c[1]) / 16 + (id%8) / 8.;
39 r->valid = true;
40 return r;
41 }
42
inplane(array<hyperpoint,3> & a,hyperpoint line)43 hyperpoint inplane(array<hyperpoint, 3>& a, hyperpoint line) {
44 hyperpoint mu = (a[1]-a[0]) ^ (a[2]-a[0]);
45 // (a[0] | mu) == (line * z | mu)
46 return line * (a[0] | mu) / (line | mu);
47 }
48
49 transmatrix matrix2;
50
51 #if CAP_TEXTURE
52 bool need_texture = true;
53 texture::texture_data tdata; // = texture::config.data;
54 #endif
55
56 int global_v, global_w;
57
make_texture()58 void make_texture() {
59 #if CAP_TEXTURE
60 rug::renderonce = true;
61 need_texture = false;
62 tdata.whitetexture();
63 int tw = tdata.twidth;
64 printf("tw = %d\n", tw);
65 int fw = tw / 4;
66 auto pix = [&] (int k, int x, int y) -> unsigned& {
67 return tdata.texture_pixels[y * tw + x + (k&3) * fw + (k>>2) * fw * tw];
68 };
69 for(int y=0; y<tw; y++)
70 for(int x=0; x<tw; x++)
71 for(int p=0; p<3; p++) {
72 int ax = x / (tw/8);
73 int ay = y / (tw/8);
74 int bx = x % (tw/8);
75 int by = y % (tw/8);
76 int id = ax * 8 + ay;
77 hyperpoint h = sideangles[id % isize(sideangles)].second * xts0;
78 if(!sphere) {
79 hyperpoint ehs[7] = {hpxyz(0,-1,-1), hpxyz(0,0,-1), hpxyz(-1,0,-1), hpxyz(-1,0,0), hpxyz(-1,-1,0), hpxyz(0,-1,0), hpxyz(0,-1,-1)};
80 ld idx = (id % global_v) * 6. / global_v;
81 h = ehs[int(idx)] * (1-(idx-int(idx))) + ehs[int(idx)+1] * (idx-int(idx));
82 }
83 ld hyp = hypot(bx-tw/16, by-tw/16) / (tw/16);
84 if(hyp > 1) hyp = 1;
85 part(pix(0,x,y), p) = 255 * (1 * hyp + (0.5 + h[p]/2) * (1-hyp));
86 }
87
88 tdata.loadTextureGL();
89 #endif
90 rug::alternate_texture = tdata.textureid;
91 }
92
93 void create_model();
94
95 void enable_hooks();
96
run_snub(int v,int w)97 void run_snub(int v, int w) {
98 global_v = v; global_w = w;
99 printf("set geometry\n");
100 stop_game(); autocheat = true;
101 int bonus;
102 if(w == 4 && v == 4) bonus = 12;
103 else if(w == 4 && v == 5) bonus = 7;
104 else if(w == 4 && v == 6) bonus = 4;
105 else if(w == 3 && v == 6) bonus = 12;
106 else if(w == 3 && v == 7) bonus = 8;
107 else if(w == 3 && v == 8) bonus = 7;
108 else if(w == 3 && v == 9) bonus = 6;
109 else bonus = 0;
110 gamerange_bonus = genrange_bonus = sightrange_bonus = bonus;
111 set_geometry(gArchimedean);
112 set_variation(eVariation::pure);
113 arcm::current.parse("("+its(v)+",3," + its(w) + ",3,3) (2,3)(1,0)(4)");
114 check_cgi();
115 cgi.require_basics();
116 specialland = laCanvas;
117 patterns::whichCanvas = 'A';
118 // vid.wallmode = 1;
119 printf("start game\n");
120 printf("distlimit = %d\n", cgi.base_distlimit);
121 start_game();
122 enable_hooks();
123 printf("ok\n");
124 printf("allcells = %d\n", isize(currentmap->allcells()));
125
126 sideangles.clear();
127 printf("gamerange = %d\n", gamerange());
128 printf("genrange = %d\n", getDistLimit() + genrange_bonus);
129 setdist(cwt.at, 7 - getDistLimit() - genrange_bonus, NULL);
130 bfs();
131
132 drawthemap();
133
134 if(euclid || sphere) for(cell *c: currentmap->allcells())
135 gmatrix[c] = shiftless(arcm::archimedean_gmatrix[c->master].second);
136
137 cellwalker cw(currentmap->gamestart(), 0);
138 p0 = cw.at;
139 t0 = (cw - 1 + wstep).at;
140 t1 = (cw + wstep).at;
141 t2 = (cw + wstep + 1 + wstep - 1).at;
142 // p1 = (cw + wstep + 1 + wstep -1 + wstep).at;
143 cc = (cw - 1 + wstep - 1 + wstep).at;
144
145 transmatrix rel = inverse(gmatrix[p0].T);
146
147 ts[0] = rel * gmatrix[t0].T * ddspin(t0, (cw - 1 + wstep).spin);
148 ts[1] = rel * gmatrix[t1].T;
149 ts[2] = rel * gmatrix[t2].T * ddspin(t2, (cw + wstep + 1 + wstep - 1).spin);
150
151 matrix2 = ts[2] * inverse(ts[0]);
152
153 for(int i=0; i<3; i++) mts[i] = ts[i] * C0;
154 hyperpoint f = facingdir(mts);
155
156 for(cell *c: currentmap->allcells()) {
157 int id = arcm::id_of(c->master);
158 if(among(id, 0, 1)) for(int d=0; d<v; d++) {
159 transmatrix T = rel * ggmatrix(c).T * spin(2*M_PI*d/v);
160 array<hyperpoint,3> hts;
161 for(int i=0; i<3; i++)
162 hts[i] = T * ts[i] * C0;
163 // for(int i=0; i<3; i++) printf("%s ", display(hts[i]));
164 hyperpoint f1 = facingdir(hts);
165
166 ld scalar = (f|f1);
167 ld alpha = (M_PI - acos(scalar)) / degree;
168 sideangles.emplace_back(alpha, T);
169 }
170 }
171
172 vector<double> sav;
173 for(auto p: sideangles) sav.push_back(p.first);
174 sort(sav.begin(), sav.end());
175
176 println(hlog, "sideangles ", sav);
177
178 xts0 = tC0(ts[0]);
179
180 println(hlog, "original ", xts0);
181
182 cor = rel * gmatrix[cc].T * C0;
183
184 rug::reopen();
185 rug::reset_view();
186 for(auto p: rug::points) p->valid = true;
187 rug::good_shape = true;
188
189 make_texture();
190
191 create_model();
192 printf("points = %d tris = %d side = %d\n", isize(rug::points), isize(rug::triangles), isize(sideangles));
193 rug::model_distance = euclid ? 4 : 2;
194 vid.rug_config.model = hyperbolic ? mdPerspective : mdEquidistant;
195 showstartmenu = false;
196 }
197
198 ld x, y;
199
create_model()200 void create_model() {
201
202 if(!inHighQual) {
203 x = (mousex - current_display->xcenter + .0) / vid.xres;
204 y = (mousey - current_display->ycenter + .0) / vid.yres;
205 }
206
207 int v = global_v;
208 rug::clear_model();
209
210 ld alpha = atan2(y, x);
211 ld h = hypot(x, y);
212
213 hyperpoint chk = ts[0] * xspinpush0(alpha, h);
214
215 mts[0] = chk;
216 mts[1] = spin(-2*M_PI/v) * chk;
217 mts[2] = matrix2 * chk;
218
219 hyperpoint c[5];
220 for(int i=0; i<5; i++)
221 c[i] = hpxy(sin(2 * i * M_PI/5), cos(2 * i * M_PI/5));
222
223 hyperpoint tria[5];
224 tria[0] = mts[0];
225 tria[1] = inplane(mts, C0);
226 tria[2] = mts[1];
227 tria[3] = mts[2];
228 tria[4] = inplane(mts, cor);
229
230 hyperpoint ctr = Hypc;
231 for(int i=0; i<5; i++) ctr += tria[i];
232 ctr = inplane(mts, ctr);
233
234 transmatrix tester = spin(1.1) * xpush(1);
235
236 int idh = 0;
237
238 for(hyperpoint h: {ctr, tria[0], tria[1], tria[2], tria[3], tria[4], ctr}) {
239 int good1 = 0, good2 = 0;
240 // printf("%d: ", idh);
241 for(int i=0; i<5; i++) {
242 array<hyperpoint, 3> testplane;
243 testplane[0] = tester * h;
244 testplane[1] = tester * tria[i];
245 testplane[2] = tester * tria[(i+1)%5];
246 hyperpoint f = facingdir(testplane);
247 if(f[0] > -1e-6 || std::isnan(f[0])) good1++;
248 if(f[0] < +1e-6 || std::isnan(f[0])) good2++;
249 }
250 // printf("\n");
251 if(good1 == 5 || good2 == 5) {ctr = h; break; }
252 idh++;
253 }
254
255 // printf("idh = %d\n", idh);
256
257 // printf("createmodel with ticks = %d\n", ticks);
258
259 transmatrix t = hyperbolic ? hr::cspin(0, 2, M_PI) * xpush(sin(ticks * M_PI * 2 / anims::period)) : hr::cspin(0, 2, ticks * M_PI * 2 / anims::period);
260
261 hyperpoint hs = hyperbolic ? hpxyz(0,0,-1) : hpxyz(0,0,0);
262
263 if(euclid) t = Id;
264
265 int id = 0;
266 for(auto& p: sideangles) {
267 auto& T = p.second;
268 array<rug::rugpoint*,5> hts;
269 auto cpt = pt(hs + t * T * ctr, C0, id);
270
271 for(int s=0; s<5; s++)
272 hts[s] = pt(hs + t * T * tria[s], c[s], id);
273
274 for(int s=0; s<5; s++)
275 rug::addTriangle(cpt, hts[s], hts[(s+1)%5]);
276
277 id++;
278 if(!sphere) id %= global_v;
279 }
280
281 }
282
283 bool animated = true;
284
rugframe()285 void rugframe() {
286 if(animated) create_model();
287 displaychr(current_display->xcenter, current_display->ycenter, 0, 10, 'X', 0xFFFFFF);
288 clearMessages();
289 }
290
show()291 void show() {
292 cmode = sm::SIDE | sm::MAYDARK;
293 gamescreen(0);
294 dialog::init("Pentagonal Exploration");
295 auto add_key = [] (char c, int a, int b) {
296 dialog::addBoolItem(lalign(0, "based on {",a,",",b,"}"), global_v == a && global_w == b, c);
297 dialog::add_action([a,b] {
298 run_snub(a, b);
299 });
300 };
301 for(int i=3; i<10; i++)
302 add_key('0'+i, i, 3);
303 add_key('$', 4, 4);
304 add_key('%', 5, 4);
305 add_key('^', 6, 4);
306 dialog::addBoolItem_action("animation and mouse control", animated, 'a');
307 dialog::addSelItem(XLAT("projection"), models::get_model_name(vid.rug_config.model), 'p');
308 dialog::add_action([] { pushScreen(models::model_menu); });
309 dialog::addBoolItem(XLAT("set perspective (allows flying with Home/End)"), vid.rug_config.model == mdPerspective, 's');
310 dialog::add_action([] { auto& m = vid.rug_config.model; m = (m==mdPerspective) ? mdEquidistant : mdPerspective; });
311 dialog::display();
312 }
313
enable_hooks()314 void enable_hooks() {
315 rv_hook(rug::hooks_rugframe, 100, rugframe);
316 rv_hook(hooks_o_key, 80, [] (o_funcs& v) { v.push_back(named_dialog("pentagonal exploration", show)); });
317 }
318
319 auto xhook =
__anon103052d00702null320 arg::add3("-snub", [] { run_snub(arg::shift_argi(), 3); })
__anon103052d00802null321 + arg::add3("-snub4", [] { run_snub(arg::shift_argi(), 4); })
__anon103052d00902(string s, vector<tour::slide>& v) 322 + addHook_rvslides(143, [] (string s, vector<tour::slide>& v) {
323 if(s != "mixed") return;
324 using namespace tour;
325 v.push_back(
326 tour::slide{"Pentagonal Exploration", 62, LEGAL::NONE | QUICKGEO,
327 "Pentagonal Exploration explained at: http://www.roguetemple.com/z/sims/snub/\n\n"
328 "Move the mouse nearer and further away from the X.",
329
330 [] (presmode mode) {
331 setCanvas(mode, '0');
332 slide_url(mode, 'u', "open the URL", "http://www.roguetemple.com/z/sims/snub/");
333 slide_backup(rug::model_distance);
334 slide_backup(vid.rug_config.model);
335 slide_backup(rug::alternate_texture);
336 if(mode == pmStart) {
337 pentagonal::run_snub(5, 3);
338 }
339 if(mode == pmKey) pushScreen(show);
340 if(mode == 3) {
341 printf("stopping\n");
342 rug::close();
343 }
344 }}
345 );
346 });
347
348 }
349 #endif
350 }
351