view src/App.cpp @ 4:df02a7de7fe2

I think I've got the Abstract Factory code working pretty robustly now, though I still need to make the factory singletons thread-safe
author Eris Caffee <discordia@eldalin.com>
date Tue, 26 Oct 2010 02:03:47 -0500
parents 0f4ad525f49a
children d691ce98f406
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 <iomanip>
13 #include <iostream>
14 #include <sstream>
15 #include <typeinfo>
16 #include <vector>
18 #if defined(WIN32)
19 #include <cstdlib>
20 #include <windows.h>
21 #include <Shlwapi.h>
23 #else
24 #include <cctype>
25 #include <cstring>
26 #include <cstdio>
28 #include <dirent.h>
29 #endif
32 #include "SDL.h"
34 #include "App.h"
36 #include "Options.h"
37 #include "Mandelbrot.h"
38 #include "Julia.h"
42 std::vector<App::view *> App::old_views;
44 ////////////////////////////////////////////////////////////////////////////////
46 App::App() :
47 running (true),
48 redraw (false),
49 surf_display (NULL),
50 surf_selection (NULL),
51 surf_fractal (NULL),
52 selection_x (-1),
53 selection_y (-1),
54 selection_width (-1),
55 setting_zoom (false),
56 can_set_julia_k (false),
57 setting_julia_k (false)
58 {
59 }
62 ////////////////////////////////////////////////////////////////////////////////
63 App::~App()
64 {
65 old_views.clear();
66 }
69 ////////////////////////////////////////////////////////////////////////////////
71 int App::run()
72 {
73 if (init() == false)
74 return 1;
76 SDL_Event event;
78 while (running)
79 {
80 SDL_WaitEvent(&event);
81 do
82 {
83 on_event(&event);
84 }
85 while (SDL_PollEvent(&event));
87 update();
89 if (redraw)
90 render();
91 }
93 cleanup();
95 return 0;
96 }
99 ////////////////////////////////////////////////////////////////////////////////
100 bool App::init()
101 {
102 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
103 {
104 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
105 return false;
106 }
108 if ((surf_display = SDL_SetVideoMode(Options::width,
109 Options::height,
110 Options::bpp,
111 SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)
112 {
113 std::cerr << "SDL_SetVideoMode() failed: "
114 << SDL_GetError() << std::endl;
115 return false;
116 }
118 SDL_EnableKeyRepeat(500, 100);
121 if (!create_new_surface(surf_selection)) { return false; }
122 if (!create_new_surface(surf_fractal)) { return false; }
125 std::cerr << "Available fractal types are" << std::endl;
126 std::map <std::string, FractalCreator *> mymap = get_Fractal_map();
127 std::map <std::string, FractalCreator *>::iterator it;
128 for ( it=mymap.begin() ; it != mymap.end(); it++ )
129 std::cerr << " " << (*it).first << std::endl;
131 FractalCreator * fractal_creator =
132 get_Fractal_map()[typeid(Mandelbrot).name()];
133 if (!fractal_creator)
134 {
135 std::cerr << "Mandelbrot set not available!" << std::endl;
136 return false;
137 }
138 fractal = fractal_creator->create();
139 fractal->init(surf_fractal);
140 redraw = true;
142 can_set_julia_k = true;
145 set_caption();
147 SDL_TimerID timer_id = SDL_AddTimer(10, App::timer_callback, NULL);
149 return true;
150 }
153 ////////////////////////////////////////////////////////////////////////////////
155 void App::update()
156 {
157 }
160 ////////////////////////////////////////////////////////////////////////////////
162 void App::render()
163 {
164 if (redraw)
165 {
166 // Start with a clean slate.
167 SDL_FillRect(surf_display, &surf_display->clip_rect,
168 SDL_MapRGB(surf_display->format, 0, 0, 255));
170 fractal->calc_set();
171 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
173 // draw a selection box
174 if (setting_zoom)
175 {
176 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
177 }
179 // Show it to the user.
180 SDL_Flip(surf_display);
182 redraw = false;
183 }
184 }
187 ////////////////////////////////////////////////////////////////////////////////
189 void App::cleanup()
190 {
192 if (surf_selection)
193 {
194 SDL_FreeSurface(surf_selection);
195 surf_selection = NULL;
196 }
197 if (surf_display)
198 {
199 SDL_FreeSurface(surf_display);
200 surf_display = NULL;
201 }
203 SDL_Quit();
204 }
207 ////////////////////////////////////////////////////////////////////////////////
209 void App::on_event(SDL_Event * event)
210 {
211 EventHandler::on_event(event);
212 }
215 ////////////////////////////////////////////////////////////////////////////////
217 void App::on_KeyDown(SDLKey sym, SDLMod mod, Uint16 unicode)
218 {
219 switch (sym)
220 {
221 case SDLK_ESCAPE:
222 case SDLK_q:
223 SDL_Event event;
224 event.type = SDL_QUIT;
225 SDL_PushEvent(&event);
226 break;
228 case SDLK_b:
229 restore_view();
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 save_view();
252 delete fractal;
253 FractalCreator * fractal_creator =
254 get_Fractal_map()[typeid(Mandelbrot).name()];
255 if (!fractal_creator)
256 {
257 std::cerr << "Mandelbrot set not available!" << std::endl;
258 SDL_Event event;
259 event.type = SDL_QUIT;
260 SDL_PushEvent(&event);
261 break;
262 }
263 fractal = fractal_creator->create();
264 fractal->init(surf_fractal);
265 fractal->set_calc_needed();
266 redraw = true;
267 set_caption();
268 can_set_julia_k = true;
269 }
270 break;
271 case SDLK_j:
272 if (fractal)
273 {
274 FractalCreator * fractal_creator =
275 get_Fractal_map()[typeid(Julia).name()];
276 if (!fractal_creator)
277 {
278 std::cerr << "Julia set not available!" << std::endl;
279 }
280 else
281 {
282 save_view();
283 delete fractal;
284 fractal = fractal_creator->create();
285 fractal->init(surf_fractal);
286 fractal->set_calc_needed();
287 redraw = true;
288 set_caption();
289 can_set_julia_k = false;
290 }
291 }
292 break;
294 case SDLK_k:
295 if (can_set_julia_k)
296 {
297 if (setting_julia_k) setting_julia_k = false;
298 else setting_julia_k = true;
299 }
300 break;
302 default:
303 break;
304 }
305 }
308 ////////////////////////////////////////////////////////////////////////////////
309 void App::on_KeyUp(SDLKey sym, SDLMod mod, Uint16 unicode)
310 {
311 switch (sym)
312 {
313 case SDLK_UP:
314 fractal->set_calc_needed();
315 redraw = true;
316 break;
317 case SDLK_DOWN:
318 fractal->set_calc_needed();
319 redraw = true;
320 break;
321 }
322 }
325 ////////////////////////////////////////////////////////////////////////////////
327 void App::on_Exit()
328 {
329 running = false;
330 }
333 ////////////////////////////////////////////////////////////////////////////////
335 void App::on_LButtonDown(int mx, int my)
336 {
337 if (setting_julia_k)
338 {
339 set_julia_k(mx, my);
340 }
341 else
342 {
343 selection_x = mx;
344 selection_y = my;
345 setting_zoom = true;
346 }
347 }
350 ////////////////////////////////////////////////////////////////////////////////
352 void App::on_LButtonUp(int mx, int my)
353 {
354 if (setting_zoom)
355 {
356 if ( (mx <= 0) ||
357 (mx >= surf_display->w - 1) ||
358 (my <= 0) ||
359 (my >= surf_display->h - 1))
360 {
361 // Selection cancelled
362 setting_zoom = false;
363 return;
364 }
366 selection_width = mouse_move_width(mx, my);
368 // calculate new min/max re/im
369 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
370 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
372 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
373 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
374 double new_size = selection_width * Re_scale;
377 save_view();
379 fractal->set_re_min(new_re_min);
380 fractal->set_im_max(new_im_max);
381 fractal->set_size(new_size);
382 fractal->set_calc_needed();
383 redraw = true;
385 set_caption();
387 setting_zoom = false;
388 }
389 }
392 ////////////////////////////////////////////////////////////////////////////////
394 void App::on_RButtonDown(int mx, int my)
395 {
396 if (can_set_julia_k)
397 {
398 set_julia_k(mx, my);
399 }
400 }
403 ////////////////////////////////////////////////////////////////////////////////
404 void App::on_MouseMove(int mx, int my,
405 int relx, int rely,
406 bool left, bool right, bool middle)
407 {
408 if (setting_zoom)
409 {
410 Uint32 clear = SDL_MapRGBA(surf_selection->format,
411 0, 0, 0, SDL_ALPHA_TRANSPARENT);
412 Uint32 white = SDL_MapRGBA(surf_selection->format,
413 255, 255, 255, SDL_ALPHA_OPAQUE);
415 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
417 SDL_Rect rect;
418 rect.x = selection_x;
419 rect.y = selection_y;
420 int w = mouse_move_width(mx, my);
421 rect.w = rect.h = w;
423 SDL_FillRect(surf_selection, &rect, white);
425 rect.x = selection_x + 1;
426 rect.y = selection_y + 1;
427 rect.w = rect.h = w - 2;
429 SDL_FillRect(surf_selection, &rect, clear);
431 redraw = true;
432 }
433 }
436 ////////////////////////////////////////////////////////////////////////////////
437 void App::on_Resize(int w, int h)
438 {
439 redraw = true;
440 }
443 ////////////////////////////////////////////////////////////////////////////////
444 void App::on_Expose()
445 {
446 redraw = true;
447 }
450 ////////////////////////////////////////////////////////////////////////////////
451 int App::mouse_move_width(int mx, int my)
452 {
453 int move_width = (mx - selection_x);
454 if (mx > selection_x)
455 {
456 move_width *= -1;
457 }
459 int tmp_i = (my - selection_y);
460 if (my < selection_y)
461 {
462 tmp_i *= -1;
463 }
465 if (tmp_i > move_width)
466 move_width = tmp_i;
468 return move_width;
469 }
472 ////////////////////////////////////////////////////////////////////////////////
473 bool App::create_new_surface(SDL_Surface * &surface)
474 {
475 if (surface)
476 {
477 SDL_FreeSurface(surface);
478 surface = NULL;
479 }
481 SDL_Surface * tmp;
482 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
483 Options::width, Options::height,
484 Options::bpp, 0, 0, 0, 0))
485 == NULL)
486 {
487 std::cerr << "SDL_CreateRGBSurface() failed in "
488 << "App::create_new_surface(): "
489 << SDL_GetError() << std::endl;
490 return false;
491 }
493 surface = SDL_DisplayFormatAlpha(tmp);
494 SDL_FreeSurface(tmp);
496 return true;
497 }
500 ////////////////////////////////////////////////////////////////////////////////
501 void App::set_julia_k(int x, int y)
502 {
503 save_view();
505 double k[2];
506 k[0]= fractal->get_re_min() + x *
507 (fractal->get_size())/(surf_fractal->w - 1);
508 k[1] = fractal->get_im_max() - y *
509 (fractal->get_size())/(surf_fractal->h - 1);
511 FractalCreator * fractal_creator = get_Fractal_map()[typeid(Julia).name()];
512 if (!fractal_creator)
513 {
514 std::cerr << "Julia set not available!" << std::endl;
515 return;
516 }
517 else
518 {
519 delete fractal;
520 fractal = fractal_creator->create();
521 fractal->init(surf_fractal);
522 fractal->set_option("k", k);
523 fractal->set_calc_needed();
524 redraw = true;
526 set_caption();
527 setting_julia_k = false;
528 }
529 }
532 ////////////////////////////////////////////////////////////////////////////////
534 void App::restore_view()
535 {
536 if (old_views.size() > 0)
537 {
538 view * v = old_views.back();
539 old_views.pop_back();
541 if (v->type.compare(fractal->get_fractal_name()) != 0)
542 {
543 delete fractal;
544 FractalCreator * fractal_creator = get_Fractal_map()[v->type];
545 if (!fractal_creator)
546 {
547 std::cerr << "Cannot restore old view: unknown Fractal type "
548 << v->type << std::endl;
549 SDL_Event event;
550 event.type = SDL_QUIT;
551 SDL_PushEvent(&event);
552 return;
553 }
554 fractal = fractal_creator->create();
555 fractal->init(surf_fractal);
556 }
558 fractal->set_re_min(v->re_min);
559 fractal->set_im_max(v->im_max);
560 fractal->set_size(v->size);
561 fractal->set_max_iter(v->max_iter);
562 fractal->set_calc_needed();
564 std::cerr << "Restored view:" << std::endl
565 << " type " << v->type << std::endl
566 << " re_min " << v->re_min << std::endl
567 << " im_max " << v->im_max << std::endl
568 << " size " << v->size << std::endl
569 << " max_iter " << v->max_iter << std::endl
570 << std::endl;
571 delete v;
572 }
573 }
576 ////////////////////////////////////////////////////////////////////////////////
577 void App::save_view()
578 {
579 view * v = new view;
580 v->type = typeid(*fractal).name();
581 v->re_min = fractal->get_re_min();
582 v->im_max = fractal->get_im_max();
583 v->size = fractal->get_size();
584 v->max_iter = fractal->get_max_iter();
585 old_views.push_back(v);
587 std::cerr << "Saved view:" << std::endl
588 << " type " << v->type << std::endl
589 << " re_min " << v->re_min << std::endl
590 << " im_max " << v->im_max << std::endl
591 << " size " << v->size << std::endl
592 << " max_iter " << v->max_iter << std::endl
593 << std::endl;
594 }
597 ////////////////////////////////////////////////////////////////////////////////
598 void App::set_caption()
599 {
600 std::stringstream ss;
601 ss << fractal->get_display_name() << " / "
602 << "Real: " << fractal->get_re_min()
603 << " to "<< fractal->get_re_min() + fractal->get_size()
604 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
605 << " to " << fractal->get_im_max()
606 << " / Max iterations: " << fractal->get_max_iter();
607 SDL_WM_SetCaption(ss.str().c_str(), NULL);
608 }
611 ////////////////////////////////////////////////////////////////////////////////
612 bool App::save_image()
613 {
614 int i = App::get_next_file_num();
615 std::stringstream name;
616 name << "mandelbrot-" << std::setw(3) << std::setfill('0') << i << ".bmp";
618 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
619 {
620 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
621 return false;
622 }
624 std::cerr << "Saved image as " << name.str() << std::endl;
626 return true;
627 }
630 ////////////////////////////////////////////////////////////////////////////////
631 Uint32 App::timer_callback(Uint32 interval, void *param)
632 {
633 SDL_Event e;
635 e.type = SDL_USEREVENT;
636 e.user.code = 0;
637 e.user.data1 = NULL;
638 e.user.data2 = NULL;
639 SDL_PushEvent(& e);
641 return interval;
642 }
645 #if defined(WIN32)
646 ////////////////////////////////////////////////////////////////////////////////
647 //Code for Windows
648 int App::get_next_file_num()
649 {
650 static int i = 0;
652 if (i == 0)
653 {
654 WIN32_FIND_DATA ffd;
655 HANDLE hFind = INVALID_HANDLE_VALUE;
657 char * file_spec = "mandelbrot-???.bmp";
658 hFind = FindFirstFile(file_spec, &ffd);
659 if (hFind != INVALID_HANDLE_VALUE)
660 {
661 std::string s;
662 std::stringstream ss;
663 do
664 {
665 std::cerr << ffd.cFileName << std::endl;
666 ss << ffd.cFileName;
667 if (ss.str().size() == 18)
668 {
669 s = ss.str().substr(0, 11);
670 if ( StrCmpI(s.c_str(), "mandelbrot-") == 0 )
671 {
672 s = ss.str().substr(14, 4);
673 if (StrCmpI(s.c_str(), ".bmp") == 0)
674 {
675 s = ss.str().substr(11, 3);
676 int j = atoi(s.c_str());
677 if (j > i) i = j;
678 }
679 }
680 }
681 }
682 while (FindNextFile(hFind, &ffd) != 0);
683 }
684 }
686 i++;
687 return i;
688 }
692 #else
693 ////////////////////////////////////////////////////////////////////////////////
694 // Code for Linux
696 int App::scandir_filter(const struct dirent * d)
697 {
698 if (memcmp(d->d_name, "mandelbrot-", 11) != 0) return 0;
699 if (memcmp(d->d_name+14, ".bmp", 4) != 0) return 0;
700 if (isdigit(d->d_name[11])
701 && isdigit(d->d_name[12])
702 && isdigit(d->d_name[13]))
703 return 1;
704 return 0;
705 }
708 int App::get_next_file_num()
709 {
710 static int i = 0;
712 if (i == 0)
713 {
714 struct dirent ** file_list;
715 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
716 if (num_files != 0)
717 sscanf(file_list[num_files-1]->d_name, "mandelbrot-%03d.bmp", &i);
718 }
719 i++;
721 return i;
722 }
724 #endif