1 #include "rogueviz.h"
2
3 /** \brief Snowball visualization
4 *
5 * This visualization puts small objects ('snowballs') randomly throughout the space.
6 * It provides a way to visualize the geometry without any tessellation.
7 *
8 * Should work for tessellations where every tile is congruent.
9 *
10 * The snow_lambda parameter gives the expected number of snowballs per cell.
11 * (The number in every region has Poisson distribution with mean proportional to its area.)
12 *
13 * Freezes for tessellations with ideal vertices
14 *
15 *
16 *
17 **/
18
19 namespace rogueviz {
20
21 namespace snow {
22
23 ld snow_lambda = 0;
24
25 color_t snow_color = 0xFFFFFFFF;
26
27 bool snow_test = false;
28
29 /* intense brightness */
30 bool snow_intense = false;
31
32 /* a funny glitch */
33 bool snow_glitch = false;
34
35 /* disable textures */
36 bool snow_texture = true;
37
38 int snow_shape = 0;
39
40 map<cell*, vector<transmatrix> > matrices_at;
41
shapeid(int i)42 hpcshape& shapeid(int i) {
43 switch(i) {
44 case 0:
45 return cgi.shSnowball;
46 case 1:
47 return cgi.shHeptaMarker;
48 case 2:
49 return cgi.shDisk;
50 default:
51 return cgi.shDisk;
52 }
53 }
54
random_snow_matrix(cell * c)55 transmatrix random_snow_matrix(cell *c) {
56 if(snow_glitch) {
57 // in the standard tiling, this is incorrect but fun
58 hyperpoint h = C0;
59 h[0] = randd() - .5;
60 h[1] = randd() - .5;
61 h[2] = randd() - .5;
62 h[2] = -h[2];
63 return rgpushxto0(h);
64 }
65 else if(prod) {
66 transmatrix T = PIU(random_snow_matrix(c));
67 return mscale(T, (randd() - .5) * cgi.plevel);
68 }
69 else if(hybri && !prod) {
70 return rots::lift_matrix(PIU(random_snow_matrix(c))); // * zpush((randd() - .5) * cgi.plevel);
71 }
72 else if(nonisotropic || bt::in()) {
73
74 int co = bt::expansion_coordinate();
75 ld aer = bt::area_expansion_rate();
76
77 hyperpoint h;
78 // randd() - .5;
79
80 for(int a=0; a<3; a++) {
81 if(a != co || aer == 1)
82 h[a] = randd() * 2 - 1;
83 else {
84 ld r = randd();
85 h[co] = log(hr::lerp(1, aer, r)) / log(aer) * 2 - 1;
86 }
87 }
88 return bt::normalized_at(h);
89 }
90 else {
91 while(true) {
92 ld maxr = WDIM == 2 ? cgi.rhexf : cgi.corner_bonus;
93 ld vol = randd() * wvolarea_auto(maxr);
94 ld r = binsearch(0, maxr, [vol] (ld r) { return wvolarea_auto(r) > vol; });
95 transmatrix T = random_spin();
96 hyperpoint h = T * xpush0(r);
97 cell* c1 = c;
98 virtualRebase(c1, h);
99 if(c1 == c)
100 return T * xpush(r);
101 }
102 }
103 }
104
draw_snow(cell * c,const shiftmatrix & V)105 bool draw_snow(cell *c, const shiftmatrix& V) {
106
107 if(!matrices_at.count(c)) {
108 auto& v = matrices_at[c];
109 int cnt = 0;
110 ld prob = randd();
111 ld poisson = exp(-snow_lambda);
112 while(cnt < 2*snow_lambda+100) {
113 if(prob < poisson) break;
114 prob -= poisson;
115 cnt++;
116 poisson *= snow_lambda / cnt;
117 }
118 if(snow_test) {
119 if(c != cwt.at)
120 cnt = 0;
121 else {
122 c->wall = waFloorA;
123 cnt = snow_lambda;
124 }
125 }
126
127 for(int t=0; t<cnt; t++)
128 v.push_back(random_snow_matrix(c));
129 }
130
131 poly_outline = 0xFF;
132 for(auto& T: matrices_at[c]) {
133 auto& p = queuepoly(V * T, shapeid(snow_shape), snow_color);
134 if(!snow_texture) p.tinf = nullptr;
135 if(snow_intense) p.flags |= POLY_INTENSE;
136 }
137
138 return false;
139 }
140
141 string cap = "non-Euclidean snowballs/";
142
snow_slide(vector<tour::slide> & v,string title,string desc,reaction_t t)143 void snow_slide(vector<tour::slide>& v, string title, string desc, reaction_t t) {
144 using namespace tour;
145 v.push_back(
146 tour::slide{cap + title, 18, LEGAL::NONE | QUICKGEO, desc,
147
148 [t] (presmode mode) {
149 setCanvas(mode, '0');
150
151 slidecommand = "auto-movement";
152 if(mode == pmKey) {
153 using namespace anims;
154 tour::slide_backup(ma, ma == maTranslation ? maNone : maTranslation);
155 tour::slide_backup<ld>(shift_angle, 0);
156 tour::slide_backup<ld>(movement_angle, 90);
157 }
158
159 if(mode == pmStart) {
160 stop_game();
161 tour::slide_backup(mapeditor::drawplayer, false);
162 tour::slide_backup<ld>(snow_lambda, 1);
163 tour::slide_backup(snow_color, 0xC0C0C0FF);
164 tour::slide_backup(snow_intense, true);
165 tour::slide_backup(smooth_scrolling, true);
166 t();
167 start_game();
168 playermoved = false;
169 }
170 }}
171 );
172 }
173
show()174 void show() {
175 cmode = sm::SIDE | sm::MAYDARK;
176 gamescreen(0);
177 dialog::init(XLAT("snowballs"), 0xFFFFFFFF, 150, 0);
178
179 dialog::addSelItem("lambda", fts(snow_lambda), 'l');
180 dialog::add_action([]() {
181 dialog::editNumber(snow_lambda, 0, 100, 1, 10, "lambda", "snowball density");
182 dialog::reaction = [] { matrices_at.clear(); };
183 });
184
185 dialog::addSelItem("size", fts(snow_shape), 's');
186 dialog::add_action([]() {
187 snow_shape = (1 + snow_shape) % 3;
188 });
189
190 dialog::addBack();
191 dialog::display();
192 }
193
o_key(o_funcs & v)194 void o_key(o_funcs& v) {
195 if(snow_lambda) v.push_back(named_dialog("snowballs", show));
196 }
197
198 auto hchook = addHook(hooks_drawcell, 100, draw_snow)
199
__anon9a3ba7ce0602() 200 + addHook(hooks_clearmemory, 40, [] () {
201 matrices_at.clear();
202 })
203
204 + addHook(hooks_o_key, 80, o_key)
205
206 #if CAP_COMMANDLINE
__anon9a3ba7ce0702null207 + addHook(hooks_args, 100, [] {
208 using namespace arg;
209
210 if(0) ;
211 else if(argis("-snow-lambda")) {
212 shift_arg_formula(snow_lambda);
213 }
214 else if(argis("-snow-shape")) {
215 shift(); snow_shape = argi();
216 }
217 else if(argis("-snow-test")) {
218 snow_test = true;
219 }
220 else if(argis("-snow-color")) {
221 shift(); snow_color = arghex();
222 }
223 else if(argis("-snow-intense")) {
224 snow_intense = true;
225 }
226 else if(argis("-snow-no-texture")) {
227 snow_texture = false;
228 }
229 else if(argis("-snow-glitch")) {
230 snow_test = true;
231 }
232 else return 1;
233 return 0;
234 })
235 #endif
236
__anon9a3ba7ce0802(string s, vector<tour::slide>& v) 237 + addHook_rvslides(161, [] (string s, vector<tour::slide>& v) {
238 if(s != "noniso") return;
239 v.push_back(tour::slide{
240 cap+"snowball visualization", 10, tour::LEGAL::NONE | tour::QUICKSKIP,
241 "Non-Euclidean visualizations usually show some regular constructions. Could we visualize the geometries themselves? Let's distribute the snowballs randomly."
242 "\n\n"
243 "You can use mouse to look in different directions. Press 5 to turn the automatic movement on or off. Press 'o' to change density and shape."
244 ,
245 [] (tour::presmode mode) {
246 slide_url(mode, 'y', "YouTube link", "https://www.youtube.com/watch?v=leuleS9SpiA");
247 slide_url(mode, 't', "Twitter link", "https://twitter.com/ZenoRogue/status/1245367263936512001");
248 }
249 });
250 snow_slide(v, "Euclidean geometry", "This is the Euclidean space. Looks a bit like space flight in some old video games.", [] {
251 set_geometry(gCubeTiling);
252 snow_lambda = 20;
253 });
254 snow_slide(v, "Euclidean geometry (torus)",
255 "Some gamers incorrectly call warped worlds (like Asteroids) \"non-Euclidean\"; the animation for them would look the same, just a bit more regular. When playing these games, I have always wondered why the stars move so fast. Such far objects should not move.", [] {
256 auto& T0 = euc::eu_input.user_axes;
257 auto bak = T0;
258 for(int i=0; i<3; i++)
259 for(int j=0; j<3; j++)
260 T0[i][j] = i==j? 1: 0;
261 euc::build_torus3();
262 set_geometry(gCubeTiling);
263 snow_lambda = 20;
264 tour::on_restore([bak] { auto& T0 = euc::eu_input.user_axes; stop_game(); T0 = bak; euc::build_torus3(); start_game(); });
265 });
266 snow_slide(v, "Hyperbolic geometry", "To the contrary, in hyperbolic geometry, parallax works in a completely different way. Everything moves. This space is expanding everywhere. Exponentially. In every geometry, snowballs close to us behave in a similar way as in the Euclidean space.", [] {
267 set_geometry(gSpace534);
268 snow_lambda = 20;
269 });
270 snow_slide(v, "H2xE", "This geometry is non-isotropic: it is hyperbolic in horizontal direction and Euclidean in (roughly) vertical direction. Since the space expands faster horizontally, the snowballs no longer look circular.", [] {
271 set_geometry(gNormal);
272 set_variation(eVariation::pure);
273 set_geometry(gProduct);
274 snow_lambda = 20;
275 });
276 snow_slide(v, "Non-isotropic hyperbolic geometry", "This geometry is hyperbolic in both directions, but the curvatures are different.", [] {
277 set_geometry(gNIH);
278 snow_lambda = 20;
279 });
280 snow_slide(v, "Spherical geometry", "Do not forget about the spherical geometry. It is weird in general. When we leave the snowballs behind us, they look as if they were in front of us. Due to geometric lensing, snowballs in the antipodal point look as if they were close to us.", [] {
281 set_geometry(gCell120);
282 snow_lambda = 5;
283 });
284 snow_slide(v, "S2xE geometry", "Snowballs which are directly above or below us will look like rings, but it is hard to catch them in exactly the right spot.", [] {
285 set_geometry(gSphere);
286 set_variation(eVariation::pure);
287 set_geometry(gProduct);
288 snow_lambda = 20;
289 });
290 snow_slide(v, "Nil", "Nil geometry, used for impossible figure constructions. Euclidean plane with another dimension added. Making a loop in the Euclidean plane makes you move in this third dimension, proportionally to the area of the loop. (Using larger snowballs here.)", [] {
291 set_geometry(gNil);
292 tour::slide_backup<ld>(sightranges[geometry], 7);
293 tour::slide_backup(snow_shape, 2);
294 snow_lambda = 5;
295 });
296 snow_slide(v, "SL(2,R)", "Here is SL(2,R), like Nil but based on hyperbolic plane instead. Geometric lensing effects are strong in both Nil and SL(2,R). (Starting with S^2 yields spherical geometry.)", [] {
297 set_geometry(gNormal);
298 set_variation(eVariation::pure);
299 set_geometry(gRotSpace);
300 snow_lambda = 5;
301 });
302 #if CAP_SOLV
303 snow_slide(v, "Solv", "Solv geometry. Like the non-isotropic hyperbolic geometry but where the horizontal and vertical curvatures work in the other way.", [] {
304 set_geometry(gSol);
305 // tour::slide_backup(snow_shape, 2);
306 snow_lambda = 3;
307 });
308 #endif
309 });
310
311 }
312 }
313