Welcome to Fractal Forums

Fractal Software => Programming => Topic started by: billtavis on May 06, 2014, 12:07:05 AM




Title: smooth external angle of Mandelbrot set?
Post by: billtavis on May 06, 2014, 12:07:05 AM
Does anyone know of a way to calculate a smooth field representing the external angle of the Mandelbrot set? Or any source code to point me towards? I have found many mathematical explanations but a lot of it is over my head.  :hmh:  I'm not looking for the fastest way, just the easiest to understand.

This site http://eventoneresearch.com/ has exactly what I'm looking for, the "field gradient". But it looks like their technique is patented. Any help is much appreciated, thanks!


Title: Re: smooth external angle of Mandelbrot set?
Post by: youhn on May 06, 2014, 07:51:34 AM
Check out Gnofract4D. Has lots of coloring methods, all open source.


Title: Re: smooth external angle of Mandelbrot set?
Post by: hobold on May 06, 2014, 09:54:52 AM
I thought that should be simple, based on a "binary decomposition". Every iteration provides another bit of external angle, from most significant to least significant. It doesn't quite work, but it comes tantalizingly close. Here is the code anyway, in case I only made some stupid error which others might spot.

Code:
// Mandelbrot iteration of a single point
// tries to compute angle of external ray passing through that point (buggy!)
unsigned iterate(const double cx, const double cy, const long maxiter) {
    unsigned i = 0;                 // iteration count
    double zx = 0.0, zy = 0.0;      // complex z
    double z2x = 0.0, z2y = 0.0;    // z real and imaginary parts squared
   
    unsigned angle = 0;             // accumulates fractional angle
    unsigned curBit = 0x80000000UL; // current bit of binary fraction
   
    do {
        i++;
        if (i > maxiter) {
            return -1;      // alternative loop end
        }
       
        z2x = zx*zx;        // squared components
        z2y = zy*zy;

        zy = 2.0*zx*zy;     // complex square, in place
        zx = z2x - z2y;
       
        zx = zx + cx;       // plus constant
        zy = zy + cy;
       
        // accumulate another bit of fractional angle
        if (zy < 0.0) {
            angle |= curBit;
        }
        curBit = curBit >> 1;   // next bit
       
    } while ((zx*zx + zy*zy) < 10000.0);
   
    // keep refining the angle for escaped points
    while (curBit > 0) {
        z2x = zx*zx;        // squared components
        z2y = zy*zy;
       
        zy = 2.0*zy*zx;     // complex square
        zx = z2x - z2y;
       
        zx += cx;           // plus constant
        zy += cy;
       
        // accumulate another bit of fractional angle
        if (zy < 0.0) {
            angle |= curBit;
        }
        curBit = curBit >> 1;   // next bit
    }
   
    return angle;
}

But it could also be the case that my understanding of the idea only really applies to Julia sets, not to the Mandelbrot set.


Title: Re: smooth external angle of Mandelbrot set?
Post by: claude on May 06, 2014, 06:29:11 PM
I'm not looking for the fastest way, just the easiest to understand.

Here's what I did:

For each exterior pixel, trace its external ray through the C plane outwards accumulating bits in reverse order as you pass each dwell band - once the ray endpoint exceeds the bailout radius then reverse the bitstring.  That gives you a path through the binary decomposition grid, you can use the final angle of the original iterate (once it passes the bailout radius) to get local coordinates within each binary decomposition cell for a bit more smoothing.

http://www.math.nagoya-u.ac.jp/~kawahira/programs/mandel-exray.pdf has an algorithm for tracing rays from outwards in, it needs a bit of fiddling to trace from inwards out - in particular when crossing dwell bands you need to check which of the two angle doubling preimages is closer to the current ray endpoint (which you choose determines one bit of the output angle bitstring).

Here's one image I made (still from a video), using the angle field and potential to map a hyperbolic tiling to the exterior:
http://mathr.co.uk/mandelbrot/2013-08-20_mandelbrot_exterior_hyperbolic_tiling.png


