view src/App.cpp @ 16:bdfe3327f4ce

Saving progress
author Eris Caffee <discordia@eldalin.com>
date Wed, 24 Nov 2010 21:34:11 -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 #include <iomanip>
29 #include <iostream>
30 #include <sstream>
31 #include <typeinfo>
32 #include <vector>
34 #if defined(WIN32)
35 #include <cstdlib>
36 #include <windows.h>
37 #include <Shlwapi.h>
39 #else
40 #include <cctype>
41 #include <cstring>
42 #include <cstdio>
44 #include <dirent.h>
45 #endif
48 #include "SDL.h"
50 #include "App.h"
52 #include "Options.h"
53 #include "Mandelbrot.h"
54 #include "Julia.h"
57 ////////////////////////////////////////////////////////////////////////////////
59 App::App() :
60 running (true),
61 redraw (false),
62 window (NULL),
63 surf_selection (NULL),
64 surf_fractal (NULL),
65 selection_x (-1),
66 selection_y (-1),
67 selection_width (-1),
68 setting_zoom (false),
69 can_set_julia_k (false),
70 setting_julia_k (false),
71 old_views (),
72 fractal (NULL)
73 {
74 }
77 ////////////////////////////////////////////////////////////////////////////////
78 App::~App()
79 {
80 old_views.clear();
81 }
84 ////////////////////////////////////////////////////////////////////////////////
86 int App::run()
87 {
88 if (init() == false)
89 return 1;
91 SDL_Event event;
93 while (running)
94 {
95 SDL_WaitEvent(&event);
96 do
97 {
98 on_Event(&event);
99 }
100 while (SDL_PollEvent(&event));
102 update();
104 if (redraw)
105 render();
106 }
108 cleanup();
110 return 0;
111 }
114 ////////////////////////////////////////////////////////////////////////////////
116 bool App::init()
117 {
118 if (!init_sdl())
119 {
120 return false;
121 }
123 if (!create_new_surface(surf_selection)) { return false; }
124 if (!create_new_surface(surf_fractal)) { return false; }
127 std::cerr << "Available fractal types are" << std::endl;
128 std::map <std::string, FractalFactory *> mymap = get_Fractal_map();
129 std::map <std::string, FractalFactory *>::iterator it;
130 for ( it=mymap.begin() ; it != mymap.end(); it++ )
131 std::cerr << " " << (*it).first << std::endl;
133 FractalFactory * fractal_factory =
134 get_Fractal_map()[typeid(Mandelbrot).name()];
135 if (!fractal_factory)
136 {
137 std::cerr << "Mandelbrot set not available!" << std::endl;
138 return false;
139 }
140 fractal = fractal_factory->create();
141 fractal->init(surf_fractal);
142 redraw = true;
144 can_set_julia_k = true;
146 set_caption();
148 return true;
149 }
152 ////////////////////////////////////////////////////////////////////////////////
154 void App::update()
155 {
156 }
159 ////////////////////////////////////////////////////////////////////////////////
161 void App::render()
162 {
163 if (redraw)
164 {
165 // Start with a clean slate.
166 SDL_SetRenderDrawColor(0, 0, 0, SDL_ALPHA_OPAQUE);
167 SDL_RenderClear();
169 fractal->calc_set();
170 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
172 // draw a selection box
173 if (setting_zoom)
174 {
175 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
176 }
178 // Show it to the user.
179 SDL_RenderPresent();
181 redraw = false;
182 }
183 }
186 ////////////////////////////////////////////////////////////////////////////////
188 void App::cleanup()
189 {
191 if (surf_selection)
192 {
193 SDL_FreeSurface(surf_selection);
194 surf_selection = NULL;
195 }
196 if (window)
197 {
198 SDL_DestroyWindow(window);
199 window = NULL;
200 }
202 SDL_Quit();
203 }
206 ////////////////////////////////////////////////////////////////////////////////
208 void App::on_InputFocus()
209 {
210 SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
211 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
212 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
213 }
216 ////////////////////////////////////////////////////////////////////////////////
218 void App::on_InputBlur()
219 {
220 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
221 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
222 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
223 }
226 ////////////////////////////////////////////////////////////////////////////////
228 void App::on_KeyDown(SDL_KeyboardEvent key)
229 {
230 switch (key.keysym.sym)
231 {
232 case SDLK_ESCAPE:
233 case SDLK_q:
234 SDL_Event event;
235 event.type = SDL_QUIT;
236 SDL_PushEvent(&event);
237 break;
239 case SDLK_b:
240 restore_view();
241 redraw = true;
242 set_caption();
243 break;
245 case SDLK_s:
246 save_image();
247 break;
249 case SDLK_UP:
250 fractal->inc_max_iter();
251 set_caption();
252 break;
253 case SDLK_DOWN:
254 fractal->dec_max_iter();
255 set_caption();
256 break;
259 case SDLK_m:
260 if (fractal)
261 {
262 FractalFactory * fractal_factory =
263 get_Fractal_map()[typeid(Mandelbrot).name()];
264 if (!fractal_factory)
265 {
266 std::cerr << "Mandelbrot set not available!" << std::endl;
267 SDL_Event event;
268 event.type = SDL_QUIT;
269 SDL_PushEvent(&event);
270 }
271 else
272 {
273 save_view();
274 delete fractal;
275 fractal = fractal_factory->create();
276 fractal->init(surf_fractal);
277 redraw = true;
278 set_caption();
279 can_set_julia_k = true;
280 }
281 }
282 break;
284 case SDLK_j:
285 if (fractal)
286 {
287 FractalFactory * fractal_factory =
288 get_Fractal_map()[typeid(Julia).name()];
289 if (!fractal_factory)
290 {
291 std::cerr << "Julia set not available!" << std::endl;
292 }
293 else
294 {
295 save_view();
296 delete fractal;
297 fractal = fractal_factory->create();
298 fractal->init(surf_fractal);
299 redraw = true;
300 set_caption();
301 can_set_julia_k = false;
302 }
303 }
304 break;
306 case SDLK_k:
307 if (can_set_julia_k)
308 {
309 if (setting_julia_k) setting_julia_k = false;
310 else setting_julia_k = true;
311 }
312 break;
314 default:
315 break;
316 }
317 }
320 ////////////////////////////////////////////////////////////////////////////////
321 void App::on_KeyUp(SDL_KeyboardEvent key)
322 {
323 switch (key.keysym.sym)
324 {
325 case SDLK_UP:
326 fractal->set_calc_needed();
327 redraw = true;
328 break;
329 case SDLK_DOWN:
330 fractal->set_calc_needed();
331 redraw = true;
332 break;
333 default:
334 break;
335 }
336 }
339 ////////////////////////////////////////////////////////////////////////////////
340 void App::on_MouseMove(SDL_MouseMotionEvent motion)
341 {
342 if (setting_zoom)
343 {
344 Uint32 clear = SDL_MapRGBA(surf_selection->format,
345 0, 0, 0, SDL_ALPHA_TRANSPARENT);
346 Uint32 white = SDL_MapRGBA(surf_selection->format,
347 255, 255, 255, SDL_ALPHA_OPAQUE);
349 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
351 SDL_Rect rect;
352 rect.x = selection_x;
353 rect.y = selection_y;
354 int w = mouse_move_width(motion.x, motion.y);
355 rect.w = rect.h = w;
357 SDL_FillRect(surf_selection, &rect, white);
359 rect.x = selection_x + 1;
360 rect.y = selection_y + 1;
361 rect.w = rect.h = w - 2;
363 SDL_FillRect(surf_selection, &rect, clear);
365 redraw = true;
366 }
367 }
369 ////////////////////////////////////////////////////////////////////////////////
371 void App::on_LButtonDown(SDL_MouseButtonEvent button)
372 {
373 if (setting_julia_k)
374 {
375 set_julia_k(button.x, button.y);
376 }
377 else
378 {
379 selection_x = button.x;
380 selection_y = button.y;
381 setting_zoom = true;
382 }
383 }
386 ////////////////////////////////////////////////////////////////////////////////
388 void App::on_LButtonUp(SDL_MouseButtonEvent button)
389 {
390 if (setting_zoom)
391 {
392 selection_width = mouse_move_width(button.x, button.y);
393 if ( (button.x <= 0) ||
394 (button.x >= surf_display->w - 1) ||
395 (button.y <= 0) ||
396 (button.y >= surf_display->h - 1) ||
397 (selection_width == 0))
398 {
399 // Selection cancelled
400 setting_zoom = false;
401 return;
402 }
404 // calculate new min/max re/im
405 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
406 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
408 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
409 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
410 double new_size = selection_width * Re_scale;
412 save_view();
414 fractal->set_re_min(new_re_min);
415 fractal->set_im_max(new_im_max);
416 fractal->set_size(new_size);
417 fractal->set_calc_needed();
418 redraw = true;
420 set_caption();
422 setting_zoom = false;
423 }
424 }
427 ////////////////////////////////////////////////////////////////////////////////
428 void App::on_Expose()
429 {
430 redraw = true;
431 }
434 ////////////////////////////////////////////////////////////////////////////////
436 void App::on_Quit()
437 {
438 running = false;
439 }
442 ////////////////////////////////////////////////////////////////////////////////
443 bool App::create_new_surface(SDL_Surface * &surface)
444 {
445 if (surface)
446 {
447 SDL_FreeSurface(surface);
448 surface = NULL;
449 }
451 SDL_Surface * tmp;
452 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
453 Options::width, Options::height,
454 Options::bpp, 0, 0, 0, 0))
455 == NULL)
456 {
457 std::cerr << "SDL_CreateRGBSurface() failed in "
458 << "App::create_new_surface(): "
459 << SDL_GetError() << std::endl;
460 return false;
461 }
463 surface = SDL_DisplayFormatAlpha(tmp);
464 SDL_FreeSurface(tmp);
466 return true;
467 }
470 ////////////////////////////////////////////////////////////////////////////////
471 bool init_sdl()
472 {
473 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
474 {
475 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
476 return false;
477 }
479 if ((window = SDL_CreateWindow("SDL 1.3 test",
480 SDL_WINDOWPOS_UNDEFINED,
481 SDL_WINDOWPOS_UNDEFINED,
482 Options::width, Options::height,
483 0)) == 0)
484 {
485 std::cerr << "SDL_CreateWindow() failed: "
486 << SDL_GetError() << std::endl;
487 return false;
488 }
490 // Go through the available renderers and find the one we need.
491 // Do not use the x11 renderer. At least for now it is not working
492 // correctly..
493 int max_drivers = SDL_GetNumRenderDrivers();
494 if (max_drivers < 1)
495 {
496 std::cerr << "SDL_GetNumRenderDrivers() failed: "
497 << SDL_GetError() << std::endl;
498 return false;
499 }
501 int driver = -1;
502 for(int index = 0; index < max_drivers ; index++)
503 {
504 SDL_RendererInfo info;
505 if(SDL_GetRenderDriverInfo(index, &info) != -1)
506 {
507 if (strncmp(info.name, "software", 8) == 0) driver = index;
508 std::cerr << "Driver " << info.name << " index: "
509 << index << std::endl;
510 }
511 else
512 {
513 std::cerr << "SDL_GetRenderDriverInfo() for index " << index
514 << " failed: "
515 << SDL_GetError() << std::endl;
516 return false;
517 }
518 }
520 if (driver == -1)
521 {
522 std::cerr << "SDL software renderer not available!" << std::endl;
523 return false;
524 }
526 if (SDL_CreateRenderer(window, -1,
527 SDL_RENDERER_PRESENTFLIP2 |
528 SDL_RENDERER_ACCELERATED) != 0)
529 {
530 std::cerr << "SDL_CreateRenderer() failed: "
531 << SDL_GetError() << std::endl;
532 return false;
533 }
535 SDL_RendererInfo info;
536 if (SDL_GetRendererInfo(&info) == -1)
537 {
538 std::cerr << "SDL_GetRendererInfo() failed: "
539 << SDL_GetError() << std::endl;
540 return false;
541 }
542 std::cerr << "Renderer: " << info.name << std::endl;
545 if (SDL_AddTimer(10, App::timer_callback, NULL) == NULL)
546 {
547 std::cerr << "SDL_AddTimer() failed: "
548 << SDL_GetError() << std::endl;
549 return false;
550 }
552 return true;
553 }
556 ////////////////////////////////////////////////////////////////////////////////
557 int App::mouse_move_width(int mx, int my)
558 {
559 int move_width = (mx - selection_x);
560 if (mx > selection_x)
561 {
562 move_width *= -1;
563 }
565 int tmp_i = (my - selection_y);
566 if (my < selection_y)
567 {
568 tmp_i *= -1;
569 }
571 if (tmp_i > move_width)
572 move_width = tmp_i;
574 return move_width;
575 }
578 ////////////////////////////////////////////////////////////////////////////////
579 void App::set_caption()
580 {
581 std::stringstream ss;
582 ss << fractal->get_display_name() << " / "
583 << "Real: " << fractal->get_re_min()
584 << " to "<< fractal->get_re_min() + fractal->get_size()
585 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
586 << " to " << fractal->get_im_max()
587 << " / Max iterations: " << fractal->get_max_iter();
588 SDL_SetWindowTitel(window, ss.str().c_str());
589 }
592 ////////////////////////////////////////////////////////////////////////////////
593 Uint32 App::timer_callback(Uint32 interval, void *param)
594 {
595 SDL_Event e;
597 e.type = SDL_USEREVENT;
598 e.user.code = 0;
599 e.user.data1 = NULL;
600 e.user.data2 = NULL;
601 SDL_PushEvent(& e);
603 return interval;
604 }
607 ////////////////////////////////////////////////////////////////////////////////
608 void App::set_julia_k(int x, int y)
609 {
610 FractalFactory * fractal_factory = get_Fractal_map()[typeid(Julia).name()];
611 if (!fractal_factory)
612 {
613 std::cerr << "Julia set not available!" << std::endl;
614 return;
615 }
616 else
617 {
618 save_view();
620 delete fractal;
621 fractal = fractal_factory->create();
622 fractal->init(surf_fractal);
624 double k[2];
625 k[0]= fractal->get_re_min() + x *
626 (fractal->get_size())/(surf_fractal->w - 1);
627 k[1] = fractal->get_im_max() - y *
628 (fractal->get_size())/(surf_fractal->h - 1);
630 fractal->set_option("k", k);
631 fractal->set_calc_needed();
632 redraw = true;
634 set_caption();
635 setting_julia_k = false;
636 }
637 }
640 ////////////////////////////////////////////////////////////////////////////////
642 void App::restore_view()
643 {
644 if (old_views.size() > 0)
645 {
646 View * v = old_views.back();
647 old_views.pop_back();
649 if (v->get_type().compare(fractal->get_fractal_name()) != 0)
650 {
651 delete fractal;
652 FractalFactory * fractal_factory = get_Fractal_map()[v->get_type()];
653 if (!fractal_factory)
654 {
655 std::cerr << "Cannot restore old view: unknown Fractal type "
656 << v->get_type() << std::endl;
657 SDL_Event event;
658 event.type = SDL_QUIT;
659 SDL_PushEvent(&event);
660 return;
661 }
662 fractal = fractal_factory->create();
663 fractal->init(surf_fractal);
664 }
666 fractal->restore_view(v);
667 delete v;
668 }
669 }
672 ////////////////////////////////////////////////////////////////////////////////
673 void App::save_view()
674 {
675 View * v = fractal->save_view();
676 old_views.push_back(v);
677 }
680 ////////////////////////////////////////////////////////////////////////////////
681 bool App::save_image()
682 {
683 int i = App::get_next_file_num();
684 std::stringstream name;
685 name << "fracter-" << std::setw(3) << std::setfill('0') << i << ".bmp";
687 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
688 {
689 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
690 return false;
691 }
693 std::cerr << "Saved image as " << name.str() << std::endl;
695 return true;
696 }
699 #if defined(WIN32)
700 ////////////////////////////////////////////////////////////////////////////////
701 //Code for Windows
702 int App::get_next_file_num()
703 {
704 static int i = 0;
706 if (i == 0)
707 {
708 WIN32_FIND_DATA ffd;
709 HANDLE hFind = INVALID_HANDLE_VALUE;
711 char * file_spec = "fracter-???.bmp";
712 hFind = FindFirstFile(file_spec, &ffd);
713 if (hFind != INVALID_HANDLE_VALUE)
714 {
715 std::string s;
716 std::stringstream ss;
717 do
718 {
719 std::cerr << ffd.cFileName << std::endl;
720 ss << ffd.cFileName;
721 if (ss.str().size() == 18)
722 {
723 s = ss.str().substr(0, 11);
724 if ( StrCmpI(s.c_str(), "fracter-") == 0 )
725 {
726 s = ss.str().substr(14, 4);
727 if (StrCmpI(s.c_str(), ".bmp") == 0)
728 {
729 s = ss.str().substr(11, 3);
730 int j = atoi(s.c_str());
731 if (j > i) i = j;
732 }
733 }
734 }
735 }
736 while (FindNextFile(hFind, &ffd) != 0);
737 }
738 }
740 i++;
741 return i;
742 }
746 #else
747 ////////////////////////////////////////////////////////////////////////////////
748 // Code for Linux
750 int App::scandir_filter(const struct dirent * d)
751 {
752 if (memcmp(d->d_name, "fracter-", 8) != 0) return 0;
753 if (memcmp(d->d_name+11, ".bmp", 4) != 0) return 0;
754 if (isdigit(d->d_name[8])
755 && isdigit(d->d_name[9])
756 && isdigit(d->d_name[10]))
757 return 1;
758 return 0;
759 }
762 int App::get_next_file_num()
763 {
764 static int i = 0;
766 if (i == 0)
767 {
768 struct dirent ** file_list;
769 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
770 if (num_files != 0)
771 sscanf(file_list[num_files-1]->d_name, "fracter-%03d.bmp", &i);
772 }
773 i++;
775 return i;
776 }
778 #endif