1 /**************************************************************************/
2 /*  Copyright 2012 Tim Day                                                */
3 /*                                                                        */
4 /*  This file is part of Evolvotron                                       */
5 /*                                                                        */
6 /*  Evolvotron is free software: you can redistribute it and/or modify    */
7 /*  it under the terms of the GNU General Public License as published by  */
8 /*  the Free Software Foundation, either version 3 of the License, or     */
9 /*  (at your option) any later version.                                   */
10 /*                                                                        */
11 /*  Evolvotron is distributed in the hope that it will be useful,         */
12 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
13 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
14 /*  GNU General Public License for more details.                          */
15 /*                                                                        */
16 /*  You should have received a copy of the GNU General Public License     */
17 /*  along with Evolvotron.  If not, see <http://www.gnu.org/licenses/>.   */
18 /**************************************************************************/
19 
20 /*! \file
21   \brief Implementation of class EvolvotronMain.
22   \todo Eliminate need to include function.h (and instantiate lots of stuff) by moving more into function_node.h/.cpp
23 */
24 
25 #include "evolvotron_main.h"
26 
27 #include "dialog_about.h"
28 #include "dialog_help.h"
29 #include "dialog_mutation_parameters.h"
30 #include "dialog_render_parameters.h"
31 #include "dialog_functions.h"
32 #include "dialog_favourite.h"
33 #include "function_node.h"
34 #include "function_post_transform.h"
35 #include "function_pre_transform.h"
36 #include "function_top.h"
37 
purge()38 void EvolvotronMain::History::purge()
39 {
40   if (_archive.size()>0) _archive.pop_back();
41 }
42 
History(EvolvotronMain * m)43 EvolvotronMain::History::History(EvolvotronMain* m)
44 :_main(m)
45  ,max_slots(32)
46 {
47   // Don't call _main->set_undoable because menus probably haven't been constructed yet.
48 }
49 
~History()50 EvolvotronMain::History::~History()
51 {}
52 
log_status() const53 void EvolvotronMain::History::log_status() const
54 {
55   std::clog << "[History: " << _archive.size() << " records (";
56   for (Archive::const_iterator it=_archive.begin();it!=_archive.end();it++)
57     std::clog << (it!=_archive.begin() ? "," : "") << (*it).second.size();
58   std::clog << ")]\n";
59 }
60 
goodbye(MutatableImageDisplay * display)61 void EvolvotronMain::History::goodbye(MutatableImageDisplay* display)
62 {
63   // First pass to delete any individual items for that display.
64   for (Archive::iterator it=_archive.begin();it!=_archive.end();it++)
65     (*it).second.erase(display);
66 
67   // Second pass to delete any undo items which are now empty
68   Archive::iterator it=_archive.begin();
69   while (it!=_archive.end())
70     {
71       if ((*it).second.empty()) it=_archive.erase(it);
72       it++;
73     }
74 
75   // Set menu label again in case we've changed the topmost item
76   const std::string action_name(_archive.empty() ? "" : _archive.front().first);
77   _main->set_undoable(undoable(),action_name);
78 }
79 
replacing(MutatableImageDisplay * display)80 void EvolvotronMain::History::replacing(MutatableImageDisplay* display)
81 {
82   if (_archive.size()==0)
83     {
84       begin_action("");
85     }
86 
87   const boost::shared_ptr<const MutatableImage> image_function=display->image_function();
88 
89   if (image_function.get())
90     {
91       const boost::shared_ptr<const MutatableImage> saved_image_function(image_function->deepclone(image_function->locked()));
92       _archive.front().second.insert(std::make_pair(display,saved_image_function));
93     }
94 }
95 
96 /*! Only creates a new slot for display-image pairs if the current top one (if any) isn't empty.
97  */
begin_action(const std::string & action_name)98 void EvolvotronMain::History::begin_action(const std::string& action_name)
99 {
100   if (_archive.size()==0 || _archive.front().second.size()!=0)
101     {
102       _archive.push_front(ArchiveRecord());
103 
104       assert(_archive.front().second.size()==0);
105     }
106 
107   _archive.front().first=action_name;
108 
109   while (_archive.size()>max_slots)
110     {
111       purge();
112     }
113 }
114 
end_action()115 void EvolvotronMain::History::end_action()
116 {
117   const std::string action_name(_archive.empty() ? "" : _archive.front().first);
118   _main->set_undoable(undoable(),action_name);
119 
120   log_status();
121 }
122 
undoable()123 bool EvolvotronMain::History::undoable()
124 {
125   if (_archive.size()==0)
126     {
127       return false;
128     }
129   else if (_archive.front().second.size()==0)
130     {
131       _archive.pop_front();
132       return undoable();
133     }
134   else
135     {
136       return true;
137     }
138 }
139 
undo()140 void EvolvotronMain::History::undo()
141 {
142   if (_archive.size()==0)
143     {
144       // Shouldn't ever see this if Undo menu item is correctly greyed out.
145       QMessageBox::warning(_main,"Evolvotron","Sorry, cannot undo any further");
146     }
147   else if (_archive.front().second.size()==0)
148     {
149       _archive.pop_front();
150       undo();
151     }
152   else
153     {
154       for (ArchiveRecordEntries::iterator it=_archive.front().second.begin();
155 	   it!=_archive.front().second.end();
156 	   it++
157 	   )
158 	{
159 	  _main->restore((*it).first,(*it).second,_archive.size()>1);
160 	}
161       _archive.pop_front();
162     }
163 
164   const std::string action_name(_archive.empty() ? "" : _archive.front().first);
165   _main->set_undoable(undoable(),action_name);
166 }
167 
last_spawned_image(const boost::shared_ptr<const MutatableImage> & image,SpawnMemberFn method)168 void EvolvotronMain::last_spawned_image(const boost::shared_ptr<const MutatableImage>& image,SpawnMemberFn method)
169 {
170   _last_spawned_image=image;
171   _last_spawn_method=method;
172 }
173 
174 /*! Constructor sets up GUI components and fires up QTimer.
175   Initialises mutation parameters using time, so different every time.
176  */
EvolvotronMain(QWidget * parent,const QSize & grid_size,uint frames,uint framerate,uint n_threads,bool separate_farm_for_enlargements,int niceness_grid,int niceness_enlargements,bool start_fullscreen,bool start_menuhidden,bool autocool,bool jitter,uint multisample_level,bool function_debug_mode,bool linear_zsweep,bool spheremap,const std::vector<std::string> & startup_filenames,bool startup_shuffle)177 EvolvotronMain::EvolvotronMain
178 (
179  QWidget* parent,
180  const QSize& grid_size,
181  uint frames,
182  uint framerate,
183  uint n_threads,
184  bool separate_farm_for_enlargements,
185  int niceness_grid,
186  int niceness_enlargements,
187  bool start_fullscreen,
188  bool start_menuhidden,
189  bool autocool,
190  bool jitter,
191  uint multisample_level,
192  bool function_debug_mode,
193  bool linear_zsweep,
194  bool spheremap,
195  const std::vector<std::string>& startup_filenames,
196  bool startup_shuffle
197  )
198   :QMainWindow(parent)
199   ,_history(new EvolvotronMain::History(this))
200   ,_linear_zsweep(linear_zsweep)
201   ,_spheremap(spheremap)
202   ,_startup_filenames(startup_filenames)
203   ,_startup_shuffle(startup_shuffle)
204   ,_mutation_parameters(time(0),autocool,function_debug_mode,this)
205   ,_render_parameters(jitter,multisample_level,this)
206   ,_statusbar_tasks_main(0)
207   ,_statusbar_tasks_enlargement(0)
208   ,_last_spawn_method(&EvolvotronMain::spawn_normal)
209 {
210   setAttribute(Qt::WA_DeleteOnClose,true);
211   setAttribute(Qt::WA_QuitOnClose,true);
212 
213   setMinimumSize(640,480);
214 
215   // Need to create this first or DialogMutationParameters might cause one to be created too.
216   _statusbar=new QStatusBar;
217   _statusbar->setSizeGripEnabled(true);
218   setStatusBar(_statusbar);
219 
220   _statusbar->addWidget(_statusbar_tasks_label=new QLabel("Ready"));
221 
222   _dialog_about=new DialogAbout(this,n_threads,separate_farm_for_enlargements);
223   _dialog_help_short=new DialogHelp(this,false);
224   _dialog_help_long=new DialogHelp(this,true);
225 
226   _dialog_mutation_parameters=new DialogMutationParameters(this,&_mutation_parameters);
227 
228   _dialog_render_parameters=new DialogRenderParameters(this,&_render_parameters);
229 
230   _dialog_functions=new DialogFunctions(this,&_mutation_parameters);
231 
232   _dialog_favourite=new DialogFavourite(this);
233 
234   _popupmenu_file=menuBar()->addMenu("&File");
235   _popupmenu_file->addAction("Reset (Reset mutation parameters, clear locks)",this,SLOT(reset_cold()),QKeySequence("r"));
236   _popupmenu_file->addAction("Restart (Preserve mutation parameters and locks)",this,SLOT(reset_warm()),QKeySequence("t"));
237   _popupmenu_file->addAction("Remix (Randomize function weights and restart)",this,SLOT(reset_randomized()),QKeySequence("x"));
238   _popupmenu_file->addSeparator();
239   _popupmenu_file->addAction("Quit",qApp,SLOT(quit()),QKeySequence("q"));
240 
241   _popupmenu_edit=menuBar()->addMenu("&Edit");
242   _popupmenu_edit_undo_action=_popupmenu_edit->addAction("Undo",this,SLOT(undo()),QKeySequence("u"));
243   _popupmenu_edit_undo_action->setEnabled(false);
244   _popupmenu_edit->addSeparator();
245   _popupmenu_edit->addAction("Simplify all functions",this,SLOT(simplify_constants()));
246 
247   _popupmenu_settings=menuBar()->addMenu("Se&ttings");
248   _popupmenu_settings->addAction("Mutation parameters...",_dialog_mutation_parameters,SLOT(show()));
249   _popupmenu_settings->addAction("Function weightings...",_dialog_functions,SLOT(show()));
250   _popupmenu_settings->addAction("Favourite function...",_dialog_favourite,SLOT(show()));
251 
252   _popupmenu_settings->addSeparator();
253 
254   _popupmenu_settings->addAction("Render parameters...",_dialog_render_parameters,SLOT(show()));
255 
256   _popupmenu_settings->addSeparator();
257 
258   _menu_action_fullscreen=_popupmenu_settings->addAction("Fullscreen",this,SLOT(toggle_fullscreen()),QKeySequence("f"));
259   _menu_action_fullscreen->setCheckable(true);
260   _menu_action_fullscreen->setChecked(start_fullscreen);
261   _menu_action_hide_menu=_popupmenu_settings->addAction("Hide menu and statusbar",this,SLOT(toggle_hide_menu()),QKeySequence("m"));
262   _menu_action_hide_menu->setCheckable(true);
263   _menu_action_hide_menu->setChecked(start_menuhidden);
264 
265   //! This doesn't seem to do anything (supposed to push help menu over to far end ?)
266   menuBar()->addSeparator();
267 
268   _popupmenu_help=menuBar()->addMenu("&Help");
269   _popupmenu_help->addAction("Quick Reference",_dialog_help_short,SLOT(show()));
270   _popupmenu_help->addAction("User Manual",_dialog_help_long,SLOT(show()));
271   _popupmenu_help->addSeparator();
272   _popupmenu_help->addAction("About",_dialog_about,SLOT(show()));
273 
274   _checkbox_autocool_enable=new QCheckBox("Autocool");
275   _checkbox_autocool_enable->setToolTip("Autocooling gradually reduces the chance and magnitude of mutations with time.");
276   _label_autocool_enable=new QLabel("");  // Used to display generation count
277   _button_autocool_reheat=new QPushButton("Reheat");
278   _button_autocool_reheat->setToolTip("Reheat restarts the autocooling generation count, restoring the full strength of mutations.");
279 
280   connect(_checkbox_autocool_enable,SIGNAL(stateChanged(int)),_dialog_mutation_parameters,SLOT(changed_autocool_enable(int)));
281   connect(_button_autocool_reheat,SIGNAL(clicked()),_dialog_mutation_parameters,SLOT(reheat()));
282 
283   _statusbar->addPermanentWidget(_checkbox_autocool_enable);
284   _statusbar->addPermanentWidget(_label_autocool_enable);
285   _statusbar->addPermanentWidget(_button_autocool_reheat);
286 
287   connect(
288 	  &_render_parameters,SIGNAL(changed()),
289 	  this,SLOT(render_parameters_changed())
290 	  );
291 
292   connect(
293 	  &_mutation_parameters,SIGNAL(changed()),
294 	  this,SLOT(mutation_parameters_changed())
295 	  );
296 
297 
298   _farm[0]=std::unique_ptr<MutatableImageComputerFarm>(new MutatableImageComputerFarm(n_threads,niceness_grid));
299   if (separate_farm_for_enlargements)
300     {
301       _farm[1]=std::unique_ptr<MutatableImageComputerFarm>(new MutatableImageComputerFarm(n_threads,niceness_enlargements));
302     }
303 
304   _grid=new QWidget;
305   QGridLayout*const grid_layout=new QGridLayout;
306   _grid->setLayout(grid_layout);
307   setCentralWidget(_grid);
308 
309   //! \todo frames and framerate should be retained and modifiable from the GUI
310   for (int r=0;r<grid_size.height();r++)
311     for (int c=0;c<grid_size.width();c++)
312       {
313 	MutatableImageDisplay*const d=new MutatableImageDisplay(this,true,false,QSize(0,0),frames,framerate);
314 	grid_layout->addWidget(d,r,c);
315 	displays().push_back(d);
316       }
317 
318   _timer=new QTimer(this);
319   connect(
320 	  _timer,SIGNAL(timeout()),
321 	  this, SLOT(tick())
322 	  );
323   // Run tick() at 100Hz
324   _timer->start(10);
325 
326   if (start_fullscreen)
327     {
328       showFullScreen();
329     }
330 
331   if (start_menuhidden)
332     {
333       menuBar()->hide();
334       statusBar()->hide();
335     }
336 }
337 
338 /*! If this is being destroyed then the whole application is going down.
339   Could be ordering issues with the display destructors though.
340  */
~EvolvotronMain()341 EvolvotronMain::~EvolvotronMain()
342 {
343   std::clog << "Evolvotron shut down begun...\n";
344 
345   // Orphan any displays which outlived us (and clear their images)  (look out: shutdown order is Qt-determined)
346   std::clog << "(There are " << _known_displays.size() << " displays remaining)\n";
347   for (std::set<MutatableImageDisplay*>::const_iterator it=_known_displays.begin();it!=_known_displays.end();it++)
348     {
349       (*it)->image_function(boost::shared_ptr<const MutatableImage>(),true);
350       (*it)->main(0);
351     }
352 
353   std::clog << "...cleared displays, deleting farm...\n";
354 
355   // Shut down the compute farms
356   _farm[0].reset();
357   _farm[1].reset();
358 
359   std::clog << "...deleted farm, deleting history...\n";
360 
361   // Clean up records.
362   _last_spawned_image.reset();
363   _history.reset();
364 
365   std::clog << "...deleted history\n";
366 
367   std::clog << "...completed Evolvotron shutdown\n";
368 }
369 
favourite_function(const std::string & f)370 bool EvolvotronMain::favourite_function(const std::string& f)
371 {
372   return _dialog_favourite->favourite_function(f);
373 }
374 
favourite_function_unwrapped(bool v)375 void EvolvotronMain::favourite_function_unwrapped(bool v)
376 {
377   _dialog_favourite->favourite_function_unwrapped(v);
378 }
379 
spawn_normal(const boost::shared_ptr<const MutatableImage> & image_function,MutatableImageDisplay * display,bool one_of_many)380 void EvolvotronMain::spawn_normal(const boost::shared_ptr<const MutatableImage>& image_function,MutatableImageDisplay* display,bool one_of_many)
381 {
382   boost::shared_ptr<const MutatableImage> new_image_function;
383 
384   do
385     {
386       new_image_function=image_function->mutated(mutation_parameters());
387     }
388   while (new_image_function->is_constant());
389 
390   history().replacing(display);
391   display->image_function(new_image_function,one_of_many);
392 }
393 
spawn_recoloured(const boost::shared_ptr<const MutatableImage> & image_function,MutatableImageDisplay * display,bool one_of_many)394 void EvolvotronMain::spawn_recoloured(const boost::shared_ptr<const MutatableImage>& image_function,MutatableImageDisplay* display,bool one_of_many)
395 {
396   std::unique_ptr<FunctionTop> new_root(image_function->top().typed_deepclone());
397 
398   new_root->reset_posttransform_parameters(mutation_parameters());
399   history().replacing(display);
400   boost::shared_ptr<const MutatableImage> it(new MutatableImage(new_root,image_function->sinusoidal_z(),image_function->spheremap(),false));
401   display->image_function(it,one_of_many);
402 }
403 
spawn_warped(const boost::shared_ptr<const MutatableImage> & image_function,MutatableImageDisplay * display,bool one_of_many)404 void EvolvotronMain::spawn_warped(const boost::shared_ptr<const MutatableImage>& image_function,MutatableImageDisplay* display,bool one_of_many)
405 {
406   std::unique_ptr<FunctionTop> new_root=std::unique_ptr<FunctionTop>(image_function->top().typed_deepclone());
407 
408   // Get the transform from whatever factory is currently set
409   const Transform transform(transform_factory()(mutation_parameters().rng01()));
410 
411   new_root->concatenate_pretransform_on_right(transform);
412   history().replacing(display);
413   boost::shared_ptr<const MutatableImage> it(new MutatableImage(new_root,image_function->sinusoidal_z(),image_function->spheremap(),false));
414   display->image_function(it,one_of_many);
415 }
416 
restore(MutatableImageDisplay * display,const boost::shared_ptr<const MutatableImage> & image_function,bool one_of_many)417 void EvolvotronMain::restore(MutatableImageDisplay* display,const boost::shared_ptr<const MutatableImage>& image_function,bool one_of_many)
418 {
419   if (is_known(display)) display->image_function(image_function,one_of_many);
420 }
421 
set_undoable(bool v,const std::string & action_name)422 void EvolvotronMain::set_undoable(bool v,const std::string& action_name)
423 {
424   _popupmenu_edit_undo_action->setText(QString(("Undo "+action_name).c_str()));
425   _popupmenu_edit_undo_action->setEnabled(v);
426 }
427 
respawn(MutatableImageDisplay * display)428 void EvolvotronMain::respawn(MutatableImageDisplay* display)
429 {
430   if (display->locked())
431     {
432       QMessageBox::warning(this,"Evolvotron","Cannot respawn a locked image.\nUnlock and try again.");
433     }
434   else
435     {
436       history().begin_action("respawn");
437 
438       if (last_spawned_image()==0)
439 	{
440 	  reset(display);
441 	}
442       else
443 	{
444 	  (this->*last_spawn_method())(last_spawned_image(),display,false);
445 	}
446 
447       history().end_action();
448     }
449 }
450 
spawn_all(MutatableImageDisplay * spawning_display,SpawnMemberFn method,const std::string & action_name)451 void EvolvotronMain::spawn_all(MutatableImageDisplay* spawning_display,SpawnMemberFn method,const std::string& action_name)
452 {
453   // Spawn potentially a bit sluggish so set the hourglass cursor.
454   QApplication::setOverrideCursor(Qt::WaitCursor);
455 
456   history().begin_action(action_name);
457 
458   // Issue new images (except to locked displays and to originator)
459   // This will cause them to abort any running tasks
460   const boost::shared_ptr<const MutatableImage> spawning_image_function(spawning_display->image_function());
461 
462   last_spawned_image(spawning_image_function,method);
463 
464   for (std::vector<MutatableImageDisplay*>::iterator it=displays().begin();it!=displays().end();it++)
465     {
466       if ((*it)!=spawning_display && !(*it)->locked())
467 	{
468 	  (this->*method)(spawning_image_function,(*it),true);
469 	}
470     }
471 
472   history().end_action();
473 
474   _mutation_parameters.autocool_generations_increment();
475 
476   QApplication::restoreOverrideCursor();
477 }
478 
479 
480 /*! If one of our sub displays has spawned, distribute a mutated copy of its image to the other non-locked images
481   in the mutation grid.
482  */
spawn_normal(MutatableImageDisplay * spawning_display)483 void EvolvotronMain::spawn_normal(MutatableImageDisplay* spawning_display)
484 {
485   spawn_all(
486 	    spawning_display,
487 	    &EvolvotronMain::spawn_normal,
488 	    "spawn"
489 	    );
490 }
491 
492 /*! This is the similar to spawn_normal, except images ARE NOT MUTATED after deepclone and have a final transform applied to change their colour.
493  */
spawn_recoloured(MutatableImageDisplay * spawning_display)494 void EvolvotronMain::spawn_recoloured(MutatableImageDisplay* spawning_display)
495 {
496   spawn_all(
497 	    spawning_display,
498 	    &EvolvotronMain::spawn_recoloured,
499 	    "spawn recoloured"
500 	    );
501 }
502 
503 /*! This is the similar to spawn_normal, except images ARE NOT MUTATED after deepclone
504   and have an initial transform (obtained from the supplied TransformFactory) applied to spatially warp them.
505  */
spawn_warped(MutatableImageDisplay * spawning_display,const TransformFactory & tfactory)506 void EvolvotronMain::spawn_warped(MutatableImageDisplay* spawning_display,const TransformFactory& tfactory)
507 {
508   transform_factory(tfactory);
509   spawn_all(
510 	    spawning_display,
511 	    &EvolvotronMain::spawn_warped,
512 	    "spawn warped"
513 	    );
514 }
515 
hello(MutatableImageDisplay * disp)516 void EvolvotronMain::hello(MutatableImageDisplay* disp)
517 {
518   _known_displays.insert(disp);
519 }
520 
goodbye(MutatableImageDisplay * disp)521 void EvolvotronMain::goodbye(MutatableImageDisplay* disp)
522 {
523   _history->goodbye(disp);
524   _known_displays.erase(disp);
525 }
526 
is_known(MutatableImageDisplay * disp) const527 bool EvolvotronMain::is_known(MutatableImageDisplay* disp) const
528 {
529   return (_known_displays.find(disp)!=_known_displays.end());
530 }
531 
list_known(std::ostream & out) const532 void EvolvotronMain::list_known(std::ostream& out) const
533 {
534   for (std::set<MutatableImageDisplay*>::const_iterator it=_known_displays.begin();it!=_known_displays.end();it++)
535     {
536       out << (*it) << " ";
537     }
538   out << "\n";
539 }
540 
541 /*! Periodically report number of remaining compute tasks and check farm's done queue for completed tasks.
542  */
tick()543 void EvolvotronMain::tick()
544 {
545   const uint tasks_main=_farm[0]->tasks();
546   const uint tasks_enlargement=(_farm[1].get() ? _farm[1]->tasks() : 0);
547   if (tasks_main!=_statusbar_tasks_main || tasks_enlargement!=_statusbar_tasks_enlargement)
548     {
549       std::ostringstream msg;
550       msg << "";
551 
552       if (tasks_main+tasks_enlargement==0)
553 	{
554 	  msg << "Ready";
555 	}
556       else
557 	{
558 	  msg << tasks_main;
559 	  if (tasks_enlargement)
560 	    {
561 	      msg << "+" << tasks_enlargement;
562 	    }
563 	  msg << " tasks remaining";
564 	}
565 
566       _statusbar_tasks_label->setText(msg.str().c_str());
567       _statusbar_tasks_main=tasks_main;
568       _statusbar_tasks_enlargement=tasks_enlargement;
569     }
570 
571   boost::shared_ptr<MutatableImageComputerTask> task;
572 
573   // If there are aborted jobs in the todo queue
574   // shift them straight over to done queue so the compute threads don't have to worry about them.
575   _farm[0]->fasttrack_aborted();
576   if (_farm[1].get()) _farm[1]->fasttrack_aborted();
577 
578   QTime watchdog;
579   watchdog.start();
580 
581   for (int which_farm=0;which_farm<(_farm[1].get() ? 2 : 1);which_farm++)
582     {
583       while ((task=_farm[which_farm]->pop_done())!=0)
584 	{
585 	  if (is_known(task->display()))
586 	    {
587 	      task->display()->deliver(task);
588 	    }
589 	  else
590 	    {
591 	      // If we don't know who owns it we just have to trash it
592 	      // (probably a top level window which was closed with incomplete tasks).
593 	      task.reset();
594 	    }
595 
596 	  // Timeout in case we're being swamped by incoming tasks (maintain app responsiveness).
597 	  if (watchdog.elapsed()>20)
598 	    break;
599 	}
600     }
601 }
602 
keyPressEvent(QKeyEvent * e)603 void EvolvotronMain::keyPressEvent(QKeyEvent* e)
604 {
605   if (e->key()==Qt::Key_Escape)
606     {
607       // Esc key used to back out of menu hide and full screen mode
608       // Might rescue a few users who have got into those states accidentally
609       showNormal();
610       menuBar()->show();
611       statusBar()->show();
612       _menu_action_fullscreen->setChecked(false);
613       _menu_action_hide_menu->setChecked(false);
614     }
615   else if (e->key()==Qt::Key_Z && !(e->modifiers()^Qt::ControlModifier))
616     {
617       //Ctrl-Z does an undo
618       undo();
619     }
620   else
621     {
622       // Perhaps it's for someone else
623       e->ignore();
624     }
625 }
626 
627 
toggle_fullscreen()628 void EvolvotronMain::toggle_fullscreen()
629 {
630   if (isFullScreen())
631     {
632       showNormal();
633       _menu_action_fullscreen->setChecked(false);
634     }
635   else
636     {
637       showFullScreen();
638       _menu_action_fullscreen->setChecked(true);
639     }
640 }
641 
toggle_hide_menu()642 void EvolvotronMain::toggle_hide_menu()
643 {
644   if (menuBar()->isHidden())
645     {
646       menuBar()->show();
647       _menu_action_hide_menu->setChecked(false);
648     }
649   else if (menuBar()->isVisible())
650     {
651       menuBar()->hide();
652       _menu_action_hide_menu->setChecked(true);
653     }
654 
655   if (statusBar()->isHidden())
656     statusBar()->show();
657   else if (statusBar()->isVisible())
658     statusBar()->hide();
659 }
660 
661 /*! Set up an initial random image in the specified display.
662   If a favourite function was specified then we use that as the top level node.
663  */
reset(MutatableImageDisplay * display)664 void EvolvotronMain::reset(MutatableImageDisplay* display)
665 {
666   std::unique_ptr<FunctionTop> root;
667   if (_dialog_favourite->favourite_function().empty())
668     {
669       root=std::unique_ptr<FunctionTop>(FunctionTop::initial(mutation_parameters()));
670     }
671   else
672     {
673       root=std::unique_ptr<FunctionTop>
674 	(
675 	 FunctionTop::initial
676 	 (
677 	  mutation_parameters(),
678 	  mutation_parameters().function_registry().lookup(_dialog_favourite->favourite_function()),
679 	  _dialog_favourite->favourite_function_unwrapped()
680 	  )
681 	 );
682     }
683 
684   history().replacing(display);
685   const boost::shared_ptr<const MutatableImage> image_function(new MutatableImage(root,!_linear_zsweep,_spheremap,false));
686   display->image_function(image_function,true);
687 }
688 
undo()689 void EvolvotronMain::undo()
690 {
691   history().undo();
692 }
693 
simplify_constants()694 void EvolvotronMain::simplify_constants()
695 {
696   history().begin_action("simplify all");
697   uint nodes_eliminated=0;
698   for (std::vector<MutatableImageDisplay*>::iterator it=_displays.begin();it!=_displays.end();it++)
699     {
700       nodes_eliminated+=(*it)->simplify_constants(false);
701     }
702   history().end_action();
703   std::stringstream msg;
704   msg << "Eliminated " << nodes_eliminated << " redundant function nodes\n";
705   QMessageBox::information(this,"Evolvotron",msg.str().c_str(),QMessageBox::Ok);
706 }
707 
708 /*! Reset each image in the grid, and the mutation parameters.
709  */
reset(bool reset_mutation_parameters,bool clear_locks)710 void EvolvotronMain::reset(bool reset_mutation_parameters,bool clear_locks)
711 {
712   history().begin_action("reset/restart");
713 
714   if (reset_mutation_parameters)
715     {
716       // Invoking reset on the 1st dialog actually resets the parameters
717       _dialog_mutation_parameters->reset();
718       // This one just serves to setup the function dialog from the now reset parameters
719       _dialog_functions->setup_from_mutation_parameters();
720     }
721   else
722     {
723       // Seems odd if we don't restart the count.
724       _mutation_parameters.autocool_generations(0);
725     }
726 
727   for (size_t i=0;i<displays().size();++i)
728   {
729     if (clear_locks)
730       displays()[i]->lock(false,false);  // lock method mustn't make it's own history recording
731   }
732 
733   if (_startup_shuffle) {
734     std::random_shuffle(_startup_filenames.begin(),_startup_filenames.end());
735   }
736 
737   for (size_t i=0;i<displays().size();++i) {
738     if (!displays()[i]->locked()) {
739       if (i<_startup_filenames.size())
740 	displays()[i]->load_function_file(_startup_filenames[i].c_str());
741       else
742 	reset(displays()[i]);
743     }
744   }
745 
746   last_spawned_image(boost::shared_ptr<const MutatableImage>(),&EvolvotronMain::spawn_normal);
747 
748   history().end_action();
749 }
750 
reset_randomized()751 void EvolvotronMain::reset_randomized()
752 {
753   _mutation_parameters.randomize_function_weightings_for_classifications(static_cast<uint>(-1));
754   reset(false,false);
755 }
756 
reset_warm()757 void EvolvotronMain::reset_warm()
758 {
759   reset(false,false);
760 }
761 
reset_cold()762 void EvolvotronMain::reset_cold()
763 {
764   reset(true,true);
765 }
766 
mutation_parameters_changed()767 void EvolvotronMain::mutation_parameters_changed()
768 {
769   _checkbox_autocool_enable->setChecked(_mutation_parameters.autocool_enable());
770   if (_mutation_parameters.autocool_enable())
771     {
772       _label_autocool_enable->setText(QString("Generations:")+QString::number(_mutation_parameters.autocool_generations()));
773       _label_autocool_enable->show();
774 
775       if (_mutation_parameters.autocool_generations()>0) _button_autocool_reheat->show();
776       else _button_autocool_reheat->hide();
777 
778     }
779   else
780     {
781       _label_autocool_enable->hide();
782       _button_autocool_reheat->hide();
783     }
784 }
785 
render_parameters_changed()786 void EvolvotronMain::render_parameters_changed()
787 {
788   for (std::set<MutatableImageDisplay*>::iterator it=_known_displays.begin();it!=_known_displays.end();it++)
789     (*it)->image_function((*it)->image_function(),true);
790 }
791