GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
aux_js.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2022, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-443271.
4 //
5 // This file is part of the GLVis visualization tool and library. For more
6 // information and source code availability see https://glvis.org.
7 //
8 // GLVis is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "aux_vis.hpp"
13 #include "palettes.hpp"
14 #include "stream_reader.hpp"
15 #include "visual.hpp"
16 
17 #ifdef GLVIS_USE_LIBPNG
18 #include <png.h>
19 #endif
20 
21 #include <SDL2/SDL_hints.h>
22 #include <emscripten/bind.h>
23 #include <emscripten/html5.h>
24 #include <emscripten/val.h>
25 
26 // used in extern context
27 thread_local std::string plot_caption;
28 thread_local std::string extra_caption;
29 thread_local mfem::GeometryRefiner GLVisGeometryRefiner;
30 
31 static VisualizationSceneScalarData * vs = nullptr;
32 
33 // either bitmap data or png bytes
34 std::vector<unsigned char> * screen_state = nullptr;
35 
37 
38 namespace em = emscripten;
39 
40 namespace js
41 {
42 
43 using namespace mfem;
44 
45 bool startVisualization(const std::string input, const std::string data_type,
46  int w, int h)
47 {
48  std::stringstream ss(input);
49 
50  // 0 - scalar data, 1 - vector data, 2 - mesh only, (-1) - unknown
51  const int field_type = stream_state.ReadStream(ss, data_type);
52 
53  // reset antialiasing
55 
56  std::string line;
57  double minv = 0.0, maxv = 0.0;
58  while (ss >> line)
59  {
60  if (line == "keys")
61  {
62  std::cout << "parsing 'keys'" << std::endl;
63  ss >> stream_state.keys;
64  }
65  else if (line == "valuerange")
66  {
67  std::cout << "parsing 'valuerange'" << std::endl;
68  ss >> minv >> maxv;
69  }
70  else
71  {
72  std::cout << "unknown line '" << line << "'" << std::endl;
73  }
74  }
75 
76  if (field_type < 0 || field_type > 2)
77  {
78  return false;
79  }
80 
81  if (InitVisualization("glvis", 0, 0, w, h))
82  {
83  return false;
84  }
85 
86  delete vs;
87  vs = nullptr;
88 
89  double mesh_range = -1.0;
90  if (field_type == 0 || field_type == 2)
91  {
92  if (stream_state.grid_f)
93  {
94  stream_state.grid_f->GetNodalValues(stream_state.sol);
95  }
96  if (stream_state.mesh->SpaceDimension() == 2)
97  {
99  if (stream_state.normals.Size() > 0)
100  {
101  vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol,
102  &stream_state.normals);
103  }
104  else
105  {
106  vs = vss = new VisualizationSceneSolution(*stream_state.mesh, stream_state.sol);
107  }
108  if (stream_state.grid_f)
109  {
110  vss->SetGridFunction(*stream_state.grid_f);
111  }
112  if (field_type == 2)
113  {
114  vs->OrthogonalProjection = 1;
115  vs->SetLight(0);
116  vs->Zoom(1.8);
117  // Use the 'bone' palette when visualizing a 2D mesh only (otherwise
118  // the 'jet-like' palette is used in 2D, see vssolution.cpp).
119  vs->palette.SetIndex(4);
120  }
121  }
122  else if (stream_state.mesh->SpaceDimension() == 3)
123  {
125  vs = vss = new VisualizationSceneSolution3d(*stream_state.mesh,
126  stream_state.sol);
127  if (stream_state.grid_f)
128  {
129  vss->SetGridFunction(stream_state.grid_f.get());
130  }
131  if (field_type == 2)
132  {
133  if (stream_state.mesh->Dimension() == 3)
134  {
135  // Use the 'white' palette when visualizing a 3D volume mesh only
136  // vs->palette.SetIndex(4);
137  vs->palette.SetIndex(11);
138  vss->SetLightMatIdx(4);
139  }
140  else
141  {
142  // Use the 'bone' palette when visualizing a surface mesh only
143  // (the same as when visualizing a 2D mesh only)
144  vs->palette.SetIndex(4);
145  }
146  // Otherwise, the 'vivid' palette is used in 3D see vssolution3d.cpp
147 
148  vss->ToggleDrawAxes();
149  vss->ToggleDrawMesh();
150  }
151  }
152  if (field_type == 2)
153  {
154  if (stream_state.grid_f)
155  {
156  mesh_range = stream_state.grid_f->Max() + 1.0;
157  }
158  else
159  {
160  mesh_range = stream_state.sol.Max() + 1.0;
161  }
162  }
163  }
164  else if (field_type == 1)
165  {
166  if (stream_state.mesh->SpaceDimension() == 2)
167  {
168  if (stream_state.grid_f)
169  {
170  vs = new VisualizationSceneVector(*stream_state.grid_f);
171  }
172  else
173  {
174  vs = new VisualizationSceneVector(*stream_state.mesh, stream_state.solu,
175  stream_state.solv);
176  }
177  }
178  else if (stream_state.mesh->SpaceDimension() == 3)
179  {
180  if (stream_state.grid_f)
181  {
182  stream_state.grid_f
183  = ProjectVectorFEGridFunction(std::move(stream_state.grid_f));
184  vs = new VisualizationSceneVector3d(*stream_state.grid_f);
185  }
186  else
187  {
188  vs = new VisualizationSceneVector3d(*stream_state.mesh, stream_state.solu,
189  stream_state.solv, stream_state.solw);
190  }
191  }
192  }
193 
194  if (vs)
195  {
196  // increase the refinement factors if visualizing a GridFunction
197  if (stream_state.grid_f)
198  {
199  vs->AutoRefine();
200  vs->SetShading(2, true);
201  }
202  if (mesh_range > 0.0)
203  {
204  vs->SetValueRange(-mesh_range, mesh_range);
205  vs->SetAutoscale(0);
206  }
207  if (stream_state.mesh->SpaceDimension() == 2 && field_type == 2)
208  {
209  SetVisualizationScene(vs, 2);
210  }
211  else
212  {
213  SetVisualizationScene(vs, 3);
214  }
215  }
216 
217  CallKeySequence(stream_state.keys.c_str());
218 
219  if (minv || maxv)
220  {
221  vs->SetValueRange(minv, maxv);
222  }
223 
224  SendExposeEvent();
225  return true;
226 }
227 
228 
229 int updateVisualization(std::string data_type, std::string stream)
230 {
231  std::stringstream ss(stream);
232 
233  if (data_type != "solution")
234  {
235  std::cerr << "unsupported data type '" << data_type << "' for stream update" <<
236  std::endl;
237  return 1;
238  }
239 
240  StreamState new_state;
241  new_state.ReadStream(ss, data_type);
242  double mesh_range = -1.0;
243 
244  if (stream_state.SetNewMeshAndSolution(std::move(new_state), vs))
245  {
246  if (mesh_range > 0.0)
247  {
248  vs->SetValueRange(-mesh_range, mesh_range);
249  }
250 
251  SendExposeEvent();
252  return 0;
253  }
254  else
255  {
256  cout << "Stream: field type does not match!" << endl;
257  return 1;
258  }
259 }
260 
262 {
263  GetAppWindow()->mainIter();
264 }
265 
266 void setCanvasId(const std::string & id)
267 {
268  std::cout << "glvis: setting canvas id to " << id << std::endl;
269  GetAppWindow()->setCanvasId(id);
270 }
271 
273 {
274  SDL_EventState(SDL_KEYDOWN, SDL_DISABLE);
275  SDL_EventState(SDL_KEYUP, SDL_DISABLE);
276 }
277 
279 {
280  SDL_EventState(SDL_KEYDOWN, SDL_ENABLE);
281  SDL_EventState(SDL_KEYUP, SDL_ENABLE);
282 }
283 
284 void setKeyboardListeningElementId(const std::string & id)
285 {
286  SDL_SetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT, id.c_str());
287 }
288 
289 void processKeys(const std::string & keys)
290 {
291  CallKeySequence(keys.c_str());
292 }
293 
294 void processKey(int sym, bool ctrl=false, bool shift=false, bool alt=false)
295 {
296  Uint16 mod = 0;
297  mod |= ctrl ? KMOD_CTRL : 0;
298  mod |= shift ? KMOD_SHIFT : 0;
299  mod |= alt ? KMOD_ALT : 0;
300  GetAppWindow()->callKeyDown(sym, mod);
301 }
302 
303 void setupResizeEventCallback(const std::string & id)
304 {
305  // typedef EM_BOOL (*em_ui_callback_func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData);
306  std::cout << "glvis: adding resize callback for " << id << std::endl;
307  auto err = emscripten_set_resize_callback(id.c_str(), nullptr,
308  true, [](int eventType, const EmscriptenUiEvent *uiEvent,
309  void *userData) -> EM_BOOL
310  {
311  std::cout << "got resize event" << std::endl;
312  return true;
313  });
314  if (err != EMSCRIPTEN_RESULT_SUCCESS)
315  {
316  std::cerr << "error (emscripten_set_resize_callback): " << err << std::endl;
317  }
318 }
319 
320 std::string getHelpString()
321 {
324  return vss->GetHelpString();
325 }
326 
327 em::val getScreenBuffer(bool flip_y=false)
328 {
329  MyExpose();
330  auto * wnd = GetAppWindow();
331  int w, h;
332  wnd->getGLDrawSize(w, h);
333 
334  // 4 bytes for rgba
335  const size_t buffer_size = w*h*4;
336  if (screen_state == nullptr)
337  {
338  screen_state = new std::vector<unsigned char>(buffer_size);
339  }
340  else if (buffer_size > screen_state->size())
341  {
342  screen_state->resize(buffer_size);
343  }
344 
345  glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, screen_state->data());
346 
347  // `canvas` starts at top left but `glReadPixels' starts at bottom left
348  if (flip_y)
349  {
350  auto * orig = screen_state;
351  auto * flip = new std::vector<unsigned char>(buffer_size);
352  // from `glReadPixels` man page: Pixels are returned in row order from
353  // the lowest to the highest row, left to right in each row.
354  for (int j = 0; j < h; ++j)
355  {
356  for (int i = 0; i < w*4; ++i)
357  {
358  (*flip)[4*w*j + i] = (*orig)[4*w*(h-j-1) + i];
359  }
360  }
361  screen_state = flip;
362  delete orig;
363  }
364 
365  return em::val(em::typed_memory_view(screen_state->size(),
366  screen_state->data()));
367 }
368 
369 #ifdef GLVIS_USE_LIBPNG
371 {
372  constexpr const char * filename = "im.png";
373  auto * wnd = GetAppWindow();
374  int w, h;
375  wnd->getGLDrawSize(w, h);
376 
377  MyExpose();
378  // save to in-memory file
379  int status = SaveAsPNG(filename, w, h, wnd->isHighDpi(), true);
380  if (status != 0)
381  {
382  fprintf(stderr, "unable to generate png\n");
383  return em::val::null();
384  }
385 
386  // load in-memory file to byte array
387  std::ifstream ifs(filename, std::ios::binary);
388  if (!ifs)
389  {
390  fprintf(stderr, "unable to load png\n");
391  return em::val::null();
392  }
393 
394  if (!screen_state)
395  {
396  screen_state = new std::vector<unsigned char>(std::istreambuf_iterator<char>
397  (ifs), {});
398  }
399  else
400  {
401  screen_state->assign(std::istreambuf_iterator<char>
402  (ifs), {});
403  }
404 
405  return em::val(em::typed_memory_view(screen_state->size(),
406  screen_state->data()));
407 }
408 #endif // GLVIS_USE_LIBPNG
409 } // namespace js
410 
411 // Info on type conversion:
412 // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions
414 {
415  em::function("startVisualization", &js::startVisualization);
416  em::function("updateVisualization", &js::updateVisualization);
417  em::function("iterVisualization", &js::iterVisualization);
418  em::function("sendExposeEvent", &SendExposeEvent);
419  em::function("disableKeyHanding", &js::disableKeyHandling);
420  em::function("enableKeyHandling", &js::enableKeyHandling);
421  em::function("setKeyboardListeningElementId",
423  em::function("getTextureMode", &GetUseTexture);
424  em::function("setTextureMode", &SetUseTexture);
425  em::function("resizeWindow", &ResizeWindow);
426  em::function("setCanvasId", &js::setCanvasId);
427  em::function("setupResizeEventCallback", &js::setupResizeEventCallback);
428  em::function("getHelpString", &js::getHelpString);
429  em::function("processKeys", &js::processKeys);
430  em::function("processKey", &js::processKey);
431  em::function("getScreenBuffer", &js::getScreenBuffer);
432 #ifdef GLVIS_USE_LIBPNG
433  em::function("getPNGByteArray", &js::getPNGByteArray);
434 #endif
435 }
int input
Definition: glvis.cpp:65
std::unique_ptr< GridFunction > ProjectVectorFEGridFunction(std::unique_ptr< GridFunction > gf)
void SendExposeEvent()
Send expose event. In our case MyReshape is executed and Draw after it.
Definition: aux_vis.cpp:346
thread_local GeometryRefiner GLVisGeometryRefiner
Definition: glvis.cpp:71
void setCanvasId(const std::string &id)
Definition: aux_js.cpp:266
void SetValueRange(double, double)
Definition: vsdata.cpp:1305
void getGLDrawSize(int &w, int &h)
Definition: sdl.cpp:563
void setupResizeEventCallback(const std::string &id)
Definition: aux_js.cpp:303
thread_local SdlWindow * wnd
Definition: aux_vis.cpp:50
gl3::MeshRenderer & getRenderer()
Definition: sdl.hpp:226
int updateVisualization(std::string data_type, std::string stream)
Definition: aux_js.cpp:229
virtual void AutoRefine()=0
void Zoom(double factor)
Definition: openglvis.cpp:1125
VisualizationScene * GetVisualizationScene()
Definition: aux_vis.cpp:63
em::val getScreenBuffer(bool flip_y=false)
Definition: aux_js.cpp:327
PaletteState palette
Definition: openglvis.hpp:178
virtual std::string GetHelpString() const
Definition: vsdata.hpp:144
mfem::Vector solv
void MyExpose(GLsizei w, GLsizei h)
Definition: aux_vis.cpp:389
void SetUseTexture(int ut)
Definition: aux_vis.cpp:1561
void SetVisualizationScene(VisualizationScene *scene, int view, const char *keys)
Definition: aux_vis.cpp:307
virtual void SetShading(int, bool)=0
std::unique_ptr< mfem::GridFunction > grid_f
thread_local string plot_caption
Definition: glvis.cpp:60
void enableKeyHandling()
Definition: aux_js.cpp:278
void SetLight(bool light_set)
Definition: vsdata.hpp:238
mfem::Vector solu
thread_local VisualizationSceneScalarData * vs
Definition: glvis.cpp:67
thread_local StreamState stream_state
Definition: glvis.cpp:66
mfem::Vector solw
void processKeys(const std::string &keys)
Definition: aux_js.cpp:289
std::string keys
void setCanvasId(std::string canvas_id)
Definition: sdl.hpp:263
bool SetNewMeshAndSolution(StreamState new_state, VisualizationScene *vs)
void SetIndex(int num)
Sets the palette texture to bind.
Definition: palettes.hpp:31
void callKeyDown(SDL_Keycode k, Uint16 mod=0)
Definition: sdl.hpp:212
EMSCRIPTEN_BINDINGS(js_funcs)
Definition: aux_js.cpp:413
int SaveAsPNG(const char *fname, int w, int h, bool is_hidpi, bool with_alpha, std::function< void(int, void *)> get_row)
Definition: aux_vis.cpp:895
std::unique_ptr< mfem::Mesh > mesh
bool isHighDpi() const
This property is set by createWindow().
Definition: sdl.hpp:224
void disableKeyHandling()
Definition: aux_js.cpp:272
std::vector< unsigned char > * screen_state
Definition: aux_js.cpp:34
em::val getPNGByteArray()
Definition: aux_js.cpp:370
void ResizeWindow(int w, int h)
Definition: aux_vis.cpp:1546
void SetLightMatIdx(unsigned i)
Definition: openglvis.cpp:1020
bool startVisualization(const std::string input, const std::string data_type, int w, int h)
Definition: aux_js.cpp:45
SdlWindow * GetAppWindow()
Definition: aux_vis.cpp:58
void iterVisualization()
Definition: aux_js.cpp:261
void SetGridFunction(GridFunction &u)
Definition: vssolution.hpp:95
thread_local string extra_caption
Definition: glvis.cpp:61
void processKey(int sym, bool ctrl=false, bool shift=false, bool alt=false)
Definition: aux_js.cpp:294
std::string getHelpString()
Definition: aux_js.cpp:320
void SetAutoscale(int _autoscale)
Definition: vsdata.cpp:1141
int InitVisualization(const char name[], int x, int y, int w, int h)
Initializes the visualization and some keys.
Definition: aux_vis.cpp:81
mfem::Vector normals
void setAntialiasing(bool aa_status)
Definition: renderer.cpp:45
int ReadStream(std::istream &is, const std::string &data_type)
void mainIter()
Definition: sdl.cpp:362
void CallKeySequence(const char *seq)
Definition: aux_vis.cpp:253
void setKeyboardListeningElementId(const std::string &id)
Definition: aux_js.cpp:284
int GetUseTexture()
Definition: aux_vis.cpp:1556
void SetGridFunction(GridFunction *gf)
mfem::Vector sol