Jump to my Home Page Send me a message Check out stuff on GitHub Check out my photography on Instagram Check out my profile on LinkedIn Check out my profile on reddit Check me out on Facebook

Mitch Richling: Multibrot Fractal

Author: Mitch Richling
Updated: 2022-12-10

multibrotSnaps_2_3.png multibrotSnaps_3_3.png multibrotSnaps_4_3.png multibrotSnaps_5_3.png multibrotSnaps_6_3.png multibrotSnaps_7_3.png multibrotSnaps_8_3.png multibrotSnaps_9_3.png

Table of Contents

1. Introduction

The Multibrot Fractal is a family of Mandelbrot'alikes in that the generating functions are a minor modification of the one used for the Mandelbrot Set:

\[\begin{eqnarray} f(z) & = & z^2+c & \,\,\,\,\,\,\mathrm{Mandelbrot} \\ f(z) & = & z^k+c & \,\,\,\,\,\,\mathrm{Multibrot} \\ \end{eqnarray}\]

Notice the only difference is the power, \(k\). Also note that \(k\) need not be an integer.

We render Multibrot Fractals using the \(L\) function just like we did with the Mandelbrot Set. The general idea is to map an array of pixels making up the raster image to a rectangular region of the complex plane, and color each pixel of the image based upon a color scale for the computed value of the \(L\) function.

2. When \(k\) is not an integer

When \(k\) not an integer, we interesting results. I think the most instructive way to look at them is to make a movie for values of \(k\) from about \(0.9\) up to \(4\). About 200 frames captures the evolution quite nicely. The following bit of C++ will draw the frames, which may then be rendered into a movie.

#include "ramCanvas.hpp"

int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const double MINPOW = 0.9;
  const double MAXPOW = 4.0;
  const int    NUMFRM = 192;
  const int    NUMITR = 255;
  const int    IMXSIZ = 7680/8;
  const int    IMYSIZ = 4320/8;

  mjr::ramCanvas3c8b theRamCanvas(IMXSIZ, IMYSIZ, -2.75, 2.75, -1.55, 1.55);

  for(int f=0;f<NUMFRM;f++) {
    double p = MAXPOW*static_cast<double>(f)/static_cast<double>(NUMFRM) + MINPOW;
    std::chrono::time_point<std::chrono::system_clock> frameStartTime = std::chrono::system_clock::now();
    theRamCanvas.clrCanvasToBlack();
    for(int y=0;y<theRamCanvas.getNumPixY();y++) {
      for(int x=0;x<theRamCanvas.getNumPixX();x++) {
        std::complex<double> c(theRamCanvas.int2realX(x), theRamCanvas.int2realY(y));
        std::complex<double> z(0.0, 0.0);
        int count = 0; 
        while((std::norm(z)<4) && (count<=NUMITR)) {
          z=std::pow(z, p) + c;
          count++;
        }
        if(count < NUMITR)
          theRamCanvas.drawPoint(x, y, mjr::ramCanvas3c8b::colorType::csCColdeFireRamp::c(static_cast<mjr::ramCanvas3c8b::csIntType>(count*20)));
      }
    }
    theRamCanvas.writeTIFFfile("multibrotMovie_" + mjr::fmtInt(f, 3, '0') + ".tiff");
    std::chrono::duration<double> frameRunTime = std::chrono::system_clock::now() - frameStartTime;
    std::cout << "Frame " << f << " Runtime " << frameRunTime.count() << " sec" << std::endl;
  }
  std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime;
  std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl;
}

The above program will generate 192 frames of a movie which we can assemble into a GIF:

multibrot_95.gif

3. When \(k\) is an integer

When \(k\) is an integer, we see nice symmetric results. The following bit of C++ will draw the cases for \(k\) from \(1\) up to \(9\).

#include "ramCanvas.hpp"

int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int MINPOW = 2;
  const int MAXPOW = 9;
  const int NUMITR = 1024;
  const int IMXSIZ = 7680/2;
  const int IMYSIZ = 4320/2;

  mjr::ramCanvas3c8b theRamCanvas(IMXSIZ, IMYSIZ, -2.75, 2.75, -1.55, 1.55);

  for(int p=MINPOW;p<=MAXPOW;p++) {
    std::chrono::time_point<std::chrono::system_clock> frameStartTime = std::chrono::system_clock::now();
    theRamCanvas.clrCanvasToBlack();
    for(int y=0;y<theRamCanvas.getNumPixY();y++) {
      for(int x=0;x<theRamCanvas.getNumPixX();x++) {
        std::complex<double> c(theRamCanvas.int2realX(x), theRamCanvas.int2realY(y));
        std::complex<double> z(0.0, 0.0);
        int count = 0; 
        while((std::norm(z)<4) && (count<=NUMITR)) {
          z=std::pow(z, p) + c;
          count++;
        }
        if(count < NUMITR)
          theRamCanvas.drawPoint(x, y, mjr::ramCanvas3c8b::colorType::csCColdeFireRamp::c(static_cast<mjr::ramCanvas3c8b::csIntType>(count*20*log(p))));
      }
    }
    //theRamCanvas.autoHistStrech();
    theRamCanvas.writeTIFFfile("multibrotSnaps_" + mjr::fmtInt(p, 3, '0') + ".tiff");
    std::chrono::duration<double> frameRunTime = std::chrono::system_clock::now() - frameStartTime;
    std::cout << "Frame " << p << " Runtime " << frameRunTime.count() << " sec" << std::endl;
  }
  std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime;
  std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl;
}

multibrotSnaps_1_10.png
multibrotSnaps_2_10.png multibrotSnaps_3_10.png multibrotSnaps_4_10.png multibrotSnaps_5_10.png multibrotSnaps_6_10.png multibrotSnaps_7_10.png multibrotSnaps_8_10.png multibrotSnaps_9_10.png

4. References

Check out the fractals section of my reading list.

All the code used to generate everything on this page may be found on github.