]>
Commit | Line | Data |
---|---|---|
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 */ | |
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 | } |