initial import
[mandelbrot-c.git] / mandelbrot.c
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 */
9 int maxiters = 256;
10 #define BAILOUT 2.0
11 int xres = 600;
12 int yres = 600;
13 double xmin = -2, ymin = -2, xmax = 2, ymax = 2;
14
15 /* Display variables */
16 SDL_Surface* screen;
17 SDL_Color palette[256],origpalette[256];
18 int ppm = 0;
19 int sdl = 1;
20 int cycling = 0;
21
22 int 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
39 void set_palette()
40 {
41         if (sdl)
42                 SDL_SetColors(screen, palette, 0, 256);
43 }
44
45 void 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
56 Uint32 cycle_colors_cbk(Uint32 interval)
57 {
58         cycle_colors();
59         return interval;
60 }
61
62 void 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
74 int videoflags = SDL_SWSURFACE | SDL_RESIZABLE;
75
76 void 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
95 double pxltofltx(int x)
96 {
97         return ((double)x)*((double)(xmax-xmin))/((double)xres)+xmin;
98 }
99 double pxltoflty(int y)
100 {
101         return ((double)y)*((double)(ymax-ymin))/((double)yres)+ymin;
102 }
103
104 void 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
149 int 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
208 void 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
234 struct 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
251 int 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.040364 seconds and 4 git commands to generate.