diff --git a/mandelbrot.c b/mandelbrot.c new file mode 100644 index 0000000..809d2e0 --- /dev/null +++ b/mandelbrot.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include + +/* + ------------------- Default opions --------------------- + */ +bool fix = false; // renders only one frame and then exit the program +bool julia = false; // renders a Julia set +long double JuliaZ[2]; // +int IterNum = 100; // number of iterations +int resolution[2]; // resolution +long double xmin = -2.1; // xmin +long double xmax = 0.7; // xmax +long double ymin = -1.5; // ymin +long double ymax = 1.5; // ymax +int scaler = 10; // scales the resolution +long double delta = 0.2; // Movement +char pixel[3] = "█"; // pixel +int ColorShift = 0; // Shifts all the color. 0 <= ColorShift < 40. +bool clear = false; // Clears the terminal after each frame +double long sqrt2 = sqrtl(2); +char RenderMode[50] = "color"; // can be color, number, red, blue, green or gray +/* + ------------------- Code -------------------------------- + */ + +// Calculates the resolution in function of xmin, xmax, ymin, ymax and scaler +void ResCalc() { + resolution[0] = round((ymax-ymin)*2*scaler); + resolution[1] = round((xmax-xmin)*5*scaler); // the 2/5 is the ration of █ +} +// Each complex number is an array of long double. +// Re(z) is z[0] +// Im(z) is z[1] + +// Adds two complex numbers and return a pointer +long double* addc(long double z1[2], long double z2[2]) { + long double* zptr; + zptr = (long double*)malloc(2*sizeof(long double)); + zptr[0] = z1[0] + z2[0]; + zptr[1] = z1[1] + z2[1]; + return zptr; // z1 + z2 +} + +// Multiplies two complex numbers and return a pointer +long double* multc(long double z1[2], long double z2[2]) { + long double* zptr; + zptr = (long double*)malloc(2*sizeof(long double)); + // (a + bi)(c + di) = ac + adi + bci - bd = (ac - bd) + (ad + bc)i + zptr[0] = z1[0]*z2[0] - z1[1]*z2[1]; + zptr[1] = z1[0]*z2[1] + z1[1]*z2[0]; + return zptr; // z1 x z2 +} + +// Return the absolute value of the complex number +long double absc(long double z[2]) { + long double temp = z[0]*z[0] + z[1]*z[1]; + return sqrt(temp); // |z| +} + +// For Julia's and Mandelbrot's sets calculates the number of iterations needed such that |z| > 2. +int iteration(long double z[2]) { +/* For Mandelbrot's set and Julia's sets: + * Each pixel represents a point on the complex plane. For each pixel, we apply a function f() many times (a maximum of IterNum times) until the absolute value is more than 2. We stop and return this value (0 if we reach IterNum). + * f() = z² + c + * where c is in Mandelbrot's sets the original value of the pixel and in Julia's sets a fixed complex number. + */ + long double start[2]; + if (julia) { + start[0] = JuliaZ[0]; + start[1] = JuliaZ[1]; + } else { + start[0] = z[0]; + start[1] = z[1]; + } + for (int i = 1; i <= IterNum; i++) { + long double* temp; + // z² + c + temp = multc(z,z); + z = addc(temp, start); + free(temp); + if (absc(z) > 2) { + return i; + } + } + return 0; +} +// prints the helping information +void print_help() { + printf("Usage: fractal [OPTIONS]\n"); + printf("Generate and render fractal images with configurable parameters.\n\n"); + printf("Default fractal is the Mandelbrot's set.\n"); + printf("Currently supported fractals are Mandelbrot's set and Julia's sets.\n\n"); + + printf("Options:\n"); + printf(" -j Set Julia set mode with the specified complex number (e.g., -0.55268 0.959456).\n"); + printf(" -xmin Set the minimum x-coordinate for the fractal viewport.\n"); + printf(" -xmax Set the maximum x-coordinate for the fractal viewport.\n"); + printf(" -ymin Set the minimum y-coordinate for the fractal viewport.\n"); + printf(" -ymax Set the maximum y-coordinate for the fractal viewport.\n"); + printf(" -c Clear the screen before rendering each frame.\n"); + printf(" -s Set the scale multiplier for image resolution (default is 10).\n"); + printf(" -p Set the string to represent fractal points (default is \"█\").\n"); + printf(" -cs Set the color shift value to alter colors in the render.\n"); + printf(" -f Render only one fixed frame and exit the program.\n"); + printf(" -i Set the number of iterations.\n"); + printf(" -m Set the render mode. Options:\n"); + printf(" 'color' (default), 'number', 'red', 'green', 'blue', 'gray'.\n"); + printf(" -h, --help Display this help message and exit.\n\n"); + printf("Controls (if not using -f):\n"); + printf(" w, s, a, d Pan the viewport up, down, left, and right.\n"); + printf(" q, e Decrease or increase the iteration count by 100.\n"); + printf(" r Zoom in on the fractal.\n"); + printf(" f Zoom out of the fractal.\n"); + printf(" x Exit the program.\n"); + printf("\nExamples:\n"); + printf(" ./fractal -j -0.55268 0.959456 -xmin -2.1 -xmax 0.7 -ymin -1.5 -ymax 1.5 -s 20 -m color\n"); + printf(" ./fractal -c -p '#' -m red -cs 10\n"); + printf("\nNote:\n"); + printf(" Ensure arguments following options are provided. Invalid or missing arguments\n"); + printf(" may lead to undefined behavior or program errors.\n"); +} +void render(int array[resolution[0]][resolution[1]]) { + if (clear) { + system("clear"); + } + if (strcmp(RenderMode, "number") == 0) { + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + printf("%d ", array[yi][xi]); + } + printf("\n"); + } + } else if (strcmp(RenderMode, "color") == 0 || strcmp(RenderMode, "colour") == 0) { + // All 40 rgb values used for the colors. Generated with https://colordesigner.io/color-wheel. + int color[40][3] = { + {255, 69, 56}, + {255, 99, 56}, + {255, 129, 56}, + {255, 159, 56}, + {255, 188, 56}, + {255, 218, 56}, + {255, 248, 56}, + {232, 255, 56}, + {202, 255, 56}, + {172, 255, 56}, + {143, 255, 56}, + {113, 255, 56}, + {83, 255, 56}, + {56, 255, 59}, + {56, 255, 89}, + {56, 255, 119}, + {56, 255, 149}, + {56, 255, 178}, + {56, 255, 208}, + {56, 255, 238}, + {56, 242, 255}, + {56, 212, 255}, + {56, 182, 255}, + {56, 152, 255}, + {56, 123, 255}, + {56, 93, 255}, + {56, 63, 255}, + {79, 56, 255}, + {109, 56, 255}, + {139, 56, 255}, + {168, 56, 255}, + {198, 56, 255}, + {228, 56, 255}, + {255, 56, 252}, + {255, 56, 222}, + {255, 56, 192}, + {255, 56, 162}, + {255, 56, 133}, + {255, 56, 103}, + {255, 56, 73} + }; + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] == 0) { + printf(" "); + } else { + printf("\033[38;2;%d;%d;%dm%s", color[(array[yi][xi] + ColorShift) % 40][0], color[(array[yi][xi] + ColorShift) % 40][1], color[(array[yi][xi] + ColorShift) % 40][2], pixel); + // \033[38;2;R;G;Bm is the ascii escape code for coloring the text with an RGB values. + } + } + printf("\n"); + } + } else if (strcmp(RenderMode, "red") == 0) { + int max = 0; + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] > max) { + max = array[yi][xi]; + //printf("%d\n",max); + } + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + array[yi][xi] = round(array[yi][xi]*255/max); + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] == 0) { + printf(" "); + } else { + printf("\033[38;2;%d;0;0m%s", array[yi][xi], pixel); + } + } + printf("\n"); + } + } else if (strcmp(RenderMode, "green") == 0) { + int max = 0; + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] > max) { + max = array[yi][xi]; + } + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + array[yi][xi] = array[yi][xi]*255/max; + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] == 0) { + printf(" "); + } else { + printf("\033[38;2;0;%d;0m%s", array[yi][xi], pixel); + } + } + printf("\n"); + } + } else if (strcmp(RenderMode, "blue") == 0) { + int max = 0; + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] > max) { + max = array[yi][xi]; + } + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + array[yi][xi] = array[yi][xi]*255/max; + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] == 0) { + printf(" "); + } else { + printf("\033[38;2;0;0;%dm%s", array[yi][xi], pixel); + } + } + printf("\n"); + } + } else if (strcmp(RenderMode, "grey") == 0 || strcmp(RenderMode, "gray") == 0) { + int max = 0; + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] > max) { + max = array[yi][xi]; + } + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + array[yi][xi] = array[yi][xi]*255/max; + } + } + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + if (array[yi][xi] == 0) { + printf(" "); + } else { + printf("\033[38;2;%d;%d;%dm%s", array[yi][xi], array[yi][xi], array[yi][xi], pixel); + } + } + printf("\n"); + } + } else { + printf("%s is an invalid render mode!\n", RenderMode); + } + printf("\033[0m\n"); + } +// main function +int main(int argc, char** argv) { + // Processes all the arguments when executing the code + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-j") == 0) { + julia = true; + JuliaZ[0] = atof(argv[i+1]); + JuliaZ[1] = atof(argv[i+2]); + } else if (strcmp(argv[i], "-xmin") == 0) { + xmin = atof(argv[i+1]); + } else if (strcmp(argv[i], "-xmax") == 0) { + xmax = atof(argv[i+1]); + } else if (strcmp(argv[i], "-ymin") == 0) { + ymin = atof(argv[i+1]); + } else if (strcmp(argv[i], "-ymax") == 0) { + ymax = atof(argv[i+1]); + } else if (strcmp(argv[i], "-c") == 0) { + clear = true; + } else if (strcmp(argv[i], "-s") == 0) { + scaler = atoi(argv[i+1]); + } else if (strcmp(argv[i], "-p") == 0) { + strcpy(pixel, argv[i+1]); + } else if (strcmp(argv[i], "-cs") == 0) { + ColorShift = atoi(argv[i+1]); + } else if (strcmp(argv[i], "-f") == 0) { + fix = true; + } else if (strcmp(argv[i], "-m") == 0) { + strcpy(RenderMode, argv[i+1]); + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_help(); + exit(0); + } else if (strcmp(argv[i], "-i") == 0) { + IterNum = atoi(argv[i+1]); + } + } + bool first = true; // is it the first time a frame is rendered? + bool NeedRender = true; // does it needs rendering? + ResCalc(); + while (first || !fix) { // if fix == true, it will render only one time because first is allways true at the start + if (!first) { + char input[255]; + scanf("%s", input); + for (int i = 0; input[i] != '\0'; i++) { + // Movements + if (input[i] == 'w') { + NeedRender=true; + ymin -= delta; + ymax -= delta; + } else if (input[i] == 's') { + NeedRender=true; + ymin += delta; + ymax += delta; + } else if (input[i] == 'a') { + NeedRender=true; + xmin -= delta; + xmax -= delta; + } else if (input[i] == 'd') { + NeedRender=true; + xmin += delta; + xmax += delta; + // Quit + } else if (input[i] == 'x') { + return 0; + // Changes the number of iterations + } else if (input[i] == 'q') { + IterNum -= 100; + NeedRender = true; + } else if (input[i] == 'e') { + IterNum += 100; + NeedRender = true; + // Zoom in + } else if (input[i] == 'r') { + long double xdist = (xmax - xmin)/(2*sqrt2); + long double xcenter = (xmax + xmin)/2; + xmin = xcenter - xdist; + xmax = xcenter + xdist; + + long double ydist = (ymax - ymin)/(2*sqrt2); + long double ycenter = (ymax + ymin)/2; + ymin = ycenter - ydist; + ymax = ycenter + ydist; + + delta /= sqrt2; + NeedRender = true; + // Zoom out + } else if (input[i] == 'f') { + long double xdist = sqrt2*(xmax-xmin)/2; + long double xcenter = (xmax + xmin)/2; + xmin = xcenter - xdist; + xmax = xcenter + xdist; + + long double ydist = sqrt2*(ymax-ymin)/2; + long double ycenter = (ymax + ymin)/2; + ymin = ycenter - ydist; + ymax = ycenter + ydist; + + delta *= sqrt2; + NeedRender = true; + } + } + } + if (NeedRender) { + int grid[resolution[0]][resolution[1]]; + long double deltax = (xmax - xmin)/resolution[1]; + long double deltay = (ymax - ymin)/resolution[0]; + long double y = ymin; + long double x = xmin; + // Computes everything + for (int yi = 0; yi < resolution[0]; yi++) { + for (int xi = 0; xi < resolution[1]; xi++) { + long double xyi[2]; + xyi[0] = x; + xyi[1] = y; + grid[yi][xi] = iteration(xyi); + x += deltax; + } + y += deltay; + x = xmin; + first = false; + } + // And then renders it + render(grid); + NeedRender = false; + } + } + return 0; +}