From 4b443079482dba5b1f00b0117fcf5d7311eaded0 Mon Sep 17 00:00:00 2001 From: Bassel Alshayeb Date: Mon, 23 Dec 2024 08:06:00 +0300 Subject: [PATCH] added fractal --- .github/workflows/build.yml | 3 + 03.Fractal/fractal.c | 162 ++++++++++++++++++++++++++++++++++++ 03.Fractal/image.c | 88 ++++++++++++++++++++ 03.Fractal/image.h | 29 +++++++ README.md | 43 +++++++++- 5 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 03.Fractal/fractal.c create mode 100644 03.Fractal/image.c create mode 100644 03.Fractal/image.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 98e6bb2..191dcdc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,9 @@ jobs: mkdir -p build gcc -o build/flush_build 01.IO_Flush/index.c || exit 1 gcc -o build/over_commit_build 02.Overcommit/main.c || exit 1 + gcc -o build/image.o -c 03.Fractal/image.c || exit 1 + gcc -c 03.Fractal/fractal.c -o fractal.o -lm + gcc build/image.o build/fractal.o -o build/fractal -lm - name: Upload executables uses: actions/upload-artifact@v4 diff --git a/03.Fractal/fractal.c b/03.Fractal/fractal.c new file mode 100644 index 0000000..54c8127 --- /dev/null +++ b/03.Fractal/fractal.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include "image.h" + +#define WIDTH 800 +#define HEIGHT 600 + +typedef enum { + FRACTAL_JULIA, + FRACTAL_SIERPINSKI, + FRACTAL_MANDELBROT +} fractal_type; + +static unsigned int clamp_iter_to_gray(int iteration, int max_iter) +{ + if (iteration >= max_iter) return 0; + return (unsigned int)(255 - (255.0f * iteration / max_iter)); +} + +void generate_julia(image_p img) +{ + float cRe = -0.7f; + float cIm = 0.27015f; + int max_iter = 300; + + float scaleX = 3.0f / (float)img->width; + float scaleY = 2.0f / (float)img->height; + + for (unsigned int py = 0; py < img->height; py++) { + for (unsigned int px = 0; px < img->width; px++) { + float zx = 1.5f * (px - img->width / 2) * scaleX; + float zy = (py - img->height / 2) * scaleY; + + int iteration = 0; + while ((zx*zx + zy*zy < 4.0f) && (iteration < max_iter)) { + float tmp = zx*zx - zy*zy + cRe; + zy = 2.0f*zx*zy + cIm; + zx = tmp; + iteration++; + } + pixel_data color = clamp_iter_to_gray(iteration, max_iter); + image_set_pixel(img, px, py, color); + } + } +} + +void generate_sierpinski(image_p img) +{ + for (unsigned int y = 0; y < img->height; y++) { + for (unsigned int x = 0; x < img->width; x++) { + image_set_pixel(img, x, y, 255); + } + } + + for (unsigned int y = 0; y < img->height; y++) { + for (unsigned int x = 0; x < img->width; x++) { + unsigned int tx = x; + unsigned int ty = y; + unsigned char color = 255; // white + while (tx > 0 || ty > 0) { + if ((tx % 3 == 1) && (ty % 3 == 1)) { + color = 0; // black + break; + } + tx /= 3; + ty /= 3; + } + image_set_pixel(img, x, y, color); + } + } +} + +void generate_mandelbrot(image_p img) +{ + int max_iter = 300; + + float minX = -2.0f, maxX = 1.0f; + float minY = -1.0f, maxY = 1.0f; + + float scaleX = (maxX - minX) / (float)img->width; + float scaleY = (maxY - minY) / (float)img->height; + + for (unsigned int py = 0; py < img->height; py++) { + for (unsigned int px = 0; px < img->width; px++) { + float cx = minX + px * scaleX; + float cy = minY + py * scaleY; + + float zx = 0.0f; + float zy = 0.0f; + int iteration = 0; + while ((zx*zx + zy*zy < 4.0f) && (iteration < max_iter)) { + float tmp = zx*zx - zy*zy + cx; + zy = 2.0f*zx*zy + cy; + zx = tmp; + iteration++; + } + pixel_data color = clamp_iter_to_gray(iteration, max_iter); + image_set_pixel(img, px, py, color); + } + } +} + +int main(int argc, char* argv[]) +{ + fractal_type chosen_fractal = FRACTAL_JULIA; + const char* output_filename = "fractal_output.pgm"; + + if (argc > 1) { + if (strcmp(argv[1], "julia") == 0) { + chosen_fractal = FRACTAL_JULIA; + } else if (strcmp(argv[1], "sierpinski") == 0) { + chosen_fractal = FRACTAL_SIERPINSKI; + } else if (strcmp(argv[1], "mandelbrot") == 0) { + chosen_fractal = FRACTAL_MANDELBROT; + } else { + fprintf(stderr, "Unknown fractal type '%s'. Use 'julia', 'sierpinski', or 'mandelbrot'.\n", argv[1]); + return 1; + } + } + if (argc > 2) { + output_filename = argv[2]; + } + + image_p img = image_create(WIDTH, HEIGHT); + if (!img) { + fprintf(stderr, "Failed to create image.\n"); + return 1; + } + + switch (chosen_fractal) { + case FRACTAL_JULIA: + printf("Generating Julia set...\n"); + generate_julia(img); + break; + case FRACTAL_SIERPINSKI: + printf("Generating Sierpinski carpet...\n"); + generate_sierpinski(img); + break; + case FRACTAL_MANDELBROT: + printf("Generating Mandelbrot set...\n"); + generate_mandelbrot(img); + break; + default: + fprintf(stderr, "Invalid fractal type.\n"); + image_delete(img); + return 1; + } + + printf("Saving to '%s'...\n", output_filename); + if (image_save_pgm(img, output_filename) != 0) { + fprintf(stderr, "Failed to save PGM '%s'\n", output_filename); + image_delete(img); + return 1; + } + + image_delete(img); + printf("Done.\n"); + return 0; +} diff --git a/03.Fractal/image.c b/03.Fractal/image.c new file mode 100644 index 0000000..6f79214 --- /dev/null +++ b/03.Fractal/image.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +#include "image.h" + + +image_p image_create(pixel_coord width, pixel_coord height) +{ + image_t *v = malloc(sizeof(image_t)); + if (!v) { + fprintf(stderr, "Failed to allocate image structure.\n"); + return NULL; + } + v->width = width; + v->height = height; + v->data = malloc(sizeof(pixel_data) * width * height); + if (!v->data) { + fprintf(stderr, "Failed to allocate image data.\n"); + free(v); + return NULL; + } + return v; +} + +void clear_image(image_p picture) +{ + memset(picture->data, 0, sizeof(pixel_data) * picture->width * picture->height); +} + +void fill_random(image_p picture) +{ + pixel_data *p = picture->data; + for (pixel_coord z = 0; z < picture->height * picture->width; ++z, ++p) { + *p = (pixel_data)(rand() % 256); + } +} + +void image_delete(image_p picture) +{ + if (!picture) return; + free(picture->data); + free(picture); +} + +int image_save_pgm(image_p picture, const char *filename) +{ + if (!picture || !filename) { + fprintf(stderr, "image_save_pgm: invalid arguments.\n"); + return 1; + } + + FILE *to = fopen(filename, "w"); + if (!to) { + perror("Failed to open output file"); + return 1; + } + + // ASCII P2 format + fprintf(to, "P2\n%u %u\n255\n", picture->width, picture->height); + + pixel_data *p = picture->data; + for (pixel_coord y = 0; y < picture->height; ++y) { + for (pixel_coord x = 0; x < picture->width; ++x) { + fprintf(to, "%u", *(p++)); + if (x == picture->width - 1) + fputc('\n', to); + else + fputc(' ', to); + } + } + + fclose(to); + return 0; +} + +void image_set_pixel(image_p picture, pixel_coord x, pixel_coord y, pixel_data color) +{ + assert(("Out of range (x,y)", x < picture->width && y < picture->height)); + picture->data[picture->width * y + x] = color; +} + +pixel_data get_pixel(image_p picture, pixel_coord x, pixel_coord y) +{ + assert(("Out of range (x,y)", x < picture->width && y < picture->height)); + return picture->data[picture->width * y + x]; +} diff --git a/03.Fractal/image.h b/03.Fractal/image.h new file mode 100644 index 0000000..ff53afd --- /dev/null +++ b/03.Fractal/image.h @@ -0,0 +1,29 @@ +#ifndef IMAGE_H +#define IMAGE_H + +typedef unsigned int pixel_coord; +typedef unsigned int pixel_data; + +struct image { + pixel_coord width, height; + pixel_data *data; +}; + +typedef struct image image_t; +typedef struct image* image_p; + +image_p image_create(pixel_coord width, pixel_coord height); + +void clear_image(image_p picture); + +void fill_random(image_p picture); + +void image_delete(image_p picture); + +int image_save_pgm(image_p picture, const char *filename); + +void image_set_pixel(image_p picture, pixel_coord x, pixel_coord y, pixel_data color); + +pixel_data get_pixel(image_p picture, pixel_coord x, pixel_coord y); + +#endif diff --git a/README.md b/README.md index 9bc2f1f..a9fc179 100644 --- a/README.md +++ b/README.md @@ -127,4 +127,45 @@ Using free -m during the pauses to observe total system memory usage in MB. - **Read Mode** : Memory usage remains stable. - **Write Mode** : Physical memory allocation grows as pages are dirtied. When exceeding the maximum system will crash. 2. Continuous Write with no pauses: - - Causes rapid memory consumption and high system load. \ No newline at end of file + - Causes rapid memory consumption and high system load. + + +## 03- Fractal + +### **Description** + +This module demonstrates generating fractals in C and saving the results in PGM format. I applied three fractals as I found: + +- Julia +- Sierpinski Carpet +- Mandelbrot Set + +I reused ```image.c``` to manage the image buffers and write the result to ```.pgm``` + +### **How To Use** + +1. The main entry point is ```fractal.c``` that contains the fractal generation codes. +2. Compilation: + +```bash +gcc -c image.c -o image.o +gcc -c fractal.c -o fractal.o -lm +gcc image.o fractal.o -o fractal -lm +``` + +3. Running: +We can pass two params: +- Fractal type: ```julia```, ```sierpinski``` or ```mandelbrot```. +- Output filename defaule ```fractal_output.pgm``` + +```bash +./fractal +./fractal sierpinski carpet.pgm +./fractal mandelbrot mandelbrot.pgm +``` + +4. Viewing PGM: + +```bash +convert fractal_output.pgm fractal_output.png +``` \ No newline at end of file