Hermes2D  3.0
scalar_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 <GL/glew.h>
19 #include <GL/freeglut.h>
20 #include <algorithm>
21 #include <cmath>
22 #include <list>
23 #include "global.h"
24 #include "scalar_view.h"
25 
26 #define GL_BUFFER_OFFSET(i) ((char *)nullptr + (i))
27 
28 /* constants */
29 #define MIN_CONT_STEP 1.0E-2
30 #define CONT_CHANGE 1.0E-2
31 #define D3DV_SCALE_STEP_COEF 1.1
32 
33 namespace Hermes
34 {
35  namespace Hermes2D
36  {
37  namespace Views
38  {
39  const int ScalarView::fovy = 50;
40  const double ScalarView::znear = 0.05;
41  const double ScalarView::zfar = 10;
42 
43  void ScalarView::init()
44  {
45  lin = new Linearizer(OpenGL);
46  pmode = mode3d = false;
47  panning = false;
48 
49  contours = false;
50  cont_orig = 0.0;
51  cont_step = 0.2;
52  cont_color[0] = 0.0f; cont_color[1] = 0.0f; cont_color[2] = 0.0f;
53 
54  show_edges = true;
55  show_aabb = false;
56  edges_color[0] = 0.5f; edges_color[1] = 0.4f; edges_color[2] = 0.4f;
57 
58  show_values = true;
59  lin_updated = false;
60 
61  do_zoom_to_fit = true;
62  is_constant = false;
63  }
64 
65 #ifndef _MSC_VER
66 
67  ScalarView::ScalarView(const char* title, WinGeom* wg) :
68  View(title, wg),
69  element_id_widget(0),
70  show_element_info(false)
71  {
72  init();
73  }
74 #else
75  ScalarView::ScalarView(WinGeom* wg) :
76  View("ScalarView", wg),
77  element_id_widget(0),
78  show_element_info(false)
79  {
80  init();
81  }
82 #endif
83 
84  ScalarView::ScalarView(char* title, WinGeom* wg) :
85  View(title, wg),
86  element_id_widget(0),
87  show_element_info(false)
88  {
89  init();
90  }
91 
92  ScalarView::~ScalarView()
93  {
94  delete lin;
95  }
96 
97  void ScalarView::on_close()
98  {
99  if (element_id_widget != 0)
100  {
101  glDeleteLists(element_id_widget, 1);
102  element_id_widget = 0;
103  }
104 
105  //call of parent implementation
106  View::on_close();
107  }
108 
109  void ScalarView::show(MeshFunctionSharedPtr<double> sln, int item, MeshFunctionSharedPtr<double> xdisp, MeshFunctionSharedPtr<double> ydisp, double dmult)
110  {
111  // For preservation of the sln's active element. Will be set back after the visualization.
112  Element* active_element = sln->get_active_element();
113 
114  if (!range_auto)
115  lin->set_max_absolute_value(std::max(fabs(range_min), fabs(range_max)));
116 
117  lin->set_displacement(xdisp, ydisp, dmult);
118  lin->lock_data();
119 
120  lin->process_solution(sln, item);
121 
122  update_mesh_info();
123 
124  // Initialize element info.
125  init_element_info(sln->get_mesh());
126 
127  lin->unlock_data();
128 
129  create();
130  update_layout();
131  wait_for_draw();
132 
133  // FIXME: find out why this has to be called after wait_for_draw in order for the view to be reset initially.
134  // setting true here makes the view always reset after calling 'show'; particularly in the adaptivity process,
135  reset_view(false);
136  // it would disallow the observation of the process from a manually set viewpoint.
137  refresh();
138 
139  // Now we reset the active element if it was set before the MeshFunction sln entered this method.
140  // Only for Solutions. This method may fail for filters, as they may not have RefMaps correctly set.
141  if (dynamic_cast<Solution<double>*>(sln.get()) != nullptr)
142  if (active_element != nullptr)
143  // Also when there has not been a call to set_active_element since assignment to this MeshFunction,
144  // there is nothing to restore to.
145  if (active_element->active)
146  sln->set_active_element(active_element);
147  }
148 
149  void ScalarView::show_linearizer_data(double eps, int item)
150  {
151  update_mesh_info();
152 
153  create();
154  update_layout();
155  wait_for_draw();
156  // FIXME: find out why this has to be called after wait_for_draw in order for the view to be reset initially.
157  // setting true here makes the view always reset after calling 'show'; particularly in the adaptivity process,
158  reset_view(false);
159  // it would disallow the observation of the process from a manually set viewpoint.
160  refresh();
161  }
162 
163  void ScalarView::update_mesh_info()
164  {
165  // Get a range of vertex values (or use the range set by the user).
166  double vert_min = lin->get_min_value();
167  double vert_max = lin->get_max_value();
168  // Special case: constant function; offset the lower limit of range so that the domain is drawn under the
169  // function and also the scale is drawn correctly.
170  if ((vert_max - vert_min) < Hermes::HermesEpsilon)
171  {
172  is_constant = true;
173  vert_min -= 0.5;
174  }
175 
176  if (range_auto)
177  {
178  range_min = vert_min;
179  range_max = vert_max;
180  }
181 
182  if (fabs(range_max - range_min) < Hermes::HermesEpsilon)
183  value_irange = 1.0;
184  else
185  value_irange = 1.0 / (range_max - range_min);
186 
187  // Calculate the axes-aligned bounding box in the xy-plane.
188  lin->calc_vertices_aabb(&vertices_min_x, &vertices_max_x, &vertices_min_y, &vertices_max_y);
189 
190  // Calculate average value.
191  value_range_avg = 0.0;
192 
193  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::vertex_t> it = lin->vertices_begin(); !it.end; ++it)
194  {
196  if (vertex[2] > range_max)
197  value_range_avg += range_max;
198  else if (vertex[2] < range_min)
199  value_range_avg += range_min;
200  else
201  value_range_avg += vertex[2];
202  }
203 
204  value_range_avg /= lin->get_vertex_count();
205  lin_updated = true;
206  }
207 
208  void ScalarView::init_element_info(MeshSharedPtr mesh)
209  {
210  //cleanup
211  element_infos.clear();
212 
213  //check how many active elements are neccessary and prepare space for them
214  element_infos.reserve(mesh->get_num_active_elements());
215 
216  //build element info
217  Element *element = nullptr;
218  for_all_active_elements(element, mesh)
219  {
220  double sum_x = 0.0, sum_y = 0.0;
221  double max_x, max_y, min_x, min_y;
222  max_x = min_x = element->vn[0]->x;
223  max_y = min_y = element->vn[0]->y;
224  for (unsigned int i = 0; i < element->get_nvert(); i++)
225  {
226  sum_x += element->vn[i]->x;
227  sum_y += element->vn[i]->y;
228 
229  if (element->vn[i]->x > max_x)
230  max_x = element->vn[i]->x;
231  if (element->vn[i]->x < min_x)
232  min_x = element->vn[i]->x;
233  if (element->vn[i]->y > max_y)
234  max_y = element->vn[i]->y;
235  if (element->vn[i]->y < min_y)
236  min_y = element->vn[i]->y;
237  }
238  element_infos.push_back(ElementInfo(element->id,
239  (float)(sum_x / element->get_nvert()), (float)(sum_y / element->get_nvert()),
240  (float)(max_x - min_x), (float)(max_y - min_y)));
241  }
242  }
243 
244  Linearizer* ScalarView::get_linearizer()
245  {
246  return this->lin;
247  }
248 
249  void ScalarView::set_linearizer_criterion(LinearizerCriterion criterion)
250  {
251  this->lin->set_criterion(criterion);
252  }
253 
254  void ScalarView::draw_element_infos_2d()
255  {
256  //create widgets
257  create_element_info_widgets();
258 
259  //prepare environment
260  glMatrixMode(GL_MODELVIEW);
261  glDisable(GL_TEXTURE_1D);
262  glDisable(GL_LIGHTING);
263  glDisable(GL_BLEND);
264 
265  //draw element IDs
266  std::vector<ElementInfo>::const_iterator iter = element_infos.begin();
267  while (iter != element_infos.end())
268  {
269  //check element dimension in pixels
270  float width = (float)(iter->width * scale);
271  float height = (float)(iter->height * scale);
272 
273  //draw if AABB of element is large enough
274  if (width > 40.f && height > 20.f)
275  {
276  //prepare environment
277  glPushMatrix();
278  glTranslatef(iter->x, iter->y, 0.0f);
279  glScalef(1 / (float)scale, 1 / (float)scale, 1.0f);
280 
281  //prepare number
282  void* font = GLUT_BITMAP_HELVETICA_10;
283  unsigned char buffer[128];
284  sprintf((char*)buffer, "%d", iter->id);
285  int width_px = glutBitmapLength(font, buffer);
286  int height_px = glutBitmapHeight(font);
287 
288  //draw background
289  glCallList(element_id_widget);
290 
291  //draw number
292  glTranslatef(-width_px / 2.0f, -height_px / 3.0f, 0.0f);
293  glColor3f(1.0f, 1.0f, 1.0f);
294  glRasterPos2f(0.0f, 0.0f);
295  glutBitmapString(font, buffer);
296 
297  //clear environment
298  glPopMatrix();
299  }
300 
301  //advance to next
302  ++iter;
303  }
304  }
305 
306  void ScalarView::create_element_info_widgets()
307  {
308  if (element_id_widget == 0)
309  {
310  element_id_widget = glGenLists(1);
311  glNewList(element_id_widget, GL_COMPILE);
312  {
313  glBegin(GL_QUADS);
314 
315  //background
316  float radius = 20.f;
317  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
318  glVertex2f(-radius*1.1, radius*0.5);
319  glVertex2f(radius*1.1, radius*0.5);
320  glVertex2f(radius*1.1, -radius*0.5);
321  glVertex2f(-radius*1.1, -radius*0.5);
322 
323  //foreground
324  radius = 18.f;
325  glColor4f(0.2f, 0.2f, 0.4f, 1.0f);
326  glVertex2f(-radius*1.1, radius*0.5);
327  glVertex2f(radius*1.1, radius*0.5);
328  glVertex2f(radius*1.1, -radius*0.5);
329  glVertex2f(-radius*1.1, -radius*0.5);
330 
331  glEnd();
332  }
333  glEndList();
334  }
335  }
336 
337  void ScalarView::show_contours(double step, double orig)
338  {
339  if (step == 0.0)
340  throw Exceptions::ValueException("step", step, 0.0);
341  contours = true;
342  cont_orig = orig;
343  cont_step = step;
344  set_palette_filter(true);
345  refresh();
346  }
347 
348  void ScalarView::draw_tri_contours(ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::triangle_t& triangle)
349  {
350  // sort the vertices by their value, keep track of the permutation sign.
351  int i, idx[3] = { 0, 1, 2 }, perm = 0;
352  for (i = 0; i < 2; i++)
353  {
354  if (triangle[idx[0]][2] > triangle[idx[1]][2])
355  {
356  std::swap(idx[0], idx[1]);
357  perm++;
358  }
359  if (triangle[idx[1]][2] > triangle[idx[2]][2])
360  {
361  std::swap(idx[1], idx[2]);
362  perm++;
363  }
364  }
365  if (fabs(triangle[idx[0]][2] - triangle[idx[2]][2]) < 1e-3 * fabs(cont_step))
366  return;
367 
368  // get the first (lowest) contour value
369  double val = triangle[idx[0]][2];
370  val = std::ceil((val - cont_orig) / cont_step) * cont_step + cont_orig;
371 
372  int l1 = 0, l2 = 1;
373  int r1 = 0, r2 = 2;
374 
375  while (val < triangle[idx[r2]][2])
376  {
377  double ld = triangle[idx[l2]][2] - triangle[idx[l1]][2];
378  double rd = triangle[idx[r2]][2] - triangle[idx[r1]][2];
379 
380  // draw a slice of the triangle
381  while (val < triangle[idx[l2]][2])
382  {
383  double lt = (val - triangle[idx[l1]][2]) / ld;
384  double rt = (val - triangle[idx[r1]][2]) / rd;
385 
386  double x1 = (1.0 - lt) * triangle[idx[l1]][0] + lt * triangle[idx[l2]][0];
387  double y1 = (1.0 - lt) * triangle[idx[l1]][1] + lt * triangle[idx[l2]][1];
388  double x2 = (1.0 - rt) * triangle[idx[r1]][0] + rt * triangle[idx[r2]][0];
389  double y2 = (1.0 - rt) * triangle[idx[r1]][1] + rt * triangle[idx[r2]][1];
390 
391  if (perm & 1)
392  {
393  glVertex2d(x1, y1);
394  glVertex2d(x2, y2);
395  }
396  else
397  {
398  glVertex2d(x2, y2);
399  glVertex2d(x1, y1);
400  }
401 
402  val += cont_step;
403  }
404  l1 = 1;
405  l2 = 2;
406  }
407  }
408 
409  void ScalarView::prepare_gl_geometry()
410  {
411  if (lin_updated)
412  lin_updated = false;
413  }
414 
415  void ScalarView::draw_values_2d()
416  {
417  //set texture for coloring
418  glEnable(GL_TEXTURE_1D);
419  glBindTexture(GL_TEXTURE_1D, gl_pallete_tex_id);
420  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
421  glColor3f(0, 1, 0);
422 
423  //set texture transformation matrix
424  glMatrixMode(GL_TEXTURE);
425  glLoadIdentity();
426  glTranslated(tex_shift, 0.0, 0.0);
427  glScaled(tex_scale, 0.0, 0.0);
428 
429  //render triangles
430  glBegin(GL_TRIANGLES);
431 
432  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::triangle_t> it = lin->triangles_begin(); !it.end; ++it)
433  {
435  glTexCoord1d((triangle[0][2] - range_min) * value_irange);
436  glVertex2d(triangle[0][0], triangle[0][1]);
437  glTexCoord1d((triangle[1][2] - range_min) * value_irange);
438  glVertex2d(triangle[1][0], triangle[1][1]);
439  glTexCoord1d((triangle[2][2] - range_min) * value_irange);
440  glVertex2d(triangle[2][0], triangle[2][1]);
441  }
442  glEnd();
443 
444  //GL clenaup
445  //switch-off texture transform
446  glMatrixMode(GL_TEXTURE);
447  glLoadIdentity();
448  glMatrixMode(GL_MODELVIEW);
449  }
450 
451  void ScalarView::draw_edges_2d()
452  {
453  glColor3fv(edges_color);
454  glBegin(GL_LINES);
455  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::edge_t> it = lin->edges_begin(); !it.end; ++it)
456  {
458  int& edge_marker = it.get_marker();
459 
460  if (show_edges || edge_marker)
461  {
462  glVertex2d(edge[0][0], edge[0][1]);
463  glVertex2d(edge[1][0], edge[1][1]);
464  }
465  }
466  glEnd();
467  }
468 
469 #define V0 vertices_min_x - xctr, range_min - yctr, -(vertices_min_y - zctr)
470 #define V1 vertices_max_x - xctr, range_min - yctr, -(vertices_min_y - zctr)
471 #define V2 vertices_max_x - xctr, range_min - yctr, -(vertices_max_y - zctr)
472 #define V3 vertices_min_x - xctr, range_min - yctr, -(vertices_max_y - zctr)
473 #define V4 vertices_min_x - xctr, range_max - yctr, -(vertices_min_y - zctr)
474 #define V5 vertices_max_x - xctr, range_max - yctr, -(vertices_min_y - zctr)
475 #define V6 vertices_max_x - xctr, range_max - yctr, -(vertices_max_y - zctr)
476 #define V7 vertices_min_x - xctr, range_max - yctr, -(vertices_max_y - zctr)
477 
478  void ScalarView::draw_aabb()
479  {
480  // Axis-aligned bounding box of the model.
481  GLdouble aabb[] =
482  {
483  V0, V1, V2, V3, // bottom
484  V0, V1, V5, V4, // front
485  V0, V3, V7, V4, // left
486  V1, V2, V6, V5, // right
487  V2, V3, V7, V6, // back
488  V4, V5, V6, V7 // top
489  };
490 
491  // Set the edge color.
492  glColor3fv(edges_color);
493 
494  // Make the cube wire frame.
495  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
496 
497  // Scale the box appropriately.
498  glPushMatrix();
499  glScaled(xzscale, yscale, xzscale);
500 
501  // Activate and specify pointer to vertex array.
502  glEnableClientState(GL_VERTEX_ARRAY);
503  glVertexPointer(3, GL_DOUBLE, 0, aabb);
504 
505  // Draw the box (glDrawElements would be better, but do not work for me).
506  glDrawArrays(GL_QUADS, 0, 24);
507 
508  // Deactivate vertex arrays after drawing.
509  glDisableClientState(GL_VERTEX_ARRAY);
510 
511  // Recover the original model/view matrix
512  glPopMatrix();
513  }
514 
515  void ScalarView::on_display_2d()
516  {
517  set_ortho_projection();
518  glDisable(GL_LIGHTING);
519  glDisable(GL_DEPTH_TEST);
520  glDisable(GL_TEXTURE_2D);
521  glDisable(GL_BLEND);
522 
523  // setup transformation (follows View::transform_x and View::transform_y)
524  glMatrixMode(GL_MODELVIEW);
525  glPushMatrix();
526  glLoadIdentity();
527  glTranslated(center_x, center_y, 0.0);
528  glScaled(1.0, -1.0, 1.0);
529  glTranslated(trans_x, trans_y, 0.0);
530  glScaled(scale, scale, 1.0);
531 
532  // draw all triangles
533  if (show_values)
534  draw_values_2d();
535 
536  // draw contours
537  glDisable(GL_TEXTURE_1D);
538  if (contours)
539  {
540  glColor3fv(cont_color);
541  //glLineWidth(2.0f);
542  glBegin(GL_LINES);
543  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::triangle_t> it = this->lin->triangles_begin(); !it.end; ++it)
544  {
546  draw_tri_contours(triangle);
547  }
548  glEnd();
549  }
550 
551  // draw edges and boundary of mesh
552  draw_edges_2d();
553 
554  //draw element IDS
555  if (show_element_info)
556  draw_element_infos_2d();
557 
558  //cleanup
559  glPopMatrix();
560  }
561 
562  void ScalarView::on_display_3d()
563  {
564  set_3d_projection(fovy, znear, zfar);
565 
566  glClear(GL_DEPTH_BUFFER_BIT);
567  glEnable(GL_DEPTH_TEST);
568 
569  // Set camera transforamtion.
570  glMatrixMode(GL_MODELVIEW);
571  glLoadIdentity();
572 
573  // Initialize light and material.
574  init_lighting();
575 
576  if (do_zoom_to_fit)
577  {
578  // If the user presses 'c' to center the view automatically, calculate how far to move
579  // the visualized model away from the viewer so that it is completely visible in current window.
580  ztrans = calculate_ztrans_to_fit_view();
581  do_zoom_to_fit = false;
582  }
583 
584  // Set model transformation.
585  glTranslated(xtrans, ytrans, ztrans);
586  glRotated(xrot, 1, 0, 0);
587  glRotated(yrot, 0, 1, 0);
588 
589  // Draw the surface.
590  glEnable(GL_LIGHTING);
591  glEnable(GL_TEXTURE_1D);
592  glBindTexture(GL_TEXTURE_1D, gl_pallete_tex_id);
593  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
594  glEnable(GL_NORMALIZE);
595  glEnable(GL_POLYGON_OFFSET_FILL);
596  glPolygonOffset(1.0, 1.0);
597  glBegin(GL_TRIANGLES);
598  double normal_xzscale = 1.0 / xzscale, normal_yscale = 1.0 / yscale;
599 
600  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::triangle_t> it = this->lin->triangles_begin(); !it.end; ++it)
601  {
602  ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::triangle_t& triangle = it.get();
603 
604  for (int j = 0; j < 3; j++)
605  {
606  glTexCoord2d((triangle[j][2] - range_min) * value_irange * tex_scale + tex_shift, 0.0);
607 
608  glVertex3d((triangle[j][0] - xctr) * xzscale, (triangle[j][2] - yctr) * yscale, -(triangle[j][1] - zctr) * xzscale);
609  }
610  }
611 
612  glEnd();
613  glDisable(GL_POLYGON_OFFSET_FILL);
614 
615  // Draw edges.
616  glDisable(GL_LIGHTING);
617  glDisable(GL_TEXTURE_1D);
618  if (show_edges)
619  {
620  glColor3fv(edges_color);
621  glBegin(GL_LINES);
622  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::edge_t> it = lin->edges_begin(); !it.end; ++it)
623  {
624  ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::edge_t& edge = it.get();
625 
626  for (int j = 0; j < 2; j++)
627  glVertex3d((edge[j][0] - xctr) * xzscale, (edge[j][2] - yctr) * yscale, -(edge[j][1] - zctr) * xzscale);
628  }
629  glEnd();
630  }
631 
632  // Draw the whole bounding box or only the boundary edges.
633  if (show_aabb)
634  draw_aabb();
635  else
636  {
637  glColor3fv(edges_color);
638  glBegin(GL_LINES);
639  for (Linearizer::Iterator<ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::edge_t> it = lin->edges_begin(); !it.end; ++it)
640  {
641  // Outline of the domain boundary at the bottom of the plot or at the bottom user-defined limit
642  double y_coord = (range_min - yctr) * yscale;
643  ScalarLinearizerDataDimensions<LINEARIZER_DATA_TYPE>::edge_t& edge = it.get();
644  int& edge_marker = it.get_marker();
645 
646  if (edge_marker)
647  {
648  glVertex3d((edge[0][0] - xctr) * xzscale, y_coord, -(edge[0][1] - zctr) * xzscale);
649  glVertex3d((edge[1][0] - xctr) * xzscale, y_coord, -(edge[1][1] - zctr) * xzscale);
650  }
651  }
652  glEnd();
653  }
654  }
655 
656  void ScalarView::on_display()
657  {
658  // lock and get data
659  lin->lock_data();
660 
661  glPolygonMode(GL_FRONT_AND_BACK, pmode ? GL_LINE : GL_FILL);
662 
663  //prepare vertices
664  prepare_gl_geometry();
665 
666  if (mode3d)
667  this->on_display_3d();
668  else
669  this->on_display_2d();
670 
671  lin->unlock_data();
672  }
673 
674  static inline void normalize(double& x, double& y, double& z)
675  {
676  double l = 1.0 / sqrt(sqr(x) + sqr(y) + sqr(z));
677  x *= l; y *= l; z *= l;
678  }
679 
680  void ScalarView::update_layout()
681  {
682  View::update_layout();
683  // (x, y, -z) coordinates (in the eye coordinate system) of the point that lies at the center of solution domain
684  // and vertically at the position of the average value of all vertices. This point will be the origin for all
685  // drawing and also the initial position of the camera.
686  xctr = (vertices_max_x + vertices_min_x) / 2.0;
687  yctr = value_range_avg;
688  zctr = (vertices_max_y + vertices_min_y) / 2.0;
689  }
690 
691  void ScalarView::reset_view(bool force_reset)
692  {
693  if (force_reset || view_not_reset) { // Reset 3d view.
694  xrot = 40.0; yrot = 0.0;
695  xtrans = ytrans = ztrans = 0.0;
696  // Ensure that the model (before applying any transformations) may be translated along the view axis completely
697  // into the viewing volume, and that it is not too flat (so that considerable amount of detail is visible).
698  // Later on, we will determine the translation distance so that the model occupies as much of the view space as possible.
699 
700  // Set scaling into the (-1, 1) range on the x- and z- axes
701  double max_radial = std::max(vertices_max_x - vertices_min_x, vertices_max_y - vertices_min_y);
702  xzscale = 2.0 / max_radial;
703 
704  // Determine the largest y-axis distance of the function surface from the line of sight
705  double max_axial = std::max(range_max - yctr, fabs(range_min - yctr));
706 
707  // We use the same scaling for the y-axis as for the x- and z- axes if after this scaling,
708  // 1. the model could be translated so that it lies completely below the top clipping plane of the viewing frustum and
709  // its farthest (away from the camera) corner is at least 1 unit before the far clipping plane (to allow some zooming out),
710  // 2. the model's bounding box's part above (or below, whichever is bigger) the average function value (yctr) has dimensions
711  // (-1, 1)x(0, height)x(-1, 1), where height > 0.1.
712  // If this is not true, the model is either too tall or too flat and will be subject to different scaling
713  // along the y-axis, such that it would fit to the viewing frustum at one third of its depth (i.e. at one third of
714  // the total available zooming range).
716  double tan_fovy_half = tan((double)fovy / 2.0 / 180.0 * M_PI);
717  double max_allowed_height = (zfar - 3)*tan_fovy_half;
718  if (max_axial * xzscale > max_allowed_height || (max_axial * xzscale < 0.1 && !is_constant))
719  yscale = (znear + zfar) / 3.0 * tan_fovy_half * value_irange;
720  else
721  yscale = xzscale;
722 
723  do_zoom_to_fit = true;
724  }
725  // Reset 2d view.
726  View::reset_view(force_reset);
727  }
728 
729  double ScalarView::calculate_ztrans_to_fit_view()
730  {
731  // Axis-aligned bounding box of the model (homogeneous coordinates in the model space), divided into the bottom and top base.
732  GLdouble aabb[2][16] =
733  {
734  {
735  V0, 1,
736  V1, 1,
737  V2, 1,
738  V3, 1
739  },
740  {
741  V4, 1,
742  V5, 1,
743  V6, 1,
744  V7, 1
745  }
746  };
747  GLdouble aabb_base[16];
748 
749  // Tangents of the half of the vertical and horizontal field of view angles.
750  double tan_fovy_half = tan((double)fovy / 2.0 / 180.0 * M_PI);
751  double tan_fovx_half = tan_fovy_half * (double)output_width / output_height;
752 
753  // The viewpoint z-coordinate we are looking for.
754  double optimal_viewpoint_pos = 0.0;
755 
756  // We will change the model transformation matrix, so save the current one.
757  glPushMatrix();
758 
759  // Create the model transformation matrix with the default z-translation.
760  glLoadIdentity();
761  glTranslated(xtrans, ytrans, ztrans);
762  glRotated(xrot, 1, 0, 0);
763  glRotated(yrot, 0, 1, 0);
764  glScaled(xzscale, yscale, xzscale);
765 
766  // As far as I know, OpenGL can only perform 4x4 matrix multiplication, so we find the 8 transformed bounding box corners by
767  // first multiplying the transformation matrix with a matrix having as its 4 columns the coordinates of the bottom base corners
768  // and then with a matrix created from the top base corners.
769  for (int i = 0; i < 2; i++)
770  {
771  glPushMatrix();
772  glMultMatrixd(aabb[i]);
773  glGetDoublev(GL_MODELVIEW_MATRIX, aabb_base);
774  glPopMatrix();
775 
776  // Go through the transformed corners and find the biggest distance of its perspective projection center to the origin.
777  GLdouble *coord_ptr = &aabb_base[0];
778  for (int j = 0; j < 4; j++)
779  {
780  double perspective_center_to_origin_dist = fabs(coord_ptr[0]) / tan_fovx_half + coord_ptr[2];
781  if (perspective_center_to_origin_dist > optimal_viewpoint_pos)
782  optimal_viewpoint_pos = perspective_center_to_origin_dist;
783 
784  perspective_center_to_origin_dist = fabs(coord_ptr[1]) / tan_fovy_half + coord_ptr[2];
785  if (perspective_center_to_origin_dist > optimal_viewpoint_pos)
786  optimal_viewpoint_pos = perspective_center_to_origin_dist;
787 
788  coord_ptr += 4;
789  }
790  }
791 
792  // Restore the original model transformation matrix.
793  glPopMatrix();
794 
795  return -optimal_viewpoint_pos;
796  }
797 
798  void ScalarView::set_vertical_scaling(double sc)
799  {
800  if (mode3d)
801  yscale *= sc;
802  else if (contours)
803  cont_step *= sc;
804  refresh();
805  }
806 
807  void ScalarView::set_min_max_range(double min, double max)
808  {
810  if (fabs(max - min) < Hermes::HermesEpsilon)
811  {
812  this->warn("Range (%f, %f) is too narrow: adjusted to (%f, %f)", min, max, min - 0.5, max);
813  min -= 0.5;
814  }
815  View::set_min_max_range(min, max);
816  }
817 
818  void ScalarView::init_lighting()
819  {
820  float light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
821  float light_ambient[] = { 0.1f, 0.1f, 0.1f, 1.0f };
822  float light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
823 
824  glEnable(GL_LIGHT0);
825  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
826  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
827  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
828  // glLightfv(GL_LIGHT0, GL_POSITION, light_position);
829 
830  float material_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
831  float material_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
832  float material_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
833 
834  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
835  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
836  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
837  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 128);
838  glDisable(GL_COLOR_MATERIAL);
839 
840  glShadeModel(GL_SMOOTH);
841  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
842  if (GLEW_EXT_separate_specular_color)
843  {
844  glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
845  }
846  }
847 
848  void ScalarView::set_3d_mode(bool enable)
849  {
850  mode3d = enable; dragging = scaling = false;
851  if (mode3d)
852  {
853  lin->lock_data();
854  lin->unlock_data();
855  }
856  refresh();
857  }
858 
859  void ScalarView::on_key_down(unsigned char key, int x, int y)
860  {
861  switch (key)
862  {
863  case 'm':
864  show_edges = !show_edges;
865  refresh();
866  break;
867 
868  case 'l':
869  pmode = !pmode;
870  refresh();
871  break;
872 
873  case 'c':
874  reset_view(true);
875  refresh();
876  break;
877 
878  case 'f':
879  set_palette_filter(pal_filter != GL_LINEAR);
880  break;
881 
882  case 'k':
883  contours = !contours;
884  if (contours)
885  this->cont_step = (this->range_max - this->range_min) / 50.;
886  refresh();
887  break;
888 
889  case 'i':
890  show_element_info = !show_element_info;
891  if (!mode3d)
892  refresh();
893  break;
894 
895  case 'b':
896  show_aabb = !show_aabb;
897  if (mode3d)
898  refresh();
899  break;
900 
901  case '3':
902  mode3d = !mode3d;
903  dragging = scaling = false;
904  refresh();
905  break;
906 
907  case '*':
908  lin->lock_data();
909  if (mode3d)
910  yscale *= D3DV_SCALE_STEP_COEF;
911  else if (contours)
912  cont_step *= D3DV_SCALE_STEP_COEF;
913  lin->unlock_data();
914  refresh();
915  break;
916 
917  case '/':
918  lin->lock_data();
919  if (mode3d)
920  yscale /= D3DV_SCALE_STEP_COEF;
921  else if (contours)
922  cont_step /= D3DV_SCALE_STEP_COEF;
923  lin->unlock_data();
924  refresh();
925  break;
926 
927  default:
928  View::on_key_down(key, x, y);
929  break;
930  }
931  }
932 
933  void ScalarView::on_mouse_move(int x, int y)
934  {
935  if (mode3d && (dragging || scaling || panning))
936  {
937  if (dragging)
938  {
939  yrot += 0.4 * (x - mouse_x);
940  xrot += 0.4 * (y - mouse_y);
941 
942  if (xrot < -90) xrot = -90;
943  else if (xrot > 90) xrot = 90;
944  }
945  else if (scaling)
946  {
947  ztrans += 0.01 * (mouse_y - y);
948  if (ztrans > -0.25) ztrans = -0.25;
949  else if (ztrans < -7) ztrans = -7;
950  }
951  else
952  {
953  xtrans += 0.002 * (x - mouse_x);
954  ytrans += 0.002 * (mouse_y - y);
955  }
956 
957  refresh();
958  mouse_x = x;
959  mouse_y = y;
960  return;
961  }
962  else
963  {
964  if (!mode3d && show_edges && !dragging && !scaling && !panning)
965  refresh();
966  else
967  View::on_mouse_move(x, y);
968  }
969  }
970 
971  void ScalarView::on_right_mouse_down(int x, int y)
972  {
973  View::on_right_mouse_down(x, y);
974  }
975 
976  void ScalarView::on_middle_mouse_down(int x, int y)
977  {
978  if (!mode3d) return;
979  dragging = scaling = false;
980  panning = true;
981  }
982 
983  void ScalarView::on_middle_mouse_up(int x, int y)
984  {
985  panning = false;
986  }
987 
988  const char* ScalarView::get_help_text() const
989  {
990  return
991  "ScalarView\n\n"
992  "Controls:\n"
993  " Left mouse - pan\n"
994  " Right mouse - zoom\n"
995  " 3 - toggle 3D mode\n"
996  " C - center image\n"
997  " F - toggle smooth palette\n"
998  " H - render high-quality frame\n"
999  " K - toggle contours\n"
1000  " M - toggle mesh\n"
1001  " B - toggle bounding box\n"
1002  " I - toggle element IDs (2d only)\n"
1003  " P - cycle palettes\n"
1004  " S - save screenshot\n"
1005  " * - increase contour density\n"
1006  " / - decrease contour density\n"
1007  " F1 - this help\n"
1008  " Esc, Q - quit\n\n"
1009  "3D mode:\n"
1010  " Left mouse - rotate\n"
1011  " Right mouse - zoom\n"
1012  " Middle mouse - pan\n"
1013  " * - increase Z scale\n"
1014  " / - decrease Z scale";
1015  }
1016  }
1017  }
1018 }
1019 #endif
double x
vertex node coordinates
Definition: element.h:63
static const int fovy
Field of view in the vertical direction (in degrees).
Definition: scalar_view.h:180
Definition: adapt.h:24
int id
element id number
Definition: element.h:112
Stores one element of a mesh.
Definition: element.h:107
bool show_edges
true to show edges of mesh
Definition: scalar_view.h:151
bool is_constant
true if the function to be displayed is constant
Definition: scalar_view.h:176
Common definitions for Hermes2D.
::xsd::cxx::tree::buffer< char > buffer
Binary buffer type.
float cont_color[3]
color of contours (RGB)
Definition: scalar_view.h:172
File containing ScalarView class.
bool show_values
true to show values
Definition: scalar_view.h:140
bool do_zoom_to_fit
true to automatically translate the view so that the whole model si displayed
Definition: scalar_view.h:174
static const double znear
Distance of the near clipping plane of the viewing frustum from the camera.
Definition: scalar_view.h:182
Linearizer * lin
LinearizerMultidimensional class responsible for obtaining linearized data.
Definition: scalar_view.h:84
bool lin_updated
true, if lin now contains new_ values
Definition: scalar_view.h:130
LinearizerMultidimensional< ScalarLinearizerDataDimensions< LINEARIZER_DATA_TYPE > > Linearizer
Linearizer for scalar cases - historically called Linearizer.
Definition: linearizer.h:201
bool contours
true to enable drawing of contours
Definition: scalar_view.h:168
static const double zfar
Distance of the Far clipping plane of the viewing frustum from the camera.
Definition: scalar_view.h:184
bool show_aabb
true to show the bounding box
Definition: scalar_view.h:153
Typedefs used throughout the Linearizer functionality.
void set_criterion(LinearizerCriterion criterion)
Definition: linearizer.cpp:68
double cont_orig
contour settings.
Definition: scalar_view.h:170
Node * vn[H2D_MAX_NUMBER_VERTICES]
vertex node pointers
Definition: element.h:134
Abstract class for criterion according to which the linearizer stops dividing elements at some point ...
float edges_color[3]
color of edges
Definition: scalar_view.h:155