root/trunk/scene/fractal.d

Revision 57, 6.9 kB (checked in by FeepingCreature, 2 years ago)
  • Let's try this again
  • HAVE A FRACTAL
Line 
1 module scene.fractal;
2
3 import scene.common, base.math, base.math: bit_isnan;
4
5 version(GNU) {
6   import gcc.builtins;
7   alias __builtin_logf logf;
8 } else {
9   alias log logf;
10 }
11
12 float ipow(int I)(float f) {
13   version(GNU) {
14     return __builtin_powif(f, I);
15   } else {
16     static if (I == 0) return 1;
17     else static if (I == 1) return f;
18     else static if ((I%2) == 1) return sqr(ipow!(I/2)(f))*f;
19     else return sqr(ipow!(I/2)(f));
20   }
21 }
22
23 string ctPowExpand(string text) {
24   string buf;
25   while (true) {
26     int pos = text.ctFind("^");
27     if (pos == -1) return buf ~ text;
28     char var = text[pos-1];
29     buf ~= text[0 .. pos-1];
30     int num = pos+1;
31     while (text[num] >= '0' && text[num] <= '9')
32       num ++;
33     buf ~= "z"~text[pos+1 .. num]~"."~var;
34     text = text[num .. $];
35   }
36 }
37
38 float fn(vec c, int iters = 20) {
39   auto z = c;
40   // vec dt = vec(1, 0, 0);
41   FP r = z.length, dl = 1;
42   // auto ÎŽ = Spherical(0, 0, 1);
43   int i;
44   for (; i < iters; ++i) {
45     auto r7 = ipow!(7)(r);
46     dl = dl*r7*8 + 1; // Will this work, I wonder?
47     /**
48       z.x = |z|^8 * cos(asin(z.z / |z| )*8) * cos(atan2(z.y, z.x)*8) + c.x
49       z.y = |z|^8 * cos(asin(z.z / |z| )*8) * sin(atan2(z.y, z.x)*8) + c.y
50       z.z = |z|^8 * sin(asin(z.z / |z| )*8) + c.y
51       Maxima codegen GO
52     **/
53     // Funs
54     {
55       vec z2 = void, z4 = void, z6 = void, z8 = void, z10 = void, z12 = void, z14 = void, z16 = void;
56       vec z3 = void, z5 = void, z7 = void, z9 = void, z11 = void, z13 = void, z15 = void;
57       // OH NOES HER NUMERIC COPROCESSORS
58       for (int k = 0; k < vec.real_size; ++k) {
59         z2.real_field[k] = z.real_field[k] * z.real_field[k];
60         z3.real_field[k] = z2.real_field[k] * z.real_field[k];
61         z4.real_field[k] = z2.real_field[k] * z2.real_field[k];
62         z5.real_field[k] = z4.real_field[k] * z.real_field[k];
63         z6.real_field[k] = z2.real_field[k] * z4.real_field[k];
64         z7.real_field[k] = z6.real_field[k] * z.real_field[k];
65         z8.real_field[k] = z4.real_field[k] * z4.real_field[k];
66       }
67       for (int k = 0; k < 2; ++k) { // z only needed up to ^8
68         z9.real_field[k] = z8.real_field[k] * z.real_field[k];
69         z10.real_field[k] = z8.real_field[k] * z2.real_field[k];
70         z11.real_field[k] = z10.real_field[k] * z.real_field[k];
71         z12.real_field[k] = z8.real_field[k] * z4.real_field[k];
72         z13.real_field[k] = z12.real_field[k] * z.real_field[k];
73         z14.real_field[k] = z8.real_field[k] * z6.real_field[k];
74         z15.real_field[k] = z14.real_field[k] * z.real_field[k];
75         z16.real_field[k] = z8.real_field[k] * z8.real_field[k];
76       }
77       vec nz = void;
78       // I wish I had smart goo.
79       // trigsimp(trigexpand(sqrt(x*x+y*y+z*z)^8*cos(asin(z/sqrt(x*x+y*y+z*z))*8)*cos(atan2(y, x)*8)));
80       nz.x = mixin(ctPowExpand("((y^8-28*x^2*y^6+70*x^4*y^4-28*x^6*y^2+x^8)*z^8+(-28*y^10+756*x^2*y^8-1176*x^4*y^6-1176*x^6*y^4+756*x^8*y^2-28*x^10)*z^6+
81 (70*y^12-1820*x^2*y^10+1050*x^4*y^8+5880*x^6*y^6+1050*x^8*y^4-1820*x^10*y^2+70*x^12)*z^4+
82 (-28*y^14+700*x^2*y^12+308*x^4*y^10-2772*x^6*y^8-2772*x^8*y^6+308*x^10*y^4+700*x^12*y^2-28*x^14)*z^2+y^16-24*x^2*y^14-36*x^4*y^12+88*x^6*y^10+198*x^8*y^8+88*x^10*y^6-36*x^12*y^4
83 -24*x^14*y^2+x^16)/(y^8+4*x^2*y^6+6*x^4*y^4+4*x^6*y^2+x^8)")) + c.x;
84       // trigsimp(trigexpand(sqrt(x*x+y*y+z*z)^8*sin(asin(z/sqrt(x*x+y*y+z*z))*8)*cos(atan2(y, x)*8)));
85       nz.y = mixin(ctPowExpand("-((8*z.x*y^7-56*x^3*y^5+56*x^5*y^3-8*x^7*z.y)*z^8+(-224*z.x*y^9+1344*x^3*y^7-1344*x^7*y^3+224*x^9*z.y)*z^6+
86 (560*z.x*y^11-2800*x^3*y^9-3360*x^5*y^7+3360*x^7*y^5+2800*x^9*y^3-560*x^11*z.y)*z^4+(-224*z.x*y^13+896*x^3*y^11+2464*x^5*y^9-2464*x^9*y^5-896*x^11*y^3+224*x^13*z.y)*z^2+8*z.x*y^15-
87 24*x^3*y^13-120*x^5*y^11-88*x^7*y^9+88*x^9*y^7+120*x^11*y^5+24*x^13*y^3-8*x^15*z.y)/(y^8+4*x^2*y^6+6*x^4*y^4+4*x^6*y^2+x^8)")) + c.y;
88       // trigsimp(trigexpand(sqrt(x*x+y*y+z*z)^8*sin(asin(z/sqrt(x*x+y*y+z*z))*8)));
89       nz.z = mixin(ctPowExpand("sqrt(y^2+x^2)*(-8*z^7+(56*y^2+56*x^2)*z^5
90       +(-56*y^4-112*x^2*y^2-56*x^4)*z^3+(8*y^6+24*x^2*y^4+24*x^4*y^2+8*x^6)*z.z)")) + c.z;
91       z = nz;
92     }
93     r = z.length;
94     if (r > 16) {
95       break;
96     }
97   }
98   // logln(c, ": estimated distance ", 0.5 * r * logf(r) / dl, " at ", i);
99   return 0.5 * r * logf(r) / dl;
100 }
101
102 final class Fractal : SceneObject {
103   float ε; int iters; bool adapt;
104   static SceneObject fractal(ref string s) {
105     float ε = 0.00001; int iters = 20; bool adapt;
106     mixin(MatchExpr("s: [([[auto$#adapt=true$] $ε$|$#adapt=false$][, $iters$])]"));
107     return new Fractal(ε, adapt, iters);
108   }
109   static this() {
110     parse_fns["fractal"] = &fractal;
111     builtin["fractal"] = true;
112     help["fractal"] = ": stuff";
113   }
114   this(float ε, bool adapt, int iters) { this.ε = ε; this.adapt = adapt; this.iters = iters; }
115   Fractal dup() { return new Fractal(ε, adapt, iters); }
116   struct FractBuffer {
117     /*void next(ref Hit hit) {
118     }*/
119   }
120   mixin ThreadSlabSet!(FractBuffer);
121   vec getGrad(vec v, float localε) {
122     auto p = fn(v);
123     return vec(
124       fn(v + vec(localε,0,0), iters) - p,
125       fn(v + vec(0,localε,0), iters) - p,
126       fn(v + vec(0,0,localε), iters) - p
127     );
128   }
129   override {
130     string toString() { return "fractal"; }
131     void iterate(void delegate(ref SceneObject) dg) { }
132     void init(ref void* threadp) {
133       auto c = cache_init(threadp);
134     }
135     void setup(ref Hit hit, ref Ray ray, ref void* threadp) {
136       auto c = cache(threadp);
137       // hit a sphere centered around origin radius 1
138       auto psq = ray.pos.lensq, dsd = ray.dir.lensq,
139         b = (-ray.pos).dot(ray.dir) / dsd, disc = b*b + (2 - psq) / dsd;
140       if (disc < 0) return;
141      
142       auto d = sqrt(disc), t1 = b - d, t2 = b + d;
143       FP[2] bracket = void;
144       bracket[0] = t1;
145       bracket[1] = t2;
146       if (bracket[1] <= HitTolerance) return;
147       auto cur = bracket[0];
148       if (cur < 0) cur = 0;
149       int x;
150       int max_steps = iters * 2; // arbitrary choice
151       while (true) {
152         ++x;
153         auto dist = fn(ray.at(cur), iters);
154         cur += dist;
155         float localε = ε;
156         /*logln("Local ε at ", cur, " => ", ray.at(cur), ": ", ray.radius(cur), " * ", localε,
157           "; ray angle ", ray.angle,
158           ", ray origin ", ray.cam_origin,
159           ", distance ", (ray.cam_origin - ray.at(cur)).length
160         );*/
161         if (adapt)
162           localε *= ray.radius(cur);
163        
164         if (localε !> float.epsilon)
165           localε = float.epsilon; // just to be sure
166        
167         if (dist < localε) {
168           auto norm = getGrad(ray.at(cur), localε).normalized;
169          
170           if (norm.dot(ray.dir) > 0 && max_steps--) {
171             cur += localε - dist; // definitely move away at least this much
172             continue; // we're pointed AWAY. Not stopping.
173           }
174           // don't produce a hit if we're clearly moving away
175           if (max_steps) hit.set(cur, true, norm);
176           return;
177         }
178         if (cur !< bracket[1]) return; // left again
179       }
180     }
181     Bound genBound() {
182       return Bound(vec(-2), vec(2)); // I hope
183     }
184   }
185 }
Note: See TracBrowser for help on using the browser.