SPH
RunPage.cpp
Go to the documentation of this file.
1 #include "gui/windows/RunPage.h"
2 #include "gui/Controller.h"
3 #include "gui/Factory.h"
4 #include "gui/MainLoop.h"
5 #include "gui/Utils.h"
6 #include "gui/objects/Camera.h"
11 #include "gui/windows/MainWindow.h"
12 #include "gui/windows/OrthoPane.h"
14 #include "gui/windows/PlotView.h"
16 #include "gui/windows/TimeLine.h"
17 #include "gui/windows/Widgets.h"
18 #include "io/FileSystem.h"
19 #include "io/LogWriter.h"
20 #include "io/Logger.h"
21 #include "sph/Diagnostics.h"
22 #include "thread/CheckFunction.h"
23 #include "thread/Pool.h"
25 #include <fstream>
26 #include <wx/app.h>
27 #include <wx/button.h>
28 #include <wx/checkbox.h>
29 #include <wx/clrpicker.h>
30 #include <wx/combobox.h>
31 #include <wx/gauge.h>
32 #include <wx/msgdlg.h>
33 #include <wx/radiobut.h>
34 #include <wx/settings.h>
35 #include <wx/sizer.h>
36 #include <wx/spinctrl.h>
37 #include <wx/statbox.h>
38 #include <wx/statline.h>
39 #include <wx/stattext.h>
40 #include <wx/textctrl.h>
41 
42 #include <wx/aui/auibook.h>
43 #include <wx/aui/framemanager.h>
44 // needs to be included after framemanager
45 #include <wx/aui/dockart.h>
47 
49 private:
50  Controller* parent;
51 
52 public:
53  explicit TimeLineCallbacks(Controller* parent)
54  : parent(parent) {}
55 
56  virtual void frameChanged(const Path& newFile) const override {
57  parent->open(newFile, false);
58  }
59 
60  virtual void startSequence(const Path& firstFile) const override {
61  parent->open(firstFile, true);
62  }
63 
64  virtual void stop() const override {
65  parent->stop(false);
66  }
67 
68  virtual void pause() const override {
69  parent->pause();
70  }
71 };
72 
73 RunPage::RunPage(wxWindow* window, Controller* parent, GuiSettings& settings)
74  : controller(parent)
75  , gui(settings) {
76 
77  wxSize size(
78  settings.get<int>(GuiSettingsId::WINDOW_WIDTH), settings.get<int>(GuiSettingsId::WINDOW_HEIGHT));
79  this->Create(window, wxID_ANY, wxDefaultPosition, size);
80 
81  manager = makeAuto<wxAuiManager>(this);
82 
83  wxPanel* visBar = createVisBar();
84  pane = alignedNew<OrthoPane>(this, parent, settings);
85  wxPanel* plotBar = createPlotBar();
86  statsBar = createStatsBar();
87 
88  timelineBar = alignedNew<TimeLine>(this, Path(), makeShared<TimeLineCallbacks>(parent));
89  progressBar = alignedNew<ProgressPanel>(this);
90 
91  wxAuiPaneInfo info;
92 
93  // info.Top().MinSize(wxSize(-1, 35)).CaptionVisible(false).DockFixed(true).CloseButton(false);
94  // manager->AddPane(toolBar, info);
95 
96  info.Center().MinSize(wxSize(300, 300)).CaptionVisible(false).DockFixed(true).CloseButton(false);
97  manager->AddPane(&*pane, info);
98 
99  info.Bottom().MinSize(wxSize(-1, 40)).CaptionVisible(false).DockFixed(true).CloseButton(false);
100  manager->AddPane(timelineBar, info.Show(false));
101  manager->AddPane(progressBar, info.Show(true));
102 
103  info.Left()
104  .MinSize(wxSize(300, -1))
105  .CaptionVisible(true)
106  .DockFixed(false)
107  .CloseButton(true)
108  .Caption("Visualization");
109  manager->AddPane(visBar, info);
110 
111  info.Right()
112  .MinSize(wxSize(300, -1))
113  .CaptionVisible(true)
114  .DockFixed(false)
115  .CloseButton(true)
116  .Caption("Run statistics");
117  manager->AddPane(statsBar, info);
118  info.Caption("Particle data");
119  manager->AddPane(plotBar, info);
120 
121 
122  manager->Update();
123 }
124 
126  manager->UnInit();
127  manager = nullptr;
128 }
129 
130 
131 const wxSize buttonSize(250, -1);
132 const wxSize spinnerSize(100, -1);
133 const int boxPadding = 10;
134 
135 wxWindow* RunPage::createParticleBox(wxPanel* parent) {
136  wxStaticBox* particleBox = new wxStaticBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 135));
137 
138  wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
139 
140  wxBoxSizer* cutoffSizer = new wxBoxSizer(wxHORIZONTAL);
141  cutoffSizer->AddSpacer(boxPadding);
142  wxStaticText* text = new wxStaticText(particleBox, wxID_ANY, "Cutoff [km]");
143  cutoffSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
144  const Float cutoff = gui.get<Float>(GuiSettingsId::CAMERA_ORTHO_CUTOFF) * 1.e-3_f;
145 
146  FloatTextCtrl* cutoffCtrl = new FloatTextCtrl(particleBox, cutoff, Interval(0, LARGE));
147  cutoffCtrl->onValueChanged = [this](const double value) {
148  this->updateCutoff(value * 1.e3_f);
149  return true;
150  };
151  cutoffCtrl->SetToolTip(
152  "Specifies the cutoff distance in kilometers for rendering particles. When set to a positive number, "
153  "only particles in a layer of specified thickness are rendered. Zero means all particles are "
154  "rendered.");
155  cutoffSizer->Add(cutoffCtrl, 1, wxALIGN_CENTER_VERTICAL);
156  cutoffSizer->AddSpacer(boxPadding);
157  boxSizer->Add(cutoffSizer);
158 
159  wxBoxSizer* particleSizeSizer = new wxBoxSizer(wxHORIZONTAL);
160  particleSizeSizer->AddSpacer(boxPadding);
161  text = new wxStaticText(particleBox, wxID_ANY, "Particle radius");
162  particleSizeSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
164  FloatTextCtrl* particleSizeCtrl = new FloatTextCtrl(particleBox, radius, Interval(1.e-3_f, 1.e3_f));
165  particleSizeCtrl->SetToolTip(
166  "Multiplier of a particle radius. Must be set to 1 to get the actual size of particles in N-body "
167  "simulations.");
168  particleSizeCtrl->onValueChanged = [this](const Float value) {
170  controller->tryRedraw();
171  return true;
172  };
173  particleSizeSizer->Add(particleSizeCtrl, 1, wxALIGN_CENTER_VERTICAL);
174  particleSizeSizer->AddSpacer(boxPadding);
175  boxSizer->Add(particleSizeSizer);
176 
177 
178  wxBoxSizer* grayscaleSizer = new wxBoxSizer(wxHORIZONTAL);
179  grayscaleSizer->AddSpacer(boxPadding);
180 
181  wxBoxSizer* keySizer = new wxBoxSizer(wxHORIZONTAL);
182  keySizer->AddSpacer(boxPadding);
183  wxCheckBox* keyBox = new wxCheckBox(particleBox, wxID_ANY, "Show key");
184  keyBox->SetToolTip(
185  "If checked, the color palette and the length scale are included in the rendered image.");
186  keyBox->SetValue(gui.get<bool>(GuiSettingsId::SHOW_KEY));
187  keySizer->Add(keyBox);
188  keySizer->AddSpacer(33);
189  wxCheckBox* ghostBox = new wxCheckBox(particleBox, wxID_ANY, "Show ghosts");
190  ghostBox->SetValue(gui.get<bool>(GuiSettingsId::RENDER_GHOST_PARTICLES));
191  keySizer->Add(ghostBox);
192 
193  boxSizer->Add(keySizer);
194 
195  wxBoxSizer* aaSizer = new wxBoxSizer(wxHORIZONTAL);
196  aaSizer->AddSpacer(boxPadding);
197  wxCheckBox* aaBox = new wxCheckBox(particleBox, wxID_ANY, "Anti-aliasing");
198  aaBox->SetValue(gui.get<bool>(GuiSettingsId::ANTIALIASED));
199  aaBox->SetToolTip(
200  "If checked, particles are drawn with anti-aliasing, creating smoother image, but it also takes "
201  "longer to render it.");
202  aaSizer->Add(aaBox);
203 
204  aaSizer->AddSpacer(boxPadding);
205  wxCheckBox* smoothBox = new wxCheckBox(particleBox, wxID_ANY, "Smooth particles");
206  smoothBox->SetToolTip(
207  "If checked, particles are drawn semi-transparently. The transparency of a particle follows "
208  "smoothing kernel, imitating actual smoothing of SPH particles.");
209  smoothBox->Enable(false);
210  aaSizer->Add(smoothBox);
211  boxSizer->Add(aaSizer);
212 
213  particleBox->SetSizer(boxSizer);
214 
215  keyBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
216  const bool value = evt.IsChecked();
217  gui.set(GuiSettingsId::SHOW_KEY, value);
218  controller->tryRedraw();
219  });
220  ghostBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
221  const bool value = evt.IsChecked();
223  controller->tryRedraw();
224  });
225  aaBox->Bind(wxEVT_CHECKBOX, [this, smoothBox](wxCommandEvent& evt) {
226  const bool value = evt.IsChecked();
227  gui.set(GuiSettingsId::ANTIALIASED, value);
228  smoothBox->Enable(value);
229  controller->tryRedraw();
230  });
231  smoothBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
232  const bool value = evt.IsChecked();
234  controller->tryRedraw();
235  });
236 
237  return particleBox;
238 }
239 
240 wxWindow* RunPage::createRaymarcherBox(wxPanel* parent) {
241  wxStaticBox* raytraceBox = new wxStaticBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 150));
242  wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
243  wxBoxSizer* levelSizer = new wxBoxSizer(wxHORIZONTAL);
244  levelSizer->AddSpacer(boxPadding);
245  wxStaticText* text = new wxStaticText(raytraceBox, wxID_ANY, "Surface level");
246  levelSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
247  const Float level = gui.get<Float>(GuiSettingsId::SURFACE_LEVEL);
248  FloatTextCtrl* levelCtrl = new FloatTextCtrl(raytraceBox, level, Interval(0._f, 10._f));
249  levelCtrl->onValueChanged = [this](const Float value) {
250  GuiSettings& gui = controller->getParams();
251  gui.set(GuiSettingsId::SURFACE_LEVEL, value);
252  controller->tryRedraw();
253  return true;
254  };
255  levelSizer->Add(levelCtrl, 1, wxALIGN_CENTER_VERTICAL);
256  levelSizer->AddSpacer(boxPadding);
257  boxSizer->Add(levelSizer);
258 
259  wxBoxSizer* sunlightSizer = new wxBoxSizer(wxHORIZONTAL);
260  sunlightSizer->AddSpacer(boxPadding);
261  text = new wxStaticText(raytraceBox, wxID_ANY, "Sunlight");
262  sunlightSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
263  const Float sunlight = gui.get<Float>(GuiSettingsId::SURFACE_SUN_INTENSITY);
264  FloatTextCtrl* sunlightCtrl = new FloatTextCtrl(raytraceBox, sunlight, Interval(0._f, 100._f));
265  sunlightCtrl->onValueChanged = [this](const Float value) {
266  GuiSettings& gui = controller->getParams();
268  controller->tryRedraw();
269  return true;
270  };
271  sunlightSizer->Add(sunlightCtrl, 1, wxALIGN_CENTER_VERTICAL);
272  sunlightSizer->AddSpacer(boxPadding);
273  boxSizer->Add(sunlightSizer);
274 
275  wxBoxSizer* ambientSizer = new wxBoxSizer(wxHORIZONTAL);
276  ambientSizer->AddSpacer(boxPadding);
277  text = new wxStaticText(raytraceBox, wxID_ANY, "Ambient");
278  ambientSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
279  const Float ambient = gui.get<Float>(GuiSettingsId::SURFACE_AMBIENT);
280  FloatTextCtrl* ambientCtrl = new FloatTextCtrl(raytraceBox, ambient, Interval(0._f, 100._f));
281  ambientCtrl->onValueChanged = [this](const Float value) {
282  GuiSettings& gui = controller->getParams();
284  controller->tryRedraw();
285  return true;
286  };
287  ambientSizer->Add(ambientCtrl, 1, wxALIGN_CENTER_VERTICAL);
288  ambientSizer->AddSpacer(boxPadding);
289  boxSizer->Add(ambientSizer);
290 
291  wxBoxSizer* emissionSizer = new wxBoxSizer(wxHORIZONTAL);
292  emissionSizer->AddSpacer(boxPadding);
293  text = new wxStaticText(raytraceBox, wxID_ANY, "Emission");
294  emissionSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
295  const Float emission = gui.get<Float>(GuiSettingsId::SURFACE_EMISSION);
296  FloatTextCtrl* emissionCtrl = new FloatTextCtrl(raytraceBox, emission, Interval(0._f, 100._f));
297  emissionCtrl->onValueChanged = [this](const Float value) {
298  GuiSettings& gui = controller->getParams();
300  controller->tryRedraw();
301  return true;
302  };
303  emissionSizer->Add(emissionCtrl, 1, wxALIGN_CENTER_VERTICAL);
304  emissionSizer->AddSpacer(boxPadding);
305  boxSizer->Add(emissionSizer);
306 
307  raytraceBox->SetSizer(boxSizer);
308  return raytraceBox;
309 }
310 
311 wxWindow* RunPage::createVolumeBox(wxPanel* parent) {
312  wxStaticBox* volumeBox = new wxStaticBox(parent, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 120));
313  wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
314 
315  wxBoxSizer* emissionSizer = new wxBoxSizer(wxHORIZONTAL);
316  emissionSizer->AddSpacer(boxPadding);
317  wxStaticText* text = new wxStaticText(volumeBox, wxID_ANY, "Emission [km^-1]");
318  emissionSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
319  const Float emission = gui.get<Float>(GuiSettingsId::VOLUME_EMISSION);
320  FloatTextCtrl* emissionCtrl = new FloatTextCtrl(volumeBox, emission * 1.e3_f, Interval(0._f, 1.e8_f));
321  emissionCtrl->onValueChanged = [this](const Float value) {
322  GuiSettings& gui = controller->getParams();
323  // value in spinner is in [km^-1]
324  gui.set(GuiSettingsId::VOLUME_EMISSION, value / 1.e3_f);
325  controller->tryRedraw();
326  return true;
327  };
328  emissionSizer->Add(emissionCtrl, 1, wxALIGN_CENTER_VERTICAL);
329  emissionSizer->AddSpacer(boxPadding);
330  boxSizer->Add(emissionSizer);
331 
332  wxBoxSizer* absorptionSizer = new wxBoxSizer(wxHORIZONTAL);
333  absorptionSizer->AddSpacer(boxPadding);
334  text = new wxStaticText(volumeBox, wxID_ANY, "Absorption [km^-1]");
335  absorptionSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
336  const Float absorption = gui.get<Float>(GuiSettingsId::VOLUME_ABSORPTION);
337  FloatTextCtrl* absorptionCtrl = new FloatTextCtrl(volumeBox, absorption * 1.e3_f, Interval(0._f, 1.e8_f));
338  absorptionCtrl->onValueChanged = [this](const Float value) {
339  GuiSettings& gui = controller->getParams();
340  // value in spinner is in [km^-1]
341  gui.set(GuiSettingsId::VOLUME_ABSORPTION, value / 1.e3_f);
342  controller->tryRedraw();
343  return true;
344  };
345  absorptionSizer->Add(absorptionCtrl, 1, wxALIGN_CENTER_VERTICAL);
346  absorptionSizer->AddSpacer(boxPadding);
347  boxSizer->Add(absorptionSizer);
348 
349  wxBoxSizer* factorSizer = new wxBoxSizer(wxHORIZONTAL);
350  factorSizer->AddSpacer(boxPadding);
351  text = new wxStaticText(volumeBox, wxID_ANY, "Compression");
352  factorSizer->Add(text, 10, wxALIGN_CENTER_VERTICAL);
354  FloatTextCtrl* factorCtrl = new FloatTextCtrl(volumeBox, factor, Interval(1.e-6_f, 1.e6_f));
355  factorCtrl->onValueChanged = [this](const Float value) {
356  GuiSettings& gui = controller->getParams();
358  controller->tryRedraw();
359  return true;
360  };
361  factorSizer->Add(factorCtrl, 1, wxALIGN_CENTER_VERTICAL);
362  factorSizer->AddSpacer(boxPadding);
363  boxSizer->Add(factorSizer);
364 
365  volumeBox->SetSizer(boxSizer);
366  return volumeBox;
367 }
368 
369 static void enableRecursive(wxWindow* window, const bool enable) {
370  window->Enable(enable);
371  for (wxWindow* child : window->GetChildren()) {
372  enableRecursive(child, enable);
373  }
374 }
375 
376 wxPanel* RunPage::createVisBar() {
378  wxPanel* visbarPanel = new wxPanel(this);
379  visbarPanel->SetLabel("Visualization");
380 
381  wxBoxSizer* visbarSizer = new wxBoxSizer(wxVERTICAL);
382 
383  wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
384  wxButton* resetView = new wxButton(visbarPanel, wxID_ANY, "Reset view");
385  resetView->SetToolTip("Resets the camera rotation.");
386  resetView->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) {
387  pane->resetView();
388  AutoPtr<ICamera> camera = controller->getCurrentCamera();
390  controller->refresh(std::move(camera));
391  });
392  buttonSizer->Add(resetView);
393 
394  wxButton* refresh = new wxButton(visbarPanel, wxID_ANY, "Refresh");
395  refresh->SetToolTip("Updates the particle order and repaints the current view");
396  refresh->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) {
397  if (!controller->tryRedraw()) {
399  AutoPtr<ICamera> camera = controller->getCurrentCamera();
400  controller->refresh(std::move(camera));
401  controller->redrawOnNextTimeStep();
402  }
403  });
404  buttonSizer->Add(refresh);
405 
406  wxButton* snap = new wxButton(visbarPanel, wxID_ANY, "Save image");
407  snap->SetToolTip("Saves the currently rendered image.");
408  buttonSizer->Add(snap);
409  snap->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) {
410  Optional<Path> path = doSaveFileDialog("Save image", { { "PNG image", "png" } });
411  if (!path) {
412  return;
413  }
414  const wxBitmap& bitmap = controller->getRenderedBitmap();
415  saveToFile(bitmap, path.value());
416  });
417 
418  visbarSizer->Add(buttonSizer);
419  visbarSizer->AddSpacer(10);
420 
421  wxCheckBox* autoRefresh = new wxCheckBox(visbarPanel, wxID_ANY, "Refresh on timestep");
422  autoRefresh->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
423  GuiSettings& gui = controller->getParams();
424  gui.set(GuiSettingsId::REFRESH_ON_TIMESTEP, evt.IsChecked());
425  });
426  autoRefresh->SetValue(gui.get<bool>(GuiSettingsId::REFRESH_ON_TIMESTEP));
427  autoRefresh->SetToolTip(
428  "When checked, the image is updated on every timestep, otherwise the image is only updated when "
429  "pressing the 'Refresh' button. Note that repainting the image on every timestep may decrease "
430  "the performance of the code.");
431  visbarSizer->Add(autoRefresh);
432 
433  wxCheckBox* autoCamera = new wxCheckBox(visbarPanel, wxID_ANY, "Auto-zoom");
434  autoCamera->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
435  GuiSettings& gui = controller->getParams();
436  gui.set(GuiSettingsId::CAMERA_AUTOSETUP, evt.IsChecked());
437  });
438  autoCamera->SetValue(gui.get<bool>(GuiSettingsId::CAMERA_AUTOSETUP));
439  autoCamera->SetToolTip(
440  "When checked, parameters of the camera (position, field of view, etc.) are automatically adjusted "
441  "during the simulation.");
442  visbarSizer->Add(autoCamera);
443  visbarSizer->AddSpacer(10);
444 
445 
446  /*wxBoxSizer* colorSizer = new wxBoxSizer(wxHORIZONTAL);
447  colorSizer->Add(new wxStaticText(visbarPanel, wxID_ANY, "Background: "), 10, wxALIGN_CENTER_VERTICAL);
448  wxColourPickerCtrl* picker = new wxColourPickerCtrl(visbarPanel, wxID_ANY);
449  picker->SetColour(wxColour(controller->getParams().get<Rgba>(GuiSettingsId::BACKGROUND_COLOR)));
450  colorSizer->Add(picker, 1, wxALIGN_CENTER_VERTICAL, 5);
451  wxCheckBox* transparentBox = new wxCheckBox(visbarPanel, wxID_ANY, "Transparent");
452  picker->Bind(wxEVT_COLOURPICKER_CHANGED, [this](wxColourPickerEvent& evt) {
453  GuiSettings& gui = controller->getParams();
454  Rgba newColor(evt.GetColour());
455  const Rgba currentColor = gui.get<Rgba>(GuiSettingsId::BACKGROUND_COLOR);
456  newColor.a() = currentColor.a();
457  gui.set(GuiSettingsId::BACKGROUND_COLOR, newColor);
458  controller->tryRedraw();
459  });
460  transparentBox->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& evt) {
461  GuiSettings& gui = controller->getParams();
462  Rgba color = gui.get<Rgba>(GuiSettingsId::BACKGROUND_COLOR);
463  color.a() = evt.IsChecked() ? 0.f : 1.f;
464  gui.set(GuiSettingsId::BACKGROUND_COLOR, color);
465  controller->tryRedraw();
466  });
467  colorSizer->Add(transparentBox, 1, wxALIGN_CENTER_VERTICAL, 5);
468  visbarSizer->Add(colorSizer);
469  visbarSizer->AddSpacer(10);*/
470 
471  wxBoxSizer* quantitySizer = new wxBoxSizer(wxHORIZONTAL);
472 
473  quantitySizer->Add(new wxStaticText(visbarPanel, wxID_ANY, "Quantity: "), 10, wxALIGN_CENTER_VERTICAL);
474  quantityBox = new ComboBox(visbarPanel, "", wxSize(200, -1));
475  quantityBox->SetToolTip(
476  "Selects which quantity to visualize using associated color scale. Quantity values can be also "
477  "obtained by left-clicking on a particle.");
478  quantityBox->SetWindowStyle(wxCB_SIMPLE | wxCB_READONLY);
479  quantityBox->SetSelection(0);
480  quantityBox->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& UNUSED(evt)) {
482  const int idx = quantityBox->GetSelection();
483  this->setColorizer(idx);
484  });
485 
486  quantitySizer->Add(quantityBox, 1, wxALIGN_CENTER_VERTICAL, 5);
487 
488  visbarSizer->Add(quantitySizer);
489  visbarSizer->AddSpacer(10);
490 
491  wxRadioButton* particleButton =
492  new wxRadioButton(visbarPanel, wxID_ANY, "Particles", wxDefaultPosition, buttonSize, wxRB_GROUP);
493  particleButton->SetToolTip("Render individual particles with optional smoothing.");
494  visbarSizer->Add(particleButton);
495  wxWindow* particleBox = this->createParticleBox(visbarPanel);
496  visbarSizer->Add(particleBox, 0, wxALL, 5);
497  visbarSizer->AddSpacer(10);
498 
499 
500  wxRadioButton* surfaceButton =
501  new wxRadioButton(visbarPanel, wxID_ANY, "Raymarched surface", wxDefaultPosition, buttonSize, 0);
502  visbarSizer->Add(surfaceButton);
503  wxWindow* raytracerBox = this->createRaymarcherBox(visbarPanel);
504  visbarSizer->Add(raytracerBox, 0, wxALL, 5);
505  visbarSizer->AddSpacer(10);
506 
507  wxRadioButton* volumeButton =
508  new wxRadioButton(visbarPanel, wxID_ANY, "Volumetric raytracer", wxDefaultPosition, buttonSize, 0);
509  visbarSizer->Add(volumeButton);
510  wxWindow* volumeBox = this->createVolumeBox(visbarPanel);
511  visbarSizer->Add(volumeBox, 0, wxALL, 5);
512  visbarSizer->AddSpacer(10);
513 
514  /*wxRadioButton* contourButton =
515  new wxRadioButton(visbarPanel, wxID_ANY, "Iso-lines", wxDefaultPosition, buttonSize, 0);
516  visbarSizer->Add(contourButton);
517  wxWindow* contourBox = this->createContourBox(visbarPanel);
518  visbarSizer->Add(contourBox, 0, wxALL, 5);
519  visbarSizer->AddSpacer(6);
520 
521  wxRadioButton* meshButton =
522  new wxRadioButton(visbarPanel, wxID_ANY, "Surface mesh", wxDefaultPosition, buttonSize, 0);
523  visbarSizer->Add(meshButton);
524  visbarSizer->AddSpacer(6);*/
525 
526 
527  auto enableControls = [=](int renderIdx) {
528  enableRecursive(particleBox, renderIdx == 0);
529  enableRecursive(raytracerBox, renderIdx == 1);
530  enableRecursive(volumeBox, renderIdx == 2);
531  };
532  enableControls(0);
533 
534  particleButton->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& UNUSED(evt)) {
536  controller->setRenderer(makeAuto<ParticleRenderer>(gui));
537  enableControls(0);
538  });
539  /*meshButton->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& UNUSED(evt)) {
540  CHECK_FUNCTION(CheckFunction::MAIN_THREAD);
541  SharedPtr<IScheduler> scheduler = Factory::getScheduler(RunSettings::getDefaults());
542  controller->setRenderer(makeAuto<MeshRenderer>(scheduler, gui));
543  enableControls(3);
544  });
545  contourButton->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& UNUSED(evt)) {
546  CHECK_FUNCTION(CheckFunction::MAIN_THREAD);
547  SharedPtr<IScheduler> scheduler = Factory::getScheduler(RunSettings::getDefaults());
548  controller->setRenderer(makeAuto<ContourRenderer>(scheduler, gui));
549  enableControls(2);
550  });*/
551  surfaceButton->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& UNUSED(evt)) {
553  try {
555  controller->setRenderer(makeAuto<RayMarcher>(scheduler, gui));
556  enableControls(1);
557  } catch (const std::exception& e) {
558  wxMessageBox(std::string("Cannot initialize raytracer.\n\n") + e.what(), "Error", wxOK);
559 
560  // switch to particle renderer (fallback option)
561  particleButton->SetValue(true);
562  controller->setRenderer(makeAuto<ParticleRenderer>(gui));
563  enableControls(0);
564  }
565  });
566  volumeButton->Bind(wxEVT_RADIOBUTTON, [=](wxCommandEvent& UNUSED(evt)) {
569  GuiSettings volumeGui = gui;
571  controller->setRenderer(makeAuto<VolumeRenderer>(scheduler, volumeGui));
572  enableControls(2);
573  });
574 
575  visbarSizer->AddSpacer(16);
576  quantityPanel = new wxPanel(visbarPanel, wxID_ANY);
577  visbarSizer->Add(quantityPanel);
578  quantityPanelSizer = visbarSizer;
579 
580  visbarPanel->SetSizer(visbarSizer);
581  return visbarPanel;
582 }
583 
584 void RunPage::updateCutoff(const double cutoff) {
587  // Note that we have to get camera from pane, not controller, as pane camera is always the one being
588  // modified and fed to controller. Using controller's camera would cause cutoff to be later overriden by
589  // the camera from pane.
590  ICamera& camera = pane->getCamera();
591  camera.setCutoff(cutoff > 0. ? Optional<float>(float(cutoff)) : NOTHING);
592  controller->refresh(camera.clone());
593  controller->tryRedraw();
594 }
595 
596 wxPanel* RunPage::createPlotBar() {
597  wxPanel* sidebarPanel = new wxPanel(this);
598  wxBoxSizer* sidebarSizer = new wxBoxSizer(wxVERTICAL);
599  probe = new ParticleProbe(sidebarPanel, wxSize(300, 155));
600  sidebarSizer->Add(probe.get(), 1, wxALIGN_TOP | wxEXPAND);
601  sidebarSizer->AddSpacer(5);
602 
603  SharedPtr<Array<PlotData>> list = makeShared<Array<PlotData>>(getPlotList(gui));
604  for (const auto& plotData : *list) {
605  plots.push(plotData.plot);
606  }
607 
608  TicsParams tics;
609  tics.minCnt = 2;
610  tics.digits = 1;
611  firstPlot = new PlotView(sidebarPanel, wxSize(300, 200), wxSize(10, 10), list, 0, tics);
612  sidebarSizer->Add(firstPlot, 1, wxALIGN_TOP | wxEXPAND);
613  sidebarSizer->AddSpacer(5);
614 
615  secondPlot = new PlotView(sidebarPanel, wxSize(300, 200), wxSize(10, 10), list, 1, tics);
616  sidebarSizer->Add(secondPlot, 1, wxALIGN_TOP | wxEXPAND);
617 
618  sidebarPanel->SetSizerAndFit(sidebarSizer);
619  return sidebarPanel;
620 }
621 
622 wxPanel* RunPage::createStatsBar() {
623  wxPanel* statsPanel = new wxPanel(this);
624  wxBoxSizer* statsSizer = new wxBoxSizer(wxVERTICAL);
625 
626  wxFont font = wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT);
627  font.Scale(0.95f);
628  statsPanel->SetFont(font);
629 
630  statsText = new wxTextCtrl(
631  statsPanel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE);
632  this->makeStatsText(0, Statistics{});
633 
634  statsSizer->Add(statsText, 1, wxEXPAND | wxALL, 5);
635  statsPanel->SetSizer(statsSizer);
636 
637  return statsPanel;
638 }
639 
640 template <typename TValue>
641 static void printStat(wxTextCtrl* text,
642  const Statistics& stats,
643  const std::string& desc,
644  const StatisticsId id,
645  const std::string units = "") {
646  if (stats.has(id)) {
647  *text << desc << stats.get<TValue>(id) << units << "\n";
648  }
649 }
650 
651 void RunPage::makeStatsText(const Size particleCnt, const Statistics& stats) {
652  statsText->Clear();
653  *statsText << " - particles: ";
654  if (particleCnt > 0) {
655  *statsText << int(particleCnt) << "\n";
656  } else {
657  *statsText << "N/A\n";
658  }
659 
660  printStat<Float>(statsText, stats, " - run time: ", StatisticsId::RUN_TIME, "s");
661  printStat<Float>(statsText, stats, " - timestep: ", StatisticsId::TIMESTEP_VALUE, "s");
662 
665  std::stringstream ss;
666  if (id == CriterionId::DERIVATIVE) {
668  } else {
669  ss << id;
670  }
671  *statsText << " * set by: " << ss.str() << "\n";
672  }
673 
674  printStat<int>(statsText, stats, " - time spent: ", StatisticsId::TIMESTEP_ELAPSED, "ms");
675  printStat<int>(statsText, stats, " * SPH evaluation: ", StatisticsId::SPH_EVAL_TIME, "ms");
676  printStat<int>(statsText, stats, " * gravity evaluation: ", StatisticsId::GRAVITY_EVAL_TIME, "ms");
677  printStat<int>(statsText, stats, " * collision evaluation: ", StatisticsId::COLLISION_EVAL_TIME, "ms");
678  printStat<int>(statsText, stats, " * tree construction: ", StatisticsId::GRAVITY_BUILD_TIME, "ms");
679  printStat<int>(
680  statsText, stats, " * visualization: ", StatisticsId::POSTPROCESS_EVAL_TIME, "ms");
681 
682  printStat<int>(statsText, stats, " - collisions: ", StatisticsId::TOTAL_COLLISION_COUNT);
683  printStat<int>(statsText, stats, " * bounces: ", StatisticsId::BOUNCE_COUNT);
684  printStat<int>(statsText, stats, " * mergers: ", StatisticsId::MERGER_COUNT);
685  printStat<int>(statsText, stats, " * breakups: ", StatisticsId::BREAKUP_COUNT);
686  printStat<int>(statsText, stats, " - overlaps: ", StatisticsId::OVERLAP_COUNT);
687  printStat<int>(statsText, stats, " - aggregates: ", StatisticsId::AGGREGATE_COUNT);
688 
689  /* CriterionId id = stats.get<CriterionId>(StatisticsId::TIMESTEP_CRITERION);
690  std::stringstream ss;
691  if (id == CriterionId::DERIVATIVE) {
692  ss << stats.get<QuantityId>(StatisticsId::LIMITING_QUANTITY);
693  } else {
694  ss << id;
695  }
696  const Float dt = stats.get<Float>(StatisticsId::TIMESTEP_VALUE);
697  logger->write(" - timestep: ", dt, " (set by ", ss.str(), ")");
698 
699  printStat<int>(*logger, stats, StatisticsId::TIMESTEP_ELAPSED, " - time spent: ",
700  "ms"); printStat<int>(*logger, stats, StatisticsId::SPH_EVAL_TIME, " * SPH evaluation:
701  ", "ms"); printStat<int>(*logger, stats, StatisticsId::GRAVITY_EVAL_TIME, " * gravity
702  evaluation:
703  ", "ms"); printStat<int>(*logger, stats, StatisticsId::COLLISION_EVAL_TIME, " * collision
704  evaluation: ", "ms"); printStat<int>(*logger, stats, StatisticsId::GRAVITY_BUILD_TIME, " *
705  tree construction: ", "ms"); printStat<int>(*logger, stats, StatisticsId::POSTPROCESS_EVAL_TIME, " *
706  visualization: ", "ms"); logger->write( " - particles: ", storage.getParticleCnt());
707  printStat<MinMaxMean>(*logger, stats, StatisticsId::NEIGHBOUR_COUNT, " - neigbours: ");
708  printStat<int>(*logger, stats, StatisticsId::TOTAL_COLLISION_COUNT, " - collisions: ");
709  printStat<int>(*logger, stats, StatisticsId::BOUNCE_COUNT, " * bounces: ");
710  printStat<int>(*logger, stats, StatisticsId::MERGER_COUNT, " * mergers: ");
711  printStat<int>(*logger, stats, StatisticsId::BREAKUP_COUNT, " * breakups: ");
712  printStat<int>(*logger, stats, StatisticsId::OVERLAP_COUNT, " - overlaps: ");
713  printStat<int>(*logger, stats, StatisticsId::AGGREGATE_COUNT, " - aggregates: ");
714  printStat<int>(*logger, stats, StatisticsId::SOLVER_SUMMATION_ITERATIONS, " - iteration #: ");*/
715 }
716 
717 void RunPage::setColorizer(const Size idx) {
718  // do this even if idx==selectedIdx, we might change the colorizerList (weird behavior, but it will do for
719  // now)
720  controller->setColorizer(colorizerList[idx]);
721  if (idx == selectedIdx) {
722  return;
723  }
724  this->replaceQuantityBar(idx);
725  selectedIdx = idx;
726 }
727 
728 void RunPage::addComponentIdBar(wxWindow* parent, wxSizer* sizer, SharedPtr<IColorizer> colorizer) {
729  sizer->AddSpacer(5);
730  RawPtr<ComponentIdColorizer> componentId = dynamicCast<ComponentIdColorizer>(colorizer.get());
731 
732  wxBoxSizer* seedSizer = new wxBoxSizer(wxHORIZONTAL);
733  seedSizer->Add(new wxStaticText(parent, wxID_ANY, "Seed"), 0, wxALIGN_CENTER_VERTICAL);
734  wxSpinCtrl* seedSpinner = new wxSpinCtrl(parent, wxID_ANY, "", wxDefaultPosition, spinnerSize);
735  seedSizer->Add(seedSpinner, 0, wxALIGN_CENTER_VERTICAL);
736  sizer->Add(seedSizer);
737  sizer->AddSpacer(15);
738 
739  wxRadioButton* overlapButton = new wxRadioButton(
740  parent, wxID_ANY, "Connected particles", wxDefaultPosition, wxSize(-1, 25), wxRB_GROUP);
741  sizer->Add(overlapButton);
742 
743  wxRadioButton* boundButton =
744  new wxRadioButton(parent, wxID_ANY, "Bound particles", wxDefaultPosition, wxSize(-1, 25), 0);
745  sizer->Add(boundButton);
746 
747  sizer->AddSpacer(15);
748  wxCheckBox* highlightBox = new wxCheckBox(parent, wxID_ANY, "Highlight component");
749  highlightBox->SetValue(bool(componentId->getHighlightIdx()));
750  sizer->Add(highlightBox);
751 
752  wxBoxSizer* highlightSizer = new wxBoxSizer(wxHORIZONTAL);
753  highlightSizer->AddSpacer(30);
754  wxStaticText* text = new wxStaticText(parent, wxID_ANY, "Index");
755  highlightSizer->Add(text, 0, wxALIGN_CENTER_VERTICAL);
756  wxSpinCtrl* highlightIndex = new wxSpinCtrl(parent, wxID_ANY, "", wxDefaultPosition, spinnerSize);
757  highlightIndex->SetValue(componentId->getHighlightIdx().valueOr(0));
758  highlightIndex->Enable(highlightBox->GetValue());
759  highlightSizer->Add(highlightIndex);
760  sizer->Add(highlightSizer);
761 
762  overlapButton->Bind(wxEVT_RADIOBUTTON, [this, componentId, colorizer](wxCommandEvent& UNUSED(evt)) {
763  componentId->setConnectivity(Post::ComponentFlag::SORT_BY_MASS | Post::ComponentFlag::OVERLAP);
764  controller->setColorizer(colorizer);
765  });
766  boundButton->Bind(wxEVT_RADIOBUTTON, [this, componentId, colorizer](wxCommandEvent& UNUSED(evt)) {
767  componentId->setConnectivity(
769  controller->setColorizer(colorizer);
770  });
771  seedSpinner->Bind(wxEVT_SPINCTRL, [this, componentId, colorizer](wxSpinEvent& evt) {
772  const int seed = evt.GetValue();
773  componentId->setSeed(seed);
774  controller->setColorizer(colorizer);
775  });
776 
777  highlightBox->Bind(wxEVT_CHECKBOX, [this, colorizer, componentId, highlightIndex](wxCommandEvent& evt) {
778  const bool value = evt.IsChecked();
779 
780  SPH_ASSERT(componentId);
781  if (value) {
782  componentId->setHighlightIdx(highlightIndex->GetValue());
783  } else {
784  componentId->setHighlightIdx(NOTHING);
785  }
786  highlightIndex->Enable(value);
789  controller->setColorizer(colorizer);
790  });
791  highlightIndex->Bind(wxEVT_SPINCTRL, [this, componentId, colorizer](wxSpinEvent& evt) {
792  // this is already executed on main thread, but we query it anyway to avoid spinner getting stuck
793  executeOnMainThread([this, componentId, colorizer, evt] {
794  const int index = evt.GetValue();
795  componentId->setHighlightIdx(index);
797  controller->setColorizer(colorizer);
798  });
799  });
800 }
801 
802 void RunPage::replaceQuantityBar(const Size idx) {
803  quantityPanel->Destroy();
804 
805  quantityPanel = new wxPanel(this, wxID_ANY);
806  // so far only needed for component id, so it is hacked like this
807  SharedPtr<IColorizer> newColorizer = colorizerList[idx];
809  if (dynamicCast<ComponentIdColorizer>(newColorizer.get())) {
810  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
811  sizer->Add(new wxStaticText(quantityPanel, wxID_ANY, newColorizer->name()));
812  wxBoxSizer* offsetSizer = new wxBoxSizer(wxHORIZONTAL);
813  offsetSizer->AddSpacer(25);
814  sizer->Add(offsetSizer);
815  wxBoxSizer* actSizer = new wxBoxSizer(wxVERTICAL);
816  addComponentIdBar(quantityPanel, actSizer, newColorizer);
817  offsetSizer->Add(actSizer);
818  quantityPanel->SetSizerAndFit(sizer);
819  }
820 
821  quantityPanelSizer->Add(quantityPanel);
822  this->Layout();
823 }
824 
825 
826 void RunPage::setProgress(const Statistics& stats) {
828  progressBar->update(stats);
829 
831  if (timelineBar->IsShown() && stats.has(StatisticsId::INDEX)) {
832  timelineBar->setFrame(stats.get<int>(StatisticsId::INDEX));
833  }
834 }
835 
836 void RunPage::newPhase(const std::string& className, const std::string& instanceName) {
837  progressBar->onRunStart(className, instanceName);
838 }
839 
841  pane->Refresh();
842 }
843 
844 void RunPage::showTimeLine(const bool show) {
845  wxAuiPaneInfo& timelineInfo = manager->GetPane(timelineBar);
846  wxAuiPaneInfo& progressInfo = manager->GetPane(progressBar);
847  wxAuiPaneInfo& statsInfo = manager->GetPane(statsBar);
848 
849  if (!timelineInfo.IsShown()) {
850  timelineInfo.Show(show);
851  progressInfo.Show(!show);
852  statsInfo.Show(!show);
853  manager->Update();
854  }
855 }
856 
857 void RunPage::runStarted(const Storage& storage, const Path& path) {
858  Statistics dummy;
859  pane->onTimeStep(storage, dummy);
860 
861  const Size particleCnt = storage.getParticleCnt();
862  executeOnMainThread([this, particleCnt] {
863  Statistics dummyStats;
864  this->makeStatsText(particleCnt, dummyStats);
865  });
866 
867  if (!path.empty()) {
868  timelineBar->update(path);
869  }
870 
871  for (auto plot : plots) {
872  plot->clear();
873  }
874 }
875 
876 void RunPage::onTimeStep(const Storage& storage, const Statistics& stats) {
877  // this is called from run thread (NOT main thread)
878 
879  // limit the refresh rate to avoid blocking the main thread
880  if (statsText && statsTimer.elapsed(TimerUnit::MILLISECOND) > 100) {
881  const Size particleCnt = storage.getParticleCnt();
882  executeOnMainThread([this, stats, particleCnt] { this->makeStatsText(particleCnt, stats); });
883  statsTimer.restart();
884  }
885 
886  pane->onTimeStep(storage, stats);
887 
888  if (selectedParticlePlot) {
889  selectedParticlePlot->selectParticle(controller->getSelectedParticle());
890 
892  SharedPtr<IColorizer> colorizer = controller->getCurrentColorizer();
893  // we need validity of arrayrefs only for the duration of this function, so weak reference is OK
894  colorizer->initialize(storage, RefEnum::WEAK);
895  selectedParticlePlot->setColorizer(colorizer);
896  }
897 
898  if (storage.has(QuantityId::MASS)) {
899  // skip plots if we don't have mass, for simplicity; this can be generalized if needed
900  for (auto plot : plots) {
901  plot->onTimeStep(storage, stats);
902  }
903 
904  executeOnMainThread([this] {
905  SPH_ASSERT(firstPlot && secondPlot);
906  firstPlot->Refresh();
907  secondPlot->Refresh();
908  });
909  }
910 }
911 
913  progressBar->onRunEnd();
914  if (waitingDialog) {
915  waitingDialog->EndModal(0);
916  }
917 }
918 
921  colorizerList = std::move(colorizers);
922  wxArrayString items;
923  for (auto& e : colorizerList) {
924  items.Add(e->name().c_str());
925  }
926  quantityBox->Set(items);
927  const Size actSelectedIdx = (selectedIdx < colorizerList.size()) ? selectedIdx : 0;
928  quantityBox->SetSelection(actSelectedIdx);
929 }
930 
931 void RunPage::setSelectedParticle(const Particle& particle, const Rgba color) {
933  probe->update(particle, color);
934 }
935 
938  probe->clear();
939 }
940 
941 wxSize RunPage::getCanvasSize() const {
942  const wxSize size = pane->GetSize();
943  return wxSize(max(size.x, 1), max(size.y, 1));
944 }
945 
946 class WaitDialog : public wxDialog {
947 public:
948  WaitDialog(wxWindow* parent)
949  : wxDialog(parent, wxID_ANY, "Info", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxSYSTEM_MENU) {
950  const wxSize size = wxSize(320, 90);
951  this->SetSize(size);
952  wxStaticText* text = new wxStaticText(this, wxID_ANY, "Waiting for simulation to finish ...");
953  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
954  sizer->AddStretchSpacer();
955  sizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
956  sizer->AddStretchSpacer();
957  this->SetSizer(sizer);
958  this->Layout();
959  this->CentreOnScreen();
960  }
961 };
962 
965  if (controller->isRunning()) {
966  const int retval =
967  wxMessageBox("Simulation is currently in progress. Do you want to stop it and close the window?",
968  "Stop?",
969  wxYES_NO | wxCENTRE);
970  if (retval == wxYES) {
971  controller->stop();
972  waitingDialog = new WaitDialog(this);
973  waitingDialog->ShowModal();
974  controller->quit(true);
975  return true;
976  } else {
977  return false;
978  }
979  } else {
980  return true;
981  }
982 }
983 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
void saveToFile(const wxBitmap &wx, const Path &path)
Definition: Bitmap.cpp:59
Defines projection transforming 3D particles onto 2D screen.
Helper functions to check the internal consistency of the code.
@ NO_THROW
Function cannot throw exceptions.
@ MAIN_THREAD
Function can only be executed from main thread.
#define CHECK_FUNCTION(flags)
Definition: CheckFunction.h:40
Object converting quantity values of particles into colors.
const float radius
Definition: CurveDialog.cpp:18
Looking for problems in SPH simulation and reporting potential errors.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
void printStat(ILogger &logger, const Statistics &stats, const StatisticsId id, const std::string &message, const std::string &unit="", const std::string &emptyValue="")
Definition: LogWriter.cpp:24
Logging routines of the run.
NAMESPACE_SPH_BEGIN void executeOnMainThread(const Function< void()> &function)
Posts a callback to be executed on main thread.
Definition: MainLoop.cpp:8
Posting events to be executed on main thread.
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
constexpr Float LARGE
Definition: MathUtils.h:34
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const NothingType NOTHING
Definition: Optional.h:16
Frame showing information about selected particle.
Renderer drawing individual particles as dots.
Drawing of plots.
Array< PlotData > getPlotList(const GuiSettings &gui)
Definition: Plots.cpp:130
Simple thread pool with fixed number of threads.
QuantityId
Unique IDs of basic quantities of SPH particles.
Definition: QuantityIds.h:19
@ MASS
Paricles masses, always a scalar quantity.
const wxSize buttonSize(250, -1)
const wxSize spinnerSize(100, -1)
const int boxPadding
Definition: RunPage.cpp:133
StatisticsId
List of values that are computed and displayed every timestep.
Definition: Statistics.h:108
@ COLLISION_EVAL_TIME
Wallclock duration of collision evaluation.
@ SPH_EVAL_TIME
Wallclock duration of evaluation of SPH derivatives.
@ BREAKUP_COUNT
Number of fragmentation collisions.
@ LIMITING_QUANTITY
Quantity that currently limits the timestep.
@ POSTPROCESS_EVAL_TIME
Wallclock spent on data dump, particle visualization, etc.
@ RUN_TIME
Current time of the simulation in code units. Does not necessarily have to be 0 when run starts.
@ TIMESTEP_VALUE
Current value of timestep.
@ AGGREGATE_COUNT
Number of aggregates in the simulation (single particles are not counted as aggregates).
@ TIMESTEP_CRITERION
Criterion that currently limits the timestep.
@ TOTAL_COLLISION_COUNT
Number of collisions in the timestep.
@ INDEX
Current number of time step, indexed from 0.
@ OVERLAP_COUNT
Number of particle overlaps detected during collision evaluation.
@ TIMESTEP_ELAPSED
Wallclock time spend on computing last timestep.
@ GRAVITY_BUILD_TIME
Wallclock duration of gravity tree building.
@ MERGER_COUNT
Number of mergers in the timestep.
@ GRAVITY_EVAL_TIME
Wallclock duration of gravity evaluation.
@ BOUNCE_COUNT
Number of bounce collisions.
Criteria for computing the time step.
CriterionId
@ DERIVATIVE
Timestep based on value-to-derivative ratio.
Optional< Path > doSaveFileDialog(const std::string &title, Array< FileFormat > &&formats)
Definition: Utils.cpp:56
Random utility functions for drawing stuff to DC.
static AffineMatrix identity()
Definition: AffineMatrix.h:132
Generic dynamically allocated resizable storage.
Definition: Array.h:43
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
Main GUI class connection the simulation with UI controls.
Definition: Controller.h:42
bool isRunning() const
Returns true if a simulation is running.
Definition: Controller.cpp:348
void setRenderer(AutoPtr< IRenderer > &&newRenderer)
Sets a new renderer used to draw particles.
Definition: Controller.cpp:520
AutoPtr< ICamera > getCurrentCamera() const
Returns the camera currently used for the rendering.
Definition: Controller.cpp:437
void quit(const bool waitForFinish=false)
Closes down the model, clears all allocated resources. Must be called only once.
Definition: Controller.cpp:185
const wxBitmap & getRenderedBitmap() const
Renders a bitmap of current view.
Definition: Controller.cpp:426
SharedPtr< IColorizer > getCurrentColorizer() const
Returns the colorizer currently used for rendering into the window.
Definition: Controller.cpp:432
void open(const Path &path, const bool sequence=false)
Opens a simulation snapshot from given file.
Definition: Controller.cpp:92
void stop(const bool waitForFinish=false)
Stops the current simulation.
Definition: Controller.cpp:132
void pause()
Pause the current simulation.
Definition: Controller.cpp:127
void refresh(AutoPtr< ICamera > &&camera)
Re-renders the particles with given camera.
Definition: Controller.cpp:638
Optional< Size > getSelectedParticle() const
Definition: Controller.cpp:570
bool tryRedraw()
If possible, redraws the particles with data from storage.
Definition: Controller.cpp:611
GuiSettings & getParams()
Returns the settings object.
Definition: Controller.cpp:316
void setColorizer(const SharedPtr< IColorizer > &newColorizer)
Sets a new colorizer to be displayed.
Definition: Controller.cpp:509
INLINE TValue get(const GuiSettingsId id) const
Definition: Settings.h:245
INLINE GuiSettings & set(const GuiSettingsId id, const TValue &value)
Definition: Settings.h:250
Interface defining a camera or view, used by a renderer.
Definition: Camera.h:62
virtual AutoPtr< ICamera > clone() const =0
virtual void setCutoff(const Optional< float > newCutoff)=0
Modifies the clipping distance of the camera.
virtual void transform(const AffineMatrix &matrix)=0
Transforms the current view by given matrix.
virtual std::string name() const =0
Returns the name of the colorizer.
virtual void initialize(const Storage &storage, const RefEnum ref)=0
Initialize the colorizer before by getting necessary quantities from storage.
virtual ICamera & getCamera()=0
virtual void onTimeStep(const Storage &storage, const Statistics &stats)=0
virtual void resetView()=0
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
Wrapper of type value of which may or may not be present.
Definition: Optional.h:23
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
void update(const Particle &selectedParticle, const Rgba colorizerColor)
Definition: ParticleProbe.h:50
Object holding information about single particle.
Definition: Particle.h:17
Object representing a path on a filesystem.
Definition: Path.h:17
bool empty() const
Checks if the path is empty.
Definition: Path.cpp:10
void update(const Statistics &stats)
Definition: ProgressPanel.h:37
void onRunStart(const std::string &className, const std::string &instanceName)
Definition: ProgressPanel.h:26
Non-owning wrapper of pointer.
Definition: RawPtr.h:19
INLINE T * get() const
Definition: RawPtr.h:67
Definition: Color.h:8
~RunPage()
Definition: RunPage.cpp:125
wxSize getCanvasSize() const
Definition: RunPage.cpp:941
bool close()
Definition: RunPage.cpp:963
void deselectParticle()
Definition: RunPage.cpp:936
void setColorizerList(Array< SharedPtr< IColorizer >> &&colorizers)
Definition: RunPage.cpp:919
void setProgress(const Statistics &stats)
Definition: RunPage.cpp:826
void refresh()
Definition: RunPage.cpp:840
void newPhase(const std::string &className, const std::string &instanceName)
Definition: RunPage.cpp:836
void runStarted(const Storage &storage, const Path &path)
Definition: RunPage.cpp:857
RunPage(wxWindow *window, Controller *controller, GuiSettings &guiSettings)
Definition: RunPage.cpp:73
void showTimeLine(const bool show)
Definition: RunPage.cpp:844
void onTimeStep(const Storage &storage, const Statistics &stats)
Definition: RunPage.cpp:876
void setSelectedParticle(const Particle &particle, const Rgba color)
Definition: RunPage.cpp:931
void onRunEnd()
Definition: RunPage.cpp:912
static const Settings & getDefaults()
\brief Returns a reference to object containing default values of all settings.
INLINE RawPtr< T > get() const
Definition: SharedPtr.h:223
Object holding various statistics about current run.
Definition: Statistics.h:22
TValue get(const StatisticsId idx) const
Returns value of a statistic.
Definition: Statistics.h:88
bool has(const StatisticsId idx) const
Checks if the object contains a statistic with given ID.
Definition: Statistics.h:44
Container storing all quantities used within the simulations.
Definition: Storage.h:230
Size getParticleCnt() const
Returns the number of particles.
Definition: Storage.cpp:445
bool has(const QuantityId key) const
Checks if the storage contains quantity with given key.
Definition: Storage.cpp:130
virtual void startSequence(const Path &firstFile) const override
Definition: RunPage.cpp:60
TimeLineCallbacks(Controller *parent)
Definition: RunPage.cpp:53
virtual void pause() const override
Definition: RunPage.cpp:68
virtual void frameChanged(const Path &newFile) const override
Definition: RunPage.cpp:56
virtual void stop() const override
Definition: RunPage.cpp:64
void setFrame(const Size newFrame)
Definition: TimeLine.h:73
void update(const Path &inputFile)
Definition: TimeLine.h:69
int64_t elapsed(const TimerUnit unit) const
Returns elapsed time in timer units. Does not reset the timer.
Definition: Timer.cpp:55
void restart()
Reset elapsed duration to zero.
Definition: Timer.cpp:51
WaitDialog(wxWindow *parent)
Definition: RunPage.cpp:948
@ COLORMAP_LOGARITHMIC_FACTOR
@ PARTICLE_RADIUS
Displayed radius of particle in units of smoothing length.
@ CAMERA_ORTHO_CUTOFF
Max z-coordinate of particle to be displayed by ortho renderer.
@ SURFACE_LEVEL
Value of iso-surface being constructed; lower value means larget bodies.
@ SURFACE_SUN_INTENSITY
Intentity of the sun.
@ SURFACE_AMBIENT
Ambient color for surface renderer.
SharedPtr< IScheduler > getScheduler(const RunSettings &settings=RunSettings::getDefaults())
Definition: Factory.cpp:178
@ OVERLAP
Specifies that overlapping particles belong into the same component.
Size digits
Definition: PlotView.h:24
Size minCnt
Definition: PlotView.h:23