Skip to content

Instantly share code, notes, and snippets.

@BYVoid
Created August 26, 2012 16:54
Show Gist options
  • Save BYVoid/3481630 to your computer and use it in GitHub Desktop.
Save BYVoid/3481630 to your computer and use it in GitHub Desktop.
Mandelbrot Set Calculator and GUI
#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));
}
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
#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;
}
#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