Hermes2D  3.0
view.cpp
1 // This file is part of Hermes2D.
2 //
3 // Hermes2D is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 2 of the License, or
6 // (at your option) any later version.
7 //
8 // Hermes2D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Hermes2D. If not, see <http://www.gnu.org/licenses/>.
15 
16 #ifndef NOGLUT
17 
18 #include <sstream>
19 #include <GL/glew.h>
20 #include <GL/freeglut.h>
21 #ifndef _WINDOWS
22 # include <sys/time.h>
23 #endif
24 
25 #include "global.h"
26 #include "view_support.h"
27 #include "view.h"
28 #include "solution.h"
29 #include "view_data.cpp"
30 
31 namespace Hermes
32 {
33  namespace Hermes2D
34  {
35  namespace Views
36  {
37 #define HERMES_WAIT_CLOSE_MSG "close all views to continue"
38 #define HERMES_WAIT_KEYPRESS_MSG "press spacebar to continue"
39 
40  int View::screenshot_no = 1;
41 
42  void View::init()
43  {
44  jitter_x = jitter_y = 0.0;
45  dragging = scaling = false;
46  hq_frame = false;
47  frame_ready = false;
48  range_auto = true;
49  range_min = 0;
50  range_max = 1;
51  pal_type = H2DV_PT_HUESCALE;
52  pal_steps = 50;
53  pal_filter = GL_NEAREST;
54  margin = 15;
55  b_scale = true;
56  b_help = false;
57  scale_focused = scale_dragging = false;
58  pos_horz = pos_vert = 0;
59  scale_x = scale_y = labels_width = 0;
60  scale_width = 16;
61  scale_height = 320;
62  scale_numticks = 9;
63  strcpy(scale_fmt, "%.3g");
64  scale_fixed_width = -1;
65  want_screenshot = false;
66 
68  memset(rendering_frames, 0, FPS_FRAME_SIZE * sizeof(double));
69  }
70 
71  View::View(const char* title, WinGeom* wg) :
72  view_not_reset(true),
73  vertices_min_x(0),
74  vertices_max_x(0),
75  vertices_min_y(0),
76  vertices_max_y(0),
77  title(title),
78  output_id(-1),
79  gl_pallete_tex_id(0)
80  {
81  if (wg == nullptr)
82  {
83  output_x = H2D_DEFAULT_X_POS;
84  output_y = H2D_DEFAULT_Y_POS;
85  output_width = H2D_DEFAULT_WIDTH;
86  output_height = H2D_DEFAULT_HEIGHT;
87  }
88  else
89  {
90  output_x = wg->x;
91  output_y = wg->y;
92  output_width = wg->width;
93  output_height = wg->height;
94  }
95 
96  init();
97  }
98 
99  View::View(char* title, WinGeom* wg) :
100  view_not_reset(true),
101  vertices_min_x(0),
102  vertices_max_x(0),
103  vertices_min_y(0),
104  vertices_max_y(0),
105  title(title),
106  output_id(-1),
107  gl_pallete_tex_id(0)
108  {
109  if (wg == nullptr)
110  {
111  output_x = H2D_DEFAULT_X_POS;
112  output_y = H2D_DEFAULT_Y_POS;
113  output_width = H2D_DEFAULT_WIDTH;
114  output_height = H2D_DEFAULT_HEIGHT;
115  }
116  else
117  {
118  output_x = wg->x;
119  output_y = wg->y;
120  output_width = wg->width;
121  output_height = wg->height;
122  }
123 
124  init();
125  }
126 
127  View::~View()
128  {
129  if (output_id >= 0)
130  close();
131  }
132 
133  int View::create()
134  {
135  if (output_id < 0) //does not need thread protection because it is set up by a callback during call of add_view
136  return add_view(this, output_x, output_y, output_width, output_height, title.c_str());
137  else
138  return output_id;
139  }
140 
141  void View::close()
142  {
143  if (output_id >= 0) //does not need thread protection because it is set up by a callback during call of add_view
144  remove_view(output_id);
145  }
146 
147  void View::wait(const char* text)
148  {
149  wait(HERMES_WAIT_CLOSE, text);
150  }
151 
152  void View::wait(ViewWaitEvent wait_event, const char* text)
153  {
154  //prepare message
155  std::stringstream str;
156  if (text != nullptr)
157  str << text;
158  else
159  {
160  str << " << ";
161  switch (wait_event)
162  {
163  case HERMES_WAIT_CLOSE: str << HERMES_WAIT_CLOSE_MSG; break;
164  case HERMES_WAIT_KEYPRESS: str << HERMES_WAIT_KEYPRESS_MSG; break;
165  default: throw Hermes::Exceptions::Exception("Unknown wait event"); break;
166  }
167  str << " >>" << std::endl;
168  }
169 
170  //do something
171  switch (wait_event)
172  {
173  case HERMES_WAIT_CLOSE: wait_for_all_views_close(str.str().c_str()); break;
174  case HERMES_WAIT_KEYPRESS: wait_for_any_key(str.str().c_str()); break;
175  default: throw Hermes::Exceptions::Exception("Unknown wait event"); break;
176  }
177  }
178 
180  {
181  bool do_refresh = true;
182  view_sync.enter();
183  if (output_id < 0)
184  do_refresh = false;
185  view_sync.leave();
186  if (do_refresh)
187  refresh_view(output_id);
188  }
189 
190  void View::reset_view(bool force_reset)
191  {
192  if (force_reset || view_not_reset)
193  {
194  double mesh_width = vertices_max_x - vertices_min_x;
195  double mesh_height = vertices_max_y - vertices_min_y;
196 
197  double usable_width = output_width - 2 * margin - lspace - rspace;
198  double usable_height = output_height - 2 * margin;
199 
200  // align in the proper direction
201  if (usable_width / usable_height < mesh_width / mesh_height)
202  scale = usable_width / mesh_width;
203  else
204  scale = usable_height / mesh_height;
205  log_scale = log(scale) / log(H2DV_SCALE_LOG_BASE);
206 
207  // center of the mesh
208  trans_x = -scale * (vertices_min_x + vertices_max_x) / 2;
209  trans_y = -scale * (vertices_min_y + vertices_max_y) / 2;
210 
211  view_not_reset = false;
212  }
213  }
214 
215  void View::on_create(int output_id)
216  {
217  //does not need thread protection because it is during execution of add_view
218  this->output_id = output_id;
220  set_palette_filter(pal_filter == GL_LINEAR);
221  }
222 
223  void View::on_close()
224  {
225  view_sync.enter();
226  output_id = -1;
227  view_sync.leave();
228  }
229 
231  {
232  glClearColor(1.0, 1.0, 1.0, 0.0);
233  glClear(GL_COLOR_BUFFER_BIT);
234  }
235 
236  void View::pre_display()
237  {
238  //this->info("display: lock");
239  view_sync.enter();
240 
241  //begin time measuring
242  double time_start = get_tick_count();
243 
244  //antialising is supported through accumulation buffer (FIXME: use ARB_MULTISAMPLE if available)
245  if (!hq_frame)
246  {
248  this->on_display();
249  }
250  else
251  {
252  display_antialiased();
253  hq_frame = false;
254  }
255 
256  if (b_help) draw_help();
257  else if (b_scale) scale_dispatch();
258 
259  //wait to finish
260  glFinish();
261 
262  //calculate statistics
264  rendering_frames_top = (rendering_frames_top + 1) % FPS_FRAME_SIZE;
265 
266  if (want_screenshot)
267  {
268  glReadBuffer(GL_BACK_LEFT);
269  save_screenshot_internal(screenshot_filename.c_str());
270  want_screenshot = false;
271  }
272 
273  glutSwapBuffers();
274 
275  //frame synchronization
276  frame_ready = true;
278  view_sync.leave();
279  }
280 
281  static float jitter16[16][2] =
282  {
283  { 0.4375, 0.4375 }, { 0.1875, 0.5625 },
284  { 0.9375, 1.1875 }, { 0.4375, 0.9375 - 1 },
285  { 0.6875, 0.5625 }, { 0.1875, 0.0625 },
286  { 0.6875, 0.3125 }, { 0.1875, 0.3125 },
287  { 0.4375, 0.1875 }, { 0.9375 - 1, 0.4375 },
288  { 0.6875, 0.8125 }, { 0.4375, 0.6875 },
289  { 0.6875, 0.0625 }, { 0.9375, 0.9375 },
290  { 1.1875, 0.8125 }, { 0.9375, 0.6875 }
291  };
292 
293  void View::display_antialiased()
294  {
295  glClear(GL_ACCUM_BUFFER_BIT);
296  for (int i = 0; i < 16; i++)
297  {
298  jitter_x = jitter16[i][0];
299  jitter_y = jitter16[i][1];
300  set_ortho_projection();
302  on_display();
303  glAccum(GL_ACCUM, 1.0 / 16);
304  }
305  glAccum(GL_RETURN, 1.0);
306  jitter_x = jitter_y = 0.0;
307  }
308 
309  void View::set_ortho_projection(bool no_jitter)
310  {
311  double jx = no_jitter ? 0.0 : jitter_x;
312  double jy = no_jitter ? 0.0 : jitter_y;
313 
314  glMatrixMode(GL_PROJECTION);
315  glLoadIdentity();
316  glOrtho(jx, output_width + jx, output_height - 1 + jy, -1 + jy, -10, 10);
317 
318  glMatrixMode(GL_MODELVIEW);
319  glLoadIdentity();
320  }
321 
322  void View::set_3d_projection(int fov, double znear, double zfar)
323  {
324  double top = znear * tan((double)fov / 2.0 / 180.0 * M_PI);
325  double right = (double)output_width / output_height * top;
326  double left = -right;
327  double bottom = -top;
328  double offsx = (right - left) / output_width * jitter_x;
329  double offsy = (top - bottom) / output_height * jitter_y;
330 
331  glMatrixMode(GL_PROJECTION);
332  glLoadIdentity();
333  glFrustum(left - offsx, right - offsx, bottom - offsy, top - offsy, znear, zfar);
334  }
335 
337  {
338  //calculate FPS
339  double frame_time_sum = 0;
340  for (int i = 0; i < FPS_FRAME_SIZE; i++)
341  frame_time_sum += rendering_frames[i];
342 
343  //prepare text
344  unsigned char buffer[128];
345  sprintf((char*)buffer, "avg. frame: %.1f ms", (float)(frame_time_sum / FPS_FRAME_SIZE));
346 
347  //prepare environment
348  void* font = GLUT_BITMAP_HELVETICA_10;
349  int width_px = glutBitmapLength(font, buffer);
350  int height_px = glutBitmapHeight(font);
351  int edge_thickness = 2;
352  set_ortho_projection(false);
353 
354  //render background
355  glEnable(GL_BLEND);
356  glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
357  glBegin(GL_QUADS);
358  glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
359  glVertex2i(output_width - (width_px + 2 * edge_thickness), 0);
360  glVertex2i(output_width, 0);
361  glVertex2i(output_width, height_px + 2 * edge_thickness);
362  glVertex2i(output_width - (width_px + 2 * edge_thickness), height_px + 2 * edge_thickness);
363  glEnd();
364 
365  // render text
366  glDisable(GL_BLEND);
367  glColor3f(1.0f, 0.0f, 0.0f);
368  glRasterPos2i(output_width - (width_px + edge_thickness), edge_thickness + height_px);
369  // iming information is printed into the image.
370  glutBitmapString(font, buffer);
371  }
372 
373  void View::on_reshape(int width, int height)
374  {
375  glViewport(0, 0, width, height);
376 
377  output_width = width;
378  output_height = height;
379  update_layout();
380  }
381 
382  void View::on_mouse_move(int x, int y)
383  {
384  if (dragging)
385  {
386  trans_x += (x - mouse_x);
387  trans_y += (mouse_y - y);
388  refresh();
389  }
390  else if (scaling)
391  {
392  log_scale += (mouse_y - y);
393  scale = pow(H2DV_SCALE_LOG_BASE, log_scale);
394  trans_x = scx - objx * scale - center_x;
395  trans_y = center_y - objy * scale - scy;
396  refresh();
397  }
398  else if (scale_dragging)
399  {
400  int oldv = pos_vert, oldh = pos_horz;
401  pos_horz = (x > output_width / 2);
402  pos_vert = (y < output_height / 2);
403  if (pos_horz != oldh || pos_vert != oldv)
404  {
405  update_layout();
406  refresh();
407  }
408  }
409  else
410  {
411  bool oldf = scale_focused;
412  scale_focused = (x >= scale_x && x <= scale_x + scale_width &&
413  y >= scale_y && y <= scale_y + scale_height);
414  if (oldf != scale_focused)
415  refresh();
416  }
417  mouse_x = x;
418  mouse_y = y;
419  }
420 
421  void View::on_left_mouse_down(int x, int y)
422  {
423  if (scale_focused)
424  scale_dragging = true;
425  else
426  dragging = true;
427  scaling = false;
428  mouse_x = x;
429  mouse_y = y;
430  }
431 
432  void View::on_left_mouse_up(int x, int y)
433  {
434  scaling = dragging = scale_dragging = false;
435  on_mouse_move(x, y);
436  }
437 
438  void View::on_right_mouse_down(int x, int y)
439  {
440  scaling = true;
441  dragging = false;
442  scx = x;
443  scy = y;
444  objx = (x - center_x - trans_x) / scale;
445  objy = (center_y - y - trans_y) / scale;
446  mouse_x = x;
447  mouse_y = y;
448  }
449 
450  void View::on_right_mouse_up(int x, int y)
451  {
452  scaling = dragging = false;
453  }
454 
455  void View::on_key_down(unsigned char key, int x, int y)
456  {
457  const char *file_name = nullptr;
458 
459  switch (key)
460  {
461  case 'h':
462  {
463  hq_frame = true;
464  refresh();
465  break;
466  }
467 
468  case 27:
469  case 'q':
470  {
471  close();
472  break;
473  }
474 
475  case 's':
476  {
477  const char *file_name = get_screenshot_file_name();
478  glReadBuffer(GL_FRONT_LEFT);
479  save_screenshot_internal(file_name);
480  break;
481  }
482 
483  case 'p':
484  {
485  switch (pal_type)
486  {
487  case H2DV_PT_HUESCALE: pal_type = H2DV_PT_GRAYSCALE; break;
488  case H2DV_PT_GRAYSCALE: pal_type = H2DV_PT_INVGRAYSCALE; break;
489  case H2DV_PT_INVGRAYSCALE: pal_type = H2DV_PT_HUESCALE; break;
490  default: throw Hermes::Exceptions::Exception("Invalid palette type");
491  }
493  refresh();
494  break;
495  }
496 
497  default:
498  view_sync.enter();
500  view_sync.leave();
501  break;
502  }
503  }
504 
505  void View::on_special_key(int key, int x, int y)
506  {
507  switch (key)
508  {
509  case GLUT_KEY_F1:
510  b_help = !b_help;
511  refresh();
512  break;
513  }
514  }
515 
516  void View::wait_for_keypress(const char* text)
517  {
519  }
520 
521  void View::wait_for_close()
522  {
523  view_sync.enter();
524  if (output_id >= 0)
526  view_sync.leave();
527  }
528 
529  void View::wait_for_draw()
530  {
531  view_sync.enter();
532  if (output_id >= 0 && !frame_ready)
534  view_sync.leave();
535  }
536 
538  {
539 #ifdef _WINDOWS
540  LARGE_INTEGER freq, ticks;
541  QueryPerformanceFrequency(&freq);
542  QueryPerformanceCounter(&ticks);
543  return (1000.0 * (double)ticks.QuadPart) / (double)freq.QuadPart;
544 #else
545  struct timeval tv;
546  gettimeofday(&tv, nullptr);
547  return (double)tv.tv_sec * 1000 + (double)tv.tv_usec / 1000;
548 #endif
549  }
550 
551  const char* View::get_title() const
552  {
553  return this->title.c_str();
554  }
555 
556  void View::set_title(const char* msg, ...)
557  {
558  char* text_contents = malloc_with_check<char>(BUF_SZ);
559 
560  //print the message
561  va_list arglist;
562  va_start(arglist, msg);
563  vsprintf(text_contents, msg, arglist);
564  va_end(arglist);
565 
566  bool do_set_title = true;
567 
568  // Always set the title property.
569  this->title = text_contents;
570 
571  view_sync.enter();
572  if (output_id < 0)
573  // If the window does not exist, do nothing else and wait until it is created.
574  do_set_title = false;
575  view_sync.leave();
576 
577  // If the window already exists, show the new_ title in its header.
578  if (do_set_title)
579  set_view_title(output_id, text_contents);
580 
581  free_with_check(text_contents);
582  }
583 
584  void View::get_palette_color(double x, float* gl_color)
585  {
586  if (pal_type == H2DV_PT_HUESCALE)
587  { //default color
588  if (x < 0.0) x = 0.0;
589  else if (x > 1.0) x = 1.0;
590  x *= num_pal_entries;
591  int n = (int)x;
592  gl_color[0] = palette_data[n][0];
593  gl_color[1] = palette_data[n][1];
594  gl_color[2] = palette_data[n][2];
595  }
596  else if (pal_type == H2DV_PT_GRAYSCALE)
597  gl_color[0] = gl_color[1] = gl_color[2] = (float)x;
598  else if (pal_type == H2DV_PT_INVGRAYSCALE)
599  gl_color[0] = gl_color[1] = gl_color[2] = (float)(1.0 - x);
600  else
601  gl_color[0] = gl_color[1] = gl_color[2] = 1.0f;
602  }
603 
604  void View::set_num_palette_steps(int num)
605  {
606  if (num < 2) num = 2;
607  if (num > 256) num = 256;
608  pal_steps = num;
609  update_tex_adjust();
610 
611  view_sync.enter();
612  if (output_id >= 0)
614  view_sync.leave();
615 
616  refresh();
617  }
618 
620  {
621  int i;
622  unsigned char palette[256][3];
623  for (i = 0; i < pal_steps; i++)
624  {
625  float gl_color[3];
626  get_palette_color((double)i / pal_steps, gl_color);
627  palette[i][0] = (unsigned char)(gl_color[0] * 255);
628  palette[i][1] = (unsigned char)(gl_color[1] * 255);
629  palette[i][2] = (unsigned char)(gl_color[2] * 255);
630  }
631  for (i = pal_steps; i < 256; i++)
632  memcpy(palette[i], palette[pal_steps - 1], 3);
633 
634  if (gl_pallete_tex_id == 0)
635  glGenTextures(1, &gl_pallete_tex_id);
636  glBindTexture(GL_TEXTURE_1D, gl_pallete_tex_id);
637  glTexImage1D(GL_TEXTURE_1D, 0, 3, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, palette);
638  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
639  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
640  }
641 
642  void View::set_palette_filter(bool linear)
643  {
644  //lock to prevent simultaneuous rendering
645  view_sync.enter();
646 
647  pal_filter = linear ? GL_LINEAR : GL_NEAREST;
648 
649  if (gl_pallete_tex_id == 0)
650  glGenTextures(1, &gl_pallete_tex_id);
651  glBindTexture(GL_TEXTURE_1D, gl_pallete_tex_id);
652  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, pal_filter);
653  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, pal_filter);
654  update_tex_adjust();
655 
656  //unlock
657  view_sync.leave();
658 
659  refresh();
660  }
661 
662  void View::set_palette(ViewPaletteType type)
663  {
664  view_sync.enter();
665  pal_type = type;
666  if (output_id >= 0)
668  view_sync.leave();
669 
670  //redisplay
671  refresh();
672  }
673 
674  void View::update_tex_adjust()
675  {
676  if (pal_filter == GL_LINEAR)
677  {
678  tex_scale = (double)(pal_steps - 1) / 256.0;
679  tex_shift = 0.5 / 256.0;
680  }
681  else
682  {
683  tex_scale = (double)pal_steps / 256.0;
684  tex_shift = 0.0;
685  }
686  }
687 
688  void View::set_min_max_range(double min, double max)
689  {
690  if (max < min)
691  {
692  std::swap(min, max);
693  this->warn("Upper bound set below the lower bound: reversing to (%f, %f).", min, max);
694  }
695  view_sync.enter();
696  range_min = min;
697  range_max = max;
698  range_auto = false;
699  if (output_id >= 0)
700  update_layout();
701  view_sync.leave();
702  refresh();
703  }
704 
705  void View::auto_min_max_range()
706  {
707  view_sync.enter();
708  range_auto = true;
709  if (output_id >= 0)
710  update_layout();
711  view_sync.leave();
712 
713  refresh();
714  }
715 
716  void View::get_min_max_range(double& min, double& max)
717  {
718  view_sync.enter();
719  min = range_min;
720  max = range_max;
721  view_sync.leave();
722  }
723 
724  void View::draw_text(double x, double y, const char* text, int align)
725  {
726  void* font = GLUT_BITMAP_9_BY_15;
727  if (align > -1)
728  {
729  int width = glutBitmapLength(font, (const unsigned char*)text);
730  // align right
731  if (align == 1) x -= width;
732  // center
733  else x -= (double)width / 2;
734  }
735  //(double) glutBitmapHeight(font) / 2 - 1;
736  y += 5;
737 
738  glDisable(GL_TEXTURE_1D);
739  glDisable(GL_LIGHTING);
740 
741  glRasterPos2d((int)(x + 0.5), (int)(y + 0.5));
742  glutBitmapString(font, (const unsigned char*)text);
743  }
744 
745  int View::get_text_width(const char* text)
746  {
747  void* font = GLUT_BITMAP_9_BY_15;
748  return glutBitmapLength(font, (const unsigned char*)text);
749  }
750 
751  void View::draw_help()
752  {
753  view_sync.enter();
754  set_ortho_projection(true);
755  glDisable(GL_DEPTH_TEST);
756  glDisable(GL_LIGHTING);
757  glDisable(GL_TEXTURE_1D);
758  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
759 
760  const char* text = get_help_text();
761 
762  int n = 1;
763  for (const char* p = text; *p; p++)
764  if (*p == '\n') n++;
765 
766  int width = get_text_width(text);
767  int height = n * glutBitmapHeight(GLUT_BITMAP_9_BY_15);
768  int x = 10, y = 10, b = 6;
769 
770  glEnable(GL_BLEND);
771  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
772  glColor4f(1.0f, 1.0f, 1.0f, 0.65f);
773  glBegin(GL_QUADS);
774  glVertex2d(x, y + height + 2 * b);
775  glVertex2d(x + width + 2 * b, y + height + 2 * b);
776  glVertex2d(x + width + 2 * b, y);
777  glVertex2d(x, y);
778  glEnd();
779 
780  glDisable(GL_BLEND);
781  glColor3f(0, 0, 0);
782  draw_text(x + b, y + b + 7, text);
783  view_sync.leave();
784  refresh();
785  }
786 
787  char* View::get_screenshot_file_name()
788  {
789  static char file_name[1024] = { 0 };
790  bool got_file_name = false;
791  do
792  {
793  sprintf(file_name, "screen%03d.bmp", screenshot_no);
794  FILE *f = fopen(file_name, "r");
795  if (f == nullptr)
796  got_file_name = true;
797  else
798  fclose(f);
799  screenshot_no++;
800  } while (!got_file_name);
801  return file_name;
802  }
803 
804  typedef unsigned int dword;
805  typedef unsigned short word;
806 
807  const word BITMAP_ID = 0x4D42;
808 
809 #pragma pack(1)
810 
812  {
813  word type;
814  dword size;
815  word reserved1;
816  word reserved2;
817  dword off_bits;
818  };
819 
821  {
822  dword size;
823  dword width;
824  dword height;
825  word planes;
826  word bit_count;
827  dword compression;
828  dword size_image;
829  dword xdpi;
830  dword ydpi;
831  dword clr_used;
832  dword clr_important;
833  };
834 
835  void View::save_screenshot_internal(const char *file_name)
836  {
837  BitmapFileHeader file_header;
838  BitmapInfoHeader info_header;
839 
840  // alloc memory for pixel data (4 bytes per pixel)
841  char* pixels = malloc_with_check_direct_size<char>(4 * output_width * output_height);
842 
843  // get pixels from framebuffer
844 #ifdef GL_BGRA_EXT
845  glReadPixels(0, 0, output_width, output_height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
846 #else
847  // FIXME!!!
848  glReadPixels(0, 0, output_width, output_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
849  this->warn("BGRA format not supported. Saved image will have inverted colors");
850 #endif
851  // opening file for binary writing
852  FILE* file = fopen(file_name, "wb");
853  if (file == nullptr)
854  throw Hermes::Exceptions::Exception("Could not open '%s' for writing", file_name);
855 
856  // fill in bitmap header
857  file_header.type = BITMAP_ID;
858  file_header.size = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) +
859  4 * output_width * output_height;
860  file_header.reserved1 = file_header.reserved2 = 0;
861  // length of both headers
862  file_header.off_bits = 14 + 40;
863 
864  if (fwrite(&file_header, sizeof(file_header), 1, file) != 1)
865  {
866  fclose(file);
867  throw Hermes::Exceptions::Exception("Error writing bitmap header");
868  }
869 
870  // fill in bitmap info header
871  info_header.size = sizeof(BitmapInfoHeader);
872  info_header.width = output_width;
873  info_header.height = output_height;
874  info_header.planes = 1;
875  // 4 bytes per pixel = 32 bits
876  info_header.bit_count = 32;
877  info_header.compression = 0;
878  info_header.size_image = output_width * output_height * 4;
879  // 72 dpi
880  info_header.xdpi = 2835;
881  // 72 dpi
882  info_header.ydpi = 2835;
883  info_header.clr_used = 0;
884  info_header.clr_important = 0;
885 
886  if (fwrite(&info_header, sizeof(info_header), 1, file) != 1)
887  {
888  fclose(file);
889  throw Hermes::Exceptions::Exception("Error writing bitmap header");
890  }
891 
892  // write image pixels
893  if (fwrite((GLubyte*)pixels, 1, info_header.size_image, file) != info_header.size_image)
894  {
895  fclose(file);
896  throw Hermes::Exceptions::Exception("Error writing pixel data");
897  }
898 
899  fclose(file);
900  free((void*)pixels);
901  printf("Image \"%s\" saved.\n", file_name);
902  }
903 
904  void View::save_screenshot(const char* bmpname, bool high_quality)
905  {
906  view_sync.enter();
907  if (output_id >= 0) { //set variable neccessary to create a screenshot
908  hq_frame = high_quality;
909  want_screenshot = true;
910  screenshot_filename = bmpname;
911  }
912  view_sync.leave();
913 
914  //request redraw
915  refresh();
916  }
917 
918  void View::save_numbered_screenshot(const char* format, int number, bool high_quality)
919  {
920  char buffer[1000];
921  sprintf(buffer, format, number);
922  save_screenshot(buffer, high_quality);
923  }
924 
925  int View::measure_scale_labels()
926  {
927  int result = 0;
928  for (int i = 0; i <= scale_numticks + 1; i++)
929  {
930  double value = range_min + (double)i * (range_max - range_min) / (scale_numticks + 1);
931  if (fabs(value) < Hermes::HermesEpsilon) value = 0.0;
932  char text[50];
933  sprintf(text, scale_fmt, value);
934  int w = get_text_width(text);
935  if (w > result) result = w;
936  }
937  return result;
938  }
939 
940  void View::draw_continuous_scale(char* title, bool righttext)
941  {
942  int i;
943  double y0 = scale_y + scale_height;
944 
945  set_ortho_projection(true);
946  glDisable(GL_DEPTH_TEST);
947  glDisable(GL_LIGHTING);
948  glDisable(GL_TEXTURE_1D);
949  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
950 
951  // background
952  const int b = 5;
953  glEnable(GL_BLEND);
954  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
955  glColor4f(1.0f, 1.0f, 1.0f, 0.65f);
956  int rt = righttext ? 0 : labels_width + 8;
957  glBegin(GL_QUADS);
958  glVertex2d(scale_x - b - rt, y0 + 5 + b);
959  glVertex2d(scale_x + scale_width + 8 + labels_width + b - rt, y0 + 5 + b);
960  glVertex2d(scale_x + scale_width + 8 + labels_width + b - rt, scale_y - 5 - b);
961  glVertex2d(scale_x - b - rt, scale_y - 5 - b);
962  glEnd();
963 
964  // palette
965  glDisable(GL_BLEND);
966  glColor3f(0.0f, 0.0f, 0.0f);
967  glBegin(GL_QUADS);
968  glVertex2d(scale_x, scale_y);
969  glVertex2d(scale_x, scale_y + scale_height + 1);
970  glVertex2d(scale_x + scale_width + 1, scale_y + scale_height + 1);
971  glVertex2d(scale_x + scale_width + 1, scale_y);
972  glEnd();
973 
974  glEnable(GL_TEXTURE_1D);
975  glBindTexture(GL_TEXTURE_1D, gl_pallete_tex_id);
976  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
977  glBegin(GL_QUADS);
978  glTexCoord1d(tex_scale + tex_shift);
979  glVertex2d(scale_x + 1, scale_y + 1);
980  glVertex2d(scale_x + scale_width, scale_y + 1);
981  glTexCoord1d(tex_shift);
982  glVertex2d(scale_x + scale_width, scale_y + scale_height);
983  glVertex2d(scale_x + 1, scale_y + scale_height);
984  glEnd();
985 
986  // focus
987  glDisable(GL_TEXTURE_1D);
988  if (scale_focused)
989  {
990  glEnable(GL_BLEND);
991  glColor4f(1.0f, 1.0f, 1.0f, 0.3f);
992  glBegin(GL_QUADS);
993  glVertex2d(scale_x + 1, scale_y + 1);
994  glVertex2d(scale_x + scale_width, scale_y + 1);
995  glVertex2d(scale_x + scale_width, scale_y + scale_height);
996  glVertex2d(scale_x + 1, scale_y + scale_height);
997  glEnd();
998  }
999 
1000  // ticks
1001  glColor3f(0, 0, 0);
1002  glDisable(GL_BLEND);
1003  glDisable(GL_LINE_STIPPLE);
1004  glLineWidth(1.0);
1005  glBegin(GL_LINES);
1006  for (i = 0; i < scale_numticks; i++)
1007  {
1008  y0 = scale_y + scale_height - (double)(i + 1) * scale_height / (scale_numticks + 1);
1009  glVertex2d(scale_x, y0);
1010  glVertex2d(scale_x + 0.2 * scale_width + 1, y0);
1011  glVertex2d(scale_x + 0.8 * scale_width, y0);
1012  glVertex2d(scale_x + scale_width, y0);
1013  }
1014  glEnd();
1015 
1016  // labels
1017  for (i = 0; i <= scale_numticks + 1; i++)
1018  {
1019  double value = range_min + (double)i * (range_max - range_min) / (scale_numticks + 1);
1020  if (fabs(value) < Hermes::HermesEpsilon) value = 0.0;
1021  char text[50];
1022  sprintf(text, scale_fmt, value);
1023  y0 = scale_y + scale_height - (double)i * scale_height / (scale_numticks + 1);
1024  if (righttext)
1025  draw_text(scale_x + scale_width + 8, y0, text);
1026  else
1027  draw_text(scale_x - 8, y0, text, 1);
1028  }
1029  }
1030 
1031  void View::draw_discrete_scale(int numboxes, const char* boxnames[], const float boxcolors[][3])
1032  {
1033  set_ortho_projection(true);
1034  glDisable(GL_DEPTH_TEST);
1035  glDisable(GL_LIGHTING);
1036  glDisable(GL_TEXTURE_1D);
1037  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1038 
1039  // background
1040  const int b = 5;
1041  glEnable(GL_BLEND);
1042  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1043  glColor4f(1.0f, 1.0f, 1.0f, 0.65f);
1044  glBegin(GL_QUADS);
1045  glVertex2d(scale_x - b, scale_y - b);
1046  glVertex2d(scale_x - b, scale_y + scale_height + b + 1);
1047  glVertex2d(scale_x + scale_width + b + 1, scale_y + scale_height + b + 1);
1048  glVertex2d(scale_x + scale_width + b + 1, scale_y - b);
1049  glEnd();
1050 
1051  // boxes
1052  glDisable(GL_BLEND);
1053  int y = scale_y;
1054  for (int i = 0; i < numboxes; i++)
1055  {
1056  glColor3f(0.0, 0.0, 0.0);
1057  glBegin(GL_QUADS);
1058  glVertex2d(scale_x, y);
1059  glVertex2d(scale_x, y + scale_box_height + 1);
1060  glVertex2d(scale_x + scale_width + 1, y + scale_box_height + 1);
1061  glVertex2d(scale_x + scale_width + 1, y);
1062  glEnd();
1063 
1064  const float* color = boxcolors[numboxes - 1 - i];
1065  float bcolor[3] = { color[0], color[1], color[2] };
1066  if (scale_focused)
1067  {
1068  bcolor[0] = color[0] * 0.7f + 1.0f*0.3f;
1069  bcolor[1] = color[1] * 0.7f + 1.0f*0.3f;
1070  bcolor[2] = color[2] * 0.7f + 1.0f*0.3f;
1071  }
1072 
1073  glColor3f(bcolor[0], bcolor[1], bcolor[2]);
1074  glBegin(GL_QUADS);
1075  glVertex2d(scale_x + 1, y + 1);
1076  glVertex2d(scale_x + 1, y + scale_box_height);
1077  glVertex2d(scale_x + scale_width, y + scale_box_height);
1078  glVertex2d(scale_x + scale_width, y + 1);
1079  glEnd();
1080 
1081  if ((color[0] + color[1] + color[2]) / 3 > 0.5)
1082  glColor3f(0, 0, 0);
1083  else
1084  glColor3f(1, 1, 1);
1085 
1086  int a = scale_x + scale_width / 2;
1087  int b = y + scale_box_height / 2;
1088  draw_text(a, b, boxnames[numboxes - 1 - i], 0);
1089  draw_text(a + 1, b, boxnames[numboxes - 1 - i], 0);
1090 
1091  y += scale_box_height + scale_box_skip;
1092  }
1093  }
1094 
1095  void View::scale_dispatch()
1096  {
1097  draw_continuous_scale(nullptr, !pos_horz);
1098  }
1099 
1101  {
1102  lspace = rspace = labels_width = 0;
1103  if (b_scale)
1104  {
1105  labels_width = scale_fixed_width;
1106  if (labels_width < 0) labels_width = measure_scale_labels();
1107  int space = scale_width + 8 + labels_width + margin;
1108  if (pos_horz == 0)
1109  {
1110  lspace = space; scale_x = margin;
1111  }
1112  else
1113  {
1114  rspace = space; scale_x = output_width - margin - scale_width;
1115  }
1116 
1117  if (pos_vert == 0)
1118  scale_y = output_height - margin - scale_height;
1119  else
1120  scale_y = margin;
1121  }
1122 
1123  center_x = ((double)output_width - 2 * margin - lspace - rspace) / 2 + margin + lspace;
1124  center_y = (double)output_height / 2;
1125  }
1126 
1127  void View::show_scale(bool show)
1128  {
1129  view_sync.enter();
1130  b_scale = show;
1131  if (output_id >= 0)
1132  update_layout();
1133  view_sync.leave();
1134  refresh();
1135  }
1136 
1137  void View::set_scale_position(int horz, int vert)
1138  {
1139  view_sync.enter();
1140  pos_horz = horz;
1141  pos_vert = vert;
1142  if (output_id >= 0)
1143  update_layout();
1144  view_sync.leave();
1145  refresh();
1146  }
1147 
1148  void View::set_scale_size(int width, int height, int numticks)
1149  {
1150  view_sync.enter();
1151  scale_width = width;
1152  scale_height = height;
1153  scale_numticks = numticks;
1154  update_layout();
1155  view_sync.leave();
1156  refresh();
1157  }
1158 
1159  void View::set_scale_format(const char* fmt)
1160  {
1161  view_sync.enter();
1162  strncpy(scale_fmt, fmt, 19);
1163  update_layout();
1164  view_sync.leave();
1165  refresh();
1166  }
1167 
1168  void View::fix_scale_width(int width)
1169  {
1170  view_sync.enter();
1171  scale_fixed_width = width;
1172  update_layout();
1173  view_sync.leave();
1174  refresh();
1175  }
1176  }
1177  }
1178 }
1179 #endif
Definition: adapt.h:24
void refresh_view(int view_id)
Forces redisplay of a view.
void set_title(const char *msg,...)
Changes the window name (in its title-bar) to 'title'.
Definition: view.cpp:556
virtual void update_layout()
Updates layout, i.e., centers mesh.
Definition: view.cpp:1100
void remove_view(int view_id)
Removes a view.
void save_numbered_screenshot(const char *format, int number, bool high_quality=false)
Definition: view.cpp:918
void create_gl_palette()
Creates pallete texture in OpenGL. Assumes that view_sync is locked.
Definition: view.cpp:619
#define H2DV_SCALE_LOG_BASE
Base of the scale coefficient. Scale = base^{mouse move}.
Definition: view.h:35
double rendering_frames[FPS_FRAME_SIZE]
time spend in rendering of frames[in ms]
Definition: view.h:136
unsigned int gl_pallete_tex_id
OpenGL texture object ID.
Definition: view.h:206
Wait for all windows to close.
Definition: view.h:42
void signal_drawing_finished()
signals drawing finished inside a protected section
Definition: view_support.h:65
Common definitions for Hermes2D.
virtual void get_palette_color(double x, float *gl_color)
Fills gl_color with palette color. Assumes that gl_color points to a vector of three components (RGB)...
Definition: view.cpp:584
virtual void reset_view(bool force_reset)
Resets view based on the axis-aligned bounding box of the mesh. Assumes that the bounding box is set ...
Definition: view.cpp:190
::xsd::cxx::tree::buffer< char > buffer
Binary buffer type.
ViewWaitEvent
Wait events.
Definition: view.h:41
::xsd::cxx::tree::type type
C++ type corresponding to the anyType XML Schema built-in type.
virtual void clear_background()
Clears background.
Definition: view.cpp:230
bool view_not_reset
True if the view was not reset and therefore it has to be.
Definition: view.h:146
void leave()
leaves protected section
Definition: view_support.h:55
File containing View abstract class.
ViewPaletteType
View palette type.
Definition: view.h:61
void wait_close()
waits for close inside a protected section
Definition: view_support.h:63
void enter()
enters protected section
Definition: view_support.h:53
void draw_fps()
draws current FPS
Definition: view.cpp:336
static void wait(const char *text)
Closes all views at once.
Definition: view.cpp:147
const char * get_title() const
Returns the title.
Definition: view.cpp:551
ViewMonitor view_sync
synchronization between all views. Used to access OpenGL and signal a window close event and a keypre...
void wait_drawing_fisnihed()
waits for drawing finished inside a protected section
Definition: view_support.h:67
int rendering_frames_top
the new_ location of the next FPS
Definition: view.h:138
void save_screenshot(const char *bmpname, bool high_quality=false)
Definition: view.cpp:904
int add_view(View *view, int x, int y, int width, int height, const char *title)
Adds a view.
void wait_for_any_key(const char *text)
Waits for a keypress which is not processed.
void signal_keypress()
signals keypress inside a protected section
Definition: view_support.h:57
static void wait_for_keypress(const char *text=nullptr)
Waits for keypress. Deprecated.
Definition: view.cpp:516
static double get_tick_count()
returns a current time[in ms]
Definition: view.cpp:537
void refresh()
Refreshes views.
Definition: view.cpp:179
Wait for any unprocessed keypress to happen.
Definition: view.h:43
A palette based on hue scale.
Definition: view.h:62
void wait_for_all_views_close(const char *text)
Forces view thread to shutdown.
void set_view_title(int view_id, const char *title)
Sets title of a view.
double vertices_min_x
AABB of shown mesh.
Definition: view.h:148