SPH
RenderPane.cpp
Go to the documentation of this file.
2 #include "gui/MainLoop.h"
7 #include "quantities/Storage.h"
8 #include "system/Timer.h"
9 #include "thread/CheckFunction.h"
10 #include <iostream>
11 #include <wx/dcclient.h>
12 #include <wx/weakref.h>
13 
15 
17 void safeRefresh(wxPanel* panel) {
18  executeOnMainThread([ref = wxWeakRef<wxPanel>(panel)] {
19  if (ref) {
20  ref->Refresh();
21  }
22  });
23 }
24 
25 void BitmapOutput::update(const Bitmap<Rgba>& bitmap, Array<Label>&& labels, const bool isFinal) {
26  this->update(bitmap.clone(), std::move(labels), isFinal);
27 }
28 
29 void BitmapOutput::update(Bitmap<Rgba>&& bitmap, Array<Label>&& UNUSED(labels), const bool UNUSED(isFinal)) {
30  {
31  std::unique_lock<std::mutex> lock(mutex);
32  render = std::move(bitmap);
33  }
34  safeRefresh(panel);
35 }
36 
38  std::unique_lock<std::mutex> lock(mutex);
39  if (render.empty()) {
40  return {};
41  }
42 
43  wxBitmap bitmap;
44  toWxBitmap(render, bitmap);
45  return bitmap;
46 }
47 
49 private:
50  RawPtr<const IJob> cameraJob;
51  AutoPtr<ICamera> camera;
52 
53 public:
54  virtual void onStart(const IJob& job) override {
55  cameraJob = addressOf(job);
56  }
57 
58  virtual void onEnd(const Storage& UNUSED(storage), const Statistics& UNUSED(stats)) override {
59  SPH_ASSERT(cameraJob);
60  SharedPtr<CameraData> data = cameraJob->getResult().getValue<CameraData>();
61  camera = std::move(data->camera);
62  }
63 
65  return camera->clone();
66  }
67 };
68 
69 Outcome InteractiveRenderer::Status::isValid() const {
70  if (!otherReason.empty()) {
71  return makeFailed(otherReason);
72  } else if (notInitialized) {
73  return makeFailed("Initializing");
74  } else if (particlesMissing) {
75  return makeFailed("Particles not connected");
76  } else if (cameraMissing) {
77  return makeFailed("Camera not connected");
78  } else if (colorizerMissing) {
79  return makeFailed("No quantity selected");
80  } else {
81  return SUCCESS;
82  }
83 }
84 
85 
87  : node(node)
88  , quitting(false)
89  , output(panel) {}
90 
93 
94  job = dynamicCast<AnimationJob, IJob>(node->getJob());
95 
96  // install the accessors
97  this->setRendererAccessor(globals);
98  for (Size i = 0; i < node->getSlotCnt(); ++i) {
99  const SlotData slot = node->getSlot(i);
100  if (slot.type == GuiJobType::CAMERA) {
101  if (slot.provider) {
102  this->setCameraAccessor(globals, slot.provider);
103  } else {
104  status.cameraMissing = true;
105  }
106  } else if (slot.type == JobType::PARTICLES) {
107  if (slot.provider) {
108  this->setNodeAccessor(slot.provider);
109  } else {
110  status.particlesMissing = true;
111  }
112  }
113  }
114 
115  // parse everything when the thread starts
116  changed.node = node;
117 
118  thread = std::thread([this, globals] { this->renderLoop(globals); });
119 }
120 
122  this->stop();
123 }
124 
125 void InteractiveRenderer::resize(const Pixel newResolution) {
126  resolution = newResolution;
127  changed.resolution = true;
128  this->update();
129 }
130 
131 AutoPtr<ICamera> InteractiveRenderer::getNewCamera(const SharedPtr<JobNode>& cameraNode,
132  const RunSettings& globals) const {
134  UpdateCameraCallbacks callbacks;
135  cameraNode->run(globals, callbacks);
136  return callbacks.getCamera();
137 }
138 
139 void InteractiveRenderer::setCameraAccessor(const RunSettings& globals,
140  const SharedPtr<JobNode>& cameraNode) {
141  SPH_ASSERT(cameraNode);
143 
144  auto accessor = [=](const JobNotificationType type, const Any& UNUSED(value)) {
147  // don't care
148  return;
149  }
150  changed.camera = this->getNewCamera(cameraNode, globals);
151  this->update();
152  };
153 
154  cameraNode->addAccessor(this->sharedFromThis(), accessor);
155 }
156 
157 void InteractiveRenderer::setRendererAccessor(const RunSettings& globals) {
158  SPH_ASSERT(node);
159 
160  auto accessor = [=](const JobNotificationType type, const Any& value) {
162  if (!preview) {
163  // we previously failed to parse the object, so we have to do it from scratch anyway
164  changed.node = cloneHierarchy(*node);
165  } else if (type == JobNotificationType::ENTRY_CHANGED) {
166  const std::string key = anyCast<std::string>(value).value();
167 
169  if (key == "quantities" || key == "surface_gravity") {
170  changed.colorizer = job->getColorizer(globals);
171  status.colorizerMissing = !changed.colorizer;
172  } else {
173 
175 
176  const GuiSettingsId id = GuiSettings::getEntryId(key).valueOr(GuiSettingsId(-1));
177  // SPH_ASSERT(id);
178  static FlatSet<GuiSettingsId> SOFT_PARAMS = {
186  };
188 
189  if (key == "transparent" || SOFT_PARAMS.contains(id)) {
190  changed.parameters = job->getRenderParams();
191  } else {
192  changed.renderer = job->getRenderer(globals);
193  }
194  }
195  } else if (type == JobNotificationType::PROVIDER_CONNECTED) {
196  SharedPtr<JobNode> provider = anyCast<SharedPtr<JobNode>>(value).value();
197  const ExtJobType jobType = provider->provides().value();
198  if (jobType == JobType::PARTICLES) {
199  this->setNodeAccessor(provider);
200  changed.node = cloneHierarchy(*node);
201  } else if (jobType == GuiJobType::CAMERA) {
202  // assuming the camera does not have providers ...
203  SPH_ASSERT(provider->getSlotCnt() == 0);
204  this->setCameraAccessor(globals, provider);
205  changed.camera = this->getNewCamera(provider, globals);
206  } else {
207  SPH_ASSERT(false, "Connected unexpected node ", provider->instanceName());
208  }
209  } else if (type == JobNotificationType::PROVIDER_DISCONNECTED) {
210  // changed.node = cloneHierarchy(*node);
211  SharedPtr<JobNode> provider = anyCast<SharedPtr<JobNode>>(value).value();
212  const ExtJobType jobType = provider->provides().value();
213  if (jobType == GuiJobType::CAMERA) {
214  status.cameraMissing = true;
215  } else if (jobType == JobType::PARTICLES) {
216  status.particlesMissing = true;
217  } else {
218  SPH_ASSERT(false, "Disconnected unexpected node ", provider->instanceName());
219  }
220  }
221 
222  this->update();
223  };
224 
225  node->addAccessor(this->sharedFromThis(), accessor);
226 }
227 
228 void InteractiveRenderer::setNodeAccessor(const SharedPtr<JobNode>& particleNode) {
229  SPH_ASSERT(particleNode);
230 
231  auto accessor = [=](const JobNotificationType type, const Any& value) {
233 
235  // don't care about connection to other nodes
236  return;
237  } else if (type == JobNotificationType::PROVIDER_CONNECTED) {
238  // install accessor to the provider and all of its providers
239  SharedPtr<JobNode> provider = anyCast<SharedPtr<JobNode>>(value).value();
240  provider->enumerate([this](const SharedPtr<JobNode>& node) { this->setNodeAccessor(node); });
241  }
242 
243  changed.node = cloneHierarchy(*node);
244  this->update();
245  };
246 
247  particleNode->addAccessor(this->sharedFromThis(), accessor);
248 }
249 
250 void InteractiveRenderer::renderLoop(const RunSettings& globals) {
251  quitting = false;
253  while (!quitting) {
254  SharedPtr<JobNode> evaluated = changed.node;
255  if (evaluated) {
256  // everything changed, re-evaluate
257  try {
258  std::cout << "Updating ALL" << std::endl;
259  NullJobCallbacks callbacks;
260  evaluated->prepare(globals, callbacks);
261  RawPtr<AnimationJob> newJob = dynamicCast<AnimationJob, IJob>(evaluated->getJob());
262  preview = newJob->getRenderPreview(globals);
263  status.clear();
264  } catch (const InvalidSetup& e) {
265  status.otherReason = e.what();
266  preview = nullptr;
267  safeRefresh(output.getPanel());
268  }
269  // not changed in the meantime
270  if (changed.node == evaluated) {
271  changed.node = nullptr;
272  }
273 
274  } else if (preview) {
275  if (changed.camera) {
276  std::cout << "Updating camera" << std::endl;
277  status.cameraMissing = false;
278  preview->update(std::move(changed.camera));
279  }
280  if (changed.parameters) {
281  std::cout << "Updating parameters" << std::endl;
282  preview->update(std::move(changed.parameters.value()));
283  changed.parameters = NOTHING;
284  }
285  if (changed.colorizer) {
286  std::cout << "Updating colorizer" << std::endl;
287  status.colorizerMissing = false;
288  preview->update(std::move(changed.colorizer));
289  }
290  if (changed.renderer) {
291  std::cout << "Updating renderer" << std::endl;
292  preview->update(std::move(changed.renderer));
293  }
294  if (changed.resolution) {
295  std::cout << "Updating resolution" << std::endl;
296  // not actually needed to do anything, just reset the flag
297  changed.resolution = false;
298  }
299  }
300 
302  if (preview && !quitting && !changed.pending() && status.isValid()) {
303  std::cout << "Re-render" << std::endl;
304  preview->render(resolution, output);
305  }
306 
307  if (!quitting && !changed.pending()) {
308  std::unique_lock<std::mutex> lock(mutex);
309  cv.wait(lock);
310  }
311  }
312 }
313 
314 void InteractiveRenderer::update() {
316 
317  if (preview) {
318  preview->cancel();
319  }
320  std::unique_lock<std::mutex> lock(mutex);
321  cv.notify_one();
322 }
323 
324 void InteractiveRenderer::stop() {
326  quitting = true;
327  if (preview) {
328  preview->cancel();
329  std::unique_lock<std::mutex> lock(mutex);
330  cv.notify_one();
331  }
332 
333  if (thread.joinable()) {
334  thread.join();
335  }
336 }
337 
338 
339 RenderPane::RenderPane(wxWindow* parent,
340  const wxSize size,
341  const SharedPtr<JobNode>& node,
342  const RunSettings& globals)
343  : wxPanel(parent, wxID_ANY, wxDefaultPosition, size) {
344  renderer = makeShared<InteractiveRenderer>(node, this);
345  renderer->start(globals);
346 
347  this->Connect(wxEVT_PAINT, wxPaintEventHandler(RenderPane::onPaint));
348  this->Bind(wxEVT_SIZE, [this](wxSizeEvent& UNUSED(evt)) {
349  const wxSize size = this->GetClientSize();
350  renderer->resize(Pixel(size.x, size.y));
351  });
352 }
353 
354 void RenderPane::onPaint(wxPaintEvent& UNUSED(evt)) {
356  // std::cout << "Paint event" << std::endl;`
357  wxPaintDC dc(this);
358  const wxSize size = dc.GetSize();
359  wxBitmap bitmap = renderer->getBitmap();
360  Outcome valid = renderer->isValid();
361 
362  if (bitmap.IsOk()) {
363  if (!valid) {
364  bitmap = bitmap.ConvertToDisabled();
365  }
366  const wxSize offset = size - bitmap.GetSize();
367  dc.DrawBitmap(bitmap, wxPoint(offset.x / 2, offset.y / 2));
368  }
369 
370  if (!valid) {
371  const wxString text = valid.error() + " ...";
372  const wxSize textSize = dc.GetTextExtent(text);
373  dc.DrawText(text, size.x / 2 - textSize.x / 2, size.y / 2 - textSize.y / 2);
374  }
375 }
376 
377 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
NAMESPACE_SPH_BEGIN void toWxBitmap(const Bitmap< Rgba > &bitmap, wxBitmap &wx)
Definition: Bitmap.cpp:12
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.
Container storing sorted unique values.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
@ PARTICLES
Job providing particles.
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.
SharedPtr< JobNode > cloneHierarchy(JobNode &node, const Optional< std::string > &prefix)
Clones all nodes in the hierarchy.
Definition: Node.cpp:282
JobNotificationType
Definition: Node.h:77
@ ENTRY_CHANGED
Definition: Node.h:78
@ PROVIDER_DISCONNECTED
Definition: Node.h:80
@ DEPENDENT_CONNECTED
Definition: Node.h:81
@ PROVIDER_CONNECTED
Definition: Node.h:79
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const NothingType NOTHING
Definition: Optional.h:16
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
INLINE RawPtr< T > addressOf(T &ref)
Definition: RawPtr.h:82
NAMESPACE_SPH_BEGIN void safeRefresh(wxPanel *panel)
Calls Refresh on given main in main thread if the panel still exists.
Definition: RenderPane.cpp:17
Container for storing particle quantities and materials.
Measuring time intervals and executing periodic events.
AutoPtr< IRenderPreview > getRenderPreview(const RunSettings &global) const
Definition: RenderJobs.cpp:415
AutoPtr< IRenderer > getRenderer(const RunSettings &global) const
Definition: RenderJobs.cpp:454
AutoPtr< IColorizer > getColorizer(const RunSettings &global) const
Definition: RenderJobs.cpp:443
RenderParams getRenderParams() const
Definition: RenderJobs.cpp:239
Type-safe object that can store value of any type, similar to std::any.
Definition: Any.h:15
Generic dynamically allocated resizable storage.
Definition: Array.h:43
INLINE const TError & error() const
Returns the error message.
Definition: Outcome.h:88
wxBitmap getBitmap()
Definition: RenderPane.cpp:37
virtual void update(const Bitmap< Rgba > &bitmap, Array< Label > &&labels, const bool isFinal) override
May be called once after render finishes or multiple times for progressive renderers.
Definition: RenderPane.cpp:25
wxPanel * getPanel() const
Definition: RenderPane.h:31
bool empty() const
Definition: Bitmap.h:76
Bitmap clone() const
Definition: Bitmap.h:31
virtual const char * what() const noexcept
Definition: Exceptions.h:18
bool contains(const T &value)
Definition: FlatSet.h:89
virtual AutoPtr< ICamera > clone() const =0
Base class for all object performing an operation in a simulation hierarchy.
Definition: Job.h:96
virtual JobContext getResult() const =0
Returns the result of the job.
virtual void update(RenderParams &&params)=0
virtual void render(const Pixel resolution, IRenderOutput &output)=0
virtual void cancel()=0
void resize(const Pixel newResolution)
Definition: RenderPane.cpp:125
Outcome isValid() const
Definition: RenderPane.h:94
void start(const RunSettings &globals)
Definition: RenderPane.cpp:91
wxBitmap getBitmap()
Definition: RenderPane.h:90
InteractiveRenderer(const SharedPtr< JobNode > &node, wxPanel *panel)
Definition: RenderPane.cpp:86
Thrown when components of the run are mutually incompatible.
Definition: Exceptions.h:24
SharedPtr< TValue > getValue() const
Returns the stored value.
Definition: Job.inl.h:25
Size getSlotCnt() const
Returns the number of provider slots of this node.
Definition: Node.cpp:132
virtual void run(const RunSettings &global, IJobCallbacks &callbacks) override
Evaluates the node and all its providers.
Definition: Node.cpp:184
void addAccessor(const SharedToken &owner, const Accessor &accessor)
Adds an accessor for entries returned by the getSettings function.
Definition: Node.cpp:52
std::string instanceName() const
Returns the instance name of the job.
Definition: Node.cpp:14
Optional< ExtJobType > provides() const
Returns the type of the job.
Definition: Node.cpp:56
RawPtr< IJob > getJob() const
Returns the underlying job.
Definition: Node.cpp:48
SlotData getSlot(const Size index) const
Returns the information about given slot.
Definition: Node.cpp:136
virtual void prepare(const RunSettings &global, IJobCallbacks &callbacks)
Evaluates all provides, without executing the node itself.
Definition: Node.cpp:189
void enumerate(Function< void(const SharedPtr< JobNode > &job)> func)
Enumerates all nodes in the hierarchy.
RenderPane(wxWindow *parent, const wxSize size, const SharedPtr< JobNode > &node, const RunSettings &globals)
Definition: RenderPane.cpp:339
static Optional< GuiSettingsId > getEntryId(const std::string &name)
Returns an ID for given entry name.
Definition: Settings.h:403
SharedPtr< InteractiveRenderer > sharedFromThis() const
Definition: SharedPtr.h:426
Object holding various statistics about current run.
Definition: Statistics.h:22
Container storing all quantities used within the simulations.
Definition: Storage.h:230
virtual void onEnd(const Storage &UNUSED(storage), const Statistics &UNUSED(stats)) override
Definition: RenderPane.cpp:58
virtual void onStart(const IJob &job) override
Notifies the caller that a new job started running.
Definition: RenderPane.cpp:54
AutoPtr< ICamera > getCamera() const
Definition: RenderPane.cpp:64
GuiSettingsId
Definition: Settings.h:91
@ COLORMAP_LOGARITHMIC_FACTOR
@ 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.
AutoPtr< ICamera > camera
Definition: CameraJobs.h:16
Definition: Point.h:101
Definition: Node.h:58
ExtJobType type
Specifies the type of the slot, or the type of the node connecting to it.
Definition: Node.h:63
SharedPtr< JobNode > provider
Node currently connected to the slot.
Definition: Node.h:74