view src/App.cpp @ 5:d691ce98f406

OK. This is the first release. Honest.
author Eris Caffee <discordia@eldalin.com>
date Fri, 12 Nov 2010 23:58:48 -0600
parents df02a7de7fe2
children 104dff305563
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 std::vector<View *> App::old_views;
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 fractal (NULL)
74 {
75 }
78 ////////////////////////////////////////////////////////////////////////////////
79 App::~App()
80 {
81 old_views.clear();
82 }
85 ////////////////////////////////////////////////////////////////////////////////
87 int App::run()
88 {
89 if (init() == false)
90 return 1;
92 SDL_Event event;
94 while (running)
95 {
96 SDL_WaitEvent(&event);
97 do
98 {
99 on_event(&event);
100 }
101 while (SDL_PollEvent(&event));
103 update();
105 if (redraw)
106 render();
107 }
109 cleanup();
111 return 0;
112 }
115 ////////////////////////////////////////////////////////////////////////////////
116 bool App::init()
117 {
118 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
119 {
120 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
121 return false;
122 }
124 if ((surf_display = SDL_SetVideoMode(Options::width,
125 Options::height,
126 Options::bpp,
127 SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)
128 {
129 std::cerr << "SDL_SetVideoMode() failed: "
130 << SDL_GetError() << std::endl;
131 return false;
132 }
134 SDL_EnableKeyRepeat(500, 100);
137 if (!create_new_surface(surf_selection)) { return false; }
138 if (!create_new_surface(surf_fractal)) { return false; }
141 std::cerr << "Available fractal types are" << std::endl;
142 std::map <std::string, FractalFactory *> mymap = get_Fractal_map();
143 std::map <std::string, FractalFactory *>::iterator it;
144 for ( it=mymap.begin() ; it != mymap.end(); it++ )
145 std::cerr << " " << (*it).first << std::endl;
147 FractalFactory * fractal_factory =
148 get_Fractal_map()[typeid(Mandelbrot).name()];
149 if (!fractal_factory)
150 {
151 std::cerr << "Mandelbrot set not available!" << std::endl;
152 return false;
153 }
154 fractal = fractal_factory->create();
155 fractal->init(surf_fractal);
156 redraw = true;
158 can_set_julia_k = true;
161 set_caption();
163 SDL_AddTimer(10, App::timer_callback, NULL);
165 return true;
166 }
169 ////////////////////////////////////////////////////////////////////////////////
171 void App::update()
172 {
173 }
176 ////////////////////////////////////////////////////////////////////////////////
178 void App::render()
179 {
180 if (redraw)
181 {
182 // Start with a clean slate.
183 SDL_FillRect(surf_display, &surf_display->clip_rect,
184 SDL_MapRGB(surf_display->format, 0, 0, 255));
186 fractal->calc_set();
187 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
189 // draw a selection box
190 if (setting_zoom)
191 {
192 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
193 }
195 // Show it to the user.
196 SDL_Flip(surf_display);
198 redraw = false;
199 }
200 }
203 ////////////////////////////////////////////////////////////////////////////////
205 void App::cleanup()
206 {
208 if (surf_selection)
209 {
210 SDL_FreeSurface(surf_selection);
211 surf_selection = NULL;
212 }
213 if (surf_display)
214 {
215 SDL_FreeSurface(surf_display);
216 surf_display = NULL;
217 }
219 SDL_Quit();
220 }
223 ////////////////////////////////////////////////////////////////////////////////
225 void App::on_KeyDown(SDL_KeyboardEvent key)
226 {
227 switch (key.keysym.sym)
228 {
229 case SDLK_ESCAPE:
230 case SDLK_q:
231 SDL_Event event;
232 event.type = SDL_QUIT;
233 SDL_PushEvent(&event);
234 break;
236 case SDLK_b:
237 restore_view();
238 redraw = true;
239 set_caption();
240 break;
242 case SDLK_s:
243 save_image();
244 break;
246 case SDLK_UP:
247 fractal->inc_max_iter();
248 set_caption();
249 break;
250 case SDLK_DOWN:
251 fractal->dec_max_iter();
252 set_caption();
253 break;
256 case SDLK_m:
257 if (fractal)
258 {
259 FractalFactory * fractal_factory =
260 get_Fractal_map()[typeid(Mandelbrot).name()];
261 if (!fractal_factory)
262 {
263 std::cerr << "Mandelbrot set not available!" << std::endl;
264 SDL_Event event;
265 event.type = SDL_QUIT;
266 SDL_PushEvent(&event);
267 }
268 else
269 {
270 save_view();
271 delete fractal;
272 fractal = fractal_factory->create();
273 fractal->init(surf_fractal);
274 redraw = true;
275 set_caption();
276 can_set_julia_k = true;
277 }
278 }
279 break;
281 case SDLK_j:
282 if (fractal)
283 {
284 FractalFactory * fractal_factory =
285 get_Fractal_map()[typeid(Julia).name()];
286 if (!fractal_factory)
287 {
288 std::cerr << "Julia set not available!" << std::endl;
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 = false;
299 }
300 }
301 break;
303 case SDLK_k:
304 if (can_set_julia_k)
305 {
306 if (setting_julia_k) setting_julia_k = false;
307 else setting_julia_k = true;
308 }
309 break;
311 default:
312 break;
313 }
314 }
317 ////////////////////////////////////////////////////////////////////////////////
318 void App::on_KeyUp(SDL_KeyboardEvent key)
319 {
320 switch (key.keysym.sym)
321 {
322 case SDLK_UP:
323 fractal->set_calc_needed();
324 redraw = true;
325 break;
326 case SDLK_DOWN:
327 fractal->set_calc_needed();
328 redraw = true;
329 break;
330 default:
331 break;
332 }
333 }
336 ////////////////////////////////////////////////////////////////////////////////
337 void App::on_MouseMove(SDL_MouseMotionEvent motion)
338 {
339 if (setting_zoom)
340 {
341 Uint32 clear = SDL_MapRGBA(surf_selection->format,
342 0, 0, 0, SDL_ALPHA_TRANSPARENT);
343 Uint32 white = SDL_MapRGBA(surf_selection->format,
344 255, 255, 255, SDL_ALPHA_OPAQUE);
346 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
348 SDL_Rect rect;
349 rect.x = selection_x;
350 rect.y = selection_y;
351 int w = mouse_move_width(motion.x, motion.y);
352 rect.w = rect.h = w;
354 SDL_FillRect(surf_selection, &rect, white);
356 rect.x = selection_x + 1;
357 rect.y = selection_y + 1;
358 rect.w = rect.h = w - 2;
360 SDL_FillRect(surf_selection, &rect, clear);
362 redraw = true;
363 }
364 }
366 ////////////////////////////////////////////////////////////////////////////////
368 void App::on_LButtonDown(SDL_MouseButtonEvent button)
369 {
370 if (setting_julia_k)
371 {
372 set_julia_k(button.x, button.y);
373 }
374 else
375 {
376 selection_x = button.x;
377 selection_y = button.y;
378 setting_zoom = true;
379 }
380 }
383 ////////////////////////////////////////////////////////////////////////////////
385 void App::on_LButtonUp(SDL_MouseButtonEvent button)
386 {
387 if (setting_zoom)
388 {
389 selection_width = mouse_move_width(button.x, button.y);
390 if ( (button.x <= 0) ||
391 (button.x >= surf_display->w - 1) ||
392 (button.y <= 0) ||
393 (button.y >= surf_display->h - 1) ||
394 (selection_width == 0))
395 {
396 // Selection cancelled
397 setting_zoom = false;
398 return;
399 }
401 // calculate new min/max re/im
402 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
403 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
405 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
406 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
407 double new_size = selection_width * Re_scale;
409 save_view();
411 fractal->set_re_min(new_re_min);
412 fractal->set_im_max(new_im_max);
413 fractal->set_size(new_size);
414 fractal->set_calc_needed();
415 redraw = true;
417 set_caption();
419 setting_zoom = false;
420 }
421 }
424 ////////////////////////////////////////////////////////////////////////////////
425 void App::on_Resize(SDL_ResizeEvent resize)
426 {
427 redraw = true;
428 }
431 ////////////////////////////////////////////////////////////////////////////////
432 void App::on_Expose()
433 {
434 redraw = true;
435 }
438 ////////////////////////////////////////////////////////////////////////////////
440 void App::on_Quit()
441 {
442 running = false;
443 }
446 ////////////////////////////////////////////////////////////////////////////////
447 int App::mouse_move_width(int mx, int my)
448 {
449 int move_width = (mx - selection_x);
450 if (mx > selection_x)
451 {
452 move_width *= -1;
453 }
455 int tmp_i = (my - selection_y);
456 if (my < selection_y)
457 {
458 tmp_i *= -1;
459 }
461 if (tmp_i > move_width)
462 move_width = tmp_i;
464 return move_width;
465 }
468 ////////////////////////////////////////////////////////////////////////////////
469 bool App::create_new_surface(SDL_Surface * &surface)
470 {
471 if (surface)
472 {
473 SDL_FreeSurface(surface);
474 surface = NULL;
475 }
477 SDL_Surface * tmp;
478 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
479 Options::width, Options::height,
480 Options::bpp, 0, 0, 0, 0))
481 == NULL)
482 {
483 std::cerr << "SDL_CreateRGBSurface() failed in "
484 << "App::create_new_surface(): "
485 << SDL_GetError() << std::endl;
486 return false;
487 }
489 surface = SDL_DisplayFormatAlpha(tmp);
490 SDL_FreeSurface(tmp);
492 return true;
493 }
496 ////////////////////////////////////////////////////////////////////////////////
497 void App::set_julia_k(int x, int y)
498 {
499 FractalFactory * fractal_factory = get_Fractal_map()[typeid(Julia).name()];
500 if (!fractal_factory)
501 {
502 std::cerr << "Julia set not available!" << std::endl;
503 return;
504 }
505 else
506 {
507 save_view();
509 delete fractal;
510 fractal = fractal_factory->create();
511 fractal->init(surf_fractal);
513 double k[2];
514 k[0]= fractal->get_re_min() + x *
515 (fractal->get_size())/(surf_fractal->w - 1);
516 k[1] = fractal->get_im_max() - y *
517 (fractal->get_size())/(surf_fractal->h - 1);
519 fractal->set_option("k", k);
520 fractal->set_calc_needed();
521 redraw = true;
523 set_caption();
524 setting_julia_k = false;
525 }
526 }
529 ////////////////////////////////////////////////////////////////////////////////
531 void App::restore_view()
532 {
533 if (old_views.size() > 0)
534 {
535 View * v = old_views.back();
536 old_views.pop_back();
538 if (v->get_type().compare(fractal->get_fractal_name()) != 0)
539 {
540 delete fractal;
541 FractalFactory * fractal_factory = get_Fractal_map()[v->get_type()];
542 if (!fractal_factory)
543 {
544 std::cerr << "Cannot restore old view: unknown Fractal type "
545 << v->get_type() << std::endl;
546 SDL_Event event;
547 event.type = SDL_QUIT;
548 SDL_PushEvent(&event);
549 return;
550 }
551 fractal = fractal_factory->create();
552 fractal->init(surf_fractal);
553 }
555 fractal->restore_view(v);
556 delete v;
557 }
558 }
561 ////////////////////////////////////////////////////////////////////////////////
562 void App::save_view()
563 {
564 View * v = fractal->save_view();
565 old_views.push_back(v);
566 }
569 ////////////////////////////////////////////////////////////////////////////////
570 void App::set_caption()
571 {
572 std::stringstream ss;
573 ss << fractal->get_display_name() << " / "
574 << "Real: " << fractal->get_re_min()
575 << " to "<< fractal->get_re_min() + fractal->get_size()
576 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
577 << " to " << fractal->get_im_max()
578 << " / Max iterations: " << fractal->get_max_iter();
579 SDL_WM_SetCaption(ss.str().c_str(), NULL);
580 }
583 ////////////////////////////////////////////////////////////////////////////////
584 bool App::save_image()
585 {
586 int i = App::get_next_file_num();
587 std::stringstream name;
588 name << "fracter-" << std::setw(3) << std::setfill('0') << i << ".bmp";
590 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
591 {
592 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
593 return false;
594 }
596 std::cerr << "Saved image as " << name.str() << std::endl;
598 return true;
599 }
602 ////////////////////////////////////////////////////////////////////////////////
603 Uint32 App::timer_callback(Uint32 interval, void *param)
604 {
605 SDL_Event e;
607 e.type = SDL_USEREVENT;
608 e.user.code = 0;
609 e.user.data1 = NULL;
610 e.user.data2 = NULL;
611 SDL_PushEvent(& e);
613 return interval;
614 }
617 #if defined(WIN32)
618 ////////////////////////////////////////////////////////////////////////////////
619 //Code for Windows
620 int App::get_next_file_num()
621 {
622 static int i = 0;
624 if (i == 0)
625 {
626 WIN32_FIND_DATA ffd;
627 HANDLE hFind = INVALID_HANDLE_VALUE;
629 char * file_spec = "fracter-???.bmp";
630 hFind = FindFirstFile(file_spec, &ffd);
631 if (hFind != INVALID_HANDLE_VALUE)
632 {
633 std::string s;
634 std::stringstream ss;
635 do
636 {
637 std::cerr << ffd.cFileName << std::endl;
638 ss << ffd.cFileName;
639 if (ss.str().size() == 18)
640 {
641 s = ss.str().substr(0, 11);
642 if ( StrCmpI(s.c_str(), "fracter-") == 0 )
643 {
644 s = ss.str().substr(14, 4);
645 if (StrCmpI(s.c_str(), ".bmp") == 0)
646 {
647 s = ss.str().substr(11, 3);
648 int j = atoi(s.c_str());
649 if (j > i) i = j;
650 }
651 }
652 }
653 }
654 while (FindNextFile(hFind, &ffd) != 0);
655 }
656 }
658 i++;
659 return i;
660 }
664 #else
665 ////////////////////////////////////////////////////////////////////////////////
666 // Code for Linux
668 int App::scandir_filter(const struct dirent * d)
669 {
670 if (memcmp(d->d_name, "fracter-", 8) != 0) return 0;
671 if (memcmp(d->d_name+11, ".bmp", 4) != 0) return 0;
672 if (isdigit(d->d_name[8])
673 && isdigit(d->d_name[9])
674 && isdigit(d->d_name[10]))
675 return 1;
676 return 0;
677 }
680 int App::get_next_file_num()
681 {
682 static int i = 0;
684 if (i == 0)
685 {
686 struct dirent ** file_list;
687 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
688 if (num_files != 0)
689 sscanf(file_list[num_files-1]->d_name, "fracter-%03d.bmp", &i);
690 }
691 i++;
693 return i;
694 }
696 #endif