// Nebulabrot / Buddhabrot generator.
// Brought to you by Wikipedia...
// Written by User:Evercat
//
// Released under the GNU Free Documentation License
// or the GNU Public License, whichever you prefer:
// November 23, 2004
//
// This code is lame and confusing. I apologise.
// As I like to point out, my C is self-taught.
//
// Note: some folk mention possible improvements on the talk page:
// http://en.wiki.x.io/wiki/User_talk:Evercat/Buddhabrot.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define OUTFILE "buddhabrot"
////////////////////////////////////////////////////////////////////////////////////
#define WIDTH 1024
#define HEIGHT 768
#define CENTRE_X -0.451 // For full set try -0.451 by 0
#define CENTRE_Y 0
#define ZOOM 310 // Try 310
#define SOURCE_COLUMNS (WIDTH * 10) // These lines control the number of source values iterated.
#define SOURCE_ROWS (HEIGHT * 10) // Try WIDTH and HEIGHT * 10
#define UPDATETICK 100 // Report status every nth column reached
#define RED_MULTIPLIER 0.09 // Multiply the number of hits in a pixel by these values
#define GREEN_MULTIPLIER 0.11 // (needs to be lower the greater the number of source
#define BLUE_MULTIPLIER 0.18 // values and iterations, else image will be too bright)
#define COLOUR_OFFSET -230 // This value is added to the number of hits before a pixel is drawn.
// It needs to be lower the more source columns/rows there are.
#define RED_ITERATIONS_LOWER 0
#define RED_ITERATIONS_UPPER 1000
#define GREEN_ITERATIONS_LOWER 0
#define GREEN_ITERATIONS_UPPER 1000
#define BLUE_ITERATIONS_LOWER 0
#define BLUE_ITERATIONS_UPPER 1000
#define SOURCE_LEFT_X -2.102613
#define SOURCE_RIGHT_X 1.200613
#define SOURCE_TOP_Y -1.237710
#define SOURCE_BOTTOM_Y 1.239710
#undef RANDOM_SOURCE
#define OPTIMISE // Don't bother iterating some values obviously in the set.
#define reduce(foo) (foo) // Macro to reduce colours, can use sqrt(n), log(n), etc, or just (n)
////////////////////////////////////////////////////////////////////////////////////
void drawpath(double x, double y, double target_startx, double target_starty, double pixel_width);
double rnd (void);
void drawbmp (char * filename);
////////////////////////////////////////////////////////////////////////////////////
long long redcount[WIDTH][HEIGHT];
long long greencount[WIDTH][HEIGHT];
long long bluecount[WIDTH][HEIGHT];
int iterations;
double red_multiplier = RED_MULTIPLIER;
double green_multiplier = GREEN_MULTIPLIER;
double blue_multiplier = BLUE_MULTIPLIER;
////////////////////////////////////////////////////////////////////////////////////
int main (int argc, char * argv[]) {
int i, j, n; // General purpose counters
double x, y; // Source coordinates of particle being tracked
int source_column, source_row; // Source grid location
double r, s, nextr, nexts; // Values as particle is iterated through the Mandelbrot function
double x_jump, y_jump; // Distances between particles in the source grid
double target_startx, target_starty; // Top-left coordinates of drawn area
double target_endx, target_endy; // Bottom-right coordinates of drawn area
double pixel_width; // Actual distance represented by a pixel in the drawn area
char filename[200];
for (i = 0; i < WIDTH; i++)
{
for (j = 0; j < HEIGHT; j++)
{
redcount[i][j] = 0;
greencount[i][j] = 0;
bluecount[i][j] = 0;
}
}
target_startx = CENTRE_X - ((double) WIDTH / (ZOOM * 2));
target_endx = CENTRE_X + ((double) WIDTH / (ZOOM * 2));
target_starty = CENTRE_Y - ((double) HEIGHT / (ZOOM * 2));
target_endy = CENTRE_Y + ((double) HEIGHT / (ZOOM * 2));
pixel_width = (target_endx - target_startx) / WIDTH;
x_jump = ((double) SOURCE_RIGHT_X - SOURCE_LEFT_X) / SOURCE_COLUMNS;
y_jump = ((double) SOURCE_BOTTOM_Y - SOURCE_TOP_Y) / SOURCE_ROWS;
iterations = RED_ITERATIONS_UPPER;
if (GREEN_ITERATIONS_UPPER > iterations) iterations = GREEN_ITERATIONS_UPPER;
if (BLUE_ITERATIONS_UPPER > iterations) iterations = BLUE_ITERATIONS_UPPER;
// Main bit...
x = SOURCE_LEFT_X;
for (source_column = 0; source_column < SOURCE_COLUMNS; source_column++, x += x_jump)
{
y = SOURCE_TOP_Y;
for (source_row = 0; source_row < SOURCE_ROWS; source_row++, y += y_jump)
{
#ifdef OPTIMISE
if
(
(x > -1.2 && x <= -1.1 && y > -0.1 && y < 0.1)
|| (x > -1.1 && x <= -0.9 && y > -0.2 && y < 0.2)
|| (x > -0.9 && x <= -0.8 && y > -0.1 && y < 0.1)
|| (x > -0.69 && x <= -0.61 && y > -0.2 && y < 0.2)
|| (x > -0.61 && x <= -0.5 && y > -0.37 && y < 0.37)
|| (x > -0.5 && x <= -0.39 && y > -0.48 && y < 0.48)
|| (x > -0.39 && x <= 0.14 && y > -0.55 && y < 0.55)
|| (x > 0.14 && x < 0.29 && y > -0.42 && y < -0.07)
|| (x > 0.14 && x < 0.29 && y > 0.07 && y < 0.42)
) continue;
#endif
r = 0; s = 0;
for (n = 0; n <= iterations; n++)
{
nextr = ((r * r) - (s * s)) + x;
nexts = (2 * r * s) + y;
r = nextr;
s = nexts;
if (n == iterations)
{
// drawpath(x, y, target_startx, target_starty, pixel_width);
break;
} else if ((r * r) + (s * s) > 4) {
drawpath(x, y, target_startx, target_starty, pixel_width);
break;
}
}
}
if (source_column % UPDATETICK == 0)
{
printf("Reached source column: %d of %d\n", source_column, SOURCE_COLUMNS);
}
}
sprintf(filename, "%s r %d g %d b %d spp %d.bmp", OUTFILE, RED_ITERATIONS_UPPER, GREEN_ITERATIONS_UPPER, BLUE_ITERATIONS_UPPER, (SOURCE_COLUMNS / WIDTH) * (SOURCE_ROWS / HEIGHT));
drawbmp(filename);
printf("Done.\n");
return 0;
}
void drawpath (double x, double y, double target_startx, double target_starty, double pixel_width)
{
double r, s, nextr, nexts;
int n;
int xpixel, ypixel;
r = 0; s = 0;
for (n = 0; n <= iterations; n++)
{
nextr = ((r * r) - (s * s)) + x;
nexts = (2 * r * s) + y;
r = nextr;
s = nexts;
if ((r * r) + (s * s) > 4) return;
xpixel = (r - target_startx) / pixel_width;
ypixel = (s - target_starty) / pixel_width;
if (xpixel > 0 && xpixel < WIDTH && ypixel > 0 && ypixel < HEIGHT)
{
if (n >= RED_ITERATIONS_LOWER && n <= RED_ITERATIONS_UPPER)
{
redcount[xpixel][ypixel] += 1;
}
if (n >= GREEN_ITERATIONS_LOWER && n <= GREEN_ITERATIONS_UPPER)
{
greencount[xpixel][ypixel] += 1;
}
if (n >= BLUE_ITERATIONS_LOWER && n <= BLUE_ITERATIONS_UPPER)
{
bluecount[xpixel][ypixel] += 1;
}
}
}
return;
}
void drawbmp (char * filename) {
unsigned int headers[13];
FILE * outfile;
int extrabytes;
int paddedsize;
int x; int y; int n;
int red, green, blue;
extrabytes = 4 - ((WIDTH * 3) % 4); // How many bytes of padding to add to each
// horizontal line - the size of which must
// be a multiple of 4 bytes.
if (extrabytes == 4)
extrabytes = 0;
paddedsize = ((WIDTH * 3) + extrabytes) * HEIGHT;
// Headers...
// Note that the "BM" identifier in bytes 0 and 1 is NOT included in these "headers".
headers[0] = paddedsize + 54; // bfSize (whole file size)
headers[1] = 0; // bfReserved (both)
headers[2] = 54; // bfOffbits
headers[3] = 40; // biSize
headers[4] = WIDTH; // biWidth
headers[5] = HEIGHT; // biHeight
// Would have biPlanes and biBitCount in position 6, but they're shorts.
// It's easier to write them out separately (see below) than pretend
// they're a single int, especially with endian issues...
headers[7] = 0; // biCompression
headers[8] = paddedsize; // biSizeImage
headers[9] = 0; // biXPelsPerMeter
headers[10] = 0; // biYPelsPerMeter
headers[11] = 0; // biClrUsed
headers[12] = 0; // biClrImportant
outfile = fopen(filename, "wb");
//
// Headers begin...
// When printing ints and shorts, we write out 1 character at a time to avoid endian issues.
//
fprintf(outfile, "BM");
for (n = 0; n <= 5; n++)
{
fprintf(outfile, "%c", headers[n] & 0x000000FF);
fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}
// These next 4 characters are for the biPlanes and biBitCount fields.
fprintf(outfile, "%c", 1);
fprintf(outfile, "%c", 0);
fprintf(outfile, "%c", 24);
fprintf(outfile, "%c", 0);
for (n = 7; n <= 12; n++)
{
fprintf(outfile, "%c", headers[n] & 0x000000FF);
fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}
//
// Headers done, now write the data...
//
for (y = HEIGHT - 1; y >= 0; y--) // BMP image format is written from bottom to top...
{
for (x = 0; x <= WIDTH - 1; x++)
{
red = reduce(redcount[x][y] + COLOUR_OFFSET) * red_multiplier;
green = reduce(greencount[x][y] + COLOUR_OFFSET) * green_multiplier;
blue = reduce(bluecount[x][y] + COLOUR_OFFSET) * blue_multiplier;
if (red > 255) red = 255; if (red < 0) red = 0;
if (green > 255) green = 255; if (green < 0) green = 0;
if (blue > 255) blue = 255; if (blue < 0) blue = 0;
// Also, it's written in (b,g,r) format...
fprintf(outfile, "%c", blue);
fprintf(outfile, "%c", green);
fprintf(outfile, "%c", red);
}
if (extrabytes) // See above - BMP lines must be of lengths divisible by 4.
{
for (n = 1; n <= extrabytes; n++)
{
fprintf(outfile, "%c", 0);
}
}
}
fclose(outfile);
return;
}