Title: Re: smooth external angle of Mandelbrot set?
Post by: laser blaster on May 06, 2014, 07:51:32 PM
Wow, that's really cool! This opens up the door for some cool texture effects.


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on May 06, 2014, 09:31:39 PM
thanks everyone for the responses! I've got a great place to start from with the explanations and code


Title: Re: smooth external angle of Mandelbrot set?
Post by: David Makin on June 12, 2014, 03:02:20 PM
I realise I'm a little late to this but I've been away from fractaling for a while.
I wrote this a while ago and I spent a long time fiddling to get my version correct to as great a depth as possible.

Here's my code from mmf3.ucl for Ultra Fractal:

*************
Code:
MMF3-FieldLines {
;
; This version of calculating field lines will work reasonably well with both
; Mandelbrot and Julia Sets for divergent fractals with a divergence that is
; close to being a positive integer >=2 but can also produce quite interesting
; results with fractals that do not fit that criteria - just not rendering the
; field lines correctly in other cases :-)
;
; Thanks to Chris Hayton for showing me how to fix the "off-by-one" problems.
;
global:
  float twopi = 2.0*#pi
  float dtwopi = 0.5/#pi
  float dp = 1.0/@power
  float m = @power*dtwopi
  float dp2 = 1.0/(2.0+@power) ; Changed from originally trying 1/power
