view src/App.cpp @ 2:0684158d38a8

Before cleanup. Still has experimental code in it.
author Eris Caffee <discordia@eldalin.com>
date Fri, 22 Oct 2010 02:21:52 -0500
parents 455406f5f021
children 0f4ad525f49a
line source
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) 2010 Sarah Eris Horsley Caffee
4 //
5 // mandelbrot - A simple Mandelbrot Set viewer.
6 //
7 // App.cpp
8 // Application class implementation
9 //
10 ////////////////////////////////////////////////////////////////////////////////
12 #include <cstdlib>
13 #include <cstring>
14 #include <iostream>
15 #include <sstream>
16 #include <vector>
18 #include <dirent.h>
20 #include "png.h"
21 #include "SDL.h"
23 #include "App.h"
24 #include "Options.h"
26 #include "Mandelbrot.h"
27 #include "Julia.h"
30 #define USE_SDL_WAITEVENT
32 std::vector<App::view *> App::old_views;
34 ////////////////////////////////////////////////////////////////////////////////
36 App::App() :
37 running (true),
38 redraw (false),
39 surf_display (NULL),
40 surf_selection (NULL),
41 surf_fractal (NULL),
42 selection_x (-1),
43 selection_y (-1),
44 selection_width (-1),
45 setting_zoom (false),
46 can_set_julia_k (false),
47 setting_julia_k (false)
48 {
49 }
51 ////////////////////////////////////////////////////////////////////////////////
52 App::~App()
53 {
54 if (old_views.size() > 0)
55 {
56 view * v = old_views.back();
57 old_views.pop_back();
58 delete v;
59 }
60 }
62 ////////////////////////////////////////////////////////////////////////////////
64 int App::run()
65 {
66 if (init() == false)
67 return 1;
69 SDL_Event event;
71 while (running)
72 {
73 #ifdef USE_SDL_WAITEVENT
74 SDL_WaitEvent(&event);
75 do
76 {
77 on_event(&event);
78 }
79 while (SDL_PollEvent(&event));
80 #else
81 while (wait_event_timeout(&event, 30) == 1)
82 {
83 on_event(&event);
84 }
85 #endif
87 update();
89 if (redraw)
90 render();
91 }
93 cleanup();
95 return 0;
96 }
98 ////////////////////////////////////////////////////////////////////////////////
99 // Sample callback from
100 // http://people.freedesktop.org/~idr/OpenGL_tutorials/01-SDL-intro.html
102 #ifdef USE_SDL_WAITEVENT
103 Uint32 App::timer_callback(Uint32 interval, void *param)
104 {
105 SDL_Event e;
107 e.type = SDL_USEREVENT;
108 e.user.code = 0;
109 e.user.data1 = NULL;
110 e.user.data2 = NULL;
111 SDL_PushEvent(& e);
113 return interval;
114 }
115 #endif
117 ////////////////////////////////////////////////////////////////////////////////
118 bool App::init()
119 {
120 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
121 {
122 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
123 return false;
124 }
126 if ((surf_display = SDL_SetVideoMode(Options::width,
127 Options::height,
128 Options::bpp,
129 SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)
130 {
131 std::cerr << "SDL_SetVideoMode() failed: "
132 << SDL_GetError() << std::endl;
133 return false;
134 }
136 SDL_EnableKeyRepeat(500, 100);
139 if (!create_new_surface(surf_selection)) { return false; }
140 if (!create_new_surface(surf_fractal)) { return false; }
142 fractal = new Mandelbrot();
143 fractal->init(surf_fractal);
144 can_set_julia_k = true;
145 redraw = true;
147 set_caption();
149 #ifdef USE_SDL_WAITEVENT
150 SDL_TimerID timer_id = SDL_AddTimer(10, App::timer_callback, NULL);
151 #endif
153 return true;
154 }
156 ////////////////////////////////////////////////////////////////////////////////
158 void App::update()
159 {
160 }
162 ////////////////////////////////////////////////////////////////////////////////
164 void App::render()
165 {
166 if (redraw)
167 {
168 // Start with a clean slate.
169 SDL_FillRect(surf_display, &surf_display->clip_rect,
170 SDL_MapRGB(surf_display->format, 0, 0, 255));
172 fractal->calc_set();
173 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
175 // draw a selection box
176 if (setting_zoom)
177 {
178 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
179 }
181 // Show it to the user.
182 SDL_Flip(surf_display);
184 redraw = false;
185 }
186 }
188 ////////////////////////////////////////////////////////////////////////////////
190 void App::cleanup()
191 {
193 if (surf_selection)
194 {
195 SDL_FreeSurface(surf_selection);
196 surf_selection = NULL;
197 }
198 if (surf_display)
199 {
200 SDL_FreeSurface(surf_display);
201 surf_display = NULL;
202 }
204 SDL_Quit();
205 }
207 ////////////////////////////////////////////////////////////////////////////////
209 void App::on_event(SDL_Event * event)
210 {
211 EventHandler::on_event(event);
212 }
214 ////////////////////////////////////////////////////////////////////////////////
216 void App::on_KeyDown(SDLKey sym, SDLMod mod, Uint16 unicode)
217 {
218 switch (sym)
219 {
220 case SDLK_ESCAPE:
221 case SDLK_q:
222 SDL_Event event;
223 event.type = SDL_QUIT;
224 SDL_PushEvent(&event);
225 break;
227 case SDLK_b:
228 restore_view();
229 fractal->set_calc_needed();
230 redraw = true;
231 set_caption();
232 break;
234 case SDLK_s:
235 save_image();
236 break;
238 case SDLK_UP:
239 fractal->inc_max_iter();
240 set_caption();
241 break;
242 case SDLK_DOWN:
243 fractal->dec_max_iter();
244 set_caption();
245 break;
248 case SDLK_m:
249 if (fractal)
250 {
251 delete fractal;
252 fractal = new Mandelbrot();
253 fractal->init(surf_fractal);
254 fractal->set_calc_needed();
255 redraw = true;
256 set_caption();
257 can_set_julia_k = true;
258 }
259 break;
260 case SDLK_j:
261 if (fractal)
262 {
263 delete fractal;
264 fractal = new Julia();
265 fractal->init(surf_fractal);
266 fractal->set_calc_needed();
267 redraw = true;
268 set_caption();
269 can_set_julia_k = false;
270 }
271 break;
273 case SDLK_k:
274 if (can_set_julia_k)
275 {
276 if (setting_julia_k) setting_julia_k = false;
277 else setting_julia_k = true;
278 }
279 break;
281 default:
282 break;
283 }
284 }
287 ////////////////////////////////////////////////////////////////////////////////
288 void App::on_KeyUp(SDLKey sym, SDLMod mod, Uint16 unicode)
289 {
290 switch (sym)
291 {
292 case SDLK_UP:
293 fractal->set_calc_needed();
294 redraw = true;
295 break;
296 case SDLK_DOWN:
297 fractal->set_calc_needed();
298 redraw = true;
299 break;
300 }
301 }
304 ////////////////////////////////////////////////////////////////////////////////
306 void App::on_Exit()
307 {
308 running = false;
309 }
312 ////////////////////////////////////////////////////////////////////////////////
314 void App::on_LButtonDown(int mx, int my)
315 {
316 if (setting_julia_k)
317 {
318 double K_Re = fractal->get_re_min() + mx *
319 (fractal->get_size())/(Options::width-1);
320 double K_Im = fractal->get_im_max() - my *
321 (fractal->get_size())/(Options::height-1);
323 delete fractal;
324 fractal = new Julia();
325 fractal->init(surf_fractal);
326 ((Julia *)fractal)->set_k(K_Re, K_Im);
327 fractal->set_calc_needed();
328 redraw = true;
330 set_caption();
331 setting_julia_k = false;
332 }
333 else
334 {
335 selection_x = mx;
336 selection_y = my;
337 setting_zoom = true;
338 }
339 }
342 ////////////////////////////////////////////////////////////////////////////////
344 void App::on_LButtonUp(int mx, int my)
345 {
346 if (setting_zoom)
347 {
348 if ( (mx <= 0) ||
349 (mx >= Options::width - 1) ||
350 (my <= 0) ||
351 (my >= Options::height - 1))
352 {
353 // Selection cancelled
354 setting_zoom = false;
355 return;
356 }
358 selection_width = mouse_move_width(mx, my);
360 // calculate new min/max re/im
361 double Re_scale = (fractal->get_size())/(Options::width-1);
362 double Im_scale = (fractal->get_size())/(Options::height-1);
364 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
365 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
366 double new_size = selection_width * Re_scale;
369 view * v = new view;
370 v->re_min = fractal->get_re_min();
371 v->im_max = fractal->get_im_max();
372 v->size = fractal->get_size();
373 old_views.push_back(v);
375 fractal->set_re_min(new_re_min);
376 fractal->set_im_max(new_im_max);
377 fractal->set_size(new_size);
378 fractal->set_calc_needed();
379 redraw = true;
381 set_caption();
383 setting_zoom = false;
384 }
385 }
388 ////////////////////////////////////////////////////////////////////////////////
389 void App::on_MouseMove(int mx, int my,
390 int relx, int rely,
391 bool left, bool right, bool middle)
392 {
393 if (setting_zoom)
394 {
395 Uint32 clear = SDL_MapRGBA(surf_selection->format,
396 0, 0, 0, SDL_ALPHA_TRANSPARENT);
397 Uint32 white = SDL_MapRGBA(surf_selection->format,
398 255, 255, 255, SDL_ALPHA_OPAQUE);
400 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
402 SDL_Rect rect;
403 rect.x = selection_x;
404 rect.y = selection_y;
405 int w = mouse_move_width(mx, my);
406 rect.w = rect.h = w;
408 SDL_FillRect(surf_selection, &rect, white);
410 rect.x = selection_x + 1;
411 rect.y = selection_y + 1;
412 rect.w = rect.h = w - 2;
414 SDL_FillRect(surf_selection, &rect, clear);
416 redraw = true;
417 }
418 }
421 ////////////////////////////////////////////////////////////////////////////////
422 void App::on_Resize(int w, int h)
423 {
424 redraw = true;
425 }
428 ////////////////////////////////////////////////////////////////////////////////
429 void App::on_Expose()
430 {
431 redraw = true;
432 }
435 ////////////////////////////////////////////////////////////////////////////////
436 int App::mouse_move_width(int mx, int my)
437 {
438 int move_width = (mx - selection_x);
439 if (mx > selection_x)
440 {
441 move_width *= -1;
442 }
444 int tmp_i = (my - selection_y);
445 if (my < selection_y)
446 {
447 tmp_i *= -1;
448 }
450 if (tmp_i > move_width)
451 move_width = tmp_i;
453 return move_width;
454 }
457 ////////////////////////////////////////////////////////////////////////////////
458 bool App::create_new_surface(SDL_Surface * &surface)
459 {
460 if (surface)
461 {
462 SDL_FreeSurface(surface);
463 surface = NULL;
464 }
466 SDL_Surface * tmp;
467 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
468 Options::width,
469 Options::height, Options::bpp, 0, 0, 0, 0))
470 == NULL)
471 {
472 std::cerr << "SDL_CreateRGBSurface() failed in "
473 << "App::create_new_surface(): "
474 << SDL_GetError() << std::endl;
475 return false;
476 }
478 surface = SDL_DisplayFormatAlpha(tmp);
479 SDL_FreeSurface(tmp);
481 return true;
482 }
485 ////////////////////////////////////////////////////////////////////////////////
487 void App::restore_view()
488 {
489 if (old_views.size() > 0)
490 {
491 view * v = old_views.back();
492 old_views.pop_back();
493 fractal->set_re_min(v->re_min);
494 fractal->set_im_max(v->im_max);
495 fractal->set_size(v->size);
496 delete v;
497 }
498 }
501 ////////////////////////////////////////////////////////////////////////////////
502 void App::set_caption()
503 {
504 std::stringstream ss;
505 ss << fractal->get_name() << " / "
506 << "Real: " << fractal->get_re_min()
507 << " to "<< fractal->get_re_min() + fractal->get_size()
508 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
509 << " to " << fractal->get_im_max()
510 << " / Max iterations: " << fractal->get_max_iter();
511 SDL_WM_SetCaption(ss.str().c_str(), NULL);
512 }
515 ////////////////////////////////////////////////////////////////////////////////
516 // This file name generation is kuldgy. Need to learn how to do file i/o for
517 // real!
519 int scandir_filter(const struct dirent * d)
520 {
521 if (memcmp(d->d_name, "mandelbrot-", 11) != 0) return 0;
522 if (memcmp(d->d_name+14, ".bmp", 4) != 0) return 0;
523 if (isdigit(d->d_name[11])
524 && isdigit(d->d_name[12])
525 && isdigit(d->d_name[13]))
526 return 1;
527 return 0;
528 }
531 bool App::save_image()
532 {
533 static int i = 0;
534 char name[20];
536 if (i == 0)
537 {
538 struct dirent ** file_list;
539 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
540 if (num_files != 0)
541 sscanf(file_list[num_files-1]->d_name, "mandelbrot-%03d.bmp", &i);
542 }
543 i++;
544 sprintf(name, "mandelbrot-%03d.bmp", i);
546 FILE * file;
547 if ((file = fopen(name, "wb")) == NULL)
548 {
549 std::cerr << "fopen() failed in save_image() on file "
550 << name << " for saving." << std::endl;
551 return false;
552 }
554 if (SDL_SaveBMP(surf_display, name) != 0)
555 {
556 std::cerr << "Unable to save image file " << name << "" << std::endl;
557 return false;
558 }
560 return true;
561 }
564 ////////////////////////////////////////////////////////////////////////////////
565 #ifdef SAVE_AS_PNG
566 void png_user_error(png_structp png, png_const_charp s);
567 void png_user_warning(png_structp png, png_const_charp s);
569 void png_user_warning(png_structp png, png_const_charp s)
570 {
571 std::cerr << "libpng: warning " << (char *) s << std::endl;
572 }
575 ////////////////////////////////////////////////////////////////////////////////
576 void png_user_error(png_structp png, png_const_charp s)
577 {
578 std::cerr << "libpng: error " << (char *) s << std::endl;
579 }
582 ////////////////////////////////////////////////////////////////////////////////
584 bool App::save_image()
585 {
586 static int i = 0;
587 char name[20];
589 i++;
590 sprintf(name, "mandelbrot-%03d.png", i);
592 png_save_surface(name, surf_display);
594 FILE * file;
595 if ((file = fopen(name, "wb")) == NULL)
596 {
597 std::cerr << "fopen() failed in save_image() on file "
598 << name << " for saving." << std::endl;
599 return false;
600 }
604 png_structp png;
605 png_infop png_info;
607 if ((png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
608 png_user_error, png_user_warning)) == NULL)
609 {
610 std::cerr << "png_create_write_struct() failed in save_image()"
611 << std::endl;
612 fclose(file);
613 return false;
614 }
617 if ((png_info = png_create_info_struct(png)) == NULL)
618 {
619 std::cerr << "png_create_info_struct() failed in save_image()"
620 << std::endl;
621 fclose(file);
622 return false;
623 }
625 if (setjmp(png_jmpbuf(png)))
626 {
627 std::cerr << "png_jmpbuf() failed in save_image()" << std::endl;
628 png_destroy_write_struct(&png, &png_info);
629 fclose(file);
630 return false;
631 }
633 png_init_io(png, file);
634 int colortype = PNG_COLOR_MASK_COLOR;
635 png_set_IHDR(png, png_info,
636 surf_display->w, surf_display->h,
637 8, colortype, PNG_INTERLACE_NONE,
638 PNG_COMPRESSION_TYPE_DEFAULT,
639 PNG_FILTER_TYPE_DEFAULT);
641 png_write_info(png, png_info);
642 png_set_packing(png);
644 png_bytep * rows = (png_bytep *) malloc(sizeof(png_bytep)*surf_display->h);
645 for (int i = 0; i < surf_display->h; i++)
646 {
647 rows[i] = (png_bytep) surf_display->pixels +
648 i * surf_display->pitch;
649 }
651 png_write_image(png, rows);
652 png_write_end(png, png_info);
654 free(rows);
655 png_destroy_write_struct(&png, &png_info);
656 fclose(file);
658 return true;
659 }
660 #endif