view src/App.cpp @ 3:0f4ad525f49a

Lot's of cleanup. Applied AbstractFactory to class Fractal and derivatives. Decoupled most objects. Removed remaining standard C function calls. Improved old_views handling.
author Eris Caffee <discordia@eldalin.com>
date Sat, 23 Oct 2010 04:24:48 -0500
parents 0684158d38a8
children df02a7de7fe2
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 #include <dirent.h>
20 #include "SDL.h"
22 #include "App.h"
23 #include "Options.h"
26 std::vector<App::view *> App::old_views;
28 ////////////////////////////////////////////////////////////////////////////////
30 App::App() :
31 running (true),
32 redraw (false),
33 surf_display (NULL),
34 surf_selection (NULL),
35 surf_fractal (NULL),
36 selection_x (-1),
37 selection_y (-1),
38 selection_width (-1),
39 setting_zoom (false),
40 can_set_julia_k (false),
41 setting_julia_k (false)
42 {
43 }
46 ////////////////////////////////////////////////////////////////////////////////
47 App::~App()
48 {
49 old_views.clear();
50 }
53 ////////////////////////////////////////////////////////////////////////////////
55 int App::run()
56 {
57 if (init() == false)
58 return 1;
60 SDL_Event event;
62 while (running)
63 {
64 SDL_WaitEvent(&event);
65 do
66 {
67 on_event(&event);
68 }
69 while (SDL_PollEvent(&event));
71 update();
73 if (redraw)
74 render();
75 }
77 cleanup();
79 return 0;
80 }
83 ////////////////////////////////////////////////////////////////////////////////
84 bool App::init()
85 {
86 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
87 {
88 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
89 return false;
90 }
92 if ((surf_display = SDL_SetVideoMode(Options::width,
93 Options::height,
94 Options::bpp,
95 SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)
96 {
97 std::cerr << "SDL_SetVideoMode() failed: "
98 << SDL_GetError() << std::endl;
99 return false;
100 }
102 SDL_EnableKeyRepeat(500, 100);
105 if (!create_new_surface(surf_selection)) { return false; }
106 if (!create_new_surface(surf_fractal)) { return false; }
109 std::cerr << "Available fractal types are" << std::endl;
110 std::map <std::string, FractalCreator *> mymap = get_Fractal_map();
111 std::map <std::string, FractalCreator *>::iterator it;
112 for ( it=mymap.begin() ; it != mymap.end(); it++ )
113 std::cerr << " " << (*it).first << std::endl;
115 FractalCreator * fractal_creator = get_Fractal_map()["Mandelbrot"];
116 if (!fractal_creator)
117 {
118 std::cerr << "Mandelbrot set not available!" << std::endl;
119 return false;
120 }
121 fractal = fractal_creator->create();
122 fractal->init(surf_fractal);
123 redraw = true;
125 can_set_julia_k = true;
128 set_caption();
130 SDL_TimerID timer_id = SDL_AddTimer(10, App::timer_callback, NULL);
132 return true;
133 }
136 ////////////////////////////////////////////////////////////////////////////////
138 void App::update()
139 {
140 }
143 ////////////////////////////////////////////////////////////////////////////////
145 void App::render()
146 {
147 if (redraw)
148 {
149 // Start with a clean slate.
150 SDL_FillRect(surf_display, &surf_display->clip_rect,
151 SDL_MapRGB(surf_display->format, 0, 0, 255));
153 fractal->calc_set();
154 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
156 // draw a selection box
157 if (setting_zoom)
158 {
159 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
160 }
162 // Show it to the user.
163 SDL_Flip(surf_display);
165 redraw = false;
166 }
167 }
170 ////////////////////////////////////////////////////////////////////////////////
172 void App::cleanup()
173 {
175 if (surf_selection)
176 {
177 SDL_FreeSurface(surf_selection);
178 surf_selection = NULL;
179 }
180 if (surf_display)
181 {
182 SDL_FreeSurface(surf_display);
183 surf_display = NULL;
184 }
186 SDL_Quit();
187 }
190 ////////////////////////////////////////////////////////////////////////////////
192 void App::on_event(SDL_Event * event)
193 {
194 EventHandler::on_event(event);
195 }
198 ////////////////////////////////////////////////////////////////////////////////
200 void App::on_KeyDown(SDLKey sym, SDLMod mod, Uint16 unicode)
201 {
202 switch (sym)
203 {
204 case SDLK_ESCAPE:
205 case SDLK_q:
206 SDL_Event event;
207 event.type = SDL_QUIT;
208 SDL_PushEvent(&event);
209 break;
211 case SDLK_b:
212 restore_view();
213 redraw = true;
214 set_caption();
215 break;
217 case SDLK_s:
218 save_image();
219 break;
221 case SDLK_UP:
222 fractal->inc_max_iter();
223 set_caption();
224 break;
225 case SDLK_DOWN:
226 fractal->dec_max_iter();
227 set_caption();
228 break;
231 case SDLK_m:
232 if (fractal)
233 {
234 save_view();
235 delete fractal;
236 FractalCreator * fractal_creator = get_Fractal_map()["Mandelbrot"];
237 if (!fractal_creator)
238 {
239 std::cerr << "Mandelbrot set not available!" << std::endl;
240 SDL_Event event;
241 event.type = SDL_QUIT;
242 SDL_PushEvent(&event);
243 break;
244 }
245 fractal = fractal_creator->create();
246 fractal->init(surf_fractal);
247 fractal->set_calc_needed();
248 redraw = true;
249 set_caption();
250 can_set_julia_k = true;
251 }
252 break;
253 case SDLK_j:
254 if (fractal)
255 {
256 FractalCreator * fractal_creator = get_Fractal_map()["Julia"];
257 if (!fractal_creator)
258 {
259 std::cerr << "Julia set not available!" << std::endl;
260 }
261 else
262 {
263 save_view();
264 delete fractal;
265 fractal = fractal_creator->create();
266 fractal->init(surf_fractal);
267 fractal->set_calc_needed();
268 redraw = true;
269 set_caption();
270 can_set_julia_k = false;
271 }
272 }
273 break;
275 case SDLK_k:
276 if (can_set_julia_k)
277 {
278 if (setting_julia_k) setting_julia_k = false;
279 else setting_julia_k = true;
280 }
281 break;
283 default:
284 break;
285 }
286 }
289 ////////////////////////////////////////////////////////////////////////////////
290 void App::on_KeyUp(SDLKey sym, SDLMod mod, Uint16 unicode)
291 {
292 switch (sym)
293 {
294 case SDLK_UP:
295 fractal->set_calc_needed();
296 redraw = true;
297 break;
298 case SDLK_DOWN:
299 fractal->set_calc_needed();
300 redraw = true;
301 break;
302 }
303 }
306 ////////////////////////////////////////////////////////////////////////////////
308 void App::on_Exit()
309 {
310 running = false;
311 }
314 ////////////////////////////////////////////////////////////////////////////////
316 void App::on_LButtonDown(int mx, int my)
317 {
318 if (setting_julia_k)
319 {
320 set_julia_k(mx, my);
321 }
322 else
323 {
324 selection_x = mx;
325 selection_y = my;
326 setting_zoom = true;
327 }
328 }
331 ////////////////////////////////////////////////////////////////////////////////
333 void App::on_LButtonUp(int mx, int my)
334 {
335 if (setting_zoom)
336 {
337 if ( (mx <= 0) ||
338 (mx >= surf_display->w - 1) ||
339 (my <= 0) ||
340 (my >= surf_display->h - 1))
341 {
342 // Selection cancelled
343 setting_zoom = false;
344 return;
345 }
347 selection_width = mouse_move_width(mx, my);
349 // calculate new min/max re/im
350 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
351 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
353 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
354 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
355 double new_size = selection_width * Re_scale;
358 save_view();
360 fractal->set_re_min(new_re_min);
361 fractal->set_im_max(new_im_max);
362 fractal->set_size(new_size);
363 fractal->set_calc_needed();
364 redraw = true;
366 set_caption();
368 setting_zoom = false;
369 }
370 }
373 ////////////////////////////////////////////////////////////////////////////////
375 void App::on_RButtonDown(int mx, int my)
376 {
377 if (can_set_julia_k)
378 {
379 set_julia_k(mx, my);
380 }
381 }
384 ////////////////////////////////////////////////////////////////////////////////
385 void App::on_MouseMove(int mx, int my,
386 int relx, int rely,
387 bool left, bool right, bool middle)
388 {
389 if (setting_zoom)
390 {
391 Uint32 clear = SDL_MapRGBA(surf_selection->format,
392 0, 0, 0, SDL_ALPHA_TRANSPARENT);
393 Uint32 white = SDL_MapRGBA(surf_selection->format,
394 255, 255, 255, SDL_ALPHA_OPAQUE);
396 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
398 SDL_Rect rect;
399 rect.x = selection_x;
400 rect.y = selection_y;
401 int w = mouse_move_width(mx, my);
402 rect.w = rect.h = w;
404 SDL_FillRect(surf_selection, &rect, white);
406 rect.x = selection_x + 1;
407 rect.y = selection_y + 1;
408 rect.w = rect.h = w - 2;
410 SDL_FillRect(surf_selection, &rect, clear);
412 redraw = true;
413 }
414 }
417 ////////////////////////////////////////////////////////////////////////////////
418 void App::on_Resize(int w, int h)
419 {
420 redraw = true;
421 }
424 ////////////////////////////////////////////////////////////////////////////////
425 void App::on_Expose()
426 {
427 redraw = true;
428 }
431 ////////////////////////////////////////////////////////////////////////////////
432 int App::mouse_move_width(int mx, int my)
433 {
434 int move_width = (mx - selection_x);
435 if (mx > selection_x)
436 {
437 move_width *= -1;
438 }
440 int tmp_i = (my - selection_y);
441 if (my < selection_y)
442 {
443 tmp_i *= -1;
444 }
446 if (tmp_i > move_width)
447 move_width = tmp_i;
449 return move_width;
450 }
453 ////////////////////////////////////////////////////////////////////////////////
454 bool App::create_new_surface(SDL_Surface * &surface)
455 {
456 if (surface)
457 {
458 SDL_FreeSurface(surface);
459 surface = NULL;
460 }
462 SDL_Surface * tmp;
463 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
464 Options::width, Options::height,
465 Options::bpp, 0, 0, 0, 0))
466 == NULL)
467 {
468 std::cerr << "SDL_CreateRGBSurface() failed in "
469 << "App::create_new_surface(): "
470 << SDL_GetError() << std::endl;
471 return false;
472 }
474 surface = SDL_DisplayFormatAlpha(tmp);
475 SDL_FreeSurface(tmp);
477 return true;
478 }
481 ////////////////////////////////////////////////////////////////////////////////
482 void App::set_julia_k(int x, int y)
483 {
484 save_view();
486 double k[2];
487 k[0]= fractal->get_re_min() + x *
488 (fractal->get_size())/(surf_fractal->w - 1);
489 k[1] = fractal->get_im_max() - y *
490 (fractal->get_size())/(surf_fractal->h - 1);
492 FractalCreator * fractal_creator = get_Fractal_map()["Julia"];
493 if (!fractal_creator)
494 {
495 std::cerr << "Julia set not available!" << std::endl;
496 return;
497 }
498 else
499 {
500 delete fractal;
501 fractal = fractal_creator->create();
502 fractal->init(surf_fractal);
503 fractal->set_option("k", k);
504 fractal->set_calc_needed();
505 redraw = true;
507 set_caption();
508 setting_julia_k = false;
509 }
510 }
513 ////////////////////////////////////////////////////////////////////////////////
515 void App::restore_view()
516 {
517 if (old_views.size() > 0)
518 {
519 view * v = old_views.back();
520 old_views.pop_back();
522 if (v->type.compare(fractal->get_type_name()) != 0)
523 {
524 delete fractal;
525 FractalCreator * fractal_creator = get_Fractal_map()[v->type];
526 if (!fractal_creator)
527 {
528 std::cerr << "Cannot restore old view: unknown Fractal type "
529 << v->type << std::endl;
530 SDL_Event event;
531 event.type = SDL_QUIT;
532 SDL_PushEvent(&event);
533 }
534 fractal = fractal_creator->create();
535 fractal->init(surf_fractal);
536 }
538 fractal->set_re_min(v->re_min);
539 fractal->set_im_max(v->im_max);
540 fractal->set_size(v->size);
541 fractal->set_max_iter(v->max_iter);
542 fractal->set_calc_needed();
544 std::cerr << "Restored view:" << std::endl
545 << " type " << v->type << std::endl
546 << " re_min " << v->re_min << std::endl
547 << " im_max " << v->im_max << std::endl
548 << " size " << v->size << std::endl
549 << " max_iter " << v->max_iter << std::endl
550 << std::endl;
551 delete v;
552 }
553 }
556 ////////////////////////////////////////////////////////////////////////////////
557 void App::save_view()
558 {
559 view * v = new view;
560 v->type = fractal->get_type_name();
561 v->re_min = fractal->get_re_min();
562 v->im_max = fractal->get_im_max();
563 v->size = fractal->get_size();
564 v->max_iter = fractal->get_max_iter();
565 old_views.push_back(v);
567 std::cerr << "Saved view:" << std::endl
568 << " type " << v->type << std::endl
569 << " re_min " << v->re_min << std::endl
570 << " im_max " << v->im_max << std::endl
571 << " size " << v->size << std::endl
572 << " max_iter " << v->max_iter << std::endl
573 << std::endl;
574 }
577 ////////////////////////////////////////////////////////////////////////////////
578 void App::set_caption()
579 {
580 std::stringstream ss;
581 ss << fractal->get_display_name() << " / "
582 << "Real: " << fractal->get_re_min()
583 << " to "<< fractal->get_re_min() + fractal->get_size()
584 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
585 << " to " << fractal->get_im_max()
586 << " / Max iterations: " << fractal->get_max_iter();
587 SDL_WM_SetCaption(ss.str().c_str(), NULL);
588 }
591 ////////////////////////////////////////////////////////////////////////////////
592 // This file name generation is kuldgy. Need to learn how to do file i/o for
593 // real!
595 int scandir_filter(const struct dirent * d)
596 {
597 if (memcmp(d->d_name, "mandelbrot-", 11) != 0) return 0;
598 if (memcmp(d->d_name+14, ".bmp", 4) != 0) return 0;
599 if (isdigit(d->d_name[11])
600 && isdigit(d->d_name[12])
601 && isdigit(d->d_name[13]))
602 return 1;
603 return 0;
604 }
607 bool App::save_image()
608 {
609 static int i = 0;
611 if (i == 0)
612 {
613 struct dirent ** file_list;
614 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
615 if (num_files != 0)
616 sscanf(file_list[num_files-1]->d_name, "mandelbrot-%03d.bmp", &i);
617 }
618 i++;
620 std::stringstream name;
621 name << "mandelbrot-" << std::setw(3) << std::setfill('0') << i << ".bmp";
623 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
624 {
625 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
626 return false;
627 }
629 std::cerr << "Saved image as " << name.str() << std::endl;
631 return true;
632 }
635 ////////////////////////////////////////////////////////////////////////////////
636 Uint32 App::timer_callback(Uint32 interval, void *param)
637 {
638 SDL_Event e;
640 e.type = SDL_USEREVENT;
641 e.user.code = 0;
642 e.user.data1 = NULL;
643 e.user.data2 = NULL;
644 SDL_PushEvent(& e);
646 return interval;
647 }