Mitch Richling: The Dancing Biomorph
Author: | Mitch Richling |
Updated: | 2024-10-31 |
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: