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: Orbit Traps

Author: Mitch Richling
Updated: 2022-12-10

mandelbrot_ltrap_3.gif

Table of Contents

1. Introduction

The classic way to render a Mandelbrot Set is to use the \(L\) function. Here we are going to use something called an "orbit trap". THe idea is to do the same iterations we do when using the \(L\) function, but keep track of how close the orbit for each point gets to the "trap". For each point we use the minimimal distance between the trap and the orbit for that point for the color. Ususally brighter colors for closer orbits. Probably the most famous orbit trap is the X & Y axes. This idea popularized by Pickover, and the resulting renderings are called "Pickover Stalks".

2. Pickover Stalks

#include "ramCanvas.hpp"

typedef mjr::ramCanvas3c8b::colorType ct;

int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int    IMGSIZ = 7680;
  const int    MAXITR = 1024;
  const double MAXZSQ = 1008.0;
  mjr::ramCanvas3c8b theRamCanvas(IMGSIZ, IMGSIZ, -2.2, 0.8, -1.5, 1.5);

# pragma omp parallel for schedule(static,1)
  for(int y=0;y<theRamCanvas.getNumPixY();y++) {
    for(int x=0;x<theRamCanvas.getNumPixX();x++) {
      std::complex<double> c = theRamCanvas.int2real(x, y);
      std::complex<double> z(0.0, 0.0);
      double minX = theRamCanvas.getCanvasWidD();
      double minY = theRamCanvas.getCanvasWidD();
      int count = 0; 
      while((std::norm(z)<MAXZSQ) && (count<=MAXITR)) {
        z=std::pow(z, 2) + c;
        if (std::abs(std::real(z)) < minX)
          minX = std::abs(std::real(z));
        if (std::abs(std::imag(z)) < minY)
          minY = std::abs(std::imag(z));
        count++;
      }
      if(count < MAXITR) 
        theRamCanvas.drawPoint(x, y, ct::csCCfractalYB::c(static_cast<ct::csIntType>(std::log(1+std::min(minX, minY))*500)));
    }
  }
  theRamCanvas.writeTIFFfile("mandelbrot_pickover.tiff");
  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 this image:

mandelbrot_pickover_10.png

3. Mandelbrot Lemniscates

The Mandelbrot Set is ringed by the "Mandelbrot Curves" or "Mandelbrot Lemniscates" – an infinite family of continuous, closed curves that converge to the Mandelbrot Set. We can use these curves as orbit traps:

#include "ramCanvas.hpp"

typedef mjr::ramCanvas3c8b::colorType ct;

double lp(std::complex<double> z, int n) {
  std::complex<double> lpv(0.0, 0.0);
  for(int i=0; i<n; i++)
    lpv = std::pow(lpv, 2) + z;
  return std::abs(lpv) - 2;
}

int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int    IMGSIZ = 7680;
  const int    MAXITR = 1024;
  const double MAXZSQ = 4.0;
  const int    NUMFRM = 10;

# pragma omp parallel for schedule(static,1)
  for(int frame=1;frame<=NUMFRM;frame++) {
#   pragma omp critical
    std::cout << "Frame " << frame << " of " << NUMFRM << std::endl;
    mjr::ramCanvas3c8b theRamCanvas(IMGSIZ, IMGSIZ, -2.2, 0.8, -1.5, 1.5);
    for(int y=0;y<theRamCanvas.getNumPixY();y++) {
      for(int x=0;x<theRamCanvas.getNumPixX();x++) {
        std::complex<double> c = theRamCanvas.int2real(x, y);
        std::complex<double> z(0.0, 0.0);
        double minDval = theRamCanvas.getCanvasWidD();
        double minDsgn = 0;
        int count = 0; 
        while((std::norm(z)<MAXZSQ) && (count<=MAXITR)) {
          z=std::pow(z, 2) + c;
          double ld = lp(z, frame);
          if (std::abs(ld) < minDval) {
            minDval = std::abs(ld);
            minDsgn = (ld > 0 ? 1 : -1);
          }
          count++;
        }
        if(count < MAXITR) {
          ct::csIntType csIdx = static_cast<ct::csIntType>(std::log(1+minDval)*100);
          if (minDsgn > 0)
            theRamCanvas.drawPoint(x, y, ct::csCCfractalYB::c(csIdx));
          else
            theRamCanvas.drawPoint(x, y, ct::csCCfractalYR::c(csIdx));
        }
      }
    }
    theRamCanvas.writeTIFFfile("mandelbrot_ltrap_" + mjr::fmtInt(frame, 2, '0') + ".tiff");
  }
  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 all of the images in the gallery below.

mandelbrot_ltrap_01_3.png mandelbrot_ltrap_02_3.png mandelbrot_ltrap_03_3.png
mandelbrot_ltrap_04_3.png mandelbrot_ltrap_05_3.png mandelbrot_ltrap_06_3.png mandelbrot_ltrap_07_3.png
mandelbrot_ltrap_08_3.png mandelbrot_ltrap_09_3.png mandelbrot_ltrap_10_3.png

These may be transformed into a movie:

mandelbrot_ltrap_10.gif

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.