commit 0be3c70181a88114da5567d3d7dc61706837690e
Author: Geoff Murphy <murphy@fake.onl>
Date:   Sat Oct 5 12:12:58 2024 +1000

    :tada: Initial commit

diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..39f3a08
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/bin
+/obj
+/output
+/tmp
diff --git a/makefile b/makefile
new file mode 100755
index 0000000..e9b7665
--- /dev/null
+++ b/makefile
@@ -0,0 +1,44 @@
+OS := $(shell uname)
+
+NAME=$(shell pwd | sed "s/.*\///g")
+
+ifeq (${OS},Darwin)
+	CC=clang++
+	FLAGS=-std=c++11 -stdlib=libc++
+	RUN=${NAME}
+else ifeq (${OS},Linux)
+	CC=g++
+	FLAGS=-std=c++11 -pthread
+	RUN=${NAME}
+	FILES=$(shell find -name *.cpp)
+else
+		CC=g++
+		FLAGS=-std=c++11
+		RUN=${NAME}.exe
+		FILES=$(shell find -name *.cpp)
+endif
+
+
+all: clean build run images video play
+
+build:
+	${CC} ${FILES} ${FLAGS} -o bin/${RUN}
+
+run:
+	bin/./${RUN}
+
+video:
+	ffmpeg -i output/frames/image_%05d.png -c:v libx264 -vf fps=25 -pix_fmt yuv420p output/video.mp4
+
+play:
+	vlc -L output/video.mp4
+
+images:
+	mogrify -verbose -format png output/frames/*.ppm
+	rm $(shell find output/frames -name "*.ppm")
+
+
+clean:
+	rm -f -r bin/*
+	rm -f -r output/frames/*
+	rm -f output/video.mp4
diff --git a/src/fractal/buddhabulb.cpp b/src/fractal/buddhabulb.cpp
new file mode 100755
index 0000000..53fd3bc
--- /dev/null
+++ b/src/fractal/buddhabulb.cpp
@@ -0,0 +1 @@
+#include "buddhabulb.h"
diff --git a/src/fractal/buddhabulb.h b/src/fractal/buddhabulb.h
new file mode 100755
index 0000000..fdcdfbb
--- /dev/null
+++ b/src/fractal/buddhabulb.h
@@ -0,0 +1,6 @@
+#ifndef BUDDHABULB_H_
+#define BUDDHABULB_H_
+
+
+
+#endif
diff --git a/src/fractal/buffer.cpp b/src/fractal/buffer.cpp
new file mode 100755
index 0000000..c28deb9
--- /dev/null
+++ b/src/fractal/buffer.cpp
@@ -0,0 +1,191 @@
+#include "buffer.h"
+
+
+
+
+/////////
+/////////   Buffer Functions
+/////////
+
+Buffer::Buffer(int _width, int _height){
+  width = _width;
+  height = _height;
+
+  itterations = 255;
+  samples = 4;
+  contrast = 2;
+  fractal = 1;
+  ratio = (float)width/(float)height;
+
+  setCursor(0, 0, 1, 0);
+  setStart(0, 0, 1, 0);
+  setEnd(0, 0, 1, 0);
+
+  buffer.resize(width, std::vector<int>(height));
+}
+
+void Buffer::clear(){
+  maxValue = 0;
+  for(int x=0; x<width; x++){
+    for(int y=0; y<height; y++){
+      buffer[x][y] = 0;
+    }
+  }
+}
+
+void Buffer::save(std::string _filename){
+  FILE *f = fopen( _filename.c_str(), "w" );         // Write image to PPM file.
+   fprintf(f, "P3\n%d %d\n%d\n", width, height, 255);
+   for(int y=0; y<height; y++){
+     for(int x=0; x<width; x++){
+
+       float bright = map((float)buffer[x][y], 0, (float)maxValue, 0, 1.0);
+       int value = (int)map(pow(bright, 1.0/contrast), 0, 1, 0, 255);
+
+       fprintf(f,"%d %d %d ", value, value, value);
+     }
+   }
+}
+
+
+
+
+/////////
+/////////   Buffer Settings
+/////////
+
+int Buffer::getMax(){
+  return maxValue;
+}
+
+void Buffer::setItterations(int _itterations){
+  itterations = _itterations;
+}
+void Buffer::setSamples(int _samples){
+  samples = _samples;
+}
+void Buffer::setContrast(float _contrast){
+  contrast = _contrast;
+}
+
+void Buffer::setFractal(unsigned char _fractal){
+  fractal = _fractal;
+}
+
+std::string Buffer::stats(){
+  std::ostringstream output;
+  output << "Buffer: " << width << "x" << height;
+  output << "    Samples: " << samples << "    Itterations: " << itterations;
+  output << "    Contrast: " << contrast;
+  return output.str();
+}
+
+
+
+
+/////////
+/////////   Translate Coordinates
+/////////
+
+void Buffer::setCursor(float _x, float _y, float _s, float _r){
+  cur.x = _x;
+  cur.y = _y;
+  cur.s = _s;
+  cur.r = _r;
+}
+void Buffer::setStart(float _x, float _y, float _s, float _r){
+  start.x = _x;
+  start.y = _y;
+  start.s = _s;
+  start.r = _r;
+}
+void Buffer::setEnd(float _x, float _y, float _s, float _r){
+  end.x = _x;
+  end.y = _y;
+  end.s = _s;
+  end.r = _r;
+}
+
+void Buffer::cursorUpdate(float _f){
+  cur.x = start.x + (end.x-start.x)*_f;
+  cur.y = start.y + (end.y-start.y)*_f;
+  cur.s = start.s + (end.s-start.s)*_f;
+  cur.r = start.r + (end.r-start.r)*_f;
+}
+
+float Buffer::map(float _v, float _a1, float _a2, float _b1, float _b2){
+  return _b1 + (_v-_a1)*(_b2-_b1)/(_a2-_a1);
+}
+
+Coords Buffer::translate(int _x, int _y){
+
+
+  float angle = -cur.r * M_PI / 180.0;
+
+  float rx = (float)rand()/(float)RAND_MAX;
+  float ry = (float)rand()/(float)RAND_MAX;
+
+  Coords org; // origin point
+  org.x = ((((float)_x+rx) / (float)width * 2) - 1) * ratio; // X Origin
+  org.y = (((float)_y+ry) / (float)height * 2) - 1;          // Y Origin
+
+  float x = cos(angle)*org.x*cur.s - sin(angle)*org.y*cur.s + cur.x;  // New X
+  float y = sin(angle)*org.x*cur.s + cos(angle)*org.y*cur.s + cur.y;  // New Y
+
+  Coords tmpCords = {x, y};
+  return tmpCords;
+}
+
+Pixel Buffer::translate(float _x, float _y){
+
+  float angle = cur.r * M_PI / 180.0;
+
+  Coords org; // origin point
+  org.x = cos(angle)*(_x-cur.x)/cur.s - sin(angle)*(_y-cur.y)/cur.s;
+  org.y = sin(angle)*(_x-cur.x)/cur.s + cos(angle)*(_y-cur.y)/cur.s;
+
+  float x = (org.x/ratio + 1) / 2 * width;  // New X
+  float y = (org.y + 1) / 2 * height;  // New Y
+
+  Pixel tmpPix = {(int)x, (int)y};
+  return tmpPix;
+}
+
+
+
+
+/////////
+/////////   Render Fractal
+/////////
+
+void Buffer::render(int _x, int _y){
+  Coords c = translate(_x, _y);
+
+  float a = c.x;
+  float b = c.y;
+
+  int n = 0;
+  float z = 0;
+  while(n<=itterations){
+    float aa = a*a - b*b;
+    float bb = 2.0 * a * b;
+    a = aa + c.x;
+    b = bb + c.y;
+    if(abs(a + b) > 4){ break; }
+
+    if(fractal==2 || fractal==0){
+      Pixel p = translate(a, b);
+      if(p.x>=0 && p.x<width && p.y>=0 && p.y<height){
+        buffer[p.x][p.y] += 1;
+        if(buffer[p.x][p.y]>maxValue){ maxValue = buffer[p.x][p.y]; }
+      }
+    }
+
+    n++;
+  }
+  if(fractal==1 || fractal==0){
+    if(n >= itterations){ n=0; }
+    buffer[_x][_y] += n;
+    if(buffer[_x][_y] > maxValue){ maxValue = buffer[_x][_y]; }
+  }
+}
diff --git a/src/fractal/buffer.h b/src/fractal/buffer.h
new file mode 100755
index 0000000..4a13416
--- /dev/null
+++ b/src/fractal/buffer.h
@@ -0,0 +1,78 @@
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+#include <iostream>
+#include <cmath>
+#include <cstdio>
+#include <sstream>
+#include <string>
+#include <vector>
+
+
+#define M_PI  3.14159265358979323846 /* pi */
+
+
+#define ALL 0
+#define MANDELBROT 1
+#define BUDDHABROT 2
+#define BUDDHABULB 3
+
+
+struct Cur{
+  float x;  // X position of cursur
+  float y;  // Y position of cursur
+  float s;  // Scale of cursur
+  float r;  // Rotation of cursur
+};
+
+struct Coords{
+  float x;
+  float y;
+};
+
+struct Pixel{
+  int x;
+  int y;
+};
+
+
+class Buffer{
+private:
+  std::vector<std::vector<int>> buffer;   // Buffer array
+  Cur cur;                // Viewing cursur
+  Cur start;
+  Cur end;
+  float ratio;            // Ratio of image width to hight
+  int itterations;        // Number of itterations for render (defult 255)
+  float contrast;         // Log contrast of render output (defult 2)
+  int maxValue;           // Maximum value of array to normalise
+  unsigned char fractal;  // Fractal type
+public:
+  int width;  // Width of buffer
+  int height; // Height of buffer
+  int samples; // Rendering subresolution (defult 2)
+
+  Buffer(int _width, int _height);    // Constructor
+  void save(std::string _filename);   // Save buffer to PPM image
+  void clear();                       // Clear buffer
+  std::string stats();                // Print buffer stats to console
+
+  int getMax();                             // Return max value in buffer
+  void setItterations(int _itterations);    // Set number of render itterations
+  void setSamples(int _subres);              // Set subresolution of render
+  void setContrast(float _contrast);        // Set contast of output image
+  void setFractal(unsigned char _fractal);  // set type of fractal to render
+
+  void setCursor(float _x, float _y, float _s, float _r);
+  void setStart(float _x, float _y, float _s, float _r);
+  void setEnd(float _x, float _y, float _s, float _r);
+  void cursorUpdate(float _f);
+  Coords translate(int _x, int _y);
+  Pixel translate(float _x, float _y);
+  float map(float _v, float _a1, float _a2, float _b1, float _b2);
+
+  void render(int _x, int _y);
+};
+
+
+#endif
diff --git a/src/fractal/timer.cpp b/src/fractal/timer.cpp
new file mode 100755
index 0000000..525c7dd
--- /dev/null
+++ b/src/fractal/timer.cpp
@@ -0,0 +1,33 @@
+#include "timer.h"
+
+
+Timer::Timer(){
+  startTime = clock();
+  std::cout << startTime << std::endl;
+}
+
+
+std::string Timer::getTime(){
+  clock_t elapsedTime = (clock() - startTime) / CLOCKS_PER_SEC;
+
+  unsigned char seconds =  elapsedTime % 60;
+  unsigned char minutes = (elapsedTime / 60) % 60;
+  unsigned char hours   = (elapsedTime / 3600) % 24;
+  unsigned char days    =  elapsedTime / 86400;
+
+  std::string tmpString = "";
+
+  if(days<10){ tmpString += "0"; }
+  tmpString += std::to_string(days);
+
+  if(hours<10){ tmpString += ":0"; }else{ tmpString+=":"; }
+  tmpString += std::to_string(hours);
+
+  if(minutes<10){ tmpString += ":0"; }else{ tmpString+=":"; }
+  tmpString += std::to_string(minutes);
+
+  if(seconds<10){ tmpString += ":0"; }else{ tmpString+=":"; }
+  tmpString += std::to_string(seconds);
+
+  return tmpString;
+}
diff --git a/src/fractal/timer.h b/src/fractal/timer.h
new file mode 100755
index 0000000..f07ef8e
--- /dev/null
+++ b/src/fractal/timer.h
@@ -0,0 +1,18 @@
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#include <ctime>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+class Timer{
+private:
+  clock_t startTime;
+public:
+  Timer();
+  std::string getTime();
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100755
index 0000000..681f8c0
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,40 @@
+#include "main.h"
+
+Buffer buffer(1920, 1080);
+
+int main(int argc, char** argv){
+
+  Timer timer;
+
+  buffer.clear();
+  buffer.setContrast(2);
+  buffer.setSamples(8);
+  buffer.setItterations(255);
+  buffer.setFractal(ALL);
+
+  buffer.setStart(0, 0, 1, -10);
+  buffer.setEnd(0, 0, 0.8, 15);
+
+  std::cout << std::endl << buffer.stats() << std::endl << std::endl;
+
+  int frames = 200;
+  for(int f=0; f<=frames; f++){
+
+    buffer.clear();
+    buffer.cursorUpdate( (float)f/(float)frames );
+    for(int y=0; y<buffer.height; y++){
+      for(int x=0; x<buffer.width; x++){
+        for(int s=0; s<buffer.samples; s++){
+          buffer.render(x, y);
+        }
+      }
+    std::cout << "     " << timer.getTime() << "    Frame: " << f << "    " << 100*y/buffer.height << "%    maxValue: " << buffer.getMax() << "           \r" << std::flush;
+    }
+    std::ostringstream filename;
+    filename << "output/frames/image_" << std::setfill('0') << std::setw(5) << f << ".ppm";
+    buffer.save( filename.str() );
+  }
+
+  std::cout << std::endl << std::endl << "Finished!" << std::endl << std::endl;
+  return 0;
+}
diff --git a/src/main.h b/src/main.h
new file mode 100755
index 0000000..72c965a
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,13 @@
+#ifndef MAIN_H_
+#define MAIN_H_
+
+#include <cmath>
+#include <iomanip>
+#include <iostream>
+#include <thread>
+
+#include "fractal/buffer.h"
+#include "fractal/buddhabulb.h"
+#include "fractal/timer.h"
+
+#endif