At 02:29 20/05/2002, Franktal Gallery wrote:
Andrew, you might like to check out my ray-traced fractals made from infinite reflections inside a stack of mirrored spheres. I think these pictures show something impossible to reproduce in the real world, because of the perturbation introduced by a real camera. This seems related to the uncertainty principle, I guess.... The virtual camera overcomes this obstacle.
I think it's probably more likely due to the difficulty of manufacturing optics, mirrored spheres, and CCDs/film emulsions to the required precision/resolution. My fairly ancient box would beg for mercy if I attempted something like these (which is not to say I don't want to); so I've been looking at such "inversion groups" (aka. "kleinian groups") with Fractint: 6Klein_Group(XYAXIS){ pi3=pi/3 c1=(0,2) c2=2*(sin(pi3)+flip(cos(pi3))) c3=2*(sin(2*pi3)+flip(cos(2*pi3))) c4=(0,-2) c5=2*(sin(4*pi3)+flip(cos(4*pi3))) c6=2*(sin(5*pi3)+flip(cos(5*pi3))) z=pixel inside=1: IF(cabs(z-c1)<1) z=conj(1/(z-c1))+c1 inside=1 ELSEIF(cabs(z-c2)<1) z=conj(1/(z-c2))+c2 inside=1 ELSEIF(cabs(z-c3)<1) z=conj(1/(z-c3))+c3 inside=1 ELSEIF(cabs(z-c4)<1) z=conj(1/(z-c4))+c4 inside=1 ELSEIF(cabs(z-c5)<1) z=conj(1/(z-c5))+c5 inside=1 ELSEIF(cabs(z-c6)<1) z=conj(1/(z-c6))+c6 inside=1 ELSE inside=0 ENDIF inside} In this example the accumulation points (where the images pile up on each other) all lie on a single circle. This is an accident due to the symmetry of the original mirrors' arrangement. In general, the curve is "nowhere-differentiable" in the jargon of a mid-1960s text where I first came across the subject. Modern terminology would use "fractal". I've just started on another where each pixel is coloured according to how many reflections a ray starting from that point goes through before escaping the system, but at the moment it's still a bit buggy: threecircles1 { ; three circles located at the cube roots of 1 ; real(p1) specifies their common radius - note that they're tangent to ; each other when real(p1)=sqrt(3)/2. ; ; From each point z in the complex plane a test particle is launched at ; an angle of imag(p1) to the positive real axis. Thus, for imag(p1)=0, ; the test particle will travel to the right. ; ; Each time the particle encounters a circle, it is reflected in the ; traditional manner and a count is kept of the number of such ; reflections. This count is used to colour the original point if and ; when the particle escapes towards infinity - that is to say, when the ; three circles no longer obstruct its course. ; ; First, we need to establish the centres of the three circles. centre1=1 centre2=exp(flip(2*pi/3)) centre3=centre2*centre2 ; Reckon this is faster than another exp()? rho=real(p1) theta=imag(p1) direction=cos(theta)+i*sin(theta) ; We'll use a vector representaion ; of the direction z = pixel trapped=(|z-centre1|<rho*rho || |z-centre2|<rho*rho || |z-centre3|<rho*rho) ; For efficiency, we see if z is _inside_ a circle. If it is, we can ; predict how long it will take to escape :-) ; Note also that since Fractint's || operator is nonstandard, we use ; rho*rho instead of rho. : ; End of initialisation section. Now beginning the loop. if(trapped) escaped=0 ; We'll continue to iterate, though - so that the insides of ; the circles really are classified as "inside" else ; Let's get dangerous ; Right, we have a ray that passes through the point z in the direction ; theta. Finding the intersections (if any) of this ray with a circle ; involves finding the solutions of a certain quadratic with real ; coefficients. If the quadratic has two real roots, then the ray cuts the ; circle. If it has one real root (necessarily of multiplicity two) then ; the ray is tangent to the circle. And if both roots are complex, then ; the ray misses the circle completely. escaped=1 ; As far as we know at the moment. hit=9999 ; Meaningless when escaped==1 ;Examining circle 1 coefA = |direction| coefB = real(z-centre1)*real(direction)+imag(z-centre1)*imag(direction) coefC = |z-centre1|-rho*rho discriminant=coefB*coefB-coefA*coefC ; If discriminant==0, then the ray is tangent to circle 1 - we won't ; count this as a "reflection" ; If discriminant<0, then the ray misses circle 1 completely. if(discriminant>0) ; Two solutions t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0) ; If t1<0 then this hit is actually behind us escaped=0 ; Sorry, not this time. hit=t1 target=1 ; The first circle endif if(t2>0 && t2<t1) ; Hits t2 before hitting t1 escaped=0 hit=t2 target=1 endif endif ; Circle 2 ; coefA = |direction| is already known coefB = real(z-centre2)*real(direction)+imag(z-centre2)*imag(direction) coefC = |z-centre2|-rho*rho discriminant=coefB*coefB-coefA*coefC if(discriminant>0) t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0 && t1<hit) escaped=0 hit=t1 target=2 endif if(t2>0 && t2<t1 && t2<hit) escaped=0 hit=t2 target=2 endif endif ; Circle 3 coefB = real(z-centre3)*real(direction)+imag(z-centre3)*imag(direction) coefC = |z-centre3|-rho*rho discriminant=coefB*coefB-coefA*coefC if(discriminant>0) t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0 && t1<hit) escaped=0 hit=t1 target=3 endif if(t2>0 && t2<t1 && t2<hit) escaped=0 hit=t2 target=3 endif endif ; We now know when we hit a circle (hit) and which circle it is (target) ; The hit point is z+direction*hit, the normal at this point is ; z-centre?, which we normalise to a unit vector (un). ; ; We set z to the hit point, and the reflected vector is ; direction-2(direction.un)un if(target==1) un=(z-centre1)/sqrt(|z-centre1|) elseif(target==2) un=(z-centre2)/sqrt(|z-centre2|) else un=(z-centre3)/sqrt(|z-centre3|) endif r=direction-2*(real(direction)*real(un)+imag(direction)*imag(un))*un z=z+direction*hit direction=r endif ;bailout continue until we escape escaped==0 } threecircles2 { ; three circles located at the cube roots of 1 ; real(p1) specifies their common radius - note that they're tangent to ; each other when real(p1)=sqrt(3)/2. ; ; p2 specifies a point (the "radiant") from which test particles are ; launched. More precisely, every point in the complex plane emits a ; test particle, which travels directly away from p2 ; ; First, we need to establish the centres of the three circles. centre1=1 centre2=exp(flip(2*pi/3)) centre3=centre2*centre2 ; Reckon this is faster than another exp()? rho=real(p1) direction=pixel-p2 z = pixel trapped=(|z-centre1|<rho*rho || |z-centre2|<rho*rho || |z-centre3|<rho*rho) ; For efficiency, we see if z is _inside_ a circle. If it is, we can ; predict how long it will take to escape :-) ; Note also that since Fractint's || operator is nonstandard, we use ; rho*rho instead of rho. : ; End of initialisation section. Now beginning the loop. if(trapped) escaped=0 ; We'll continue to iterate, though - so that the insides of ; the circles really are classified as "inside" else ; Let's get dangerous ; Right, we have a ray that passes through the point z in the direction ; theta. Finding the intersections (if any) of this ray with a circle ; involves finding the solutions of a certain quadratic with real ; coefficients. If the quadratic has two real roots, then the ray cuts the ; circle. If it has one real root (necessarily of multiplicity two) then ; the ray is tangent to the circle. And if both roots are complex, then ; the ray misses the circle completely. escaped=1 ; As far as we know at the moment. hit=9999 ; Meaningless when escaped==1 ;Examining circle 1 coefA = |direction| coefB = real(z-centre1)*real(direction)+imag(z-centre1)*imag(direction) coefC = |z-centre1|-rho*rho discriminant=coefB*coefB-coefA*coefC ; If discriminant==0, then the ray is tangent to circle 1 - we won't ; count this as a "reflection" ; If discriminant<0, then the ray misses circle 1 completely. if(discriminant>0) ; Two solutions t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0) ; If t1<0 then this hit is actually behind us escaped=0 ; Sorry, not this time. hit=t1 target=1 ; The first circle endif if(t2>0 && t2<t1) ; Hits t2 before hitting t1 escaped=0 hit=t2 target=1 endif endif ; Circle 2 ; coefA = |direction| is already known coefB = real(z-centre2)*real(direction)+imag(z-centre2)*imag(direction) coefC = |z-centre2|-rho*rho discriminant=coefB*coefB-coefA*coefC if(discriminant>0) t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0 && t1<hit) escaped=0 hit=t1 target=2 endif if(t2>0 && t2<t1 && t2<hit) escaped=0 hit=t2 target=2 endif endif ; Circle 3 coefB = real(z-centre3)*real(direction)+imag(z-centre3)*imag(direction) coefC = |z-centre3|-rho*rho discriminant=coefB*coefB-coefA*coefC if(discriminant>0) t1=(-coefB-sqrt(discriminant))/coefA t2=(-coefB+sqrt(discriminant))/coefA if(t1>0 && t1<hit) escaped=0 hit=t1 target=3 endif if(t2>0 && t2<t1 && t2<hit) escaped=0 hit=t2 target=3 endif endif ; We now know when we hit a circle (hit) and which circle it is (target) ; The hit point is z+direction*hit, the normal at this point is ; z-centre?, which we normalise to a unit vector (un). ; ; We set z to the hit point, and the reflected vector is ; direction-2(direction.un)un if(target==1) un=(z-centre1)/sqrt(|z-centre1|) elseif(target==2) un=(z-centre2)/sqrt(|z-centre2|) else un=(z-centre3)/sqrt(|z-centre3|) endif r=direction-2*(real(direction)*real(un)+imag(direction)*imag(un))*un z=z+direction*hit direction=r endif ;bailout continue until we escape escaped==0 } ...and while we're on the subject of Fractint formulae, can someone explain why it is that it's still possible after all this time for BoF to still contain things that haven't been reproduced in Fractint yet? You'd have thought that the two-dimensional Newton systems described therein would've been: twodnewton{ h = real(p1) m = imag(p1) z=pixel: oz=z x=real(z),y=imag(z) f=2*x-y-m*(x-x*x) g=2*y-x-m*(y-y*y) fx=2-m*(1-2*x) gy=2-m*(1-2*y) delta =(-g-f*gy)/(fx*gy-1) epsilon=(-f-g*fx)/(fx*gy-1) nx=x+h*delta ny=y+h*epsilon z=nx+flip(ny) |oz-z|>.001 } (E.g. h=0.5, m=2) Morgan L. Owens "I'm gonna build a room that echoes."