]> git.itanic.dy.fi Git - mandelbrot/blob - mandelbrot.c
Fix broken aspect ratio calculation
[mandelbrot] / mandelbrot.c
1 #include <SDL.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <time.h>
5 #include <string.h>
6
7 struct context {
8         struct SDL_Surface *screen;
9         long double origo_x, origo_y;
10         int motion_x, motion_y;         /* mouse motion */
11         int mx, my;                     /* mouse position */
12         long double zoom;
13         int scroll_enabled;
14         int zooms;
15 };
16
17 static void putpixel(struct SDL_Surface *screen, const int x, const int y,
18                      const unsigned char r, const unsigned char g,
19                      const unsigned char b)
20 {
21         int offset = y * screen->pitch + x * 4;
22         unsigned char *buf = screen->pixels;
23
24         buf[offset++] = b;
25         buf[offset++] = g;
26         buf[offset]   = r;
27 }
28
29 #define MAX_ITERATION 1000
30
31 int get_mandelbrot_iterations(long double x0, long double y0)
32 {
33         long double x = 0, y = 0, xtemp;
34         int iteration = 0;
35
36         while ((x * x + y * y < 2 * 2) && iteration < MAX_ITERATION) {
37
38                 xtemp = x * x - y * y + x0;
39                 y = 2 * x * y + y0;
40
41                 x = xtemp;
42                 iteration++;
43         }
44
45         return iteration;
46 }
47
48 int draw_mandelbrot(struct SDL_Surface *screen, long double x1, long double x2,
49                 long double y1, long double y2)
50 {
51         long double x0, y0, xlen, ylen, xstep, ystep;
52         int iteration, xs, ys;
53
54         xlen = x2 - x1;
55         ylen = y2 - y1;
56         xstep = xlen / (long double)screen->w;
57         ystep = ylen / (long double)screen->h;
58
59         y0 = y1;
60 #pragma omp parallel for private(xs, ys, x0, y0)
61         for (ys = 0; ys < screen->h; ys++) {
62                 y0 = y1 + ystep * ys;
63                 x0 = x1;
64                 for (xs = 0; xs < screen->w; xs++) {
65                         iteration = get_mandelbrot_iterations(x0, y0);
66
67                         if (iteration == MAX_ITERATION)
68                                 putpixel(screen, xs, ys, 255, 255, 255);
69                         else
70                                 putpixel(screen, xs, ys,
71                                         iteration * 8, iteration,
72                                         iteration / 4);
73                         x0 += xstep;
74                 }
75         }
76
77         SDL_Flip(screen);
78         return 0;
79 }
80
81 #define ZOOM_FACTOR     1.5
82
83 static int read_events(struct context *ctx)
84 {
85         SDL_Event event;
86         int wait = 1, ret, exit = 0, delayed_exit = 0;
87
88         while (!exit) {
89                 if (wait) {
90                         SDL_WaitEvent(&event);
91                         wait = 0;
92                 } else {
93                         ret = SDL_PollEvent(&event);
94                         if (!ret) {
95                                 wait = 1;
96                                 exit = delayed_exit;
97                         }
98                 }
99
100                 switch (event.type) {
101                 case SDL_KEYDOWN:
102                         switch (event.key.keysym.sym) {
103                         case SDLK_ESCAPE:
104                                 goto quit;
105                         default:
106                                 break;
107                         }
108                         break;
109                 case SDL_KEYUP:
110                         switch (event.key.keysym.sym) {
111                         default:
112                                 break;
113                         }
114                         break;
115                 case SDL_MOUSEBUTTONDOWN:
116                         if (event.button.button == 4) {
117                                 ctx->zooms++;
118                                 delayed_exit++;
119                         } else if (event.button.button == 5) {
120                                 ctx->zooms--;
121                                 delayed_exit++;
122                         } else {
123                                 ctx->scroll_enabled = 1;
124                         }
125                         break;
126                 case SDL_MOUSEBUTTONUP:
127                         ctx->scroll_enabled = 0;
128                         delayed_exit++;
129                         break;
130                 case SDL_MOUSEMOTION:
131                         ctx->mx = event.motion.x;
132                         ctx->my = event.motion.y;
133                         if (!ctx->scroll_enabled)
134                                 break;
135
136                         ctx->motion_x -= event.motion.xrel;
137                         ctx->motion_y -= event.motion.yrel;
138                         delayed_exit++;
139                         break;
140                 case SDL_VIDEORESIZE:
141                         ctx->screen =
142                                 SDL_SetVideoMode(event.resize.w,
143                                                  event.resize.h,
144                                                  32,
145                                                  ctx->screen->flags);
146                         exit++;
147                         break;
148                 case SDL_QUIT:
149                         goto quit;
150                 }
151         }
152
153         return 0;
154 quit:
155         printf("\nExiting. Good bye!\n");
156         return 1;
157 }
158
159 static void loop(struct context *ctx)
160 {
161         long double aspect_ratio;
162         long double x1, x2, y1, y2;
163         int last_zoom;
164
165         do {
166                 aspect_ratio = ctx->screen->w / (long double)ctx->screen->h;
167                 ctx->origo_x += ctx->motion_x / ctx->zoom / ctx->screen->w;
168                 ctx->origo_y += ctx->motion_y / ctx->zoom / ctx->screen->h;
169                 ctx->motion_x = ctx->motion_y = 0;
170
171                 if (ctx->zooms != last_zoom) {
172                         int dx, dy;
173
174                         dx = ctx->mx - ctx->screen->w / 2;
175                         dy = ctx->my - ctx->screen->h / 2;
176
177                         if (ctx->zooms > last_zoom) {
178                                 ctx->zoom *= ZOOM_FACTOR;
179                                 ctx->origo_x += dx / ctx->zoom /
180                                         (long double) ctx->screen->w;
181                                 ctx->origo_y += dy / ctx->zoom /
182                                         (long double) ctx->screen->h;
183                         } else {
184                                 ctx->origo_x -= dx / ctx->zoom /
185                                         (long double) ctx->screen->w;
186                                 ctx->origo_y -= dy / ctx->zoom /
187                                         (long double) ctx->screen->h;
188                                 ctx->zoom /= ZOOM_FACTOR;
189                         }
190
191                         last_zoom = ctx->zooms;
192                 }
193
194                 x1 = ctx->origo_x - aspect_ratio / ctx->zoom;
195                 x2 = ctx->origo_x + aspect_ratio / ctx->zoom;
196                 y1 = ctx->origo_y - 1 / ctx->zoom;
197                 y2 = ctx->origo_y + 1 / ctx->zoom;
198
199                 printf("Drawing area (%LF, %LF)(%LF, %LF), zoom %d\n",
200                         x1, y1, x2, y2, ctx->zooms);
201
202                 draw_mandelbrot(ctx->screen, x1, x2, y1, y2);
203         } while (!read_events(ctx));
204 }
205
206 int main(int argc, char *argv[])
207 {
208         struct context ctx;
209         int flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_RESIZABLE;
210         int xres = 800, yres = 600;
211
212         memset(&ctx, 0, sizeof(ctx));
213         ctx.zoom = 1;
214
215         if (SDL_Init(SDL_INIT_VIDEO) != 0) {
216                 fprintf(stderr, "Unable to initialize SDL: %s\n",
217                         SDL_GetError());
218
219                 return 1;
220         }
221         atexit(SDL_Quit);
222
223         ctx.screen = SDL_SetVideoMode(xres, yres, 32, flags);
224         if (ctx.screen == NULL) {
225                 fprintf(stderr, "Unable to set video mode: %s\n",
226                         SDL_GetError());
227                 return 2;
228         }
229
230         SDL_WM_SetCaption(argv[0], NULL);
231
232         loop(&ctx);
233
234         return 0;
235 }