view src/App.cpp @ 15:bdd2e7b37220

Added a comment
author Eris Caffee <discordia@eldalin.com>
date Sat, 20 Nov 2010 02:49:02 -0600
parents 9efbc0c6637b
children
line source
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Fracter - A simple Mandelbrot Set viewer.
4 //
5 // Copyright (C) 2010 Sarah Eris Horsley Caffee
6 //
7 // This file is part of Fracter.
8 //
9 // Fracter is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 //
22 //
23 // App.cpp
24 // Application class implementation
25 //
26 ////////////////////////////////////////////////////////////////////////////////
28 // Added a comment!
30 #include <iomanip>
31 #include <iostream>
32 #include <sstream>
33 #include <typeinfo>
34 #include <vector>
36 #if defined(WIN32)
37 #include <cstdlib>
38 #include <windows.h>
39 #include <Shlwapi.h>
41 #else
42 #include <cctype>
43 #include <cstring>
44 #include <cstdio>
46 #include <dirent.h>
47 #endif
50 #include "SDL.h"
52 #include "App.h"
54 #include "Options.h"
55 #include "Mandelbrot.h"
56 #include "Julia.h"
59 ////////////////////////////////////////////////////////////////////////////////
61 App::App() :
62 running (true),
63 redraw (false),
64 surf_display (NULL),
65 surf_selection (NULL),
66 surf_fractal (NULL),
67 selection_x (-1),
68 selection_y (-1),
69 selection_width (-1),
70 setting_zoom (false),
71 can_set_julia_k (false),
72 setting_julia_k (false),
73 old_views (),
74 fractal (NULL)
75 {
76 }
79 ////////////////////////////////////////////////////////////////////////////////
80 App::~App()
81 {
82 old_views.clear();
83 }
86 ////////////////////////////////////////////////////////////////////////////////
88 int App::run()
89 {
90 if (init() == false)
91 return 1;
93 SDL_Event event;
95 while (running)
96 {
97 SDL_WaitEvent(&event);
98 do
99 {
100 on_Event(&event);
101 }
102 while (SDL_PollEvent(&event));
104 update();
106 if (redraw)
107 render();
108 }
110 cleanup();
112 return 0;
113 }
116 ////////////////////////////////////////////////////////////////////////////////
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; }
143 std::cerr << "Available fractal types are" << std::endl;
144 std::map <std::string, FractalFactory *> mymap = get_Fractal_map();
145 std::map <std::string, FractalFactory *>::iterator it;
146 for ( it=mymap.begin() ; it != mymap.end(); it++ )
147 std::cerr << " " << (*it).first << std::endl;
149 FractalFactory * fractal_factory =
150 get_Fractal_map()[typeid(Mandelbrot).name()];
151 if (!fractal_factory)
152 {
153 std::cerr << "Mandelbrot set not available!" << std::endl;
154 return false;
155 }
156 fractal = fractal_factory->create();
157 fractal->init(surf_fractal);
158 redraw = true;
160 can_set_julia_k = true;
163 set_caption();
165 SDL_AddTimer(10, App::timer_callback, NULL);
167 return true;
168 }
171 ////////////////////////////////////////////////////////////////////////////////
173 void App::update()
174 {
175 }
178 ////////////////////////////////////////////////////////////////////////////////
180 void App::render()
181 {
182 if (redraw)
183 {
184 // Start with a clean slate.
185 SDL_FillRect(surf_display, &surf_display->clip_rect,
186 SDL_MapRGB(surf_display->format, 0, 0, 255));
188 fractal->calc_set();
189 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
191 // draw a selection box
192 if (setting_zoom)
193 {
194 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
195 }
197 // Show it to the user.
198 SDL_Flip(surf_display);
200 redraw = false;
201 }
202 }
205 ////////////////////////////////////////////////////////////////////////////////
207 void App::cleanup()
208 {
210 if (surf_selection)
211 {
212 SDL_FreeSurface(surf_selection);
213 surf_selection = NULL;
214 }
215 if (surf_display)
216 {
217 SDL_FreeSurface(surf_display);
218 surf_display = NULL;
219 }
221 SDL_Quit();
222 }
225 ////////////////////////////////////////////////////////////////////////////////
227 void App::on_InputFocus()
228 {
229 SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
230 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
231 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
232 }
235 ////////////////////////////////////////////////////////////////////////////////
237 void App::on_InputBlur()
238 {
239 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
240 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
241 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
242 }
245 ////////////////////////////////////////////////////////////////////////////////
247 void App::on_KeyDown(SDL_KeyboardEvent key)
248 {
249 switch (key.keysym.sym)
250 {
251 case SDLK_ESCAPE:
252 case SDLK_q:
253 SDL_Event event;
254 event.type = SDL_QUIT;
255 SDL_PushEvent(&event);
256 break;
258 case SDLK_b:
259 restore_view();
260 redraw = true;
261 set_caption();
262 break;
264 case SDLK_s:
265 save_image();
266 break;
268 case SDLK_UP:
269 fractal->inc_max_iter();
270 set_caption();
271 break;
272 case SDLK_DOWN:
273 fractal->dec_max_iter();
274 set_caption();
275 break;
278 case SDLK_m:
279 if (fractal)
280 {
281 FractalFactory * fractal_factory =
282 get_Fractal_map()[typeid(Mandelbrot).name()];
283 if (!fractal_factory)
284 {
285 std::cerr << "Mandelbrot set not available!" << std::endl;
286 SDL_Event event;
287 event.type = SDL_QUIT;
288 SDL_PushEvent(&event);
289 }
290 else
291 {
292 save_view();
293 delete fractal;
294 fractal = fractal_factory->create();
295 fractal->init(surf_fractal);
296 redraw = true;
297 set_caption();
298 can_set_julia_k = true;
299 }
300 }
301 break;
303 case SDLK_j:
304 if (fractal)
305 {
306 FractalFactory * fractal_factory =
307 get_Fractal_map()[typeid(Julia).name()];
308 if (!fractal_factory)
309 {
310 std::cerr << "Julia set not available!" << std::endl;
311 }
312 else
313 {
314 save_view();
315 delete fractal;
316 fractal = fractal_factory->create();
317 fractal->init(surf_fractal);
318 redraw = true;
319 set_caption();
320 can_set_julia_k = false;
321 }
322 }
323 break;
325 case SDLK_k:
326 if (can_set_julia_k)
327 {
328 if (setting_julia_k) setting_julia_k = false;
329 else setting_julia_k = true;
330 }
331 break;
333 default:
334 break;
335 }
336 }
339 ////////////////////////////////////////////////////////////////////////////////
340 void App::on_KeyUp(SDL_KeyboardEvent key)
341 {
342 switch (key.keysym.sym)
343 {
344 case SDLK_UP:
345 fractal->set_calc_needed();
346 redraw = true;
347 break;
348 case SDLK_DOWN:
349 fractal->set_calc_needed();
350 redraw = true;
351 break;
352 default:
353 break;
354 }
355 }
358 ////////////////////////////////////////////////////////////////////////////////
359 void App::on_MouseMove(SDL_MouseMotionEvent motion)
360 {
361 if (setting_zoom)
362 {
363 Uint32 clear = SDL_MapRGBA(surf_selection->format,
364 0, 0, 0, SDL_ALPHA_TRANSPARENT);
365 Uint32 white = SDL_MapRGBA(surf_selection->format,
366 255, 255, 255, SDL_ALPHA_OPAQUE);
368 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
370 SDL_Rect rect;
371 rect.x = selection_x;
372 rect.y = selection_y;
373 int w = mouse_move_width(motion.x, motion.y);
374 rect.w = rect.h = w;
376 SDL_FillRect(surf_selection, &rect, white);
378 rect.x = selection_x + 1;
379 rect.y = selection_y + 1;
380 rect.w = rect.h = w - 2;
382 SDL_FillRect(surf_selection, &rect, clear);
384 redraw = true;
385 }
386 }
388 ////////////////////////////////////////////////////////////////////////////////
390 void App::on_LButtonDown(SDL_MouseButtonEvent button)
391 {
392 if (setting_julia_k)
393 {
394 set_julia_k(button.x, button.y);
395 }
396 else
397 {
398 selection_x = button.x;
399 selection_y = button.y;
400 setting_zoom = true;
401 }
402 }
405 ////////////////////////////////////////////////////////////////////////////////
407 void App::on_LButtonUp(SDL_MouseButtonEvent button)
408 {
409 if (setting_zoom)
410 {
411 selection_width = mouse_move_width(button.x, button.y);
412 if ( (button.x <= 0) ||
413 (button.x >= surf_display->w - 1) ||
414 (button.y <= 0) ||
415 (button.y >= surf_display->h - 1) ||
416 (selection_width == 0))
417 {
418 // Selection cancelled
419 setting_zoom = false;
420 return;
421 }
423 // calculate new min/max re/im
424 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
425 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
427 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
428 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
429 double new_size = selection_width * Re_scale;
431 save_view();
433 fractal->set_re_min(new_re_min);
434 fractal->set_im_max(new_im_max);
435 fractal->set_size(new_size);
436 fractal->set_calc_needed();
437 redraw = true;
439 set_caption();
441 setting_zoom = false;
442 }
443 }
446 ////////////////////////////////////////////////////////////////////////////////
447 void App::on_Expose()
448 {
449 redraw = true;
450 }
453 ////////////////////////////////////////////////////////////////////////////////
455 void App::on_Quit()
456 {
457 running = false;
458 }
461 ////////////////////////////////////////////////////////////////////////////////
462 int App::mouse_move_width(int mx, int my)
463 {
464 int move_width = (mx - selection_x);
465 if (mx > selection_x)
466 {
467 move_width *= -1;
468 }
470 int tmp_i = (my - selection_y);
471 if (my < selection_y)
472 {
473 tmp_i *= -1;
474 }
476 if (tmp_i > move_width)
477 move_width = tmp_i;
479 return move_width;
480 }
483 ////////////////////////////////////////////////////////////////////////////////
484 bool App::create_new_surface(SDL_Surface * &surface)
485 {
486 if (surface)
487 {
488 SDL_FreeSurface(surface);
489 surface = NULL;
490 }
492 SDL_Surface * tmp;
493 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
494 Options::width, Options::height,
495 Options::bpp, 0, 0, 0, 0))
496 == NULL)
497 {
498 std::cerr << "SDL_CreateRGBSurface() failed in "
499 << "App::create_new_surface(): "
500 << SDL_GetError() << std::endl;
501 return false;
502 }
504 surface = SDL_DisplayFormatAlpha(tmp);
505 SDL_FreeSurface(tmp);
507 return true;
508 }
511 ////////////////////////////////////////////////////////////////////////////////
512 void App::set_julia_k(int x, int y)
513 {
514 FractalFactory * fractal_factory = get_Fractal_map()[typeid(Julia).name()];
515 if (!fractal_factory)
516 {
517 std::cerr << "Julia set not available!" << std::endl;
518 return;
519 }
520 else
521 {
522 save_view();
524 delete fractal;
525 fractal = fractal_factory->create();
526 fractal->init(surf_fractal);
528 double k[2];
529 k[0]= fractal->get_re_min() + x *
530 (fractal->get_size())/(surf_fractal->w - 1);
531 k[1] = fractal->get_im_max() - y *
532 (fractal->get_size())/(surf_fractal->h - 1);
534 fractal->set_option("k", k);
535 fractal->set_calc_needed();
536 redraw = true;
538 set_caption();
539 setting_julia_k = false;
540 }
541 }
544 ////////////////////////////////////////////////////////////////////////////////
546 void App::restore_view()
547 {
548 if (old_views.size() > 0)
549 {
550 View * v = old_views.back();
551 old_views.pop_back();
553 if (v->get_type().compare(fractal->get_fractal_name()) != 0)
554 {
555 delete fractal;
556 FractalFactory * fractal_factory = get_Fractal_map()[v->get_type()];
557 if (!fractal_factory)
558 {
559 std::cerr << "Cannot restore old view: unknown Fractal type "
560 << v->get_type() << std::endl;
561 SDL_Event event;
562 event.type = SDL_QUIT;
563 SDL_PushEvent(&event);
564 return;
565 }
566 fractal = fractal_factory->create();
567 fractal->init(surf_fractal);
568 }
570 fractal->restore_view(v);
571 delete v;
572 }
573 }
576 ////////////////////////////////////////////////////////////////////////////////
577 void App::save_view()
578 {
579 View * v = fractal->save_view();
580 old_views.push_back(v);
581 }
584 ////////////////////////////////////////////////////////////////////////////////
585 void App::set_caption()
586 {
587 std::stringstream ss;
588 ss << fractal->get_display_name() << " / "
589 << "Real: " << fractal->get_re_min()
590 << " to "<< fractal->get_re_min() + fractal->get_size()
591 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
592 << " to " << fractal->get_im_max()
593 << " / Max iterations: " << fractal->get_max_iter();
594 SDL_WM_SetCaption(ss.str().c_str(), NULL);
595 }
598 ////////////////////////////////////////////////////////////////////////////////
599 bool App::save_image()
600 {
601 int i = App::get_next_file_num();
602 std::stringstream name;
603 name << "fracter-" << std::setw(3) << std::setfill('0') << i << ".bmp";
605 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
606 {
607 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
608 return false;
609 }
611 std::cerr << "Saved image as " << name.str() << std::endl;
613 return true;
614 }
617 ////////////////////////////////////////////////////////////////////////////////
618 Uint32 App::timer_callback(Uint32 interval, void *param)
619 {
620 SDL_Event e;
622 e.type = SDL_USEREVENT;
623 e.user.code = 0;
624 e.user.data1 = NULL;
625 e.user.data2 = NULL;
626 SDL_PushEvent(& e);
628 return interval;
629 }
632 #if defined(WIN32)
633 ////////////////////////////////////////////////////////////////////////////////
634 //Code for Windows
635 int App::get_next_file_num()
636 {
637 static int i = 0;
639 if (i == 0)
640 {
641 WIN32_FIND_DATA ffd;
642 HANDLE hFind = INVALID_HANDLE_VALUE;
644 char * file_spec = "fracter-???.bmp";
645 hFind = FindFirstFile(file_spec, &ffd);
646 if (hFind != INVALID_HANDLE_VALUE)
647 {
648 std::string s;
649 std::stringstream ss;
650 do
651 {
652 std::cerr << ffd.cFileName << std::endl;
653 ss << ffd.cFileName;
654 if (ss.str().size() == 18)
655 {
656 s = ss.str().substr(0, 11);
657 if ( StrCmpI(s.c_str(), "fracter-") == 0 )
658 {
659 s = ss.str().substr(14, 4);
660 if (StrCmpI(s.c_str(), ".bmp") == 0)
661 {
662 s = ss.str().substr(11, 3);
663 int j = atoi(s.c_str());
664 if (j > i) i = j;
665 }
666 }
667 }
668 }
669 while (FindNextFile(hFind, &ffd) != 0);
670 }
671 }
673 i++;
674 return i;
675 }
679 #else
680 ////////////////////////////////////////////////////////////////////////////////
681 // Code for Linux
683 int App::scandir_filter(const struct dirent * d)
684 {
685 if (memcmp(d->d_name, "fracter-", 8) != 0) return 0;
686 if (memcmp(d->d_name+11, ".bmp", 4) != 0) return 0;
687 if (isdigit(d->d_name[8])
688 && isdigit(d->d_name[9])
689 && isdigit(d->d_name[10]))
690 return 1;
691 return 0;
692 }
695 int App::get_next_file_num()
696 {
697 static int i = 0;
699 if (i == 0)
700 {
701 struct dirent ** file_list;
702 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
703 if (num_files != 0)
704 sscanf(file_list[num_files-1]->d_name, "fracter-%03d.bmp", &i);
705 }
706 i++;
708 return i;
709 }
711 #endif