initial import
[mandelbrot-c.git] / mandelbrot.c
CommitLineData
28dc4dc2
JW
1#include "SDL.h"
2#include <math.h>
3#include <unistd.h>
4#include <getopt.h>
5#include <ctype.h>
6#include <zlib.h>
7
8/* Algorithmic variables */
9int maxiters = 256;
10#define BAILOUT 2.0
11int xres = 600;
12int yres = 600;
13double xmin = -2, ymin = -2, xmax = 2, ymax = 2;
14
15/* Display variables */
16SDL_Surface* screen;
17SDL_Color palette[256],origpalette[256];
18int ppm = 0;
19int sdl = 1;
20int cycling = 0;
21
22int iterate(double x, double y)
23{
24 int i;
25 double cplxr, cplxi;
26 cplxr = y;
27 cplxi = x;
28 i = 0;
29 while ((i < maxiters) && (hypot(cplxr, cplxi) < BAILOUT))
30 {
31 double a=cplxr, b=cplxi, c=cplxr, d=cplxi;
32 i++;
33 cplxr = a*c-b*d+y;
34 cplxi = a*d+b*c+x;
35 }
36 return i;
37}
38
39void set_palette()
40{
41 if (sdl)
42 SDL_SetColors(screen, palette, 0, 256);
43}
44
45void cycle_colors()
46{
47 SDL_Color x;
48 int i;
49 x = palette[0];
50 for (i=0; i<255; i++)
51 palette[i] = palette[i+1];
52 palette[255] = x;
53 set_palette();
54}
55
56Uint32 cycle_colors_cbk(Uint32 interval)
57{
58 cycle_colors();
59 return interval;
60}
61
62void init_palette()
63{
64 int i;
65 for (i=0; i<256; i++)
66 {
67 palette[i].r = sin(((double)i)/256.0*2.0*M_PI*5.0f)*256.0;
68 palette[i].g = sin(((double)(i+64))/256.0*2.0*M_PI*4.0f)*256.0;
69 palette[i].b = sin(((double)(i+128))/256.0*2.0*M_PI*3.0f)*256.0;
70 origpalette[i] = palette[i];
71 }
72}
73
74int videoflags = SDL_SWSURFACE | SDL_RESIZABLE;
75
76void start_sdl()
77{
78 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
79 {
80 printf("SDL init failed: %s\n", SDL_GetError());
81 exit(1);
82 }
83 atexit(SDL_Quit);
84
85 screen = SDL_SetVideoMode(xres, yres, 8, videoflags);
86 if (!screen)
87 {
88 printf("SDL video init failed: %s\n", SDL_GetError());
89 exit(1);
90 }
91
92 SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
93}
94
95double pxltofltx(int x)
96{
97 return ((double)x)*((double)(xmax-xmin))/((double)xres)+xmin;
98}
99double pxltoflty(int y)
100{
101 return ((double)y)*((double)(ymax-ymin))/((double)yres)+ymin;
102}
103
104void render()
105{
106 int x,y;
107 Uint8* buffer;
108 if (ppm)
109 printf("P6\n%d %d\n255\n", xres, yres);
110 if (sdl)
111 {
112 cycling = 0;
113 SDL_WM_SetCaption("Mandelbrot Set [working]", "Mandelbrot");
114 SDL_SetTimer(0, NULL);
115 SDL_LockSurface(screen);
116 buffer = (Uint8*)screen->pixels;
117 }
118 for (y=0;y<yres;y++)
119 {
120 for (x=0;x<xres;x++)
121 {
122 int val = iterate(pxltofltx(x), pxltoflty(y));
123 if (ppm)
124 printf("%c%c%c", palette[val].r, palette[val].g, palette[val].b);
125 if (sdl)
126 buffer[x] = val;
127 }
128 if (sdl)
129 buffer += screen->pitch;
130 if (!(y%10))
131 {
132 if (sdl)
133 {
134 for (x=0;x<xres;x++)
135 buffer[x]=0;
136 SDL_UpdateRect(screen,0,0,0,0);
137 } else
138 fprintf(stderr, "%d\n", y);
139 }
140 }
141 if (sdl)
142 {
143 SDL_UpdateRect(screen, 0, 0, 0, 0);
144 SDL_UnlockSurface(screen);
145 SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
146 }
147}
148
149int load_palette(char* name)
150{
151 gzFile file;
152 char c[1024];
153 int idx;
154 file = gzopen(name, "rb");
155 if (file == NULL)
156 {
157 perror("gzopen");
158 return 0;
159 }
160 for (idx = 0; idx < 256; idx++)
161 {
162 char* cidx = c;
163 int accum;
164 int element;
165 gzgets(file, c, 1024);
166
167 if ((c[0] == '\r') || (c[0] == '\n'))
168 {
169 idx--;
170 continue;
171 }
172 if (c[0] == 0x1A /* EOF */)
173 {
174 gzclose(file);
175 return 1;
176 }
177 for (element = 0; element < 3; element++)
178 {
179 accum = 0;
180 while (isblank(*cidx))
181 cidx++;
182 if (!isdigit(*cidx))
183 {
184 fprintf(stderr, "%s: palette file format fault on color %d -- expected digit, got `%c'\n", name, idx, *cidx);
185 gzclose(file);
186 return;
187 }
188 while (isdigit(*cidx))
189 {
190 accum *= 10;
191 accum += *cidx - '0';
192 cidx++;
193 }
194 switch(element)
195 {
196 case 0: origpalette[idx].r = palette[idx].r = accum; break;
197 case 1: origpalette[idx].g = palette[idx].g = accum; break;
198 case 2: origpalette[idx].b = palette[idx].b = accum; break;
199 default: abort();
200 }
201 }
202 }
203 gzclose(file);
204 set_palette();
205 return 1;
206}
207
208void usage(char* me)
209{
210 printf(
211 "Usage: %s [OPTION]...\n"
212 "Renders the Mandelbrot set.\n"
213 "\n"
214 "Mandatory arguments to long options are mandatory for short options too.\n"
215 " -x, --width=PIXELS sets the output width\n"
216 " -y, --height=PIXELS sets the output height\n"
217 " -m, --maxiters=ITERATIONS sets the maximum number of iterations\n"
218 " -s, --sdl enables SDL as an output device (default)\n"
219 " -S, --no-sdl disables SDL as an output device (implies -q)\n"
220 " -p, --ppm enables .ppm to stdout as an output device (implies -q)\n"
221 " -P, --no-ppm disables .ppm as an output device (default)\n"
222 " -q, --quit-immediately exits immediately after rendering\n"
223 " -l, --location=XMIN,XMAX,YMIN,YMAX\n"
224 " sets the starting location\n"
225 " -t, --palette=FILENAME sets the palette\n"
226 " --help shows this help\n"
227 "\n"
228 "Written by Joshua Wise <joshua@joshuawise.com>.\n"
229 , me
230 );
231 exit(2);
232}
233
234struct option longopts[] = {
235 {"x", required_argument, NULL, 'x'},
236 {"width", required_argument, NULL, 'x'},
237 {"y", required_argument, NULL, 'y'},
238 {"height", required_argument, NULL, 'y'},
239 {"maxiters", required_argument, NULL, 'm'},
240 {"sdl", no_argument, NULL, 's'},
241 {"ppm", no_argument, NULL, 'p'},
242 {"no-sdl", no_argument, NULL, 'S'},
243 {"no-ppm", no_argument, NULL, 'P'},
244 {"help", no_argument, NULL, 'h'},
245 {"location", required_argument, NULL, 'l'},
246 {"palette", required_argument, NULL, 't'},
247 {"quit-immediately", no_argument, NULL, 'q'},
248 {0,0,0,0}
249};
250
251int main(int argc, char** argv)
252{
253 SDL_Event event;
254 int startx, starty;
255 int buttondown = 0;
256 int dragged = 0;
257 int arg;
258 int custompalette = 0;
259 int quitimmediately = 0;
260 double yctr, ysz;
261
262 while ((arg = getopt_long(argc, argv, "x:y:m:l:t:sSpPq", longopts, NULL)) != -1)
263 {
264 switch(arg)
265 {
266 case 'x':
267 xres = atoi(optarg);
268 if (!xres)
269 {
270 printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
271 printf("Try `%s --help' for more information.\n", argv[0]);
272 exit(2);
273 }
274 break;
275 case 'y':
276 yres = atoi(optarg);
277 if (!yres)
278 {
279 printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
280 printf("Try `%s --help' for more information.\n", argv[0]);
281 exit(2);
282 }
283 break;
284 case 'm':
285 maxiters = atoi(optarg);
286 if (!maxiters)
287 {
288 printf("%s: option `%c' requires an integer argument\n", argv[0], arg);
289 printf("Try `%s --help' for more information.\n", argv[0]);
290 exit(2);
291 }
292 break;
293 case 's':
294 sdl = 1;
295 break;
296 case 'p':
297 ppm = 1;
298 quitimmediately = 1;
299 break;
300 case 'S':
301 sdl = 0;
302 break;
303 case 'P':
304 ppm = 0;
305 break;
306 case 'h':
307 usage(argv[0]);
308 break;
309 case 'l':
310 {
311 float f1,f2,f3,f4;
312 if (sscanf(optarg, "%f,%f,%f,%f", &f1, &f2, &f3, &f4) != 4)
313 {
314 printf("%s: incorrect syntax for option `%c'\n", argv[0], arg);
315 printf("Try %s --help' for more information.\n", argv[0]);
316 exit(2);
317 }
318 xmin = f1; xmax = f2; ymin = f3; ymax = f4;
319 }
320 break;
321 case 't':
322 custompalette = load_palette(optarg);
323 break;
324 case 'q':
325 quitimmediately = 1;
326 break;
327 case '?':
328 case ':':
329 printf("Try `%s --help' for more information.\n", argv[0]);
330 exit(2);
331 break;
332 default:
333 printf("Oh God I am not good with computers how did this get here?\n");
334 exit(127);
335 }
336 }
337
338 if (sdl)
339 start_sdl();
340 yctr = ymin + (ymax - ymin) / 2.0;
341 // get y in proportion
342 ysz = (xmax-xmin)*((double)yres)/((double)xres);
343 ymax = yctr+ysz/2; ymin = yctr-ysz/2;
344
345 if (!custompalette)
346 init_palette();
347 set_palette();
348 render();
349 if (quitimmediately)
350 exit(0);
351 while ( SDL_WaitEvent(&event) ) {
352 switch (event.type) {
353 case SDL_KEYDOWN:
354 if (event.key.keysym.sym == SDLK_c)
355 {
356 if (!cycling)
357 {
358 SDL_WM_SetCaption("Mandelbrot Set [color cycling]", "Mandelbrot");
359 SDL_SetTimer(50, cycle_colors_cbk);
360 } else {
361 SDL_WM_SetCaption("Mandelbrot Set", "Mandelbrot");
362 SDL_SetTimer(0, NULL);
363 }
364 cycling = !cycling;
365 } else if (event.key.keysym.sym == SDLK_r) {
366 int i;
367 for (i=0;i<256;i++)
368 {
369 palette[i].r = origpalette[i].r;
370 palette[i].g = origpalette[i].g;
371 palette[i].b = origpalette[i].b;
372 }
373 set_palette();
374 } else if (event.key.keysym.sym == SDLK_ESCAPE ||
375 event.key.keysym.sym == SDLK_q) {
376 exit(0);
377 } else if (event.key.keysym.sym == SDLK_p) {
378 ppm = 1;
379 render();
380 ppm = 0;
381 } else if (event.key.keysym.sym == SDLK_RETURN) {
382 render();
383 } else if (event.key.keysym.sym == SDLK_i) {
384 printf("mandelbrot by Joshua Wise\n");
385 printf("2005-06-19, Little Cayman\n");
386 printf("\n");
387 printf("Current settings:\n");
388 printf(" xmin: %f, xmax: %f\n", xmin, xmax);
389 printf(" ymin: %f, ymax: %f\n", ymin, ymax);
390 printf(" maxiters: %d, bailout: %f\n", maxiters, BAILOUT);
391 printf(" xres: %d, yres: %d\n", xres, yres);
392 printf("Recreate with:\n");
393 printf(" %s -x%d -y%d -l%f,%f,%f,%f -m%d\n", argv[0], xres,yres,xmin,xmax,ymin,ymax,maxiters);
394 }
395 break;
396 case SDL_MOUSEBUTTONDOWN:
397 if (event.button.button == 1)
398 buttondown = 1;
399 startx = event.button.x;
400 starty = event.button.y;
401 break;
402 case SDL_MOUSEMOTION:
403 if (buttondown)
404 dragged = 1;
405 break;
406 case SDL_MOUSEBUTTONUP:
407 if (event.button.button == 1)
408 buttondown = 0;
409 if (!dragged)
410 {
411 if (event.button.button == 1 || event.button.button == 3)
412 {
413 double xsz = xmax-xmin, ysz = ymax-ymin;
414 double x,y;
415 double scalar;
416 if (event.button.button == 1)
417 scalar = 1.0/2.0;
418 else
419 scalar = 2.0/1.0;
420 xsz *= scalar;
421 ysz *= scalar;
422 x = pxltofltx(event.button.x);
423 y = pxltoflty(event.button.y);
424 xmax = x + xsz / 2;
425 xmin = x - xsz / 2;
426 ymax = y + ysz / 2;
427 ymin = y - ysz / 2;
428 render();
429 } else if (event.button.button == 2) {
430 xmax = ymax = 2;
431 xmin = ymin = -2;
432 render();
433 }
434 dragged = 0;
435 break;
436 } else {
437 double newmaxx, newminx, newmaxy, newminy;
438 if (event.button.x > startx)
439 {
440 newmaxx = pxltofltx(event.button.x);
441 newminx = pxltofltx(startx);
442 } else {
443 newminx = pxltofltx(event.button.x);
444 newmaxx = pxltofltx(startx);
445 }
446 if (event.button.y > starty)
447 {
448 newmaxy = pxltoflty(event.button.y);
449 newminy = pxltoflty(starty);
450 } else {
451 newminy = pxltoflty(event.button.y);
452 newmaxy = pxltoflty(starty);
453 }
454 yctr = newminy + (newmaxy - newminy) / 2.0;
455 // get y in proportion
456 ysz = (newmaxx-newminx)*((double)yres)/((double)xres);
457 ymax = yctr+ysz/2; ymin = yctr-ysz/2;
458 xmax = newmaxx; xmin = newminx;
459 render();
460 dragged = 0;
461 break;
462 }
463 case SDL_VIDEORESIZE:
464 {
465 screen = SDL_SetVideoMode(event.resize.w, event.resize.h,
466 screen->format->BitsPerPixel,
467 videoflags);
468 if (!screen)
469 {
470 fprintf(stderr, "Couldn't resize screen\n");
471 exit(1);
472 }
473 SDL_SetColors(screen, palette, 0, 256);
474 xres = event.resize.w;
475 yres = event.resize.h;
476 yctr = ymin + (ymax - ymin) / 2.0;
477 // get y in proportion
478 ysz = (xmax-xmin)*((double)yres)/((double)xres);
479 ymax = yctr+ysz/2; ymin = yctr-ysz/2;
480 render();
481 break;
482 }
483 case SDL_QUIT:
484 return;
485 }
486 }
487 return 1;
488}
This page took 0.056685 seconds and 4 git commands to generate.