graphpaintergl.cpp
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 #include "graphpainter.h"
15 #include "graphwidget.h"
16 #include <QTimer>
17 #include <GL/glu.h>
18 #include <QPainter>
19 
20 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
21  #include <QWindow>
22 #endif
23 
24 using std::min;
25 using std::max;
26 
27 #include<stdio.h>
28 #include <QString>
29 #include <errno.h>
30 
31 #define DEFAULT_FONT_SIZE 12
32 
33 #define checkGLError() \
34 { \
35  GLenum err = glGetError(); \
36  if(err != GL_NO_ERROR) { \
37  switch(err) \
38  { \
39  case GL_INVALID_ENUM: \
40  dbgPrint("GL_INVALID_ENUM"); \
41  break; \
42  case GL_INVALID_VALUE: \
43  dbgPrint("GL_INVALID_VALUE"); \
44  break; \
45  case GL_INVALID_OPERATION: \
46  dbgPrint("GL_INVALID_OPERATION"); \
47  break; \
48  case GL_STACK_OVERFLOW: \
49  dbgPrint("GL_STACK_OVERFLOW"); \
50  break; \
51  case GL_STACK_UNDERFLOW: \
52  dbgPrint("GL_STACK_UNDERFLOW"); \
53  break; \
54  case GL_OUT_OF_MEMORY: \
55  dbgPrint("GL_OUT_OF_MEMORY"); \
56  break; \
57  } \
58  } \
59 }
60 
61 XQGraphPainter::XQGraphPainter(const shared_ptr<XGraph> &graph, XQGraph* item) :
62  m_graph(graph),
63  m_pItem(item),
64  m_selectionStateNow(Selecting),
65  m_selectionModeNow(SelNone),
66  m_listpoints(0),
67  m_listaxes(0),
68  m_listgrids(0),
69  m_listplanemarkers(0),
70  m_listaxismarkers(0),
71  m_bIsRedrawNeeded(true),
72  m_bIsAxisRedrawNeeded(false),
73  m_bTilted(false),
74  m_bReqHelp(false) {
75  item->m_painter.reset(this);
76  graph->iterate_commit([=](Transaction &tr){
77  m_lsnRedraw = tr[ *graph].onUpdate().connectWeakly(
78  shared_from_this(), &XQGraphPainter::onRedraw,
79  XListener::FLAG_MAIN_THREAD_CALL | XListener::FLAG_AVOID_DUP | XListener::FLAG_DELAY_ADAPTIVE);
80  });
81  m_pixel_ratio = m_pItem->devicePixelRatio();
82 }
83 XQGraphPainter::~XQGraphPainter() {
84  m_pItem->makeCurrent();
85 
86  if(m_listplanemarkers) glDeleteLists(m_listplanemarkers, 1);
87  if(m_listaxismarkers) glDeleteLists(m_listaxismarkers, 1);
88  if(m_listgrids) glDeleteLists(m_listgrids, 1);
89  if(m_listaxes) glDeleteLists(m_listaxes, 1);
90  if(m_listpoints) glDeleteLists(m_listpoints, 1);
91 }
92 
93 int
94 XQGraphPainter::windowToScreen(int x, int y, double z, XGraph::ScrPoint *scr) {
95  GLdouble nx, ny, nz;
96  int ret = gluUnProject(x * m_pixel_ratio, (double)m_viewport[3] - y * m_pixel_ratio, z,
97  m_model, m_proj, m_viewport, &nx, &ny, &nz);
98  scr->x = nx;
99  scr->y = ny;
100  scr->z = nz;
101  return (ret != GL_TRUE);
102 }
103 int
104 XQGraphPainter::screenToWindow(const XGraph::ScrPoint &scr, double *x, double *y, double *z) {
105  GLdouble nx, ny, nz;
106  int ret = gluProject(scr.x, scr.y, scr.z, m_model, m_proj, m_viewport, &nx, &ny, &nz);
107  *x = nx / m_pixel_ratio;
108  *y = (m_viewport[3] - ny) / m_pixel_ratio;
109  *z = nz;
110  return (ret != GL_TRUE);
111 }
112 
113 void
114 XQGraphPainter::repaintBuffer(int x1, int y1, int x2, int y2) {
115  if((x1 != x2) || (y1 != y2)) {
116  m_pItem->update();
117  }
118 }
119 void
121  m_bIsRedrawNeeded = true;
122 }
123 
124 void
125 XQGraphPainter::beginLine(double size) {
126  glLineWidth(size);
127  checkGLError();
128  glBegin(GL_LINES);
129 }
130 void
131 XQGraphPainter::endLine() {
132  glEnd();
133  checkGLError();
134 }
135 
136 void
137 XQGraphPainter::beginPoint(double size) {
138  glPointSize(size);
139  checkGLError();
140  glBegin(GL_POINTS);
141 }
142 void
143 XQGraphPainter::endPoint() {
144  glEnd();
145  checkGLError();
146 }
147 void
148 XQGraphPainter::beginQuad(bool ) {
149  glBegin(GL_QUADS);
150  checkGLError();
151 }
152 void
153 XQGraphPainter::endQuad() {
154  glEnd();
155  checkGLError();
156 }
157 
158 void
159 XQGraphPainter::defaultFont() {
160  m_curAlign = 0;
161  m_curFontSize = std::min(14L, std::max(9L,
162  lrint(DEFAULT_FONT_SIZE * m_pItem->height() / m_pItem->logicalDpiY() / 3.5)));
163 }
164 int
166  const XGraph::ScrPoint &start, const XGraph::ScrPoint &dir, const XGraph::ScrPoint &swidth, int sizehint) {
167  XGraph::ScrPoint d = dir;
168  d.normalize();
169  XGraph::ScrPoint s1 = start;
170  double x, y, z;
171  if(screenToWindow(s1, &x, &y, &z)) return -1;
172  XGraph::ScrPoint s2 = s1;
173  d *= 0.001;
174  s2 += d;
175  double x1, y1, z1;
176  if(screenToWindow(s2, &x1, &y1, &z1)) return -1;
177  XGraph::ScrPoint s3 = s1;
178  XGraph::ScrPoint wo2 = swidth;
179  wo2 *= 0.5;
180  s3 += wo2;
181  double x2, y2, z2;
182  if(screenToWindow(s3, &x2, &y2, &z2)) return -1;
183  XGraph::ScrPoint s4 = s1;
184  s4 -= wo2;
185  double x3, y3, z3;
186  if(screenToWindow(s4, &x3, &y3, &z3)) return -1;
187  int align = 0;
188 // width and height, restrict text
189  double w = fabs(x3 - x2), h = fabs(y3 - y2);
190  if( fabs(x - x1) > fabs( y - y1) ) {
191  //dir is horizontal
192  align |= Qt::AlignVCenter;
193  h = min(h, 2 * min(y, m_pItem->height() - y));
194  if( x > x1 ) {
195  align |= Qt::AlignRight;
196  w = x;
197  }
198  else {
199  align |= Qt::AlignLeft;
200  w = m_pItem->width() - x;
201  }
202  }
203  else {
204  //dir is vertical
205  align |= Qt::AlignHCenter;
206  w = min(w, 2 * min(x, m_pItem->width() - x));
207  if( y < y1 ) {
208  align |= Qt::AlignTop;
209  h = m_pItem->height() - y;
210  }
211  else {
212  align |= Qt::AlignBottom;
213  h = y;
214  }
215  }
216  defaultFont();
217  m_curFontSize += sizehint;
218  int fontsize_org = m_curFontSize;
219  m_curAlign = align;
220 
221  {
222  QFont font(m_pItem->font());
223  for(;;) {
224  font.setPointSize(m_curFontSize);
225  QFontMetrics fm(font);
226  QRect bb = fm.boundingRect(str);
227  if(m_curFontSize < fontsize_org - 4) return -1;
228  if((bb.width() < w ) && (bb.height() < h)) break;
229  m_curFontSize--;
230  }
231  }
232  return 0;
233 }
234 void
235 XQGraphPainter::drawText(const XGraph::ScrPoint &p, const XString &str) {
236  {
237  double x,y,z;
238  screenToWindow(p, &x, &y, &z);
239 
240  QFont font(m_pItem->font());
241  font.setPointSize(m_curFontSize);
242  QFontMetrics fm(font);
243  QRect bb = fm.boundingRect(str);
244  if( (m_curAlign & Qt::AlignBottom) ) y -= bb.bottom();
245  if( (m_curAlign & Qt::AlignVCenter) ) y += -bb.bottom() + bb.height() / 2;
246  if( (m_curAlign & Qt::AlignTop) ) y -= bb.top();
247  if( (m_curAlign & Qt::AlignHCenter) ) x -= bb.left() + bb.width() / 2;
248  if( (m_curAlign & Qt::AlignRight) ) x -= bb.right();
249 
250  //draws texts later.
251  Text txt;
252  txt.text = str;
253  txt.x = lrint(x);
254  txt.y = lrint(y);
255  txt.fontsize = m_curFontSize;
256  txt.rgba = m_curTextColor;
257  m_textOverpaint.push_back(txt);
258  }
259 }
260 
261 #define VIEW_NEAR -1.5
262 #define VIEW_FAR 0.5
263 
264 
265 void
266 XQGraphPainter::setInitView() {
267  glLoadIdentity();
268  glOrtho(0.0,1.0,0.0,1.0,VIEW_NEAR,VIEW_FAR);
269 }
270 void
271 XQGraphPainter::viewRotate(double angle, double x, double y, double z, bool init) {
272  m_pItem->makeCurrent();
273  glGetError(); //reset error
274 
275  glMatrixMode(GL_PROJECTION);
276  if(init) {
277  glLoadIdentity();
278  glGetDoublev(GL_PROJECTION_MATRIX, m_proj_rot);
279  setInitView();
280  }
281  if(angle != 0.0) {
282  glLoadIdentity();
283  glTranslated(0.5, 0.5, 0.5);
284  glRotatef(angle, x, y, z);
285  glTranslated(-0.5, -0.5, -0.5);
286  glMultMatrixd(m_proj_rot);
287  glGetDoublev(GL_PROJECTION_MATRIX, m_proj_rot);
288  setInitView();
289  glMultMatrixd(m_proj_rot);
290  }
291  checkGLError();
292  bool ov = m_bTilted;
293  m_bTilted = !init;
294  if(ov != m_bTilted) m_bIsRedrawNeeded = true;
295 
296  m_bIsAxisRedrawNeeded = true;
297 }
298 
299 
300 #define MAX_SELECTION 100
301 
302 double
303 XQGraphPainter::selectGL(int x, int y, int dx, int dy, GLint list,
304  XGraph::ScrPoint *scr, XGraph::ScrPoint *dsdx, XGraph::ScrPoint *dsdy ) {
305  m_pItem->makeCurrent();
306 
307  glGetError(); //reset error
308 
309  GLuint selections[MAX_SELECTION];
310  glGetDoublev(GL_PROJECTION_MATRIX, m_proj);
311  glGetDoublev(GL_MODELVIEW_MATRIX, m_model);
312  glGetIntegerv(GL_VIEWPORT, m_viewport);
313  glSelectBuffer(MAX_SELECTION, selections);
314  glRenderMode(GL_SELECT);
315  glInitNames();
316  glPushName((unsigned int)-1);
317  glMatrixMode(GL_PROJECTION);
318  glPushMatrix();
319  //pick up small region
320  glLoadIdentity();
321  gluPickMatrix((double)(x - dx) * m_pixel_ratio, (double)m_viewport[3] - (y + dy) * m_pixel_ratio,
322  2 * dx * m_pixel_ratio, 2 * dy * m_pixel_ratio, m_viewport);
323  glMultMatrixd(m_proj);
324 
325  glEnable(GL_DEPTH_TEST);
326  glLoadName(1);
327  glCallList(list);
328 
329  glMatrixMode(GL_PROJECTION);
330  glPopMatrix();
331  int hits = glRenderMode(GL_RENDER);
332  double zmin = 1.1;
333  double zmax = -0.1;
334  GLuint *ptr = selections;
335  for (int i = 0; i < hits; i++) {
336  double zmin1 = (double)ptr[1] / (double)0xffffffffu;
337  double zmax1 = (double)ptr[2] / (double)0xffffffffu;
338  int n = ptr[0];
339  ptr += 3;
340  for (int j = 0; j < n; j++) {
341  int k = *(ptr++);
342  if(k != -1) {
343  zmin = min(zmin1, zmin);
344  zmax = max(zmax1, zmax);
345  }
346  }
347  }
348  if((zmin < 1.0) && (zmax > 0.0) ) {
349  windowToScreen(x, y, zmax, scr);
350  windowToScreen(x + 1, y, zmax, dsdx);
351  windowToScreen(x, y + 1, zmax, dsdy);
352  }
353  checkGLError();
354  return zmin;
355 }
356 
357 double
358 XQGraphPainter::selectPlane(int x, int y, int dx, int dy,
359  XGraph::ScrPoint *scr, XGraph::ScrPoint *dsdx, XGraph::ScrPoint *dsdy ) {
360  return selectGL(x, y, dx, dy, m_listplanemarkers, scr, dsdx, dsdy);
361 }
362 double
363 XQGraphPainter::selectAxis(int x, int y, int dx, int dy,
364  XGraph::ScrPoint *scr, XGraph::ScrPoint *dsdx, XGraph::ScrPoint *dsdy ) {
365  return selectGL(x, y, dx, dy, m_listaxismarkers, scr, dsdx, dsdy);
366 }
367 double
368 XQGraphPainter::selectPoint(int x, int y, int dx, int dy,
369  XGraph::ScrPoint *scr, XGraph::ScrPoint *dsdx, XGraph::ScrPoint *dsdy ) {
370  return selectGL(x, y, dx, dy, m_listpoints, scr, dsdx, dsdy);
371 }
372 void
374  initializeOpenGLFunctions();
375 
376  glEnable(GL_MULTISAMPLE);
377 
378  //define display lists etc.:
379  if(m_listplanemarkers) glDeleteLists(m_listplanemarkers, 1);
380  if(m_listaxismarkers) glDeleteLists(m_listaxismarkers, 1);
381  if(m_listgrids) glDeleteLists(m_listgrids, 1);
382  if(m_listaxes) glDeleteLists(m_listaxes, 1);
383  if(m_listpoints) glDeleteLists(m_listpoints, 1);
384  m_listplanemarkers = glGenLists(1);
385  m_listaxismarkers = glGenLists(1);
386  m_listgrids = glGenLists(1);
387  m_listaxes = glGenLists(1);
388  m_listpoints = glGenLists(1);
389  glMatrixMode(GL_PROJECTION);
390  glLoadIdentity();
391  //save model view matrix
392  viewRotate(0.0, 0.0, 0.0, 0.0, true);
393 }
394 void
395 XQGraphPainter::resizeGL ( int width , int height ) {
396  // setup viewport, projection etc.:
397  glMatrixMode(GL_PROJECTION);
398  m_bIsRedrawNeeded = true;
399 // drawLists();
400 }
401 void
403  glGetError(); // flush error
404 
405  GLint depth_func_org;
406  //stores states
407  glGetIntegerv(GL_DEPTH_FUNC, &depth_func_org);
408  glMatrixMode(GL_MODELVIEW);
409  glPushMatrix();
410  m_textOverpaint.clear();
411 
412  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
413  glDepthFunc(GL_LEQUAL);
414 
415  glMatrixMode(GL_PROJECTION);
416  // be aware of retina display.
417  glViewport( 0, 0, (GLint)(m_pItem->width() * m_pixel_ratio),
418  (GLint)(m_pItem->height() * m_pixel_ratio));
419  glGetDoublev(GL_PROJECTION_MATRIX, m_proj);
420  glGetDoublev(GL_MODELVIEW_MATRIX, m_model);
421  glGetIntegerv(GL_VIEWPORT, m_viewport);
422 
423  // Set up the rendering context,
424  glEnable(GL_BLEND);
425 
426  checkGLError();
427 
428  // Ghost stuff.
429  XTime time_started = XTime::now();
430  if(m_bIsRedrawNeeded || m_bIsAxisRedrawNeeded) {
431  m_modifiedTime = time_started;
432  if(m_lastFrame.size())
433  m_updatedTime = time_started;
434  else
435  m_updatedTime = XTime();
436  }
437 
438  Snapshot shot( *m_graph);
439 
440  if(m_bIsRedrawNeeded) {
441  shot = startDrawing();
442 
443  QColor bgc = (QRgb)shot[ *m_graph->backGround()];
444  glClearColor(bgc.redF(), bgc.greenF(), bgc.blueF(), bgc.alphaF());
445 
446  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
447 
448  glMatrixMode(GL_MODELVIEW);
449  glEnable(GL_DEPTH_TEST);
450 
451  checkGLError();
452 
453  glNewList(m_listgrids, GL_COMPILE_AND_EXECUTE);
454  drawOffScreenGrids(shot);
455  glEndList();
456 
457  checkGLError();
458 
459  glNewList(m_listpoints, GL_COMPILE_AND_EXECUTE);
460  drawOffScreenPoints(shot);
461  glEndList();
462 
463  checkGLError();
464 
465 // glDisable(GL_DEPTH_TEST);
466  glNewList(m_listaxes, GL_COMPILE_AND_EXECUTE);
467  drawOffScreenAxes(shot);
468  glEndList();
469 
470  checkGLError();
471 
472  glNewList(m_listaxismarkers, GL_COMPILE);
474  glEndList();
475 
476  checkGLError();
477 
478  glNewList(m_listplanemarkers, GL_COMPILE);
480  glEndList();
481 
482  checkGLError();
483 
484  m_bIsRedrawNeeded = false;
485  m_bIsAxisRedrawNeeded = false;
486  }
487  else {
488  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
489 
490  glMatrixMode(GL_MODELVIEW);
491  glEnable(GL_DEPTH_TEST);
492 
493  glCallList(m_listgrids);
494  glCallList(m_listpoints);
495 // glDisable(GL_DEPTH_TEST);
496  if(1) { //renderText() have to be called every time.
497 // if(m_bIsAxisRedrawNeeded) {
498  glNewList(m_listaxes, GL_COMPILE_AND_EXECUTE);
499  drawOffScreenAxes(shot);
500  glEndList();
501  m_bIsAxisRedrawNeeded = false;
502  }
503  else {
504  glCallList(m_listaxes);
505  }
506  }
507 
508  drawOnScreenObj(shot);
509 
510  glMatrixMode(GL_PROJECTION);
511  glPushMatrix();
512  setInitView();
513  glGetDoublev(GL_PROJECTION_MATRIX, m_proj);
514  glMatrixMode(GL_MODELVIEW);
515 
516  double persist = shot[ *m_graph->persistence()];
517  if(persist > 0) {
518  #define OFFSET 0.1
519  double tau = persist / (-log(OFFSET)) * 0.4;
520  double scale = exp(-(time_started - m_updatedTime)/tau);
521  double offset = -OFFSET*(1.0-scale);
522  bool update = (time_started - m_modifiedTime) < persist;
523  GLint accum;
524  glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accum);
525  checkGLError();
526  //! \todo QGLContext might clear accumration buffer.
527  if(0) {
528  if(update) {
529  glAccum(GL_MULT, scale);
530  checkGLError();
531  glAccum(GL_ACCUM, 1.0 - scale);
532  checkGLError();
533  glAccum(GL_RETURN, 1.0);
534  checkGLError();
535  }
536  else {
537  glAccum(GL_LOAD, 1.0);
538  checkGLError();
539  }
540  }
541  else {
542  m_lastFrame.resize(m_pItem->width() * m_pItem->height() * 4);
543  if(update) {
544  glPixelTransferf(GL_ALPHA_SCALE, scale);
545  checkGLError();
546  glPixelTransferf(GL_ALPHA_BIAS, offset);
547  checkGLError();
548  glRasterPos2i(0,0);
549  checkGLError();
550  glDrawPixels((GLint)m_pItem->width(), (GLint)m_pItem->height(),
551  GL_RGBA, GL_UNSIGNED_BYTE, &m_lastFrame[0]);
552  checkGLError();
553  glPixelTransferf(GL_ALPHA_SCALE, 1.0);
554  checkGLError();
555  glPixelTransferf(GL_ALPHA_BIAS, 0.0);
556  checkGLError();
557  }
558  glReadPixels(0, 0, (GLint)m_pItem->width(), (GLint)m_pItem->height(),
559  GL_RGBA, GL_UNSIGNED_BYTE, &m_lastFrame[0]);
560  checkGLError();
561  }
562  m_updatedTime = time_started;
563  if(update) {
564  QTimer::singleShot(50, m_pItem, SLOT(update()));
565  }
566  }
567  else {
568  m_lastFrame.clear();
569  m_updatedTime = XTime();
570  }
571 
572  drawOnScreenViewObj(shot);
573 
574  glDisable(GL_DEPTH_TEST);
575  glMatrixMode(GL_PROJECTION);
576  glPopMatrix();
577 // glFlush();
578 
579  {
580  //restores states
581  glShadeModel(GL_FLAT);
582  glDisable(GL_CULL_FACE);
583  glDisable(GL_DEPTH_TEST);
584  glDisable(GL_LIGHTING);
585  glDepthFunc(depth_func_org);
586  glMatrixMode(GL_MODELVIEW);
587  glPopMatrix();
588 
589  QPainter qpainter(m_pItem);
590  qpainter.setRenderHint(QPainter::Antialiasing);
591 // qpainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
592  drawTextOverpaint(qpainter);
593  if(m_bReqHelp) {
594  drawOnScreenHelp(shot, &qpainter);
595  drawTextOverpaint(qpainter);
596  }
597  qpainter.end();
598  }
599 }
600 
601 void
602 XQGraphPainter::drawTextOverpaint(QPainter &qpainter) {
603  QFont font(qpainter.font());
604  for(auto it = m_textOverpaint.begin(); it != m_textOverpaint.end(); ++it) {
605  qpainter.setPen(QColor(it->rgba));
606  font.setPointSize(it->fontsize);
607  qpainter.setFont(font);
608  qpainter.drawText(it->x, it->y, it->text);
609  }
610  m_textOverpaint.clear();
611 }

Generated for KAME4 by  doxygen 1.8.3