GLVis  v4.2
Accurate and flexible finite element visualization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
aux_vis.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 <iostream>
13 #include <sstream>
14 #include <fstream>
15 #include <cmath>
16 #include <chrono>
17 
18 #include "mfem.hpp"
19 #include "sdl.hpp"
20 #include "palettes.hpp"
21 #include "visual.hpp"
22 #include "gl2ps.h"
23 
24 #if defined(GLVIS_USE_LIBTIFF)
25 #include "tiffio.h"
26 #elif defined(GLVIS_USE_LIBPNG)
27 #include <png.h>
28 #endif
29 
30 #include "font.hpp"
31 #ifndef __EMSCRIPTEN__
32 #include <fontconfig/fontconfig.h>
33 #endif
34 
35 using namespace mfem;
36 
37 thread_local int visualize = 0;
39 thread_local GLVisCommand *glvis_command = NULL;
40 
41 #ifdef GLVIS_MULTISAMPLE
42 static int glvis_multisample = GLVIS_MULTISAMPLE;
43 #else
44 static int glvis_multisample = -1;
45 #endif
46 
47 float line_w = 1.f;
49 
50 thread_local SdlWindow * wnd = nullptr;
51 bool wndLegacyGl = false;
52 bool wndUseHiDPI = true;
53 void SDLMainLoop(bool server_mode)
54 {
55  SdlWindow::StartSDL(server_mode);
56 }
57 
59 {
60  return wnd;
61 }
62 
64 {
65  return locscene;
66 }
67 
68 void SetLegacyGLOnly(bool status)
69 {
70  wndLegacyGl = true;
71 }
72 
73 void SetUseHiDPI(bool status)
74 {
75  wndUseHiDPI = status;
76 }
77 
78 void MyExpose(GLsizei w, GLsizei h);
79 void MyExpose();
80 
81 int InitVisualization (const char name[], int x, int y, int w, int h)
82 {
83 
84 #ifdef GLVIS_DEBUG
85  cout << "OpenGL Visualization" << endl;
86 #endif
87  if (!wnd)
88  {
89  wnd = new SdlWindow();
90  if (!wnd->createWindow(name, x, y, w, h, wndLegacyGl))
91  {
92  return 1;
93  }
94  }
95  else
96  {
97  wnd->clearEvents();
98  }
99 
100 #ifdef GLVIS_DEBUG
101  cout << "Window should be up" << endl;
102 #endif
103  InitFont();
106 
107  // auxReshapeFunc (MyReshape); // not needed, MyExpose calls it
108  // auxReshapeFunc (NULL);
109  void (*exposeFunc)(void) = MyExpose;
110  wnd->setOnExpose(exposeFunc);
111 
112  wnd->setOnMouseDown(SDL_BUTTON_LEFT, LeftButtonDown);
113  wnd->setOnMouseUp(SDL_BUTTON_LEFT, LeftButtonUp);
114  wnd->setOnMouseMove(SDL_BUTTON_LEFT, LeftButtonLoc);
115  wnd->setOnMouseDown(SDL_BUTTON_MIDDLE, MiddleButtonDown);
116  wnd->setOnMouseUp(SDL_BUTTON_MIDDLE, MiddleButtonUp);
117  wnd->setOnMouseMove(SDL_BUTTON_MIDDLE, MiddleButtonLoc);
118  wnd->setOnMouseDown(SDL_BUTTON_RIGHT, RightButtonDown);
119  wnd->setOnMouseUp(SDL_BUTTON_RIGHT, RightButtonUp);
120  wnd->setOnMouseMove(SDL_BUTTON_RIGHT, RightButtonLoc);
121 
123 
124  // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp
125  wnd->setOnKeyDown (SDLK_s, KeyS);
126  wnd->setOnKeyDown ('S', KeyS);
127 
128  wnd->setOnKeyDown (SDLK_q, KeyQPressed);
129  // wnd->setOnKeyDown (SDLK_Q, KeyQPressed);
130 
131  wnd->setOnKeyDown (SDLK_LEFT, KeyLeftPressed);
132  wnd->setOnKeyDown (SDLK_RIGHT, KeyRightPressed);
133  wnd->setOnKeyDown (SDLK_UP, KeyUpPressed);
134  wnd->setOnKeyDown (SDLK_DOWN, KeyDownPressed);
135 
136  wnd->setOnKeyDown (SDLK_KP_0, Key0Pressed);
137  wnd->setOnKeyDown (SDLK_KP_1, Key1Pressed);
138  wnd->setOnKeyDown (SDLK_KP_2, Key2Pressed);
139  wnd->setOnKeyDown (SDLK_KP_3, Key3Pressed);
140  wnd->setOnKeyDown (SDLK_KP_4, Key4Pressed);
141  wnd->setOnKeyDown (SDLK_KP_5, Key5Pressed);
142  wnd->setOnKeyDown (SDLK_KP_6, Key6Pressed);
143  wnd->setOnKeyDown (SDLK_KP_7, Key7Pressed);
144  wnd->setOnKeyDown (SDLK_KP_8, Key8Pressed);
145  wnd->setOnKeyDown (SDLK_KP_9, Key9Pressed);
146 
147  wnd->setOnKeyDown (SDLK_KP_MEMSUBTRACT, KeyMinusPressed);
148  wnd->setOnKeyDown (SDLK_KP_MEMADD, KeyPlusPressed);
149 
150  wnd->setOnKeyDown (SDLK_KP_DECIMAL, KeyDeletePressed);
151  wnd->setOnKeyDown (SDLK_KP_ENTER, KeyEnterPressed);
152 
153  wnd->setOnKeyDown (SDLK_PERIOD, KeyDeletePressed);
154  wnd->setOnKeyDown (SDLK_RETURN, KeyEnterPressed);
155 
156  wnd->setOnKeyDown (SDLK_0, Key0Pressed);
157  wnd->setOnKeyDown (SDLK_1, Key1Pressed);
158  wnd->setOnKeyDown (SDLK_2, Key2Pressed);
159  wnd->setOnKeyDown (SDLK_3, Key3Pressed);
160  wnd->setOnKeyDown (SDLK_4, Key4Pressed);
161  wnd->setOnKeyDown (SDLK_5, Key5Pressed);
162  wnd->setOnKeyDown (SDLK_6, Key6Pressed);
163  wnd->setOnKeyDown (SDLK_7, Key7Pressed);
164  wnd->setOnKeyDown (SDLK_8, Key8Pressed);
165  wnd->setOnKeyDown (SDLK_9, Key9Pressed);
166 
167  wnd->setOnKeyDown (SDLK_MINUS, KeyMinusPressed);
168  wnd->setOnKeyDown (SDLK_PLUS, KeyPlusPressed);
169  wnd->setOnKeyDown (SDLK_EQUALS, KeyPlusPressed);
170 
171  wnd->setOnKeyDown (SDLK_j, KeyJPressed);
172  // wnd->setOnKeyDown (AUX_J, KeyJPressed);
173 
174  wnd->setOnKeyDown (SDLK_KP_MULTIPLY, ZoomIn);
175  wnd->setOnKeyDown (SDLK_KP_DIVIDE, ZoomOut);
176 
177  wnd->setOnKeyDown (SDLK_ASTERISK, ZoomIn);
178  wnd->setOnKeyDown (SDLK_SLASH, ZoomOut);
179 
180  wnd->setOnKeyDown (SDLK_LEFTBRACKET, ScaleDown);
181  wnd->setOnKeyDown (SDLK_RIGHTBRACKET, ScaleUp);
182  wnd->setOnKeyDown (SDLK_AT, LookAt);
183 
184 #ifndef __EMSCRIPTEN__
185  wnd->setOnKeyDown(SDLK_LEFTPAREN, ShrinkWindow);
186  wnd->setOnKeyDown(SDLK_RIGHTPAREN, EnlargeWindow);
187 
188  if (locscene)
189  {
190  delete locscene;
191  }
192 #endif
193  locscene = nullptr;
194 
195  return 0;
196 }
197 
198 void SendKeySequence(const char *seq)
199 {
200  for (const char* key = seq; *key != '\0'; key++)
201  {
202  if (*key == '~')
203  {
204  key++;
205  switch (*key)
206  {
207  case 'e': // expose event
208  SendExposeEvent();
209  break;
210  case 'l': // left arrow
211  wnd->signalKeyDown(SDLK_LEFT);
212  break;
213  case 'r': // right arrow
214  wnd->signalKeyDown(SDLK_RIGHT);
215  break;
216  case 'u': // up arrow
217  wnd->signalKeyDown(SDLK_UP);
218  break;
219  case 'd': // down arrow
220  wnd->signalKeyDown(SDLK_DOWN);
221  break;
222  case '3': // F3
223  wnd->signalKeyDown(SDLK_F3);
224  break;
225  case '5': // F5
226  wnd->signalKeyDown(SDLK_F5);
227  break;
228  case '6': // F6
229  wnd->signalKeyDown(SDLK_F6);
230  break;
231  case '7': // F7
232  wnd->signalKeyDown(SDLK_F7);
233  break;
234  case '.': // Keypad ./Del
235  wnd->signalKeyDown(SDLK_PERIOD);
236  break;
237  case 'E': // Keypad Enter
238  wnd->signalKeyDown(SDLK_RETURN);
239  break;
240  }
241  continue;
242  }
243  else
244  {
245  wnd->signalKeyDown(*key);
246  }
247  }
248 }
249 
250 
251 thread_local bool disableSendExposeEvent = false;
252 
253 void CallKeySequence(const char *seq)
254 {
255  const char *key = seq;
256 
257  disableSendExposeEvent = true;
258  for ( ; *key != '\0'; key++ ) // see /usr/include/X11/keysymdef.h
259  {
260  if (*key != '~')
261  {
262  wnd->callKeyDown(*key);
263  }
264  else
265  {
266  key++;
267  switch (*key)
268  {
269  case 'l': // left arrow
270  wnd->callKeyDown(SDLK_LEFT);
271  break;
272  case 'r': // right arrow
273  wnd->callKeyDown(SDLK_RIGHT);
274  break;
275  case 'u': // up arrow
276  wnd->callKeyDown(SDLK_UP);
277  break;
278  case 'd': // down arrow
279  wnd->callKeyDown(SDLK_DOWN);
280  break;
281  case '3': // F3
282  wnd->callKeyDown(SDLK_F3);
283  break;
284  case '5': // F5
285  wnd->callKeyDown(SDLK_F5);
286  break;
287  case '6': // F6
288  wnd->callKeyDown(SDLK_F6);
289  break;
290  case '7': // F7
291  wnd->callKeyDown(SDLK_F7);
292  break;
293  case '.': // Keypad ./Del
294  wnd->callKeyDown(SDLK_PERIOD);
295  break;
296  case 'E': // Keypad Enter
297  wnd->callKeyDown(SDLK_RETURN);
298  break;
299  }
300  }
301  }
302  disableSendExposeEvent = false;
303 }
304 
305 void InitIdleFuncs();
306 
308  const char *keys)
309 {
310  locscene = scene;
311  locscene -> view = view;
312  if (view == 2)
313  {
314  scene -> CenterObject2D();
315  }
316  else
317  {
318  scene -> CenterObject();
319  }
320 
321  InitIdleFuncs();
322  if (scene -> spinning)
323  {
325  }
326 
327  if (keys)
328  {
329  SendKeySequence(keys);
330  }
332 }
333 
335 {
336  visualize = 1;
337 #ifndef __EMSCRIPTEN__
338  wnd->mainLoop();
339 #endif
340  InitIdleFuncs();
341  delete locscene;
342  delete wnd;
343  wnd = nullptr;
344 }
345 
347 {
348  if (disableSendExposeEvent) { return; }
349  wnd->signalExpose();
350 }
351 
352 void MyReshape(GLsizei w, GLsizei h)
353 {
354  wnd->getRenderer().setViewport(w, h);
355 
356  gl3::GlMatrix projmtx;
357  projmtx.identity();
358 
359  double ViewCenterX = locscene->ViewCenterX;
360  double ViewCenterY = locscene->ViewCenterY;
361 
363  {
364  double scale = locscene->ViewScale;
365  if (w <= h)
366  {
367  projmtx.ortho(-1.0, 1.0, -double(h)/w, double(h)/w, -10, 10);
368  }
369  else
370  {
371  projmtx.ortho(-double(w)/h, double(w)/h, -1, 1, -10, 10);
372  }
373  projmtx.scale(scale, scale, 1.0);
374  }
375  else
376  {
377  double ViewAngle = locscene->ViewAngle;
378 
379  if (w < h)
380  ViewAngle = (360.0/M_PI)*atan( tan( ViewAngle*(M_PI/360.0) ) *
381  double(h)/w );
382 
383  projmtx.perspective(ViewAngle, double(w)/h, 0.1, 5.0);
384  }
385  projmtx.translate(-ViewCenterX, -ViewCenterY, 0.0);
386  locscene->SetProjectionMtx(projmtx.mtx);
387 }
388 
389 void MyExpose(GLsizei w, GLsizei h)
390 {
391  MyReshape (w, h);
392  GLuint color_tex = locscene->palette.GetColorTexture();
393  GLuint alpha_tex = locscene->palette.GetAlphaTexture();
394  wnd->getRenderer().setColorTexture(color_tex);
395  wnd->getRenderer().setAlphaTexture(alpha_tex);
397  for (auto drawable_ptr : frame.needs_buffering)
398  {
399  wnd->getRenderer().buffer(drawable_ptr);
400  }
401  wnd->getRenderer().render(frame.queue);
402 }
403 
404 void MyExpose()
405 {
406  int w, h;
407  wnd->getGLDrawSize(w, h);
408  MyExpose(w, h);
409  wnd->signalSwap();
410 }
411 
412 
413 thread_local Array<void (*)()> IdleFuncs;
414 thread_local int LastIdleFunc;
415 thread_local bool use_idle = false;
416 
417 bool MainIdleFunc();
418 
420 {
421  IdleFuncs.SetSize(0);
422  LastIdleFunc = 0;
423  if (glvis_command)
424  {
426  }
427 }
428 
430 {
431  int status = glvis_command->Execute();
432  if (status < 0)
433  {
434  cout << "GLVisCommand signalled exit" << endl;
435  wnd->signalQuit();
436  }
437  else if (status == 1)
438  {
439  // no commands right now - main loop should sleep
440  return true;
441  }
442  return false;
443 }
444 
446 {
447  bool sleep = true;
448 #ifndef __EMSCRIPTEN__
449  if (glvis_command && visualize == 1
450  && !(IdleFuncs.Size() > 0 && use_idle))
451  {
452  // Execute the next event from the communication thread if:
453  // - a valid GLVisCommand has been set
454  // - the communication thread is not stopped
455  // - The idle function flag is not set, or no idle functions have been
456  // registered
457  sleep = CommunicationIdleFunc();
458  if (IdleFuncs.Size() > 0) { sleep = false; }
459  }
460  else if (IdleFuncs.Size() > 0)
461  {
462  LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size();
463  if (IdleFuncs[LastIdleFunc])
464  {
465  (*IdleFuncs[LastIdleFunc])();
466  }
467  // Continue executing idle functions
468  sleep = false;
469  }
470  use_idle = !use_idle;
471 #else
472  if (IdleFuncs.Size() > 0)
473  {
474  LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size();
475  if (IdleFuncs[LastIdleFunc])
476  {
477  (*IdleFuncs[LastIdleFunc])();
478  }
479  // Continue executing idle functions
480  sleep = false;
481  }
482 #endif
483  return sleep;
484  LastIdleFunc = (LastIdleFunc + 1) % IdleFuncs.Size();
485  if (IdleFuncs[LastIdleFunc])
486  {
487  (*IdleFuncs[LastIdleFunc])();
488  }
489 }
490 
491 void AddIdleFunc(void (*Func)(void))
492 {
493  IdleFuncs.Union(Func);
495 }
496 
497 void RemoveIdleFunc(void (*Func)(void))
498 {
499  IdleFuncs.DeleteFirst(Func);
500  if (IdleFuncs.Size() == 0 && glvis_command == nullptr)
501  {
502  wnd->setOnIdle(NULL);
503  }
504 }
505 
506 
507 thread_local double xang = 0., yang = 0.;
508 thread_local gl3::GlMatrix srot;
509 thread_local double sph_t, sph_u;
510 thread_local GLint oldx, oldy, startx, starty;
511 
512 thread_local int constrained_spinning = 0;
513 
514 
515 void MainLoop()
516 {
517  static int p = 1;
518  if (locscene->spinning)
519  {
521  {
523  SendExposeEvent();
524  }
525  else
526  {
527  locscene->PreRotate(xang, 0.0, 0.0, 1.0);
528  SendExposeEvent();
529  }
530  std::this_thread::sleep_for(std::chrono::milliseconds{10}); // sleep for 0.01 seconds
531  }
532  if (locscene->movie)
533  {
534  char fname[20];
535  snprintf(fname, 20, "GLVis_m%04d", p++);
536  wnd->screenshot(fname);
537  }
538 }
539 
540 // Pressed mouse buttons events
541 
542 inline double sqr(double t)
543 {
544  return t*t;
545 }
546 
547 inline void ComputeSphereAngles(int &newx, int &newy,
548  double &new_sph_u, double &new_sph_t)
549 {
550  GLint viewport[4] = { 0, 0, 0, 0 };
551  double r, x, y, rr;
552  const double maxr = 0.996194698091745532295;
553 
554  wnd->getGLDrawSize(viewport[2], viewport[3]);
555  r = sqrt(sqr(viewport[2])+sqr(viewport[3]))*M_SQRT1_2;
556 
557  x = double(newx-viewport[0]-viewport[2]/2) / r;
558  y = double(-newy+viewport[1]+viewport[3]/2) / r;
559 
560  rr = sqrt(x*x+y*y);
561  if (rr > maxr)
562  {
563  x *= maxr/rr, y *= maxr/rr, rr = maxr;
564  }
565 
566  new_sph_u = 2.0 * acos(rr) - M_PI_2;
567  new_sph_t = atan2(y, x);
568 }
569 
571 {
572  locscene -> spinning = 0;
574 
575  oldx = event->mouse_x;
576  oldy = event->mouse_y;
577 
579 
580  srot.identity();
583 
584  startx = oldx;
585  starty = oldy;
586 }
587 
589 {
590  GLint newx = event->mouse_x;
591  GLint newy = event->mouse_y;
592  int sendexpose = 1;
593 
594  if (event->keymod & KMOD_CTRL)
595  {
596  if (event->keymod & KMOD_SHIFT)
597  {
598  locscene->PreRotate(double(newx-oldx)/2, 0.0, 0.0, 1.0);
599  }
600  else
601  {
602  double new_sph_u, new_sph_t;
603 
604  ComputeSphereAngles(newx, newy, new_sph_u, new_sph_t);
605 
606  gl3::GlMatrix newrot;
607  newrot.identity();
608 
609  double scoord[3], ncoord[3], inprod, cross[3];
610  scoord[0] = scoord[1] = cos(sph_u); scoord[2] = sin(sph_u);
611  ncoord[0] = ncoord[1] = cos(new_sph_u); ncoord[2] = sin(new_sph_u);
612  scoord[0] *= cos(sph_t); scoord[1] *= sin(sph_t);
613  ncoord[0] *= cos(new_sph_t); ncoord[1] *= sin(new_sph_t);
614  inprod = InnerProd(scoord, ncoord);
615  CrossProd(scoord, ncoord, cross);
616 
617  newrot.mult(locscene->cam.TransposeRotMatrix());
618  newrot.rotate(acos(inprod)*(180.0/M_PI), cross[0], cross[1], cross[2]);
619  newrot.mult(srot.mtx);
620  locscene->rotmat = newrot.mtx;
621  }
622  }
623  else if (event->keymod & KMOD_ALT)
624  {
625  locscene->Rotate(double(newx-oldx)/2, 0.0, 0.0, 1.0);
626  }
627  else if (event->keymod & KMOD_SHIFT)
628  {
629  locscene->Rotate(double(newx-oldx)/2, double(newy-oldy)/2);
630  }
631  else
632  {
633  locscene->Rotate(double(newy-oldy)/2, 1.0, 0.0, 0.0);
634  locscene->PreRotate(double(newx-oldx)/2, 0.0, 0.0, 1.0);
635  }
636 
637  oldx = newx;
638  oldy = newy;
639 
640  if (sendexpose)
641  {
642  SendExposeEvent();
643  }
644 }
645 
646 void LeftButtonUp (EventInfo *event)
647 {
648  GLint newx = event->mouse_x;
649  GLint newy = event->mouse_y;
650 
651  xang = (newx-startx)/5.0;
652  yang = (newy-starty)/5.0;
653 
654  if ( (event->keymod & KMOD_SHIFT) && (xang != 0.0 || yang != 0.0) )
655  {
656  locscene -> spinning = 1;
658  if (xang > 20) { xang = 20; } if (xang < -20) { xang = -20; }
659  if (yang > 20) { yang = 20; } if (yang < -20) { yang = -20; }
660 
661  if (event->keymod & KMOD_CTRL)
662  {
664  }
665  else
666  {
668  }
669  }
670 }
671 
673 {
674  startx = oldx = event->mouse_x;
675  starty = oldy = event->mouse_y;
676 }
677 
679 {
680  GLint newx = event->mouse_x;
681  GLint newy = event->mouse_y;
682 
683  if ( !( event->keymod & KMOD_CTRL ) )
684  {
685  int w, h;
686  double TrX, TrY, scale;
687 
689  {
690  scale = locscene->ViewScale;
691  }
692  else
693  {
694  scale = 0.4142135623730950488/tan(locscene->ViewAngle*(M_PI/360));
695  }
696  wnd->getGLDrawSize(w, h);
697  if (w < h)
698  {
699  scale *= w;
700  }
701  else
702  {
703  scale *= h;
704  }
705  TrX = 2.0*double(oldx-newx)/scale;
706  TrY = 2.0*double(newy-oldy)/scale;
707  locscene->ViewCenterX += TrX;
708  locscene->ViewCenterY += TrY;
709  }
710  else
711  {
712  // locscene->Translate((double)(newx-oldx)/200,(double)(newy-oldy)/200);
713 
714  double dx = double(newx-oldx)/400;
715  double dy = double(oldy-newy)/400;
716 
717  if (event->keymod & KMOD_SHIFT) // ctrl + shift
718  {
719  double sx = double(newx-startx)/400;
720  double sy = double(starty-newy)/400;
721 
722  locscene->cam.TurnLeftRight(dx-sx);
723  locscene->cam.TurnUpDown(sy-dy);
724 
725  locscene->cam.TurnUpDown(-sy);
727  }
728  else if (event->keymod & KMOD_ALT) // ctrl + alt
729  {
731  locscene->cam.TiltLeftRight(-dx);
732  }
733  else // ctrl
734  {
736  locscene->cam.MoveUpDown(-dy);
737  }
738  }
739 
740  SendExposeEvent();
741 
742  oldx = newx;
743  oldy = newy;
744 }
745 
747 {}
748 
750 {
751  startx = oldx = event->mouse_x;
752  starty = oldy = event->mouse_y;
753 }
754 
756 {
757  GLint newx = event->mouse_x;
758  GLint newy = event->mouse_y;
759 
760  if (event->keymod & KMOD_SHIFT)
761  {
762  // glLoadIdentity();
763  // GLfloat light[] = {newx,-newy, sqrt((float)(newx*newx+newy*newy)), 0.0 };
764  newx -= startx;
765  newy -= starty;
766  double l, x, y, z;
767  x = (double)newx / 300;
768  y = -(double)newy / 300;
769  l = sqrt (x*x + y*y);
770  if (l <= 1.)
771  {
772  z = sqrt (1. - l*l);
773  }
774  else if (l < 2.)
775  {
776  x *= (2./l-1);
777  y *= (2./l-1);
778  l = 2. - l;
779  z = -sqrt (1. - l*l);
780  }
781  else
782  {
783  x = 0.; y = 0.; z = -1.;
784  }
785  cout << "(x,y,z) = (" << x << ',' << y << ',' << z << ')' << endl;
786  locscene->SetLight0CustomPos({(float)x, (float)y, (float)z, 0.f});
787  }
788  else if ( !( event->keymod & KMOD_CTRL ) )
789  {
790  locscene -> Zoom (exp ( double (oldy-newy) / 100 ));
791  }
792  else
793  {
794  locscene -> Scale ( exp ( double (oldy-newy) / 50 ) );
795  }
796 
797  SendExposeEvent();
798 
799  oldx = newx;
800  oldy = newy;
801 }
802 
804 {}
805 
806 void TouchPinch(SDL_MultiGestureEvent & e)
807 {
808  // Scale or Zoom?
809  locscene->Zoom(exp(e.dDist*10));
810  SendExposeEvent();
811 }
812 
813 #if defined(GLVIS_USE_LIBTIFF)
814 const char *glvis_screenshot_ext = ".tif";
815 #elif defined(GLVIS_USE_LIBPNG)
816 const char *glvis_screenshot_ext = ".png";
817 #else
818 const char *glvis_screenshot_ext = ".bmp";
819 #endif
820 
821 // https://wiki.libsdl.org/SDL_CreateRGBSurfaceFrom
822 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
823 Uint32 rmask = 0xff000000;
824 Uint32 gmask = 0x00ff0000;
825 Uint32 bmask = 0x0000ff00:
826  Uint32 amask = 0x000000ff;
827 #else // little endian, like x86
828 Uint32 rmask = 0x000000ff;
829 Uint32 gmask = 0x0000ff00;
830 Uint32 bmask = 0x00ff0000;
831 Uint32 amask = 0xff000000;
832 #endif
833 
834 // https://halfgeek.org/wiki/Vertically_invert_a_surface_in_SDL
835 #define SDL_LOCKIFMUST(s) (SDL_MUSTLOCK(s) ? SDL_LockSurface(s) : 0)
836 #define SDL_UNLOCKIFMUST(s) { if(SDL_MUSTLOCK(s)) SDL_UnlockSurface(s); }
837 
838 int InvertSurfaceVertical(SDL_Surface *surface)
839 {
840  Uint8 *t, *a, *b, *last;
841  Uint16 pitch;
842 
843  if ( SDL_LOCKIFMUST(surface) < 0 )
844  {
845  return -2;
846  }
847 
848  /* do nothing unless at least two lines */
849  if (surface->h < 2)
850  {
851  SDL_UNLOCKIFMUST(surface);
852  return 0;
853  }
854 
855  /* get a place to store a line */
856  pitch = surface->pitch;
857  t = (Uint8*)malloc(pitch);
858 
859  if (t == NULL)
860  {
861  SDL_UNLOCKIFMUST(surface);
862  return -2;
863  }
864 
865  /* get first line; it's about to be trampled */
866  memcpy(t,surface->pixels,pitch);
867 
868  /* now, shuffle the rest so it's almost correct */
869  a = (Uint8*)surface->pixels;
870  last = a + pitch * (surface->h - 1);
871  b = last;
872 
873  while (a < b)
874  {
875  memcpy(a,b,pitch);
876  a += pitch;
877  memcpy(b,a,pitch);
878  b -= pitch;
879  }
880 
881  /* in this shuffled state, the bottom slice is too far down */
882  memmove( b, b+pitch, last-b );
883 
884  /* now we can put back that first row--in the last place */
885  memcpy(last,t,pitch);
886 
887  /* everything is in the right place; close up. */
888  free(t);
889  SDL_UNLOCKIFMUST(surface);
890 
891  return 0;
892 }
893 
894 #ifdef GLVIS_USE_LIBPNG
895 int SaveAsPNG(const char *fname, int w, int h, bool is_hidpi, bool with_alpha,
896  std::function<void(int,void*)> get_row)
897 {
898  png_byte *pixels = new png_byte[(with_alpha ? 4 : 3)*w];
899  if (!pixels)
900  {
901  return 1;
902  }
903 
904  png_structp png_ptr =
905  png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
906  if (!png_ptr)
907  {
908  delete [] pixels;
909  return 1;
910  }
911  png_infop info_ptr = png_create_info_struct(png_ptr);
912  if (!info_ptr)
913  {
914  png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
915  delete [] pixels;
916  return 1;
917  }
918 
919  FILE *fp = fopen(fname, "wb");
920  if (!fp)
921  {
922  png_destroy_write_struct(&png_ptr, &info_ptr);
923  delete [] pixels;
924  return 2;
925  }
926 
927  if (setjmp(png_jmpbuf(png_ptr)))
928  {
929  fclose(fp);
930  png_destroy_write_struct(&png_ptr, &info_ptr);
931  delete [] pixels;
932  return 3;
933  }
934 
935  png_uint_32 ppi = is_hidpi ? 144 : 72; // pixels/inch
936  png_uint_32 ppm = ppi/0.0254 + 0.5; // pixels/meter
937  png_set_pHYs(png_ptr, info_ptr, ppm, ppm, PNG_RESOLUTION_METER);
938 
939  png_init_io(png_ptr, fp);
940  png_set_IHDR(png_ptr, info_ptr, w, h, 8,
941  with_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
942  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
943  PNG_FILTER_TYPE_DEFAULT);
944 
945 
946  png_write_info(png_ptr, info_ptr);
947  for (int i = 0; i < h; i++)
948  {
949  if (!get_row)
950  {
951  glReadPixels(0, h-1-i, w, 1, with_alpha ? GL_RGBA : GL_RGB,
952  GL_UNSIGNED_BYTE, pixels);
953  }
954  else
955  {
956  get_row(i, pixels);
957  }
958  png_write_row(png_ptr, pixels);
959  }
960  png_write_end(png_ptr, info_ptr);
961 
962  fclose(fp);
963  png_destroy_write_struct(&png_ptr, &info_ptr);
964  delete [] pixels;
965 
966  return 0;
967 }
968 #endif // GLVIS_USE_LIBPNG
969 
970 int Screenshot(const char *fname, bool convert)
971 {
972 #ifdef GLVIS_DEBUG
973  cout << "Screenshot: glFinish() ... " << flush;
974 #endif
975  glFinish();
976 #ifdef GLVIS_DEBUG
977  cout << "done." << endl;
978 #endif
979 #ifndef __EMSCRIPTEN__
980  if (wnd->isExposePending())
981  {
982  MFEM_WARNING("Expose pending, some events may not have been handled." << endl);
983  }
984  string filename = fname;
985  string convert_name = fname;
986  bool call_convert = false;
987  if (convert)
988  {
989  // check the extension of 'fname' to see if convert is needed
990  size_t ext_size = strlen(glvis_screenshot_ext);
991  if (filename.size() < ext_size ||
992  filename.compare(filename.size() - ext_size,
993  ext_size, glvis_screenshot_ext) != 0)
994  {
995  call_convert = true;
996  filename += glvis_screenshot_ext;
997  }
998  }
999  else // do not call convert
1000  {
1001  filename += glvis_screenshot_ext;
1002  }
1003 
1004  int w, h;
1005  wnd->getGLDrawSize(w, h);
1006  if (wnd->isSwapPending())
1007  {
1008 #ifdef GLVIS_DEBUG
1009  cerr << "Screenshot: reading image data from back buffer..." << endl;
1010 #endif
1011  glReadBuffer(GL_BACK);
1012  }
1013  else
1014  {
1015 #ifdef GLVIS_DEBUG
1016  cerr << "Screenshot: reading image data from front buffer..." << endl;
1017 #endif
1018  MFEM_WARNING("Screenshot: Reading from the front buffer is unreliable. "
1019  << " Resulting screenshots may be incorrect." << endl);
1020  glReadBuffer(GL_FRONT);
1021  }
1022 #if defined(GLVIS_USE_LIBTIFF)
1023  // Save a TIFF image. This requires the libtiff library, see www.libtiff.org
1024  TIFF* image;
1025 
1026  // MyExpose(w,h);
1027 
1028  unsigned char *pixels = new unsigned char[3*w];
1029  if (!pixels)
1030  {
1031  return 1;
1032  }
1033 
1034  image = TIFFOpen(filename.c_str(), "w");
1035  if (!image)
1036  {
1037  delete [] pixels;
1038  return 2;
1039  }
1040 
1041  TIFFSetField(image, TIFFTAG_IMAGEWIDTH, w);
1042  TIFFSetField(image, TIFFTAG_IMAGELENGTH, h);
1043  TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, 8);
1044  TIFFSetField(image, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
1045  TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1046  TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, 3);
1047  TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, 1);
1048  TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
1049  TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
1050  for (int i = 0; i < h; i++)
1051  {
1052  glReadPixels(0, h-i-1, w, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels);
1053  if (TIFFWriteScanline(image, pixels, i, 0) < 0)
1054  {
1055  TIFFClose(image);
1056  delete [] pixels;
1057  return 3;
1058  }
1059  }
1060 
1061  TIFFFlushData(image);
1062  TIFFClose(image);
1063  delete [] pixels;
1064 
1065 #elif defined(GLVIS_USE_LIBPNG)
1066  // Save as png image. Requires libpng.
1067  int status = SaveAsPNG(filename.c_str(), w, h, wnd->isHighDpi());
1068  if (status != 0) { return status; }
1069 
1070 #else
1071  // use SDL for screenshots
1072 
1073  // https://stackoverflow.com/questions/20233469/how-do-i-take-and-save-a-bmp-screenshot-in-sdl-2
1074  unsigned char * pixels = new unsigned char[w*h*4]; // 4 bytes for RGBA
1075  glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
1076 
1077  SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(pixels, w, h, 8*4, w*4, rmask,
1078  gmask, bmask, amask);
1079  if (surf == nullptr)
1080  {
1081  std::cerr << "unable to take screenshot: " << SDL_GetError() << std::endl;
1082  }
1083  else
1084  {
1085  if (InvertSurfaceVertical(surf))
1086  {
1087  std::cerr << "failed to invert surface, your screenshot may be upside down" <<
1088  std::endl;
1089  }
1090  SDL_SaveBMP(surf, filename.c_str());
1091  SDL_FreeSurface(surf);
1092  // automatically convert to png if not being used
1093  if (!call_convert)
1094  {
1095  call_convert = true;
1096  convert_name += ".png";
1097  }
1098  }
1099  delete [] pixels;
1100 #endif
1101 
1102  if (call_convert)
1103  {
1104  ostringstream cmd;
1105  cmd << "convert " << filename << ' ' << convert_name;
1106  if (system(cmd.str().c_str()))
1107  {
1108  return 1;
1109  }
1110  remove(filename.c_str());
1111  }
1112  return 0;
1113 #else
1114  cout << "Screenshots not yet implemented for JS" << endl;
1115  return 1;
1116 #endif
1117 }
1118 
1119 void KeyS()
1120 {
1121  static int p = 1;
1122 
1123  if (locscene -> spinning)
1124  {
1125  locscene -> movie = 1 - locscene -> movie;
1126  if (locscene -> movie)
1127  {
1128  cout << "Recording a movie (series of snapshots)..." << endl;
1129  }
1130  else
1131  {
1132  cout << endl;
1133  }
1134  // use (ImageMagik's) convert GLVis_m* GLVis.{gif,mpg}
1135  }
1136  else
1137  {
1138  cout << "Taking snapshot number " << p << "... ";
1139  char fname[20];
1140  snprintf(fname, 20, "GLVis_s%02d", p++);
1141  wnd->screenshot(fname);
1142  cout << "done" << endl;
1143  }
1144  SendExposeEvent();
1145 }
1146 
1148 {
1149  return
1150  {
1151  {v.position.x, v.position.y, v.position.z},
1152  {v.color.r, v.color.g, v.color.b, v.color.a}
1153  };
1154 }
1155 
1157 {
1158  //print lines
1159  for (size_t i = 0; i < cbuf.lines.size(); i += 2)
1160  {
1161  GL2PSvertex lineOut[2] =
1162  {
1163  CreatePrintVtx(cbuf.lines[i]),
1164  CreatePrintVtx(cbuf.lines[i+1])
1165  };
1166  gl2psAddPolyPrimitive(GL2PS_LINE, 2, lineOut, 0, 0.f, 0.f,
1167  0xFFFF, 1, 0.2, 0, 0, 0);
1168  }
1169  // print triangles
1170  for (size_t i = 0; i < cbuf.triangles.size(); i += 3)
1171  {
1172  GL2PSvertex triOut[3] =
1173  {
1174  CreatePrintVtx(cbuf.triangles[i]),
1175  CreatePrintVtx(cbuf.triangles[i+1]),
1176  CreatePrintVtx(cbuf.triangles[i+2])
1177  };
1178  gl2psAddPolyPrimitive(GL2PS_TRIANGLE, 3, triOut, 0, 0.f, 0.f,
1179  0xFFFF, 1, 1, 0, 0, 0);
1180  }
1181  // print text
1182  for (const auto &entry : cbuf.text)
1183  {
1184  GL2PSvertex rpos = CreatePrintVtx({entry.offset, entry.color});
1185  gl2psForceRasterPos(&rpos);
1186  gl2psText(entry.text.c_str(), "Times", 12);
1187  }
1188 }
1189 
1190 void KeyCtrlP()
1191 {
1192 #ifdef __EMSCRIPTEN__
1193  cerr << "Printing in WebGL is not supported at this time." << endl;
1194 #else
1195  cout << "Printing the figure to GLVis.pdf... " << flush;
1196  locscene -> print = 1;
1197  FILE * fp;
1198  fp = fopen("GLVis.pdf", "wb");
1199  GLint viewport[4] = { 0, 0, 0, 0 };
1200  wnd->getGLDrawSize(viewport[2], viewport[3]);
1201  {
1202  gl3::SceneInfo wnd_scn = locscene->GetSceneObjs();
1203  for (auto to_buf : wnd_scn.needs_buffering)
1204  {
1205  wnd->getRenderer().buffer(to_buf);
1206  }
1207  gl2psBeginPage ( "GLVis.pdf", "GLVis", viewport,
1208  GL2PS_PDF, // or GL2PS_SVG, or GL2PS_EPS
1209  GL2PS_BSP_SORT,
1210  GL2PS_SIMPLE_LINE_OFFSET |
1211  // GL2PS_NO_PS3_SHADING |
1212  // GL2PS_NO_BLENDING |
1213  // GL2PS_OCCLUSION_CULL |
1214  // GL2PS_BEST_ROOT |
1215  GL2PS_SILENT |
1216  //GL2PS_DRAW_BACKGROUND |
1217  GL2PS_NO_BLENDING |
1218  GL2PS_NO_OPENGL_CONTEXT,
1219  GL_RGBA, 0, NULL, 16, 16, 16, 0, fp, "a" );
1220  gl3::CaptureBuffer cbuf = wnd->getRenderer().capture(wnd_scn.queue);
1221  PrintCaptureBuffer(cbuf);
1222  gl2psEndPage();
1223  }
1224  locscene -> print = 0;
1225  fclose(fp);
1226  cout << "done" << endl;
1227  wnd->signalExpose();
1228 #endif
1229 }
1230 
1232 {
1233  wnd->signalQuit();
1234  visualize = 0;
1235 }
1236 
1238 {
1239  static const char *state[] = { "running", "stopped" };
1240  if (visualize > 0 && visualize < 3)
1241  {
1242  visualize = 3 - visualize; // 1 <-> 2
1243  cout << "Communication thread(s): " << state[visualize-1] << endl;
1244  }
1245 }
1246 
1247 void ThreadsPauseFunc(GLenum state)
1248 {
1249  if (state & KMOD_CTRL)
1250  {
1252  }
1253  else
1254  {
1255  ToggleThreads();
1256  }
1257 }
1258 
1260 {
1261  if (visualize == 1)
1262  {
1263  ToggleThreads();
1264  }
1265 }
1266 
1268 {
1269  if (visualize == 2)
1270  {
1271  ToggleThreads();
1272  }
1273 }
1274 
1276 {
1277  if (fabs(xang) < 1.e-2)
1278  {
1279  xang = 0.;
1280  }
1281  if (xang != 0. || yang != 0.)
1282  {
1283  locscene->spinning = 1;
1285  }
1286  else
1287  {
1288  locscene->spinning = 0;
1290  }
1291  cout << "Spin angle: " << xang << " degrees / frame" << endl;
1292 }
1293 
1294 const double xang_step = 0.2; // angle in degrees
1295 
1297 {
1298  if (!locscene -> spinning)
1299  {
1300  xang = 0;
1301  }
1302  xang -= xang_step;
1303  CheckSpin();
1304 }
1305 
1307 {
1308  if (locscene -> spinning)
1309  {
1310  xang = yang = 0.;
1311  locscene -> spinning = 0;
1314  }
1315  else
1316  {
1317  xang = xang_step;
1318  locscene -> spinning = 1;
1321  }
1322 }
1323 
1325 {
1326  if (!locscene -> spinning)
1327  {
1328  xang = 0;
1329  }
1330  xang += xang_step;
1331  CheckSpin();
1332 }
1333 
1335 {
1336  locscene->PreRotate(1.0, 0.0, -1.0, 0.0);
1337  SendExposeEvent();
1338 }
1339 
1341 {
1342  locscene->Rotate(0.0, -1.0);
1343  SendExposeEvent();
1344 }
1345 
1347 {
1348  locscene->PreRotate(-1.0, 1.0, 0.0, 0.0);
1349  SendExposeEvent();
1350 }
1351 
1353 {
1354  locscene->PreRotate(-1.0, 0.0, 0.0, 1.0);
1355  SendExposeEvent();
1356 }
1357 
1359 {
1360  if (locscene->view == 2)
1361  {
1363  }
1364  else
1365  {
1367  }
1368  SendExposeEvent();
1369 }
1370 
1372 {
1373  locscene->PreRotate(1.0, 0.0, 0.0, 1.0);
1374  SendExposeEvent();
1375 }
1376 
1378 {
1379  locscene->PreRotate(1.0, 1.0, 0.0, 0.0);
1380  SendExposeEvent();
1381 }
1382 
1384 {
1385  locscene->Rotate(1.0, 1.0, 0.0, 0.0);
1386  SendExposeEvent();
1387 }
1388 
1390 {
1391  locscene->PreRotate(1.0, 0.0, 1.0, 0.0);
1392  SendExposeEvent();
1393 }
1394 
1395 void ShiftView(double dx, double dy)
1396 {
1397  double scale;
1399  {
1400  scale = locscene->ViewScale;
1401  }
1402  else
1403  {
1404  scale = 0.4142135623730950488/tan(locscene->ViewAngle*(M_PI/360));
1405  }
1406  locscene->ViewCenterX += dx/scale;
1407  locscene->ViewCenterY += dy/scale;
1408 }
1409 
1410 void KeyLeftPressed(GLenum state)
1411 {
1412  if (state & KMOD_CTRL)
1413  {
1414  ShiftView(0.05, 0.);
1415  }
1416  else
1417  {
1418  locscene->Rotate(-5, 0.0, 1.0, 0.0);
1419  }
1420  SendExposeEvent();
1421 }
1422 
1423 void KeyRightPressed(GLenum state)
1424 {
1425  if (state & KMOD_CTRL)
1426  {
1427  ShiftView(-0.05, 0.);
1428  }
1429  else
1430  {
1431  locscene->Rotate(5, 0.0, 1.0, 0.0);
1432  }
1433  SendExposeEvent();
1434 }
1435 
1436 void KeyUpPressed(GLenum state)
1437 {
1438  if (state & KMOD_CTRL)
1439  {
1440  ShiftView(0., -0.05);
1441  }
1442  else
1443  {
1444  locscene->Rotate(-5, 1.0, 0.0, 0.0);
1445  }
1446  SendExposeEvent();
1447 }
1448 
1449 void KeyDownPressed(GLenum state)
1450 {
1451  if (state & KMOD_CTRL)
1452  {
1453  ShiftView(0., 0.05);
1454  }
1455  else
1456  {
1457  locscene->Rotate(5, 1.0, 0.0, 0.0);
1458  }
1459  SendExposeEvent();
1460 }
1461 
1463 {
1465  SendExposeEvent();
1466 }
1467 
1469 {
1470  locscene -> Scale(1., 1., 1./1.1);
1471  SendExposeEvent();
1472 }
1473 
1475 {
1476  locscene -> Scale(1., 1., 1.1);
1477  SendExposeEvent();
1478 }
1479 
1480 void ZoomIn()
1481 {
1482  locscene->Zoom(exp (0.05));
1483  SendExposeEvent();
1484 }
1485 
1486 void ZoomOut()
1487 {
1488  locscene->Zoom(exp (-0.05));
1489  SendExposeEvent();
1490 }
1491 
1492 void ScaleUp()
1493 {
1494  locscene->Scale(1.025);
1495  SendExposeEvent();
1496 }
1497 
1499 {
1500  locscene->Scale(1.0/1.025);
1501  SendExposeEvent();
1502 }
1503 
1504 void LookAt()
1505 {
1506  cout << "ViewCenter = (" << locscene->ViewCenterX << ','
1507  << locscene->ViewCenterY << ")\nNew x = " << flush;
1508  cin >> locscene->ViewCenterX;
1509  cout << "New y = " << flush;
1510  cin >> locscene->ViewCenterY;
1511  SendExposeEvent();
1512 }
1513 
1514 const double window_scale_factor = 1.1;
1515 
1517 {
1518  int w, h;
1519  wnd->getWindowSize(w, h);
1520  w = (int)ceil(w / window_scale_factor);
1521  h = (int)ceil(h / window_scale_factor);
1522 
1523  cout << "New window size : " << w << " x " << h << endl;
1524 
1525  ResizeWindow(w, h);
1526 }
1527 
1529 {
1530  int w, h;
1531  wnd->getWindowSize(w, h);
1532  w = (int)ceil(w * window_scale_factor);
1533  h = (int)ceil(h * window_scale_factor);
1534 
1535  cout << "New window size : " << w << " x " << h << endl;
1536 
1537  ResizeWindow(w, h);
1538 }
1539 
1540 void MoveResizeWindow(int x, int y, int w, int h)
1541 {
1542  wnd->setWindowSize(w, h);
1543  wnd->setWindowPos(x, y);
1544 }
1545 
1546 void ResizeWindow(int w, int h)
1547 {
1548  wnd->setWindowSize(w, h);
1549 }
1550 
1551 void SetWindowTitle(const char *title)
1552 {
1553  wnd->setWindowTitle(title);
1554 }
1555 
1557 {
1558  return locscene->palette.GetSmoothSetting();
1559 }
1560 
1561 void SetUseTexture(int ut)
1562 {
1563  if (ut == 0)
1564  {
1566  }
1567  else
1568  {
1570  }
1571 }
1572 
1574 {
1575  return glvis_multisample;
1576 }
1577 
1578 void SetMultisample(int m)
1579 {
1580  if (glvis_multisample > -2)
1581  {
1582  glvis_multisample = m;
1583  }
1584  else
1585  {
1586  cout << "Multisampling is disabled." << endl;
1587  }
1588 }
1589 
1590 void SetLineWidth(float width)
1591 {
1592  line_w = width;
1593  if (wnd)
1594  {
1596  }
1597 }
1598 
1599 void SetLineWidthMS(float width_ms)
1600 {
1601  line_w_aa = width_ms;
1602  if (wnd)
1603  {
1605  }
1606 
1607 }
1608 
1610 {
1611  return line_w;
1612 }
1613 
1615 {
1616  return line_w_aa;
1617 }
1618 
1619 
1620 // Fontconfig patterns to use for finding a font file.
1621 // Use the command:
1622 // fc-list "pattern" file style
1623 // to find the font files that match the "pattern".
1624 vector<string> fc_font_patterns =
1625 {
1626  "Ubuntu Light:style=Regular",
1627  "Ubuntu:style=Regular:weight=80",
1628  "OpenSans:style=Regular",
1629  "DejaVu Sans:style=Book:width=Normal",
1630  "DejaVu LGC Sans:style=Book:width=Normal",
1631  "Bitstream Vera Sans:style=Roman",
1632  "FreeSans:style=Medium",
1633  "Ubuntu Mono:style=Regular",
1634  "DejaVu Sans Mono:style=Book",
1635  "DejaVu LGC Sans Mono:style=Book",
1636  "Helvetica:style=Regular",
1637  "Arial:style=Regular:weight=80"
1638 };
1639 
1640 constexpr int default_font_size = 12;
1642 
1643 thread_local GlVisFont glvis_font;
1644 std::string priority_font;
1645 
1646 void InitFont()
1647 {
1648  // This function is called after the window is created.
1649  GLenum alphaChannel =
1650  gl3::GLDevice::useLegacyTextureFmts() ? GL_ALPHA : GL_RED;
1651  glvis_font.setAlphaChannel(alphaChannel);
1652  bool try_fc_patterns = true;
1653  if (!priority_font.empty())
1654  {
1655  if (SetFont({priority_font}, font_size) ||
1656  glvis_font.LoadFont(priority_font, 0, font_size))
1657  {
1658  try_fc_patterns = false;
1659  }
1660  else
1661  {
1662  cerr << "InitFont(): Font not found: " << priority_font << endl;
1663  }
1664  }
1665  if (try_fc_patterns)
1666  {
1667  if (!SetFont(fc_font_patterns, font_size))
1668  {
1669  cerr <<
1670  "InitFont(): No fonts found matching the built-in patterns.\n"
1671  "Use the -fn option or edit 'fc_font_patterns' in lib/aux_vis.cpp"
1672  << endl;
1673  }
1674  }
1675  wnd->getRenderer().setFontTexture(glvis_font.getFontTex());
1676 }
1677 
1679 {
1680  return &glvis_font;
1681 }
1682 
1683 bool SetFont(const vector<std::string>& font_patterns, int height)
1684 {
1685 #ifdef __EMSCRIPTEN__
1686  return glvis_font.LoadFont("OpenSans.ttf", 0, height);
1687 #else
1688  if (!FcInit())
1689  {
1690  return false;
1691  }
1692 
1693  FcObjectSet * os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE,
1694  FC_SCALABLE, FC_INDEX, FC_WEIGHT,
1695  nullptr);
1696 
1697  for (const string& pattern : font_patterns)
1698  {
1699  string patternScale = pattern + ":scalable=True";
1700  FcPattern * pat = FcNameParse((FcChar8*)patternScale.c_str());
1701  if (!pat)
1702  {
1703  continue;
1704  }
1705 
1706  FcFontSet * fs = FcFontList(0, pat, os);
1707  if (!fs)
1708  {
1709  FcPatternDestroy(pat);
1710  continue;
1711  }
1712 #ifdef GLVIS_DEBUG
1713  if (fs->nfont >= 1)
1714  {
1715  cout << "Font pattern '" << pattern << "' matched fonts:\n";
1716  }
1717  else
1718  {
1719  cout << "Font pattern '" << pattern << "' matched no fonts.\n";
1720  }
1721 #endif
1722  std::string font_file = "";
1723  std::string font_name = "";
1724  int font_index = 0;
1725  for (int match_idx = 0; match_idx < fs->nfont; match_idx++)
1726  {
1727  FcChar8 * s;
1728  FcBool scalable;
1729  int curr_font_idx;
1730  FcPatternGetBool(fs->fonts[match_idx], FC_SCALABLE, 0, &scalable);
1731  FcPatternGetInteger(fs->fonts[match_idx], FC_INDEX, 0, &curr_font_idx);
1732  FcResult res = FcPatternGetString(fs->fonts[match_idx], FC_FILE, 0, &s);
1733  FcChar8 * fnt = FcNameUnparse(fs->fonts[match_idx]);
1734 #ifdef GLVIS_DEBUG
1735  cout << " - " << fnt << endl;
1736 #endif
1737  if (res == FcResultMatch && s && font_file == std::string(""))
1738  {
1739  font_file = (char*) s;
1740  font_name = (char*) fnt;
1741  font_index = curr_font_idx;
1742  }
1743  free(fnt);
1744  }
1745 
1746  FcFontSetDestroy(fs);
1747  if (font_file != std::string(""))
1748  {
1749  if (glvis_font.LoadFont(font_file, font_index, height))
1750  {
1751  break;
1752  }
1753  }
1754  }
1755 
1756  if (os)
1757  {
1758  FcObjectSetDestroy(os);
1759  }
1760 
1761  FcFini();
1762 
1763  return glvis_font.isFontLoaded();
1764 #endif
1765 }
1766 
1767 void SetFont(const std::string& fn)
1768 {
1769  priority_font = fn;
1770  size_t pos = priority_font.rfind('-');
1771  if (pos != string::npos)
1772  {
1773  font_size = std::stoi(priority_font.substr(pos + 1));
1774  priority_font.erase(pos);
1775  }
1776 #ifdef GLVIS_DEBUG
1777  cout << "SetFont: name = '"
1778  << (priority_font.empty() ? "(default)" : priority_font)
1779  << "', height = " << font_size << endl;
1780 #endif
1781 }
void signalQuit()
Definition: sdl.hpp:235
void SendExposeEvent()
Send expose event. In our case MyReshape is executed and Draw after it.
Definition: aux_vis.cpp:346
void MiddleButtonUp(EventInfo *)
Definition: aux_vis.cpp:746
thread_local int LastIdleFunc
Definition: aux_vis.cpp:414
void LookAt()
Definition: aux_vis.cpp:1504
void signalExpose()
Definition: sdl.hpp:233
void signalKeyDown(SDL_Keycode k, SDL_Keymod m=KMOD_NONE)
Definition: sdl.cpp:630
void signalSwap()
Definition: sdl.hpp:234
void identity()
Sets the matrix to the identity matrix.
Definition: types.hpp:164
int view
This is set by SetVisualizationScene.
Definition: openglvis.hpp:224
void translate(double x, double y, double z)
Applies a translation transform to the matrix.
Definition: types.hpp:135
void EnlargeWindow()
Definition: aux_vis.cpp:1528
thread_local double sph_u
Definition: aux_vis.cpp:509
void KeyMinusPressed()
Definition: aux_vis.cpp:1468
const double window_scale_factor
Definition: aux_vis.cpp:1514
void Key4Pressed()
Definition: aux_vis.cpp:1352
float line_w
Definition: aux_vis.cpp:47
void TouchPinch(SDL_MultiGestureEvent &e)
Definition: aux_vis.cpp:806
Uint32 bmask
Definition: aux_vis.cpp:825
float GetLineWidth()
Definition: aux_vis.cpp:1609
void SendKeySequence(const char *seq)
Send a sequence of keystrokes to the visualization window.
Definition: aux_vis.cpp:198
int Screenshot(const char *fname, bool convert)
Take a screenshot using libtiff, libpng or sdl2.
Definition: aux_vis.cpp:970
void getGLDrawSize(int &w, int &h)
Definition: sdl.cpp:563
void LeftButtonDown(EventInfo *event)
Definition: aux_vis.cpp:570
bool isExposePending()
Definition: sdl.hpp:259
thread_local SdlWindow * wnd
Definition: aux_vis.cpp:50
void mainLoop()
Runs the window loop.
Definition: sdl.cpp:483
void Key1Pressed()
Definition: aux_vis.cpp:1377
void setViewport(GLsizei w, GLsizei h)
Definition: renderer.hpp:261
float GetLineWidthMS()
Definition: aux_vis.cpp:1614
gl3::MeshRenderer & getRenderer()
Definition: sdl.hpp:226
void SetLineWidthMS(float width_ms)
Definition: aux_vis.cpp:1599
void setPalette(PaletteState *pal)
Definition: renderer.hpp:228
thread_local GLint oldx
Definition: aux_vis.cpp:510
void setAlphaTexture(GLuint tex_h)
Definition: renderer.hpp:233
void Key8Pressed()
Definition: aux_vis.cpp:1340
bool wndUseHiDPI
Definition: aux_vis.cpp:52
void Zoom(double factor)
Definition: openglvis.cpp:1125
thread_local GlVisFont glvis_font
Definition: aux_vis.cpp:1643
VisualizationScene * GetVisualizationScene()
Definition: aux_vis.cpp:63
bool SetFont(const vector< std::string > &font_patterns, int height)
Definition: aux_vis.cpp:1683
SDL_Keymod keymod
Definition: sdl.hpp:29
void TiltLeftRight(double angle)
Definition: openglvis.cpp:46
PaletteState palette
Definition: openglvis.hpp:178
void KeyJPressed()
Definition: aux_vis.cpp:1462
void Key3Pressed()
Definition: aux_vis.cpp:1389
void KeyDeletePressed()
Definition: aux_vis.cpp:1306
void render(const RenderQueue &queued)
Definition: renderer.cpp:109
glm::vec3 position
Definition: renderer.hpp:68
void setWindowTitle(std::string &title)
Definition: sdl.cpp:601
void MyExpose(GLsizei w, GLsizei h)
Definition: aux_vis.cpp:389
void ThreadsPauseFunc(GLenum state)
Definition: aux_vis.cpp:1247
void SetUseTexture(int ut)
Definition: aux_vis.cpp:1561
void TurnUpDown(double angle)
Definition: openglvis.cpp:59
void SetVisualizationScene(VisualizationScene *scene, int view, const char *keys)
Definition: aux_vis.cpp:307
void MyReshape(GLsizei w, GLsizei h)
Definition: aux_vis.cpp:352
void setWindowPos(int x, int y)
Definition: sdl.cpp:618
void setLineWidth(float w)
Definition: renderer.cpp:73
void RightButtonLoc(EventInfo *event)
Definition: aux_vis.cpp:755
void AddIdleFunc(void(*Func)(void))
Definition: aux_vis.cpp:491
thread_local GLint startx
Definition: aux_vis.cpp:510
vector< FeedbackText > text
Definition: renderer.hpp:93
void Key6Pressed()
Definition: aux_vis.cpp:1371
bool MainIdleFunc()
Definition: aux_vis.cpp:445
GL2PSvertex CreatePrintVtx(gl3::FeedbackVertex v)
Definition: aux_vis.cpp:1147
void SDLMainLoop(bool server_mode)
Definition: aux_vis.cpp:53
int GetMultisample()
Definition: aux_vis.cpp:1573
void InitFont()
Definition: aux_vis.cpp:1646
void ZoomOut()
Definition: aux_vis.cpp:1486
void KeyEnterPressed()
Definition: aux_vis.cpp:1324
void PreRotate(double angle, double x, double y, double z)
Definition: openglvis.cpp:1060
void SetProjectionMtx(glm::mat4 projection)
Definition: openglvis.hpp:213
CaptureBuffer capture(const RenderQueue &queued)
Definition: renderer.cpp:297
void MoveUpDown(double dist)
Definition: openglvis.hpp:48
thread_local GLint starty
Definition: aux_vis.cpp:510
void screenshot(std::string filename, bool convert=false)
Queues a screenshot to be taken.
Definition: sdl.hpp:241
constexpr int default_font_size
Definition: aux_vis.cpp:1640
void MiddleButtonDown(EventInfo *event)
Definition: aux_vis.cpp:672
void RightButtonUp(EventInfo *)
Definition: aux_vis.cpp:803
void ComputeSphereAngles(int &newx, int &newy, double &new_sph_u, double &new_sph_t)
Definition: aux_vis.cpp:547
void getWindowSize(int &w, int &h)
Definition: sdl.cpp:534
thread_local gl3::GlMatrix srot
Definition: aux_vis.cpp:508
bool CommunicationIdleFunc()
Definition: aux_vis.cpp:429
static bool useLegacyTextureFmts()
Definition: renderer.cpp:27
GLuint GetColorTexture() const
Definition: palettes.hpp:54
void ToggleThreads()
Definition: aux_vis.cpp:1237
GLuint GetAlphaTexture() const
Definition: palettes.hpp:56
Uint32 rmask
Definition: aux_vis.cpp:823
void KeyQPressed()
Definition: aux_vis.cpp:1231
void KeyS()
Definition: aux_vis.cpp:1119
void setOnMouseDown(int btn, MouseDelegate func)
Definition: sdl.hpp:194
void CrossProd(const double a[], const double b[], double cp[])
Definition: geom_utils.hpp:31
void SetLight0CustomPos(std::array< float, 4 > pos)
Definition: openglvis.cpp:1029
void LeftButtonLoc(EventInfo *event)
Definition: aux_vis.cpp:588
thread_local double yang
Definition: aux_vis.cpp:507
thread_local Array< void(*)()> IdleFuncs
Definition: aux_vis.cpp:413
void setFontTexture(GLuint tex_h)
Definition: renderer.hpp:235
thread_local double xang
Definition: aux_vis.cpp:507
void ThreadsStop()
Definition: aux_vis.cpp:1259
GlVisFont * GetFont()
Definition: aux_vis.cpp:1678
glm::mat4 mtx
Definition: types.hpp:121
void KeyUpPressed(GLenum state)
Definition: aux_vis.cpp:1436
void InitIdleFuncs()
Definition: aux_vis.cpp:419
void RemoveIdleFunc(void(*Func)(void))
Definition: aux_vis.cpp:497
void ShiftView(double dx, double dy)
Definition: aux_vis.cpp:1395
void callKeyDown(SDL_Keycode k, Uint16 mod=0)
Definition: sdl.hpp:212
static void StartSDL(bool server_mode)
Definition: sdl.cpp:83
void setOnMouseMove(int btn, MouseDelegate func)
Definition: sdl.hpp:196
void Key0Pressed()
Definition: aux_vis.cpp:1296
void ortho(double left, double right, double bottom, double top, double z_near, double z_far)
Sets the matrix to an orthographic projection.
Definition: types.hpp:147
thread_local int visualize
Definition: aux_vis.cpp:37
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
double InnerProd(const double a[], const double b[])
Definition: geom_utils.hpp:26
void Key2Pressed()
Definition: aux_vis.cpp:1383
bool wndLegacyGl
Definition: aux_vis.cpp:51
void ShrinkWindow()
Definition: aux_vis.cpp:1516
void LeftButtonUp(EventInfo *event)
Definition: aux_vis.cpp:646
void scale(double x, double y, double z)
Applies a scale transform to the matrix.
Definition: types.hpp:141
virtual gl3::SceneInfo GetSceneObjs()=0
void rotate(float angle, double x, double y, double z)
Applies a rotation transform to the matrix.
Definition: types.hpp:124
void CheckSpin()
Definition: aux_vis.cpp:1275
void buffer(GlDrawable *buf)
Definition: renderer.cpp:378
void Scale(double s)
Definition: openglvis.cpp:1087
void clearEvents()
Definition: sdl.hpp:201
void setTouchPinchCallback(TouchDelegate cb)
Definition: sdl.hpp:198
bool isHighDpi() const
This property is set by createWindow().
Definition: sdl.hpp:224
void SetLineWidth(float width)
Definition: aux_vis.cpp:1590
void KeyRightPressed(GLenum state)
Definition: aux_vis.cpp:1423
void ResizeWindow(int w, int h)
Definition: aux_vis.cpp:1546
const double xang_step
Definition: aux_vis.cpp:1294
void Key5Pressed()
Definition: aux_vis.cpp:1358
int Execute()
Definition: threads.cpp:396
void perspective(double fov, double aspect, double z_near, double z_far)
Sets the matrix to a perspective projection.
Definition: types.hpp:158
SdlWindow * GetAppWindow()
Definition: aux_vis.cpp:58
void MoveLeftRight(double dist)
Definition: openglvis.hpp:47
void MoveResizeWindow(int x, int y, int w, int h)
Definition: aux_vis.cpp:1540
int GetSmoothSetting()
Gets whether the smooth texture is being used (1 = true)
Definition: palettes.hpp:29
void SetLegacyGLOnly(bool status)
Definition: aux_vis.cpp:68
Uint32 gmask
Definition: aux_vis.cpp:824
void UseSmooth()
Binds the smooth version of the current palette texture.
Definition: palettes.hpp:27
void MainLoop()
Definition: aux_vis.cpp:515
void KeyLeftPressed(GLenum state)
Definition: aux_vis.cpp:1410
glm::mat4 TransposeRotMatrix()
Definition: openglvis.cpp:85
void setOnExpose(Delegate func)
Definition: sdl.hpp:185
std::string priority_font
Definition: aux_vis.cpp:1644
void ToggleAutopause()
Definition: threads.cpp:688
thread_local GLVisCommand * glvis_command
Definition: aux_vis.cpp:39
void mult(glm::mat4 rhs)
Definition: types.hpp:129
bool createWindow(const char *title, int x, int y, int w, int h, bool legacyGlOnly)
Definition: sdl.cpp:90
thread_local int constrained_spinning
Definition: aux_vis.cpp:512
void SetWindowTitle(const char *title)
Definition: aux_vis.cpp:1551
double sqr(double t)
Definition: aux_vis.cpp:542
thread_local GLint oldy
Definition: aux_vis.cpp:510
void KeyDownPressed(GLenum state)
Definition: aux_vis.cpp:1449
thread_local double sph_t
Definition: aux_vis.cpp:509
vector< string > fc_font_patterns
Definition: aux_vis.cpp:1624
vector< FeedbackVertex > lines
Definition: renderer.hpp:91
void MoveForwardBackward(double dist)
Definition: openglvis.hpp:46
void KeyCtrlP()
Definition: aux_vis.cpp:1190
void setLineWidthMS(float w)
Definition: renderer.cpp:82
void TurnLeftRight(double angle)
Definition: openglvis.cpp:52
void setColorTexture(GLuint tex_h)
Definition: renderer.hpp:231
void setOnIdle(IdleDelegate func)
Definition: sdl.hpp:184
int InitVisualization(const char name[], int x, int y, int w, int h)
Initializes the visualization and some keys.
Definition: aux_vis.cpp:81
void RunVisualization()
Start the infinite visualization loop.
Definition: aux_vis.cpp:334
vector< GlDrawable * > needs_buffering
Definition: renderer.hpp:62
vector< FeedbackVertex > triangles
Definition: renderer.hpp:92
float line_w_aa
Definition: aux_vis.cpp:48
void SetMultisample(int m)
Definition: aux_vis.cpp:1578
void setOnKeyDown(int key, Delegate func)
Definition: sdl.hpp:188
RenderQueue queue
Definition: renderer.hpp:63
void MiddleButtonLoc(EventInfo *event)
Definition: aux_vis.cpp:678
bool isSwapPending()
Definition: sdl.hpp:258
void KeyPlusPressed()
Definition: aux_vis.cpp:1474
const char * glvis_screenshot_ext
Definition: aux_vis.cpp:814
void Key7Pressed()
Definition: aux_vis.cpp:1334
const float LINE_WIDTH_AA
Definition: renderer.hpp:32
void ThreadsRun()
Definition: aux_vis.cpp:1267
thread_local bool use_idle
Definition: aux_vis.cpp:415
int InvertSurfaceVertical(SDL_Surface *surface)
Definition: aux_vis.cpp:838
void ScaleUp()
Definition: aux_vis.cpp:1492
void PrintCaptureBuffer(gl3::CaptureBuffer &cbuf)
Definition: aux_vis.cpp:1156
thread_local VisualizationScene * locscene
Definition: aux_vis.cpp:38
glm::mat4 RotMatrix()
Definition: openglvis.cpp:69
void CallKeySequence(const char *seq)
Definition: aux_vis.cpp:253
thread_local bool disableSendExposeEvent
Definition: aux_vis.cpp:251
void UseDiscrete()
Binds the discrete version of the current palette texture.
Definition: palettes.hpp:25
void ZoomIn()
Definition: aux_vis.cpp:1480
void ScaleDown()
Definition: aux_vis.cpp:1498
Uint32 amask
Definition: aux_vis.cpp:831
int GetUseTexture()
Definition: aux_vis.cpp:1556
void SetUseHiDPI(bool status)
Definition: aux_vis.cpp:73
void Rotate(double angle, double x, double y, double z)
Definition: openglvis.cpp:1049
void setOnMouseUp(int btn, MouseDelegate func)
Definition: sdl.hpp:195
int font_size
Definition: aux_vis.cpp:1641
void RightButtonDown(EventInfo *event)
Definition: aux_vis.cpp:749
void Key9Pressed()
Definition: aux_vis.cpp:1346
void setWindowSize(int w, int h)
Definition: sdl.cpp:611