Created
August 26, 2012 16:54
-
-
Save BYVoid/3481630 to your computer and use it in GitHub Desktop.
Mandelbrot Set Calculator and GUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mandelbrot.h" | |
GtkLabel ** axis_imag_lables, ** axis_real_lables; | |
int numXLabels, numYLabels; | |
gboolean redrawed; | |
gboolean expose(GtkDrawingArea *drawing_area, GdkEventExpose *event, gpointer data) | |
{ | |
GtkWidget *widget = GTK_WIDGET(drawing_area); | |
GdkGC *gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)]; | |
GdkDrawable *drawable = widget->window; | |
GdkColor color; | |
int x, y; | |
for (x = 0; x < PaintWidth; x++) { | |
for (y = 0; y < PaintHeight; y++) { | |
color.red = color.green = color.blue = COLOR_MAX - pixel[x * PaintHeight + y] * COLOR_MAX / Iteration; | |
gdk_gc_set_rgb_fg_color(gc, &color); | |
gdk_draw_point(drawable, gc, x, y); | |
} | |
} | |
return TRUE; | |
} | |
void redraw(GtkDrawingArea * drawing_area) | |
{ | |
GdkWindow * window = GTK_WIDGET(drawing_area)->window; | |
gdk_window_invalidate_rect(window, NULL, FALSE); | |
} | |
gboolean refresher(GtkDrawingArea * drawing_area) | |
{ | |
if (!completed) { | |
redrawed = FALSE; | |
expose(drawing_area, NULL, NULL); | |
} else if (completed && !redrawed) { | |
redraw(drawing_area); | |
redrawed = TRUE; | |
} | |
return TRUE; | |
} | |
gchar * double_to_string(double num, double min, double max) | |
{ | |
if (max - min < 0.001) { | |
return g_strdup_printf("%.2E", num); | |
} else { | |
return g_strdup_printf("%.3lf", num); | |
} | |
} | |
void refresh_axis() | |
{ | |
int i; | |
for (i = 0; i < numXLabels; i++) { | |
double num = (cMax.imag - cMin.imag) / numXLabels * i + cMin.imag; | |
gchar * text = double_to_string(num, cMin.imag, cMax.imag); | |
gtk_label_set_text(axis_imag_lables[i], text); | |
g_free(text); | |
} | |
for (i = 0; i < numYLabels; i++) { | |
double num = (cMax.real - cMin.real) / numYLabels * i + cMin.real; | |
gchar * text = double_to_string(num, cMin.real, cMax.real); | |
gtk_label_set_text(axis_real_lables[i], text); | |
g_free(text); | |
} | |
} | |
gboolean drawing_area_clicked(GtkDrawingArea * drawing_area, GdkEventButton * event, gpointer data) | |
{ | |
if (!completed) { | |
return TRUE; | |
} | |
double imag_scale = cMax.imag - cMin.imag; | |
double real_scale = cMax.real - cMin.real; | |
double axis_imag = event->y / PaintHeight * imag_scale + cMin.imag; | |
double axis_real = event->x / PaintWidth * real_scale + cMin.real; | |
if (event->button == 1) { //Left button | |
imag_scale /= 2; | |
real_scale /= 2; | |
} else if (event->button == 3) { //Right button | |
imag_scale *= 2; | |
real_scale *= 2; | |
} else { | |
return TRUE; | |
} | |
cMin.imag = axis_imag - imag_scale / 2; | |
cMax.imag = axis_imag + imag_scale / 2; | |
cMin.real = axis_real - real_scale / 2; | |
cMax.real = axis_real + real_scale / 2; | |
g_print("cMin: (%lf, %lf) cMax: (%lf, %lf)\n", cMin.imag, cMin.real, cMax.imag, cMax.real); | |
start_mandelbrot(); | |
refresh_axis(); | |
return TRUE; | |
} | |
void initialize_gtk() | |
{ | |
GtkWindow * window; | |
GtkTable * table; | |
GtkDrawingArea * drawing_area; | |
GtkFixed * axis_imag, * axis_real; | |
int i; | |
/* window */ | |
window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | |
gtk_window_set_title(window, "Mandelbrot Set"); | |
gtk_window_set_resizable(window, FALSE); | |
/* table */ | |
table = GTK_TABLE(gtk_table_new(2, 2, FALSE)); | |
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table)); | |
/* drawing_area */ | |
drawing_area = GTK_DRAWING_AREA(gtk_drawing_area_new()); | |
gtk_widget_set_size_request(GTK_WIDGET(drawing_area), PaintWidth, PaintHeight); | |
gtk_table_attach(table, GTK_WIDGET(drawing_area), 1, 2, 1, 2, GTK_EXPAND, GTK_EXPAND, 0, 0); | |
gtk_widget_add_events(GTK_WIDGET(drawing_area), GDK_BUTTON_PRESS_MASK); | |
/* misc */ | |
GdkColor blueColor, redColor; | |
gdk_color_parse ("blue", &blueColor); | |
gdk_color_parse ("red", &redColor); | |
/* axis */ | |
axis_imag = GTK_FIXED(gtk_fixed_new()); | |
axis_real = GTK_FIXED(gtk_fixed_new()); | |
numXLabels = PaintHeight / 32; | |
axis_imag_lables = (GtkLabel **) malloc(sizeof(GtkLabel *) * numXLabels); | |
for (i = 0; i < numXLabels; i++) { | |
axis_imag_lables[i] = GTK_LABEL(gtk_label_new("")); | |
gtk_widget_modify_fg(GTK_WIDGET(axis_imag_lables[i]), GTK_STATE_NORMAL, &blueColor); | |
int pos = (double)PaintHeight / (numXLabels + 1) * i - (double)PaintHeight / numXLabels; | |
gtk_fixed_put(axis_imag, GTK_WIDGET(axis_imag_lables[i]), 0, pos); | |
} | |
numYLabels = PaintWidth / 128; | |
axis_real_lables = (GtkLabel **) malloc(sizeof(GtkLabel *) * numYLabels); | |
for (i = 0; i < numYLabels; i++) { | |
axis_real_lables[i] = GTK_LABEL(gtk_label_new("")); | |
gtk_widget_modify_fg(GTK_WIDGET(axis_real_lables[i]), GTK_STATE_NORMAL, &blueColor); | |
int pos = (double)PaintWidth / (numYLabels + 1) * i - (double)PaintWidth / numYLabels; | |
gtk_fixed_put(axis_real, GTK_WIDGET(axis_real_lables[i]), pos, 0); | |
} | |
refresh_axis(); | |
gtk_table_attach(table, GTK_WIDGET(axis_imag), 0, 1, 1, 2, GTK_EXPAND, GTK_EXPAND, 0, 0); | |
gtk_table_attach(table, GTK_WIDGET(axis_real), 1, 2, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); | |
/* events */ | |
g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(expose), NULL); | |
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); | |
g_signal_connect(G_OBJECT(drawing_area), "button-press-event", G_CALLBACK(drawing_area_clicked), NULL); | |
g_timeout_add(REFRESH_INTERVAL, (GSourceFunc)refresher, drawing_area); | |
/* show all widgets, than call main loop */ | |
gtk_widget_queue_draw(GTK_WIDGET(drawing_area)); | |
gtk_widget_show_all(GTK_WIDGET(window)); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all:mandelbrot mandelbrot_mpi | |
mpi:mandelbrot_mpi | |
openmp:mandelbrot_openmp | |
mandelbrot: mandelbrot.c gui.c mandelbrot.h | |
gcc mandelbrot.c gui.c -o mandelbrot -O2 `pkg-config --cflags --libs gtk+-2.0` | |
mandelbrot_mpi: mandelbrot.c mandelbrot.h | |
mpicc mandelbrot.c -DMPI -o mandelbrot_mpi -O2 | |
mandelbrot_openmp: mandelbrot.c gui.c mandelbrot.h | |
gcc mandelbrot.c gui.c -DOPENMP -o mandelbrot_openmp -O2 `pkg-config --cflags --libs gtk+-2.0` -fopenmp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mandelbrot.h" | |
#ifdef MPI | |
#include <mpi.h> | |
#endif | |
#ifdef OPENMP | |
#include <omp.h> | |
#endif | |
struct WorkThread { | |
pthread_t thd; | |
int start; | |
int end; | |
}; | |
struct Complex cMin = {-1.5, -1.0}; | |
struct Complex cMax = {0.5, 1.0}; | |
int Iteration = 0, IterationMin = 4, IterationMax = 256; | |
int PaintWidth = 1024, PaintHeight = 768; | |
int numWorkThreads = 1; | |
int showGui = 1; | |
struct Complex scale; | |
int * pixel; | |
pthread_t manager_thd; | |
struct WorkThread * work_thds; | |
int completed; | |
int mpi; | |
int mpiNodes; | |
int mpiRank; | |
int rootNode = 0; | |
int calculate_pixel(struct Complex c) | |
{ | |
int count = 0; | |
struct Complex z = {0, 0}; | |
double temp, lengthsq; | |
do { | |
temp = z.real * z.real - z.imag * z.imag + c.real; | |
z.imag = 2 * z.real * z.imag + c.imag; | |
z.real = temp; | |
lengthsq = z.real * z.real + z.imag * z.imag; | |
count++; | |
} while (lengthsq < 4.0 && count < Iteration); | |
return count; | |
} | |
void * mandelbrot_calculate(void * data) | |
{ | |
struct WorkThread * current = (struct WorkThread *)data; | |
int calcX; | |
int calcY; | |
#ifdef OPENMP | |
#pragma omp parallel for | |
#endif | |
for (calcX = 0; calcX < PaintWidth; calcX++) { | |
for (calcY = current->start; calcY < current->end; calcY++) { | |
struct Complex c = {cMin.real + calcX * scale.real, cMin.imag + calcY * scale.imag}; | |
pixel[calcX * PaintHeight + calcY] = calculate_pixel(c); | |
} | |
} | |
} | |
void mandelbrot() | |
{ | |
completed = 0; | |
scale.real = (cMax.real - cMin.real) / PaintWidth; | |
scale.imag = (cMax.imag - cMin.imag) / PaintHeight; | |
int eachHeight = PaintHeight / numWorkThreads; | |
#ifdef MPI | |
work_thds[0].start = mpiRank * eachHeight; | |
if (mpiRank != mpiNodes - 1) { | |
work_thds[0].end = (mpiRank + 1) * eachHeight; | |
} else { | |
work_thds[0].end = PaintHeight; | |
} | |
mandelbrot_calculate(&work_thds[0]); | |
int eachCount = eachHeight * PaintWidth; | |
MPI_Gather(pixel + work_thds[0].start, eachCount, MPI_INT, pixel, eachCount, MPI_INT, rootNode, MPI_COMM_WORLD); | |
#else | |
#ifdef OPENMP | |
work_thds[0].start = 0; | |
work_thds[0].end = PaintHeight; | |
mandelbrot_calculate(&work_thds[0]); | |
#else | |
int i; | |
for (i = 0; i < numWorkThreads; i++) { | |
work_thds[i].start = i * eachHeight; | |
if (i != numWorkThreads - 1) { | |
work_thds[i].end = (i + 1) * eachHeight; | |
} else { | |
work_thds[i].end = PaintHeight; | |
} | |
pthread_create(&(work_thds[i].thd), NULL, mandelbrot_calculate, &work_thds[i]); | |
} | |
for (i = 0; i < numWorkThreads; i++) { | |
pthread_join(work_thds[i].thd, NULL); | |
} | |
#endif | |
#endif | |
completed = 1; | |
} | |
void clear() | |
{ | |
memset(pixel, 0, sizeof(int) * PaintWidth * PaintHeight); | |
} | |
long long get_time() | |
{ | |
struct timeval tv; | |
gettimeofday(&tv, NULL); | |
return tv.tv_sec * 1000 + tv.tv_usec / 1000; | |
} | |
void * manager(void * data) | |
{ | |
if (Iteration == 0) { | |
Iteration = IterationMin; | |
} | |
for (; Iteration <= IterationMax; Iteration *= 2) { | |
clear(); | |
if (!mpi || (mpi && mpiRank == rootNode)) { | |
debug("iteration: %d\n", Iteration); | |
} | |
long long start = get_time(); | |
mandelbrot(); | |
if (!mpi || (mpi && mpiRank == rootNode)) { | |
double elapsed = (get_time() - start) / 1000.0; | |
printf("%.3lf\n", elapsed); | |
} | |
if (showGui) { | |
sleep(SLEEP_TIME); | |
} | |
if (Iteration * 2 > IterationMax) { | |
break; | |
} | |
} | |
return NULL; | |
} | |
void start_mandelbrot() | |
{ | |
if (showGui) { | |
pthread_create(&manager_thd, NULL, manager, NULL); | |
} else { | |
manager(NULL); | |
} | |
} | |
void initialize_mandelbrot() | |
{ | |
pixel = (int *) malloc(PaintWidth * PaintHeight * sizeof(int)); | |
work_thds = (struct WorkThread *) malloc(numWorkThreads * sizeof(struct WorkThread)); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
if (argc != 1 && argc != 11) { | |
fprintf(stderr, "\nMandelbrot Set\n\n"); | |
fprintf(stderr, "Usage: %s [ShowGui] [PaintWidth] [PaintHeight] [IterationMin] [IterationMax] " | |
"[cMin.imag] [cMin.real] [cMax.imag] [cMax.real] [numWorkThreads]" | |
"\n\n", argv[0]); | |
fprintf(stderr, "Example: %s 1 1024 768 8 1024 -1.0 -1.5 1.0 0.5 8\n\n", argv[0]); | |
fprintf(stderr, "Author: BYVoid <http://www.byvoid.com>\n2012-08-23\n\n", argv[0]); | |
return 0; | |
} else if (argc == 11) { | |
int showGuiTemp; | |
sscanf(argv[1], "%d", &showGuiTemp); | |
showGui = !!showGuiTemp; | |
sscanf(argv[2], "%d", &PaintWidth); | |
sscanf(argv[3], "%d", &PaintHeight); | |
sscanf(argv[4], "%d", &IterationMin); | |
sscanf(argv[5], "%d", &IterationMax); | |
sscanf(argv[6], "%lf", &cMin.imag); | |
sscanf(argv[7], "%lf", &cMin.real); | |
sscanf(argv[8], "%lf", &cMax.imag); | |
sscanf(argv[9], "%lf", &cMax.real); | |
sscanf(argv[10], "%d", &numWorkThreads); | |
} | |
#ifdef MPI | |
MPI_Init(&argc, &argv); | |
MPI_Comm_size(MPI_COMM_WORLD, &mpiNodes); | |
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank); | |
showGui = 0; | |
mpi = 1; | |
numWorkThreads = mpiNodes; | |
#else | |
#ifdef OPENMP | |
omp_set_num_threads(numWorkThreads); | |
#endif | |
if (showGui) { | |
gtk_init(&argc, &argv); | |
initialize_gtk(); | |
} | |
mpi = 0; | |
#endif | |
initialize_mandelbrot(); | |
start_mandelbrot(); | |
#ifdef MPI | |
MPI_Finalize(); | |
#else | |
if (showGui) { | |
gtk_main(); | |
} | |
#endif | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MANDELBROT_H_ | |
#define MANDELBROT_H_ | |
#define REFRESH_INTERVAL 15 | |
#define COLOR_MAX 65536 | |
#define SLEEP_TIME 1 | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <pthread.h> | |
#include <sys/time.h> | |
#ifndef MPI | |
#include <gtk/gtk.h> | |
#endif | |
struct Complex { | |
double real; | |
double imag; | |
}; | |
extern int PaintWidth, PaintHeight; | |
extern int Iteration; | |
extern int * pixel; | |
extern int completed; | |
extern struct Complex cMin; | |
extern struct Complex cMax; | |
#ifdef DEBUG | |
#define debug(a, b) fprintf(stderr, a, b) | |
#else | |
#define debug(a, b) | |
#endif | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment