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: The Dancing Biomorph

Author: Mitch Richling
Updated: 2024-10-31

biomorphMorph_sin2_y_5.gif biomorphMorph_sin3_y_5.gif biomorphMorph_tan2_y_5.gif

Table of Contents

1. Introduction

Given some function, \(f(z)\), we define a series for each point in the complex plane, \(z_0\), by \(g^{(n)}(z_0)\) where \(g(z)=f(z)+c\) for some fixed \(c\). This is pretty similar to how we draw the Mandelbrot Set, but note the different role played by the variable \(c\)! For any given \(f(z)\), the \(c\) parameter defines a fractal – so for each function \(f(z)\) we have a family of fractals.

Unlike the Mandelbrot Set, iteration stops for a biomorph when \(\vert\Re(z)\vert \ge 10\) and \(\vert\Im(z)\vert \ge 10\).

Another difference between biomorphs and the Mandelbrot Set is the way they are normally colorized. Biomorphs are generally colored based on the escape function, but by a combination of escape function, magnitude/phase of z at iteration end, etc…

2. Algorithm & Code

#include "ramCanvas.hpp"

typedef mjr::ramCanvas3c8b::colorType ct;
typedef ct::csIntType cit;

int main(void) {
  std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
  const int    NUMITR = 7;
  const int    NUMFRM = 24*4;
  const int    IMXSIZ = 7680/4;
  const int    IMYSIZ = 7680/4;
  const int    LIM    = 10;

  mjr::ramCanvas3c8b theRamCanvas(IMXSIZ, IMYSIZ, -2.75, 2.75, -2.75, 2.75);
  for(int frame=0; frame<NUMFRM; frame++) {
    std::cout << "Frame: " << frame << std::endl;
    double angle = frame*std::numbers::pi*2/NUMFRM;    
    theRamCanvas.clrCanvasToBlack();
    for(int y=0;y<theRamCanvas.getNumPixY();y++) {
      for(int x=0;x<theRamCanvas.getNumPixX();x++) {
        std::complex<double> c(std::cos(angle), std::sin(angle));
        std::complex<double> z = theRamCanvas.int2real(x, y);
        std::complex<double> zL(0.0, 0.0);
        int count = 0; 
        while( ((std::abs(std::real(z))<LIM) || (std::abs(std::imag(z))<LIM)) && (count<NUMITR) ) {
          zL = z;
          z=std::sin(z) + std::pow(z, 2) + c;
          count++;
        }
        if(count < NUMITR) {
          if(std::abs(std::real(zL)) < LIM)
            theRamCanvas.drawPoint(x, y, ct::csCCu0W::c(std::abs(std::real(zL))/LIM));
          else if(std::abs(std::imag(zL)) < LIM)
            theRamCanvas.drawPoint(x, y, ct::csCCu0W::c(1.0-std::abs(std::imag(zL))/LIM));
        } else {
          if(std::abs(std::real(zL)) < LIM)
            theRamCanvas.drawPoint(x, y, ct::csCCu0Y::c(std::abs(std::real(zL))/LIM));
          else if(std::abs(std::imag(zL)) < LIM)
            theRamCanvas.drawPoint(x, y, ct::csCCu0Y::c(1.0-std::abs(std::imag(zL))/LIM));
        }
      }
    }
    theRamCanvas.writeTIFFfile("biomorphMorph_" + mjr::math::str::fmt_int(frame, 3, '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 96 frames of a movie which we can assemble into a GIF:

biomorphMorph_sin2_y_45.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.