I managed to implement single-step generation using Open MPI by distributing Mandelbrot computation across 4 processors. Now, I want to add a zoom feature, but I do not know where to begin.
#include <SFML/Graphics.hpp>
#include <complex>
#include <vector>
#include <iostream>
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define SIZE 800 // widht & height
using namespace std;
sf::Color interpolateColor(const sf::Color& start, const sf::Color& end, float t) {
return sf::Color(
start.r + t * (end.r - start.r),
start.g + t * (end.g - start.g),
start.b + t * (end.b - start.b)
);
}
// Function to compute the number of iterations at which the Mandelbrot sequence escapes
int mandelbrot(const complex<double>& c, int max_iter) {
complex<double> z = 0;
int n = 0;
while (abs(z) <= 2.0 && n < max_iter) {
z = z * z + c;
++n;
}
return n;
}
void draw(int mandelbrotdata[SIZE][SIZE], int max_iter){
const sf::Color gradientStart = sf::Color::Blue;
const sf::Color gradientMiddle = sf::Color::Green;
const sf::Color gradientEnd = sf::Color::Red;
sf::Image image;
image.create(SIZE, SIZE, sf::Color::Black);
for (int i = 0; i < SIZE; ++i) {
for (int j = 0; j < SIZE; ++j) {
int n = mandelbrotdata[i][j];
sf::Color color;
if (n == max_iter) {
// Mandelbrot setinin barçası ise siyah yap
color = sf::Color::Black;
} else {
float t = static_cast<float>(n) / max_iter;
if (t < 0.5f) {
// Interpolate between gradientStart and gradientMiddle
color = interpolateColor(gradientStart, gradientMiddle, t * 2);
} else {
// Interpolate between gradientMiddle and gradientEnd
color = interpolateColor(gradientMiddle, gradientEnd, (t - 0.5f) * 2);
}
}
image.setPixel(j, i, color);
}
}
// Create a texture and sprite to display the image
sf::Texture texture;
texture.loadFromImage(image);
sf::Sprite sprite(texture);
// Create a window to display the Mandelbrot set
sf::RenderWindow window(sf::VideoMode(SIZE, SIZE), "Mandelbrot Set");
//window.draw(sprite);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::X) {
window.close();
}
}
window.clear();
window.draw(sprite);
window.display();
}
}
void scatter_gather_mandelbrot(int size_of_cluster, double scale, double cx, double cy){
// Determine root's rank
int root_rank = 0;
// Get my rank
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
int max_iter = 512;
double recvbuf_r1[SIZE/2]={0};
double recvbuf_r2[SIZE/2]={0};
//Scattering
if (my_rank == root_rank) {
double r1[SIZE]; // ilk yarısını 0 ve 1'ye, ikinci yarısını 2 ve 3'e
double r2[SIZE]; // ilk yarısını 0 ve 2'e, ikinci yarısını 1 ve 3'e
// Define the range and resolution of the fractal
double xmin = -scale, xmax = scale, ymin = -scale, ymax = scale;
for (int i = 0; i < SIZE; ++i) {
r1[i] = xmin + (i * (xmax - xmin) / (SIZE - 1));
r2[i] = ymin + (i * (ymax - ymin) / (SIZE - 1));
}
/*
printf("===Original R1:====");
for (int i = 0; i < SIZE; i++)
{
printf("%lf ", r1[i]);
}
*/
printf("n");
int counts[4];
int displacements_r1[4] = {0, 0, SIZE/2, SIZE/2};
int displacements_r2[4] = {0, SIZE/2, 0, SIZE/2};
for (int i = 0; i < size_of_cluster; i++)
{
counts[i] = SIZE/2;
}
MPI_Scatterv(r1, counts, displacements_r1, MPI_DOUBLE, recvbuf_r1, SIZE/2, MPI_DOUBLE, root_rank, MPI_COMM_WORLD);
MPI_Scatterv(r1, counts, displacements_r2, MPI_DOUBLE, recvbuf_r2, SIZE/2, MPI_DOUBLE, root_rank, MPI_COMM_WORLD);
}
else {
MPI_Scatterv(NULL, NULL, NULL, MPI_DOUBLE, recvbuf_r1, SIZE/2, MPI_DOUBLE, root_rank, MPI_COMM_WORLD);
MPI_Scatterv(NULL, NULL, NULL, MPI_DOUBLE, recvbuf_r2, SIZE/2, MPI_DOUBLE, root_rank, MPI_COMM_WORLD);
}
for (int i = 0; i < SIZE/2; i++)
{
printf("Process %d received value = %lf.n", my_rank, recvbuf_r2[i]);
}
MPI_Barrier(MPI_COMM_WORLD);
//vector<vector<int>> local_result(SIZE/2, vector<int>(SIZE/2));
int local_result[SIZE/2][SIZE/2];
for (int i = 0; i < SIZE/2; ++i) {
for (int j = 0; j < SIZE/2; ++j) {
complex<double> c(cx+recvbuf_r1[j], cy+recvbuf_r2[i]);
local_result[i][j] = mandelbrot(c, max_iter);
}
}
MPI_Barrier(MPI_COMM_WORLD);
// NxN matrix 4 equal blocks
const int NPROWS=2; // block row width
const int NPCOLS=2; // block column length
const int CELLS_PER_ROW = SIZE/NPROWS; // number of rows in block
const int CELLS_PER_COLUMN = SIZE/NPCOLS; // number of cols in block
const int BLOCK_SIZE = CELLS_PER_ROW * CELLS_PER_COLUMN;
// Create the vector datatype
MPI_Datatype column_not_resized;
MPI_Type_vector(CELLS_PER_COLUMN, CELLS_PER_ROW, SIZE, MPI_INT, &column_not_resized);
// Resize it to make sure it is interleaved when repeated
MPI_Datatype column_resized;
MPI_Type_create_resized(column_not_resized, 0, sizeof(int), &column_resized);
MPI_Type_commit(&column_resized);
// Gathering
if (my_rank == root_rank) {
int mandelbrotdata[SIZE][SIZE];
int counts[4];
int displacements[4] = {0, SIZE*(SIZE/2), (SIZE/2), SIZE*(SIZE/2)+(SIZE/2)};
for (int i = 0; i < 4; i++) counts[i] = BLOCK_SIZE;
printf("n");
MPI_Gatherv(local_result, BLOCK_SIZE, MPI_INT, mandelbrotdata, counts, displacements, column_resized, root_rank, MPI_COMM_WORLD);
draw(mandelbrotdata, max_iter);
}
else {
MPI_Gatherv(local_result, BLOCK_SIZE, MPI_INT, NULL, NULL, NULL, column_resized, root_rank, MPI_COMM_WORLD);
}
}
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
// Get number of processes and check that 4 processes are used
int size_of_cluster;
MPI_Comm_size(MPI_COMM_WORLD, &size_of_cluster);
if(size_of_cluster != 4)
{
printf("This application is meant to be run with 4 processes.n");
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
double scale = 0.3;
double cx = -1.0;
double cy = 0.0;
scatter_gather_mandelbrot(size_of_cluster, scale, cx, cy);
MPI_Finalize();
return EXIT_SUCCESS;
}
Inside the main function, I have thought about something like this:
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
if(my_rank==0){
sf::RenderWindow window(sf::VideoMode(SIZE, SIZE), "Mandelbrot Control");
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Escape) {
window.close();
} else if (event.key.code == sf::Keyboard::I) {
scale *= 0.9;
} else if (event.key.code == sf::Keyboard::K) {
scale *= 1.1;
} else if (event.key.code == sf::Keyboard::A) {
cx -= 0.1 * scale;
} else if (event.key.code == sf::Keyboard::S) {
cy += 0.1 * scale;
} else if (event.key.code == sf::Keyboard::D) {
cx += 0.1 * scale;
} else if (event.key.code == sf::Keyboard::W) {
cy -= 0.1 * scale;
}
scatter_gather_mandelbrot(size_of_cluster, scale, cx, cy);
}
}
window.clear();
window.display();
}
}
I have tought about changing the double scale, cx, cy by user input. But don’t know how to implement it.
New contributor
ALTUĞ PARLAK is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.