root/trunk/qd/ff.d

Revision 778, 14.7 kB (checked in by FeepingCreature, 2 years ago)

Small fixes

Line 
1 module ff;
2
3 import qd, tools.base, std.math, dglut.vector;
4 alias PI π;
5
6 alias Vector!(float, 6) vec6f;
7 alias Vector!(float, 8) vec8f;
8
9 float int_pow(float a, int i) {
10   float res = 1f;
11   while (i--) res *= a;
12   return res;
13 }
14
15 float ps(float f) { return f; }
16
17 vec4f calcΞ(ref vec4f x, ref vec4f y) {
18   vec4f Ξ = void;
19   // yes it's the right way around. Srsly, look it up in the PDF.
20   for (int i = 0; i < 4; ++i) Ξ.field[i] = atan2(x[i], y[i]);
21   return Ξ;
22 }
23
24 void linear_0(ref vec4f x, ref vec4f y) { }
25 void sinusoidal_1(ref vec4f x, ref vec4f y) { x = x.sin(); y = y.sin(); }
26 void spherical_2(ref vec4f x, ref vec4f y) {
27   auto sum = x * x + y * y;
28   x /= sum; y /= sum;
29 }
30 void swirl_3(ref vec4f x, ref vec4f y) {
31   auto sum = x * x + y * y;
32   auto sins = sum.sin(), coss = sum.cos();
33   auto xres = x * sins - y * coss, yres = x * coss + y * sins;
34   x = xres; y = yres;
35 }
36 void horseshoe_4(ref vec4f x, ref vec4f y) {
37   auto xsq = x * x, ysq = y * y, sum = xsq + ysq, r = sum.sqrt();
38   auto nx = (xsq - ysq), ny = 2 * x * y;
39   x = nx; y = ny;
40   x /= r; y /= r;
41 }
42 void polar_5(ref vec4f x, ref vec4f y) {
43   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
44   x = Ξ / π; y = r - vec4f(1);
45 }
46 void handkerchief_6(ref vec4f x, ref vec4f y) {
47   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt(), Ξpr = Ξ + r, Ξmr = Ξ - r;
48   x = Ξpr.sin() * r; y = Ξmr.cos() * r;
49 }
50 void heart_7(ref vec4f x, ref vec4f y) {
51   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt(), Ξr = Ξ * r;
52   x = Ξr.sin() * r; y = -Ξr.cos() * r;
53 }
54 void disc_8(ref vec4f x, ref vec4f y) {
55   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
56   auto πr = r * π, Ξ_π = Ξ / π;
57   x = πr.sin() * Ξ_π; y = πr.cos() * Ξ_π;
58 }
59 void spiral_9(ref vec4f x, ref vec4f y) {
60   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
61   x = (Ξ.cos() - r.sin()) / r; y = (Ξ.sin() - r.cos()) / r;
62 }
63 void hyperbolic_10(ref vec4f x, ref vec4f y) {
64   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
65   x = Ξ.sin() / r; y = Ξ.cos() * r;
66 }
67 void diamond_11(ref vec4f x, ref vec4f y) {
68   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
69   x = Ξ.sin() * r.cos(); y = Ξ.cos() * r.sin();
70 }
71 void ex_12(ref vec4f x, ref vec4f y) {
72   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
73   x = (Ξ + r).sin(); y = (Ξ - r).cos();
74   x *= x*x*r; y *= y*y*r; // ^3 * r
75 }
76 Vector!(byte, 4) Ωs_1, Ωs_2 = void; // intentionally not threadsafe - it's supposed to be random after all
77                                     // don't worry - enough randomness is created in the point selection.
78                                     // even if there's threading-related errors, at thousands of hits per pixel
79                                     // they're completely irrelevant.
80                                     // this is art, not maths. ;)
81 static this() {
82   foreach (ref Ω; Ωs_1.field) if (Ω >= 0) Ω = 1; else Ω = -1;
83   foreach (ref Ω; Ωs_2.field) if (Ω >= 0) Ω = 1; else Ω = -1;
84 }
85 void julia_13(ref vec4f x, ref vec4f y) {
86   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
87   auto rsqt = r.sqrt(), Ξ_2 = Ξ / 2f;
88   x = Ξ_2.cos() * Ωs_1 * rsqt;
89   y = Ξ_2.sin() * Ωs_2 * rsqt;
90   Ωs_1 *= -1; Ωs_2 *= -1; // "so-called randomness"
91 }
92 void bent_14(ref vec4f x, ref vec4f y) {
93   auto xs = (-x.sgn() + 3) / 2, ys = (-y.sgn() + 3) / 2; // 1/-1 -> -1/1 -> 2/4 -> 1/2
94   x *= xs; y /= ys;
95 }
96 void waves_15(ref vec4f x, ref vec4f y, float b, float c, float e, float f) {
97   auto
98     nx = x + b * (y / (c*c)).sin(),
99     ny = y + e * (x / (f*f)).sin();
100   x = nx; y = ny;
101 }
102 void fisheye_16(ref vec4f x, ref vec4f y) {
103   auto r = (x * x + y * y).sqrt();
104   auto factor = 2f / (r + 1f);
105   auto nx = y * factor, ny = x * factor;
106   x = nx; y = ny;
107 }
108 void popcorn_17(ref vec4f x, ref vec4f y, float c, float f) {
109   x = (x*3).tan().sin() * c + x; y = (y*3).tan().sin() * f + y;
110 }
111 void exponential_18(ref vec4f x, ref vec4f y) {
112   auto ex = (x-1).exp();
113   x *= π; y *= π;
114   x = x.cos(); y = y.sin();
115   x *= ex; y *= ex;
116 }
117 void power_19(ref vec4f x, ref vec4f y) {
118   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
119   auto r_sinΞ = y = Ξ.sin();
120   for (int i = 0; i < 4; ++i) r_sinΞ.field[i] = pow(r.field[i], r_sinΞ.field[i]);
121   x = Ξ.cos() * r_sinΞ; y *= r_sinΞ;
122 }
123 void cosine_20(ref vec4f x, ref vec4f y) {
124   vec4f ch = void, sh = void;
125   for (int i = 0; i < 4; ++i) {
126     ch.field[i] = cosh(y.field[i]);
127     sh.field[i] = sinh(y.field[i]);
128   }
129   auto cp = π * x;
130   x = cp.cos()*ch; y = -cp.sin()*sh;
131 }
132 void rings_21(ref vec4f x, ref vec4f y, float c) {
133   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
134   auto csq = c*c;
135   auto n = r + csq, o = 2f*csq;
136   for (int i = 0; i < 4; ++i) if (n.field[i] > o) n.field[i] -= cast(int)(n.field[i] / o) * o;
137   n -= csq;
138   n += r*(1f-csq);
139   x = Ξ.cos() * n; y = Ξ.sin() * n;
140 }
141 void fan_22(ref vec4f x, ref vec4f y, float c, float f) {
142   auto t = π * c * c;
143   auto Ξ = calcΞ(x, y), r = (x * x + y * y).sqrt();
144   auto test = Ξ + f;
145   for (int i = 0; i < 4; ++i) while (test.field[i] > t) test.field[i] -= t;
146   vec4f v = void;
147   auto t_half = t/2f;
148   for (int i = 0; i < 4; ++i)
149     if (test.field[i] > t_half) v.field[i] = Ξ.field[i] - t_half;
150     else v.field[i] = Ξ.field[i] + t_half;
151   x = v.cos() * r; y = v.sin()*r;
152 }
153 void eyefish_27(ref vec4f x, ref vec4f y) {
154   auto r = (x * x + y * y).sqrt();
155   auto factor = 2f / (r + 1f);
156   x *= factor; y *= factor;
157 }
158 void bubble_28(ref vec4f x, ref vec4f y) {
159   auto r = (x * x + y * y).sqrt();
160   auto factor = 4f / (r*r + 4f);
161   x *= factor; y *= factor;
162 }
163 void cylinder_29(ref vec4f x, ref vec4f y) { x = x.sin(); }
164 void tangent_42(ref vec4f x, ref vec4f y) {
165   auto nx = x.sin() / y.cos();
166   y = y.sin() / x.cos(); x=nx;
167 }
168 void cross_48(ref vec4f x, ref vec4f y) {
169   auto temp = x*x + y*y;
170   auto factor = (1f/(temp*temp)).sqrt();
171   x *= factor; y *= factor;
172 }
173
174 const num_functions = 28;
175 void fun(int which, ref vec4f x, ref vec4f y, ref vec6f coeffs) {
176   switch (which) {
177     case 0: linear_0(x, y); break;
178     case 1: sinusoidal_1(x, y); break;
179     case 2: spherical_2(x, y); break;
180     case 3: swirl_3(x, y); break;
181     case 4: horseshoe_4(x, y); break;
182     case 5: polar_5(x, y); break;
183     case 6: handkerchief_6(x, y); break;
184     case 7: heart_7(x, y); break;
185     case 8: disc_8(x, y); break;
186     case 9: spiral_9(x, y); break;
187     case 10: hyperbolic_10(x, y); break;
188     case 11: diamond_11(x, y); break;
189     case 12: ex_12(x, y); break;
190     case 13: julia_13(x, y); break;
191     case 14: bent_14(x, y); break;
192     case 15: waves_15(x, y, coeffs.tuple[1..3], coeffs.tuple[4..6]); break;
193     case 16: fisheye_16(x, y); break;
194     case 17: popcorn_17(x, y, coeffs.tuple[2], coeffs.tuple[5]); break;
195     case 18: exponential_18(x, y); break;
196     case 19: power_19(x, y); break;
197     case 20: cosine_20(x, y); break;
198     case 21: rings_21(x, y, coeffs.tuple[2]); break;
199     case 22: fan_22(x, y, coeffs.tuple[2], coeffs.tuple[5]); break;
200     case 23: eyefish_27(x, y); break;
201     case 24: bubble_28(x, y); break;
202     case 25: cylinder_29(x, y); break;
203     case 26: tangent_42(x, y); break;
204     case 27: cross_48(x, y); break;
205     default: fail;
206   }
207 }
208
209 import tools.ca_rng;
210 struct variation {
211   vec6f pre;
212   vec6f post;
213   vec3f color;
214   ushort fn;
215   void exec(ref vec4f x, ref vec4f y) {
216     transform(x, y, pre);
217     fun(fn, x, y, pre);
218     transform(x, y, post);
219   }
220   static {
221     void transform(ref vec4f x, ref vec4f y, vec6f coeff) {
222       vec4f nx = x * coeff.field[0] + y * coeff.field[1] + coeff.field[2];
223       vec4f ny = x * coeff.field[3] + y * coeff.field[4] + coeff.field[5];
224       x = nx; y = ny;
225     }
226     void random_matrix(ref vec6f v) {
227       auto a = vec2f(0), b = a;
228       a.x = 1; b.y = 1; // identity matrix
229       a.rotate(int_pow(randf(), 3)*360f-180f); b.rotate(int_pow(randf(), 3)*360f-180f);
230       a *= sqrt(randf()*2f); b *= sqrt(randf()*2f);
231       v.field[0..2] = a.field; v.field[2] = randf-0.5f;
232       v.field[3..5] = b.field; v.field[5] = randf-0.5f;
233     }
234     variation rand() {
235       variation res;
236       random_matrix(res.pre);
237       random_matrix(res.post);
238       res.pre *= 2f; res.post /= 2f;
239       auto r = .rand;
240       res.fn = r % num_functions;
241       logln("fn: ", res.fn, ", Pre: ", res.pre, ", Post: ", res.post);
242       res.color = vec3f.rand() / 2f + 0.5f;
243       with (res.color) {
244         auto h = hsv(rgb(r*255f, g*255f, b*255f));
245         //h.s = 128 + h.s/2;
246         h.v = 128 + h.v/2;
247         auto re = rgb(h);
248         r = re.r/255f; g = re.g/255f; b = re.b/255f;
249       }
250       return res;
251     }
252   }
253 }
254
255 import tools.array2d;
256 class buffer {
257   array2d!(vec3f) sum;
258   array2d!(long) count;
259   vec6f final_transform;
260   this(int w, int h) {
261     sum = typeof(sum)(w, h); count = typeof(count)(w, h);
262     sum = vec3f(0);
263     final_transform = vec6f.rand() * 0.4f;
264     final_transform.field[2] = final_transform.field[5] = 0f;
265     final_transform.field[0] += 1; final_transform.field[4] += 1; // unit
266   }
267   ulong total_sum;
268   invariant { assert (sum.w == count.w); assert (sum.h == count.h); }
269   void increase (float px, float py, vec3f c) {
270     int nx, ny;
271     with (final_transform) {
272       nx = cast(int) (px * field[0] + py * field[1] + field[2]);
273       ny = cast(int) (px * field[3] + py * field[4] + field[5]);
274     }
275     if (nx<0 || ny<0 || nx!<sum.w || ny!<sum.h) return;
276     sum[nx, ny] = sum[nx, ny] + c;
277     count[nx, ny] = count[nx, ny] + 1;
278     ++total_sum;
279   }
280   void render(void delegate(int, int, rgb) callback) {
281     const γ = 2.2;
282     for (int y = 0; y < sum.h; ++y) {
283       // yes, do do this every line
284       auto scale_factor = (1.0 * total_sum) / (sum.w * sum.h);
285       for (int x = 0; x < sum.w; ++x) {
286         real c = count[x, y], f = c / ((scale_factor+1)) + 1;
287         f = log(f);
288         f = pow(f, 1.0/γ); // gamma
289         vec3f cc = sum[x, y] / (c+1) * vec3f(256) * f;
290         foreach (ref entry; cc.field) if (entry>255) entry=255;
291         callback(x, y, rgb(cc.tuple));
292       }
293     }
294   }
295   void draw() { render((int x, int y, rgb c) {
296     pset(x, y, c);
297       if (!x) { logln(y); flip; events; }
298     });
299   }
300   import std.stream, std.file;
301   void writePPM(string file) {
302     auto temp = file ~ "_temp";
303     if (temp.exists()) temp.remove();
304     with (new File(temp, FileMode.Out)) {
305       ubyte[16384] buffer;
306       size_t cur = 0;
307       void flush() {
308         write(buffer[0 .. cur]);
309         cur = 0;
310       }
311       void dump(rgb c) {
312         buffer[cur++] = c.r; buffer[cur++] = c.g; buffer[cur++] = c.b;
313         if (cur >= buffer.length - 3) flush;
314       }
315       writefln("P6 ", sum.w, " ", sum.h, " ", 255);
316       render((int x, int y, rgb c) {
317         if (!x && (y%16 == 0)) {
318           logln(y, " - ", total_sum);
319           //events;
320         }
321         dump(c);
322       });
323       flush;
324       close();
325     }
326     if (file.exists()) file.remove();
327     temp.rename(file);
328   }
329 }
330
331 vec6f delegate(float) vecbezier(vec6f[] fields) {
332   auto values = new float[][6];
333   foreach (vec; fields) foreach (i, v; vec.field) values[i] ~= v;
334   auto bzrs = values /map/ &multibezier;
335   return (typeof(bzrs) bs, float x) {
336     vec6f res = void;
337     foreach (i, ref field; res.field) field = bs[i](x);
338     return res;
339   } /fix/ bzrs;
340 }
341
342 import std.string, std.stdio, std.date, tools.threadpool, tools.threads, tools.functional, tools.log: logln;
343 import splines;
344 void main(string[] args) {
345   auto start = getUTCtime(), seedval = cast(ushort) start;
346   if (args.length > 1) seedval = atoi(args[1]);
347   int[] subseeds; auto transition = 0.0;
348   if (args.length > 2) subseeds = args[2].split(",") /map/ (string s) { return cast(int) atoi(s); };
349   if (args.length > 4) transition = atoi(args[4]) * 1f / atoi(args[3]);
350   string file;
351   if (args.length > 5) file = args[5];
352   logln("Seed: ", seedval);
353   seed(seedval);
354   vec_randf = &randf;
355   int width = 8192, height = 8192;
356   //screen(width, height, 0, false);
357  
358   auto tp = new Threadpool(5);
359  
360   auto buf = new buffer(width, height);
361   float[32] xs, ys; // (<= 256), so we can use one rand call to determine all four points
362   vec3f[32] colors;
363   for (int i = 0; i < 32; ++i) colors[i] = vec3f(xs [i] = ys [i] = 0f);
364   variation[] vars;
365   for (int i = 0; i < 3; ++i) vars ~= variation.rand();
366   vec6f delegate(float)[] var_pre_generators, var_post_generators;
367   if (subseeds.length) {
368     vec6f[][] targets_pre, targets_post;
369     targets_pre.length = targets_post.length = vars.length;
370     foreach (subseed; subseeds) {
371       if (!subseed) {
372         foreach (i, var; vars) {
373           targets_pre[i] ~= var.pre;
374           targets_post[i] ~= var.post;
375         }
376         continue;
377       }
378       seed(subseed);
379       foreach (i, var; vars) {
380         vec6f my_pre, my_post;
381         variation.random_matrix(my_pre); variation.random_matrix(my_post);
382         targets_pre[i] ~= my_pre; targets_post[i] ~= my_post;
383       }
384     }
385     logln("Building generators");
386     foreach (i, var; vars) {
387       var_pre_generators ~= vecbezier(targets_pre[i]);
388       var_post_generators ~= vecbezier(targets_post[i]);
389     }
390     logln("Transitioning");
391     foreach (i, ref var; vars) {
392       var.pre = var_pre_generators[i](transition);
393       var.post = var_post_generators[i](transition);
394     }
395   }
396  
397   flip = false;
398   TLS!(long) count; New(count, { return new long; });
399   long total_count() {
400     long res;
401     count.each((long l) { res += l; });
402     return res;
403   }
404   TLS!(Generator) gen; New(gen, { return new Generator(23); });
405   logln("Setup done");
406   void iterate(ref int iter) {
407     union IntUbyteConv { int i; ubyte[4] bf; }
408     IntUbyteConv cv = void; cv.i = gen()();
409     foreach (ref p; cv.bf) p %= xs.length;
410     vec4f cxs = void, cys = void;
411     foreach (k, bogus; cxs.tuple) {
412       cxs.tuple[k] = xs[cv.bf[k]];
413       cys.tuple[k] = ys[cv.bf[k]];
414     }
415     scope(exit) {
416       iter++;
417       if (iter == vars.length) iter = 0;
418     }
419    
420     vars[iter].exec(cxs, cys);
421     foreach (k, p; cv.bf) {
422       xs[p] = cxs[k]; ys[p] = cys[k]; // write-back
423       auto
424         x = xs[p] * (width/2) + (width/2),
425         y = ys[p] * (height/2) + (height/2);
426       colors[p] = (colors[p] + vars[iter].color) * 0.5;
427       buf.increase(x, y, colors[p]);
428     }
429     count() += 4; // four points at a time
430   }
431   MessageMultiChannel!(bool, false, true) stop; New(stop);
432   foreach (bogus; tp.threads) tp.addTask({
433     int iter;
434     while (true) {
435       iterate(iter);
436       if (stop.active) { stop.get; return; }
437     }
438   });
439   auto last_count = total_count();
440   bool halted;
441   void halt() { if (halted) return; foreach (bogus; tp.threads) stop.put(true); halted = true; }
442   scope(exit) halt;
443   long last_dumped = 0;
444   while (true) {
445     //logln("Drawing");
446     buf.draw;
447     float t = (getUTCtime() - start) / 1000f;
448     writefln(total_count, " / ", t, "s -> ", total_count / t, " iter/s");
449     /*if (total_count() > 1024*1024*2) {
450       halt;
451       buf.draw;
452       if (args.length > 4) {
453         string file_nr = args[4];
454         while (file_nr.length < 4) file_nr = "0"~file_nr;
455         file ~= file_nr~".bmp";
456       }
457       if (file) SDL_SaveBMP(display.target, file);
458       break;
459     }*/
460     yield();
461   }
462 }
Note: See TracBrowser for help on using the browser.