init:
  complex zv[#maxiter]
  int i = 0
  float a = 0.0
  float b = 0.0
  float c = 0.0
  float s = 0.0
  if @julia
    zv[i] = #z
    i = i + 1
  endif
loop:
  zv[i] = #z
  i = i + 1
final:
  if (s = atan2(#z))<0
    s = s + twopi
  endif
  while i > @skipiter
    i = i - 1
    if @version==0
      b = (@power*atan2(zv[i]))%twopi
    else
      b = atan2(zv[i]^@power)
    endif
    if b < 0.0
      b = b + twopi
    endif
    c = c + b - s
    if c >= #pi
      s = s + twopi
      if @fixit || @accident
        c = c - twopi
      endif
    elseif c < -#pi
      s = s - twopi
      if @fixit || @accident
        c = c + twopi
      endif
    endif
    if @fixit
      c = dp2*c
    elseif !@accident
      c = 0.0
    endif
    if (a = atan2(zv[i])) < 0.0
      a = a + twopi
    endif
    s = dp*(s + twopi*floor(a*m))
  endwhile
  s = s*dtwopi
  if @fixskip
    while s<0.0
      s = s + 1.0
    endwhile
    while s>=1.0
      s = s - 1.0
    endwhile
  endif
  #index = s
default:
  title = "MMF3 Field Lines (use high bailouts)"
  heading
    caption = "Information"
;    text = "This colouring is a little touchy around certain values, if you \
;            get obvious lines or dots that shouldn't be there but the 'Degree \
;            of Divergence' is set properly for the main formula and you are \
;            using a high bailout of say 1e20 or higher then try \
;            adjusting the location very slightly. For example if vertical \
;            lines appear on an unrotated fractal then try changing the x \
;            (real) location very slightly, if horizontal lines appear then try \
;            changing the y (imag) location very slightly."
  endheading
  heading
  endheading
  param version
    caption = "Version"
    enum = "Original" "Alternative"
    default = 1
    hint = "Allows you to modify one of the calculations in the formula that \
            is particularly sensitive to small errors. Ideally both methods \
            should produce identical results but they don't, so if you get \
            visible errors using one method then try the other (assuming you \
            have the divergence set correctly and are using a high bailout)."
  endparam
  param power
    caption = "Degree of divergence"
    default = 2.0
    hint = "This is an estimate of the divergence of the main formula, e.g. 2 \
            for z^2+c, 3 for z^3+c etc."
  endparam
  param julia
    caption = "Julia ?"
    default = false
    hint = "Enable for correct rendering of Julia Sets."
  endparam
  param skipiter
    caption = "'Start' iteration"
    default = 0
    min = 0
    hint = "You can use this to attempt to get better detail at higher \
            iteration depths, note that it only works as intended with 'Color \
            Density' as a whole integer >=1."
  endparam
  param fixskip
    caption = "Fix 'Start' iteration"
    default = false
    hint = "Fixes a problem when the start iteration is non-zero and maybe in \
            other cases - specifically if you get solid areas of palette \
            colour zero when the Transfer Function is Linear this may fix the \
            problem."
  endparam
  param fixit
    caption = "Fix Field Lines"
    default = false
    hint = "Enable this to make the formula much more accurate at rendering \
            the field lines correctly. It's a fudge produced after trial and \
            error but is quite effective at correcting some areas."
  endparam
  param accident
    caption = "Happy Accident"
    default = false
    hint = "You may find the colouring useful when this is enabled. This \
            is available as I found the results when initially adding the \
            'Fix it' option quite interesting."
    visible = !@fixit
  endparam
}

**************


Title: Re: smooth external angle of Mandelbrot set?
Post by: David Makin on June 12, 2014, 08:21:26 PM
Theere's a later version of my formula for field lines - though somewhat extended to include smooth iteration, distance estimation and distance estimation angles in my class formulas for UF - mmf.ulb called "MMF Field Estimator"
It's a UF class formula so not quite so easy to follow but I think it should be clear enough.
I boiled down "corrections" to field lines that work in most places but at the moment require manual adjustment to work properly - these are the two parameters that can be modified by the user to fix errors in the field lines, unfortunately you can't always fix them everywhere in view even then but this is about as close as you can get I think.
I tried including the MMF Field Lines code here but unfortunately it's too long, it's in mmf.ulb in te Ultra Fractal Formula database at:

http://formulas.ultrafractal.com/ (http://formulas.ultrafractal.com/)


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on June 13, 2014, 07:00:37 PM
thanks David! I'll look through that code and see what I can get from it, what I have now isn't entirely satisfactory. I guess I should be learning more about UltraFractal as well! I don't think I've ever tried that one


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on February 19, 2015, 05:59:47 PM
Thank you everyone for your input. I was finally able to get what I was looking for: a very smooth external angle rendering that I can understand (and yes it's very slow to calculate). I solved it by pretending that from every pixel, I drop a ball and let it roll downhill away from the set. Once it reaches a very large distance from the set, I use it's angle on that large circle. Every step of the ball rolling requires computing the gradient at its current location, which requires a full set of iterations. This is why it's so slow. However the advantage is that by decreasing the step size and using 4th order Runge-Kutta integration I was able to get it quite accurate:

(https://38.media.tumblr.com/9ae232eb91320d1e77402e0320f7463d/tumblr_njtoknk8SA1tre1zbo1_400.gif)


Title: Re: smooth external angle of Mandelbrot set?
Post by: Adam Majewski on February 19, 2015, 06:26:38 PM
cool. Src code with extensive description is welcome


Title: Re: smooth external angle of Mandelbrot set?
Post by: youhn on February 19, 2015, 06:27:32 PM
That looks nice, like arms scanning/touching the 2D boundary of the set.  :)

Also reminds me of synapsis, or how do you call those neuron-ends in our brain?


Title: Re: smooth external angle of Mandelbrot set?
Post by: DarkBeam on February 19, 2015, 06:47:23 PM
Is this related to triangle inequality average? No... :dink:


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on February 20, 2015, 05:48:27 PM
I don't know about triangle inequality average, the pictures I found looks related but different.

So the first thing I did was to get a binary version working, based on adding bits to a fractional angle. I used the source code found here: http://www.mndynamics.com/indexp.html to get a working version. The resulting grayscale image was very smooth (shown by the contour lines) but it's pretty obvious that it doesn't capture the complexity of the boundary.
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleSideBySideBINARY_zpsw7akyaxr.jpg)

Then I found a formula for calculating the gradient of the potential (smoothed iteration count) at this site: http://linas.org/art-gallery/escape/ray.html  So, for a given c-value in the plane, I would get the distance to the set using the standard distance estimation, and then take a step of that length going down the gradient. This would give me a new c-value, where I would find the new distance and gradient and take another step, until c was very far from the origin. Then, I would plot the original pixel with the angle of the distant c-value. The resulting grayscale image looked just as smooth as the binary version, and it captured the detail at the boundary. However, when I ran contour lines along it there was obvious errors creeping in from taking too large of a step.
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleSideBySideEULER_zpsnuzipe8n.jpg)

I realized that this was a case of Euler integration, so I knew I could improve the accuracy by switching to Runge-Kutta instead. Also, I limit the step size to a fraction of the distance, and I give a maximum cutoff. Limiting the step size along with using Runge-Kutta gave a result just as smooth as the binary method, but it captures the boundary (and I actually understand what it's doing LOL). 
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleSideBySideRUNGEKUTTA_zpsks8ihwkj.jpg)


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on February 20, 2015, 06:12:34 PM
Here is a c++ function to get the gradient of a c-value based on the formulas given by Linas Vepstas. The gradient is returned by reference to the variables gzx and gzy. I also return the length and derivative values by reference to make it work with my other function below.
Code:
void getGradient(long double cx, long double cy, long double &xDer,
                 long double &yDer, long double &gzx, long double &gzy,
                 long double &length, unsigned int maxIter) {
        // based on gradient equation found at:
        // http://linas.org/art-gallery/escape/ray.html
    long double zx = 0.0L;
    long double zy = 0.0L;
    xDer = 0.0L; yDer = 0.0L; // initialize values
    long double xTemp;
    unsigned int n = 0;
    long double huge = std::numeric_limits<float>::max();
    while ((zx*zx + zy*zy) < huge && n < maxIter) {
            // compute new derivative:
        xTemp = 2.0L*(zx*xDer - zy*yDer) + 1.0L;
        yDer = 2.0L*(zx*yDer + zy*xDer);
        xDer = xTemp;
            // compute new z value:
        xTemp = zx*zx - zy*zy + cx;
        zy = 2.0L*zx*zy + cy;
        zx = xTemp;
        n++;
    }
    length = std::sqrt(zx*zx + zy*zy);
    long double m = n + 1 - (std::log(std::log(length)) / std::log(2.0L));
    long double f = pow(2.0L,-m); // Douady-Hubbard potential
    long double Dx = zx*(xDer/2.0) - zy*(-yDer/2.0); // real part of zn * Dn
    long double Dy = zx*(-yDer/2.0) + zy*(xDer/2.0); // imag part of zn * Dn
    gzx = f*Dx / (length*length*std::log(length)); // x-gradient for cx,cy
    gzy = f*Dy / (length*length*std::log(length)); // y-gradient for cx,cy
}

And here is a function that uses the getGradeint function to perform Runge-Kutta integration and find the external angle. It's assumed that the point has already been checked for being outside the set.
Code:
double externalAngle(long double cx, long double cy, std::size_t maxIter) {
        // uses 4th order Runge-Kutta integration to find external angle
    long double huge = 640000.0; // bigger is better, but also slower
    long double ckx = cx;
    long double cky = cy;
    long double ckxTemp, ckyTemp;
    long double xDer,yDer,length,derLength,dist,gzx,gzy,gzxA,gzyA,gzxB,gzyB,gzxC,gzyC,gzxD,gzyD,gzlength;
    unsigned int k = 0;
    unsigned int maxStep = maxIter*2; // make sure we try to walk far away
    while((ckx*ckx + cky*cky) < huge && k++ < maxStep) {
        // integrate with Runge-Kutta outwards along gradient:
        getGradient(ckx,cky,xDer,yDer,gzxA,gzyA,length,maxIter);
        derLength = std::sqrt(xDer*xDer + yDer*yDer);
        dist = 0.5L * std::log(length) * length / derLength;
        dist = std::min(16.0L,0.5*dist); // reduce step size
        gzlength = std::sqrt(gzxA*gzxA + gzyA*gzyA);
        ckxTemp = ckx + (0.5 * dist * gzxA/gzlength); // walk to midpoint
        ckyTemp = cky + (0.5 * dist * gzyA/gzlength); // using A
        getGradient(ckxTemp,ckyTemp,xDer,yDer,gzxB,gzyB,length,maxIter);
        gzlength = std::sqrt(gzxB*gzxB + gzyB*gzyB);
        ckxTemp = ckx + (0.5 * dist * gzxB/gzlength); // walk to midpoint
        ckyTemp = cky + (0.5 * dist * gzyB/gzlength); // using B
        getGradient(ckxTemp,ckyTemp,xDer,yDer,gzxC,gzyC,length,maxIter);
        gzlength = std::sqrt(gzxC*gzxC + gzyC*gzyC);
        ckxTemp = ckx + (dist * gzxC/gzlength); // walk full step
        ckyTemp = cky + (dist * gzyC/gzlength); // using C
        getGradient(ckxTemp,ckyTemp,xDer,yDer,gzxD,gzyD,length,maxIter);
        gzx = (1.0L/6.0L) * (gzxA + 2.0L*(gzxB + gzxC) + gzxD); // average the values together
        gzy = (1.0L/6.0L) * (gzyA + 2.0L*(gzyB + gzyC) + gzyD);
        gzlength = std::sqrt(gzx*gzx + gzy*gzy); // get length to normalize
        ckx += dist * gzx/gzlength; // normalize gradient and scale by dist
        cky += dist * gzy/gzlength;
    }
    return (std::atan2(cky,ckx));
}
Even though Runge-Kutta method requires solving the gradient 4 times per step, it winds up being much faster than Euler integration in practice because it is so much more accurate. Notice that I used
Code:
dist = std::min(16.0L,0.5*dist);
for the step size. To get an image just as smooth with plain Euler integration required reducing the step size to
Code:
dist = std::min(1.0L,0.05*dist);
which was way too slow.


Title: Re: smooth external angle of Mandelbrot set?
Post by: Adam Majewski on February 23, 2015, 05:09:46 PM
The algorithm you use is not standard.

It would be nice if you could make a detailed description on your page,
here or maybe in wikibooks https://en.wikibooks.org/wiki/Fractals


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on February 23, 2015, 05:40:01 PM
LOL yeah it's probably not the best way either. :D  Claude mentioned above that you could walk away from the set using Newton's method. But I have no idea what that means or how to do it, and thinking in terms of Euler integration is natural to me because a lot of my experience on computers is with simulation-based animations.

This article gives a good overview of the integration methods: http://gafferongames.com/game-physics/integration-basics/

Basically, I start at a pixel, and get a c-value (it's coordinate location in space). Next I treat that pixel like a particle, and the potential of the mandelbrot set like a velocity field. I "run the simulation" until the "particle" is far enough away from the origin to measure its angle.

I'm not sure how else I can elaborate on it. Certainly, I am not going to be able to produce any maths about it. Are there any parts you are having trouble understanding? I'd be happy to clarify.


Title: Re: smooth external angle of Mandelbrot set?
Post by: claude on February 26, 2015, 04:42:09 AM
And here is a function that uses the getGradeint function to perform Runge-Kutta integration and find the external angle. It's assumed that the point has already been checked for being outside the set.
Code:
double externalAngle(...) {
  ...
  return (std::atan2(cky,ckx));
}

This gets you the angle in only double-precision, but using double precision floating point throughout it's possible to get the external angle in much higher precision - the trick is to collect bits from the binary representation of the angle as you cross each dwell band - whether the final iterate that escaped has a positive or negative imaginary part determines if the bit is 0 or 1 respectively, see binary decomposition colouring http://www.mrob.com/pub/muency/binarydecomposition.html .

This is orthogonal to using Newton's method - it should also work with your potential field particle simulation technique as long as you never skip more than one dwell band at a time.

The idea behind Newton's method is to gradually reduce your target potential keeping the angle fixed to find a target final-z value, then solve for c in f^n(0, c) = z-final using the current c-value as initial guess for Newton's method iterations.  The amount you reduce the potential by is controlled by a parameter called "sharpness", essentially the number of steps to take within each dwell band - usually I use between 4 and 8.  This means the number of steps taken is known in advance (sharpness * initial dwell) and usually you don't need many Newton's method iterations to converge at each step (around 2 - 5 was typical in some tests I did a while ago).

You can only decrease potential so much for a fixed n with a fixed escape radius, so eventaully you decrement n, which increases |z-final| and gives you a choice of two angles (the pre-images under angle doubling) for arg z-final, you pick the one which is closer to the n'th iterate of your current c value, and which one you pick also tells you another bit of the external angle).

The algorithm is described here, for the other direction (starting from an external angle and tracing inwards to the Mandelbrot set - it's simpler because there is only one image under angle-doubling instead of two possibilities for the inverse): http://www.math.nagoya-u.ac.jp/~kawahira/programs/mandel-exray.pdf

I have a few implementations, the currently maintained ones are:

external ray tracing inwards (double precision floating point, libgmp arbitrary precision rational for external angle)
https://gitorious.org/mandelbrot/mandelbrot-numerics/source/3d55cfc99cc97decb0ba0d9c2fb271a504b8e504:c/lib/m_d_exray_in.c
http://code.mathr.co.uk/mandelbrot-numerics/blob/72eeac97a15f6c8c5f06be4b82bcf63d17c4851e:/c/lib/m_d_exray_in.c

external ray tracing outwards (double precision floating point, libgmp arbitrary precision rational for external angle)
https://gitorious.org/mandelbrot/mandelbrot-numerics/source/3d55cfc99cc97decb0ba0d9c2fb271a504b8e504:c/lib/m_d_exray_out.c
http://code.mathr.co.uk/mandelbrot-numerics/blob/72eeac97a15f6c8c5f06be4b82bcf63d17c4851e:/c/lib/m_d_exray_out.c

external ray tracing inwards (libmpfr arbitrary precision floating point, libgmp arbitrary precision rational for external angle)
https://gitorious.org/mandelbrot/mandelbrot-numerics/source/3d55cfc99cc97decb0ba0d9c2fb271a504b8e504:c/lib/m_r_exray_in.c
http://code.mathr.co.uk/mandelbrot-numerics/blob/72eeac97a15f6c8c5f06be4b82bcf63d17c4851e:/c/lib/m_r_exray_in.c

external ray tracing outwards (libmpfr arbitrary precision floating point, libgmp arbitrary precision rational for external angle)
(still to be implemented)


Title: Re: smooth external angle of Mandelbrot set?
Post by: claude on February 27, 2015, 12:51:05 PM
I adapted billtavis' code to collect bits.  First I changed the getGradient function to return the continuous iteration count and the sign of the last iterate:

Code:
void getGradient(long double cx, long double cy, long double &xDer,
                 long double &yDer, long double &gzx, long double &gzy,
                 long double &length, unsigned int maxIter,
                 long double &m, bool &b) { // these two are the new ones
    ...
    b = zy < 0 || (zy == 0 && zx < 0);
    m = n + 1 - std::log2(std::log(length) / (0.5 * std::log(huge)));
    ...
}

I used a different formula for m, because I had issues with the original (but they only differ by a constant factor and offset afaict) - the one I use has integer spacing of dwell bands and m = 2 for |c| = sqrt(huge) = escape radius.  I also wrote a wrapper without the extra arguments, that just calls the one with extra arguments, so I didn't have to change so much code in externalAngle().

Then I added bit collection in externalAngle():

Code:
typedef std::vector<bool> BITS;
BITS externalAngle(long double cx, long double cy, size_t maxIter, long double &e) {  // returns a vector of bits, atan2 result in e
  ...
    long double m = maxIter * 2;
    long double m_old = m;
    BITS bs;
    bool b;
    while (m_old > 2) { // this replaces the previous while loop start
        getGradient(ckx,cky,xDer,yDer,gzxA,gzyA,length,maxIter, m, b);
        if (std::ceil(m) < m_old) {  // collect a bit if we crossed a dwell band
          bs.push_back(b);
          m_old = m;
        }
        ...
        dist = std::min(std::max(16.0L, 0.01 * std::sqrt(ckx * ckx + cky * cky)),0.5*dist); // replaces the previous clamping, 16 is a tiny step size with very large |c|
        ... // rest of loop body unchanged
   }
    e = (std::atan2(cky,ckx));
    reverse(bs.begin(), bs.end());  // the bits were collected least-significant first
    return bs;
}

Then there's the main program which takes command line arguments, converts the atan2 result from long double to a vector of bits, compares it with the (hopefully more accurate) vector of bits result, and prints them out.  See the attached source code file for details.

Here are some tests, which seem to indicate the atan2 result is valid to about 16 bits, even though long double has 64 bits of mantissa precision:

Code:
$ #        cre   cim   iter  # pre(periodic) angle of nearby landing point
$ ./tavis  0.251 0     1000  # .(0)
.0000000000000000000000000000000000000000000000000000000000000000
.0000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000
$ ./tavis -2.001 0     1000  # .1(0)
.100000000000 0000000000000000000000000000000000000000000000000000
.100000000000
$ ./tavis -0.75  0.01  1000  # .(01)
.01010101010101001 01000001111000101111100010001010101110101011011
.01010101010101010 1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101001001111
$ ./tavis  1e-16 1     1000  # .0(01)
.0010101010101001 111110001001100001110001111010000110100110001111
.0010101010101010 101010101010101010101010101100100111011
$ ./tavis -1.749 1e-10 1000  # .(011)
.01101101101100 00100000101001101000000110000110110110011010111011
.01101101101101 101101101101101101101101101101001001001001001001001001001001001100100100101001110001
$ ./tavis -1.01  0.251 1000  # .(01011001)
.010110010101011 1001111010111001000000101111001011001111100001000
.010110010101100 101011001010110010101100101011001010110010101100101011001010110100100111111
$ ./tavis -0.99  0.251 1000  # .(01010110)
.01010110010101011 00111001100111100010010001110011010011101111011
.01010110010101100 1010110010101100101011001010110010101100101011001010110010101010110100011101


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on March 02, 2015, 10:14:55 PM
wow! :thanks2:
Thanks Claude, your new way of measuring the angle is great! I've had some impressive preliminary results... I will post some pictures once I get a few more kinks worked out.



Title: Re: smooth external angle of Mandelbrot set?
Post by: claude on March 03, 2015, 11:55:58 AM
:D

You can get a longer bitstring by starting with the binary representation of the argument of the first escaped iterate of the initial point (must use the same escape radius), instead of an empty bitstring.  See updated code attached.

Refering to the image in http://www.fractalforums.com/index.php?topic=20866.0 the bit collection gets one bit per vertical dwell band going upwards, but you can use the horizontal position within the lowest cell too - billtavis' original algorithm effectively only uses the horizontal cell position once the ray gets to the top.


EDIT: I just fixed a bug in the code (missing 'else' around line 173), attachment replaced...


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on March 04, 2015, 07:33:41 PM
soo... I did lots of tests... here's some results...
Here's a closeup of the main needle, done with my algorithm as I posted it above:
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleAtanOldContours_zpsyfpiibo9.png)
The moire patterns are nice and psychedelic, but obviously the angle measurement has way too much error. So then I tried the modifications Claude gave above. One really cool thing I realized is that the step size did not need to be clipped at all. Now I just use
Code:
dist *= step;
where dist is the distance given by distance estimation and step is a variable greater than 0 and less than or equal to 1.  I also found that the binary method is highly dependant on a very very huge escape radius (more on this below), so I raised this as high as I could. With these changes I got this:
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleBinaryNEWcontours_zpsvewil0y5.png)
What a difference! There is still some errors creeping in, off the tip of each branch (look for black pixels in white regions and vice-versa). I minimized the errors by taking the variable step down to 0.05.  Just for shits and giggles, I tried atan2 again, and to my amazement, I got this:
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleAtanNEWcontours_zpskjp7ev0z.png)
Even smoother with no visible errors! And I got this result using a step size of 0.5 which was much faster. So the big thing I missed above and that I got from Claude was making sure the escape radius is big enough, and making sure that I actually get all the way there before measuring the angle (His new calculation of 'm' and using 'm>2' as the condition to stop looping).


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on March 04, 2015, 07:47:54 PM
The binary method of measuring the angle causes steps in the gradient, which only smooth out when the escape radius equals infinity. I am guessing that the "horizontal positioning" mentioned by Claude above would help alleviate this. To show what I mean, here is the binary external angle using an escape radius of 3.40282e+38
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleFloatGrad_zpsbbpxhfqe.png)
Seeing this, I raised the escape radius (defined as the variable "huge" in the getGradient function) to the highest value I felt I could, which was 4.49423e+307 and got really close to a smooth gradient. However, the contours reveal that it's still not quite there:
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleDoubleGrad_zpsbp75xhqg.png)
For comparison, here is the atan2 angles, also using the largest escape radius possible. There is no visible break in smoothness, even in the contours:
(http://i288.photobucket.com/albums/ll174/BillTavis/AngleAtanGrad_zpspbevukay.png)


Title: Re: smooth external angle of Mandelbrot set?
Post by: claude on March 04, 2015, 10:11:09 PM
One really cool thing I realized is that the step size did not need to be clipped at all. Now I just use
Code:
dist *= step;
where dist is the distance given by distance estimation and step is a variable greater than 0 and less than or equal to 1.

This is risky, you absolutely mustn't skip more than one dwell band at once, or you'll lose bits and get a corrupted result.  The Newton method ray tracing works robustly by taking a fixed number of steps within each dwell band.

I am guessing that the "horizontal positioning" mentioned by Claude above would help alleviate this.

Yep, should give you an extra 64 bits (with long double), which should get rid of any steppy appearance.


Title: Re: smooth external angle of Mandelbrot set?
Post by: billtavis on March 05, 2015, 12:17:26 AM
This is risky, you absolutely mustn't skip more than one dwell band at once, or you'll lose bits and get a corrupted result.
That's what I thought at first too, but I checked and using a step size of 0.5*dist was small enough to never miss a dwell band in the examples I tried, and there was no difference in the output compared to the clipped step length. And this way the step size is large enough to get to the edge of the very large escape radius in a reasonable amount of time. I think it works because the distance estimation seems to be related in some way to the width of the current dwell band. Although, there is probably a more direct way to measure the width of the dwell band?
I'm still trying to wrap my head around Newton's method, it semms that's probably a more accurate way to walk out from the set.


Title: Re: smooth external angle of Mandelbrot set?
Post by: Adam Majewski on June 24, 2017, 11:01:14 AM
Code:
; Thanks to Chris Hayton for showing me how to fix the "off-by-one" problems.


What means "off-by-one" problems" ?


Title: Re: smooth external angle of Mandelbrot set?
Post by: Adam Majewski on June 24, 2017, 12:22:50 PM
http://www.mndynamics.com/indexp.html#XR


Title: Re: smooth external angle of Mandelbrot set?
Post by: Adam Majewski on November 18, 2017, 10:12:56 PM
What means "off-by-one" problems" ?
https://en.wikipedia.org/wiki/Off-by-one_error