COPASI API  4.16.103
CQGLNetworkPainter.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010 - 2014 by Pedro Mendes, Virginia Tech Intellectual
2 // Properties, Inc., University of Heidelberg, and The University
3 // of Manchester.
4 // All rights reserved.
5 
6 // Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
7 // Properties, Inc., EML Research, gGmbH, University of Heidelberg,
8 // and The University of Manchester.
9 // All rights reserved.
10 
11 // Copyright (C) 2007 by Pedro Mendes, Virginia Tech Intellectual
12 // Properties, Inc. and EML Research, gGmbH.
13 // All rights reserved.
14 
15 #ifdef WIN32
16 # define _USE_MATH_DEFINES
17 #endif
18 
19 #include <QtCore/QCoreApplication>
20 #include <QtCore/QEvent>
21 #include <QtCore/QFileInfo>
22 #include <QtCore/QList>
23 #include <QtCore/QPoint>
24 #include <QtCore/QRect>
25 #include <QtCore/QSize>
26 #include <QtCore/QString>
27 #include <QtCore/QTimer>
28 
29 #include <QtGui/QAction>
30 #include <QtGui/QBitmap>
31 #include <QtGui/QContextMenuEvent>
32 #include <QtGui/QFontDatabase>
33 #include <QtGui/QFontInfo>
34 #include <QtGui/QGraphicsScene>
35 #include <QtGui/QGraphicsTextItem>
36 #include <QtGui/QMenu>
37 #include <QtGui/QPainter>
38 #include <QtGui/QPixmap>
39 #include <QtGui/QProgressDialog>
40 #include <QtOpenGL/QGLFramebufferObject>
41 
42 #include <cmath>
43 #include <iostream>
44 #include <limits>
45 #include <utility>
46 
47 #include "copasi.h"
49 
50 #include "FontChooser.h"
51 
52 #if (defined WIN32 && !defined log2)
53 C_FLOAT64 log2(const C_FLOAT64 __x)
54 {return log(__x) / M_LN2;}
55 #endif // WIN32
56 
57 #include "CQGLNetworkPainter.h"
58 #include "CQLayoutMainWindow.h"
59 
61 #include "UI/qtUtilities.h"
62 #include "layout/CLayout.h"
65 #include "layoutUI/CDataEntity.h"
66 #include "layoutUI/BezierCurve.h"
67 
68 // TODO check why the arrow heads are off when switching back from size
69 // animation to rectangular view
70 //
71 // TODO implement the possibility to make screenshots of the visible area as
72 // well as the whole network and let the user choose the resolution
73 //
74 // TODO improve zooming (no clue how to do that best)
75 //
76 // TODO fix arrow positioning problems in size animation
77 
78 // TODO change the text rendering or the texture creation. Right now it seems
79 // to work reasonably well, but it could be improved since the text is
80 // sometimes longer or higher than reported by Qt.
81 
82 // below species and speciesreference
83 const float CQGLNetworkPainter::COMPARTMENT_DEPTH = 0.001f;
86 
87 const float CQGLNetworkPainter::SPECIES_DEPTH = 0.005f;
88 const float CQGLNetworkPainter::SPECIES_SHADOW_DEPTH = 0.0049f;
89 const float CQGLNetworkPainter::SPECIES_FRAME_DEPTH = 0.0051f;
90 
91 //below species
93 
94 const GLfloat CQGLNetworkPainter::MIRROR_X[] =
95 {
96  1.0f, 0.0f, 0.0f, 0.0f
97  , 0.0f, -1.0f, 0.0f, 0.0f
98  , 0.0f, 0.0f, 1.0f, 0.0f
99  , 0.0f, 0.0f, 0.0f, 1.0f
100 };
101 
102 const GLfloat CQGLNetworkPainter::MIRROR_Y[] =
103 {
104  -1.0f, 0.0f, 0.0f, 0.0f
105  , 0.0f, 1.0f, 0.0f, 0.0f
106  , 0.0f, 0.0f, 1.0f, 0.0f
107  , 0.0f, 0.0f, 0.0f, 1.0f
108 };
109 
111 
112 CQGLNetworkPainter::CQGLNetworkPainter(const QGLFormat& format, QWidget *parent)
113  : QGLWidget(format, parent),
114  mIsInitialized(false)
115 {
116  initializeGraphPainter(parent);
117 }
118 
120 {
121  std::map<std::string, RGTextureSpec*>::iterator it = labelTextureMap.begin(), endit = labelTextureMap.end();
122 
123  while (it != endit)
124  {
125  delete[] it->second->textureData;
126  delete it->second;
127  ++it;
128  }
129 
130  // delete the node display list
131  glDeleteLists(this->mDisplayLists, 17);
132 }
133 
135 {
137  // convert the node into a display list that is created once and call once
138  // for each node.
139  // this might safe some cpu cycles, especially when the nodes get more fancy.
140  this->mDisplayLists = glGenLists(17);
141  // this list is for the rectangular nodes
142  GLfloat compartmentColor_080[] = {mCompartmentColor[0] + (1.0f - mCompartmentColor[0]) * 0.8f, mCompartmentColor[1] + (1.0f - mCompartmentColor[1]) * 0.8f, mCompartmentColor[2] + (1.0f - mCompartmentColor[2]) * 0.8f};
143  GLfloat speciesColor_080[] = {mSpeciesColor[0] + (1.0f - mSpeciesColor[0]) * 0.8f, mSpeciesColor[1] + (1.0f - mSpeciesColor[1]) * 0.8f, mSpeciesColor[2] + (1.0f - mSpeciesColor[2]) * 0.8f};
144  glNewList(mDisplayLists, GL_COMPILE);
145  // approximate a quarter circle by a triangle fan with 3 triangles
146  glBegin(GL_TRIANGLE_FAN);
147  glColor3fv(compartmentColor_080);
148  glVertex3f(0.0f, 0.4f, COMPARTMENT_DEPTH);
149  glColor3f(1.0f, 1.0f, 1.0f);
150  glVertex3f(0.0f, 0.5f, COMPARTMENT_DEPTH);
151  glColor3f(mCompartmentColor[0] + (1.0f - mCompartmentColor[0]) * 0.9f, mCompartmentColor[1] + (1.0f - mCompartmentColor[1]) * 0.9f, mCompartmentColor[2] + (1.0f - mCompartmentColor[2]) * 0.9f); // 90% value
152  glVertex3f(0.05f, 0.4866f, COMPARTMENT_DEPTH);
153  glColor3f(mCompartmentColor[0] + (1.0f - mCompartmentColor[0]) * 0.973f, mCompartmentColor[1] + (1.0f - mCompartmentColor[1]) * 0.973f, mCompartmentColor[2] + (1.0f - mCompartmentColor[2]) * 0.973f); // 97.32% value
154  glVertex3f(0.0866f, 0.45f, COMPARTMENT_DEPTH);
155  glColor3fv(compartmentColor_080);
156  glVertex3f(0.1f, 0.4f, COMPARTMENT_DEPTH);
157  glEnd();
158  glBegin(GL_POLYGON);
159  glColor3fv(compartmentColor_080);
160  glVertex3f(0.0f, 0.4f, COMPARTMENT_DEPTH);
161  glVertex3f(0.1f, 0.4f, COMPARTMENT_DEPTH);
162  glColor3fv(mCompartmentColor);
163  glVertex3f(0.1f, 0.0f, COMPARTMENT_DEPTH);
164  glVertex3f(0.0f, 0.0f, COMPARTMENT_DEPTH);
165  glEnd();
166  glColor4fv(mFrameColor);
167  glLineWidth(1.0f);
168  // raise the line strip a bit to circumvent clipping errors
169  glDisable(GL_DEPTH_TEST);
170  glBegin(GL_LINE_STRIP);
171  glVertex3f(0.0f, 0.5f, COMPARTMENT_FRAME_DEPTH);
172  glVertex3f(0.05f, 0.4866f, COMPARTMENT_FRAME_DEPTH);
173  glVertex3f(0.0866f, 0.45f, COMPARTMENT_FRAME_DEPTH);
174  glVertex3f(0.1f, 0.4f, COMPARTMENT_FRAME_DEPTH);
175  glVertex3f(0.1f, 0.0f, COMPARTMENT_FRAME_DEPTH);
176  glEnd();
177  glEnable(GL_DEPTH_TEST);
178  glEndList();
179 
180  // now copy the first call list and mirror the copy at the x-axis
181  glNewList(mDisplayLists + 1, GL_COMPILE);
182  glPushMatrix();
183  glTranslatef(0.0f, 0.5f, 0.0f);
184  glCallList(mDisplayLists);
185  // mirror transformation
186  glMultMatrixf(MIRROR_X);
187  glCallList(mDisplayLists);
188  glPopMatrix();
189  glEndList();
190 
191  // next list is the center piece for the compartment glyph
192  glNewList(mDisplayLists + 2, GL_COMPILE);
193  glBegin(GL_POLYGON);
194  glColor3f(1.0f, 1.0f, 1.0f);
195  glVertex3f(0.0f, 1.0f, COMPARTMENT_DEPTH);
196  glVertex3f(1.0f, 1.0f, COMPARTMENT_DEPTH);
197  glColor3fv(mCompartmentColor);
198  glVertex3f(1.0f, 0.5f, COMPARTMENT_DEPTH);
199  glVertex3f(0.0f, 0.5f, COMPARTMENT_DEPTH);
200  glEnd();
201  glBegin(GL_POLYGON);
202  glColor3fv(mCompartmentColor);
203  glVertex3f(0.0f, 0.5f, COMPARTMENT_DEPTH);
204  glVertex3f(1.0f, 0.5f, COMPARTMENT_DEPTH);
205  glColor3f(1.0f, 1.0f, 1.0f);
206  glVertex3f(1.0f, 0.0f, COMPARTMENT_DEPTH);
207  glVertex3f(0.0f, 0.0f, COMPARTMENT_DEPTH);
208  glEnd();
209  glColor4fv(mFrameColor);
210  glLineWidth(1.0f);
211  // raise the lines a bit to circumvent clipping errors
212  glDisable(GL_DEPTH_TEST);
213  glBegin(GL_LINES);
214  glVertex3f(0.0f, 1.0f, COMPARTMENT_FRAME_DEPTH);
215  glVertex3f(1.0f, 1.0f, COMPARTMENT_FRAME_DEPTH);
216  glVertex3f(0.0f, 0.0f, COMPARTMENT_FRAME_DEPTH);
217  glVertex3f(1.0f, 0.0f, COMPARTMENT_FRAME_DEPTH);
218  glEnd();
219  glEnable(GL_DEPTH_TEST);
220  glEndList();
221 
222  // call lists for the species nodes
223  glNewList(mDisplayLists + 3, GL_COMPILE);
224  // approximate a quarter circle by a triangle fan with 3 triangles
225  glBegin(GL_TRIANGLE_FAN);
226  glColor3fv(speciesColor_080);
227  glVertex3f(0.0f, 0.4f, SPECIES_DEPTH);
228  glColor3f(1.0f, 1.0f, 1.0f);
229  glVertex3f(0.0f, 0.5f, SPECIES_DEPTH);
230  glColor3f(mSpeciesColor[0] + (1.0f - mSpeciesColor[0]) * 0.9f, mSpeciesColor[1] + (1.0f - mSpeciesColor[1]) * 0.9f, mSpeciesColor[2] + (1.0f - mSpeciesColor[2]) * 0.9f); // 90% value
231  glVertex3f(0.05f, 0.4866f, SPECIES_DEPTH);
232  glColor3f(mSpeciesColor[0] + (1.0f - mSpeciesColor[0]) * 0.973f, mSpeciesColor[1] + (1.0f - mSpeciesColor[1]) * 0.973f, mSpeciesColor[2] + (1.0f - mSpeciesColor[2]) * 0.973f); // 97.32% value
233  glVertex3f(0.0866f, 0.45f, SPECIES_DEPTH);
234  glColor3fv(speciesColor_080);
235  glVertex3f(0.1f, 0.4f, SPECIES_DEPTH);
236  glEnd();
237  glBegin(GL_POLYGON);
238  glColor3fv(speciesColor_080);
239  glVertex3f(0.0f, 0.4f, SPECIES_DEPTH);
240  glVertex3f(0.1f, 0.4f, SPECIES_DEPTH);
241  glColor4fv(mSpeciesColor);
242  glVertex3f(0.1f, 0.0f, SPECIES_DEPTH);
243  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
244  glEnd();
245  glColor4fv(mFrameColor);
246  glLineWidth(1.0f);
247  // raise the line strip a bit to circumvent clipping errors
248  glDisable(GL_DEPTH_TEST);
249  glBegin(GL_LINE_STRIP);
250  glVertex3f(0.0f, 0.5f, SPECIES_FRAME_DEPTH);
251  glVertex3f(0.05f, 0.4866f, SPECIES_FRAME_DEPTH);
252  glVertex3f(0.0866f, 0.45f, SPECIES_FRAME_DEPTH);
253  glVertex3f(0.1f, 0.4f, SPECIES_FRAME_DEPTH);
254  glVertex3f(0.1f, 0.0f, SPECIES_FRAME_DEPTH);
255  glEnd();
256  glEnable(GL_DEPTH_TEST);
257  glEndList();
258 
259  // now copy the first call list and mirror the copy at the x-axis
260  glNewList(mDisplayLists + 4, GL_COMPILE);
261  glPushMatrix();
262  glTranslatef(0.0f, 0.5f, 0.0f);
263  glCallList(mDisplayLists + 3);
264  // mirror transformation
265  glMultMatrixf(MIRROR_X);
266  glCallList(mDisplayLists + 3);
267  glPopMatrix();
268  glEndList();
269 
270  // next list is the center piece for the species glyph
271  glNewList(mDisplayLists + 5, GL_COMPILE);
272  glBegin(GL_POLYGON);
273  glColor3f(1.0f, 1.0f, 1.0f);
274  glVertex3f(0.0f, 1.0f, SPECIES_DEPTH);
275  glVertex3f(1.0f, 1.0f, SPECIES_DEPTH);
276  glColor3fv(mSpeciesColor);
277  glVertex3f(1.0f, 0.5f, SPECIES_DEPTH);
278  glVertex3f(0.0f, 0.5f, SPECIES_DEPTH);
279  glEnd();
280  glBegin(GL_POLYGON);
281  glColor3fv(mSpeciesColor);
282  glVertex3f(0.0f, 0.5f, SPECIES_DEPTH);
283  glVertex3f(1.0f, 0.5f, SPECIES_DEPTH);
284  glColor3f(1.0f, 1.0f, 1.0f);
285  glVertex3f(1.0f, 0.0f, SPECIES_DEPTH);
286  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
287  glEnd();
288  glColor4fv(mFrameColor);
289  glLineWidth(1.0f);
290  // raise the lines a bit to circumvent clipping errors
291  glDisable(GL_DEPTH_TEST);
292  glBegin(GL_LINES);
293  glVertex3f(0.0f, 1.0f, SPECIES_FRAME_DEPTH);
294  glVertex3f(1.0f, 1.0f, SPECIES_FRAME_DEPTH);
295  glVertex3f(0.0f, 0.0f, SPECIES_FRAME_DEPTH);
296  glVertex3f(1.0f, 0.0f, SPECIES_FRAME_DEPTH);
297  glEnd();
298  glEnable(GL_DEPTH_TEST);
299  glEndList();
300 
301  // display lists for arrow heads (try to be SBGN like)
302 
303  // head for stimulation (unfilled arrow)
304  glNewList(mDisplayLists + 6, GL_COMPILE);
305  glColor4fv(mBackgroundColor);
306  glBegin(GL_POLYGON);
307  glVertex3f(0.0f, 0.0f, SPECIESREFERENCE_DEPTH);
308  glVertex3f(2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
309  glVertex3f(0.0f, -3.0f, SPECIESREFERENCE_DEPTH);
310  glVertex3f(-2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
311  glEnd();
312  glDisable(GL_DEPTH_TEST);
313  glLineWidth(2.0f);
314  glColor4fv(mSpeciesReferenceColor);
315  glBegin(GL_LINE_LOOP);
316  glVertex3f(0.0f, 0.0f, SPECIESREFERENCE_DEPTH);
317  glVertex3f(2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
318  glVertex3f(0.0f, -3.0f, SPECIESREFERENCE_DEPTH);
319  glVertex3f(-2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
320  glEnd();
321  glLineWidth(1.0f);
322  glEnable(GL_DEPTH_TEST);
323  glEndList();
324 
325  // head for transition (filled arrow head)
326  glNewList(mDisplayLists + 7, GL_COMPILE);
327  glBegin(GL_POLYGON);
328  glVertex3f(0.0f, 0.0f, SPECIESREFERENCE_DEPTH);
329  glVertex3f(2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
330  glVertex3f(0.0f, -3.0f, SPECIESREFERENCE_DEPTH);
331  glVertex3f(-2.0f, -5.0f, SPECIESREFERENCE_DEPTH);
332  glEnd();
333  glEndList();
334 
335  // head for inhibition (perpendicular bar)
336  glNewList(mDisplayLists + 8, GL_COMPILE);
337  glBegin(GL_POLYGON);
338  glVertex3f(-3.0f, 0.5f, SPECIESREFERENCE_DEPTH);
339  glVertex3f(3.0f, 0.5f, SPECIESREFERENCE_DEPTH);
340  glVertex3f(3.0f, -0.5f, SPECIESREFERENCE_DEPTH);
341  glVertex3f(-3.0f, -0.5f, SPECIESREFERENCE_DEPTH);
342  glEnd();
343  glEndList();
344 
345  // head for modulation (unfilled diamond)
346  glNewList(mDisplayLists + 9, GL_COMPILE);
347  glColor4fv(mBackgroundColor);
348  glBegin(GL_POLYGON);
349  glVertex3f(0.0f, 0.0f, SPECIESREFERENCE_DEPTH);
350  glVertex3f(2.0f, -3.0f, SPECIESREFERENCE_DEPTH);
351  glVertex3f(0.0f, -6.0f, SPECIESREFERENCE_DEPTH);
352  glVertex3f(-2.0f, -3.0f, SPECIESREFERENCE_DEPTH);
353  glEnd();
354  glDisable(GL_DEPTH_TEST);
355  glColor4fv(mSpeciesReferenceColor);
356  glLineWidth(2.0f);
357  glBegin(GL_LINE_LOOP);
358  glVertex3f(0.0f, 0.0f, SPECIESREFERENCE_DEPTH);
359  glVertex3f(2.0f, -3.0f, SPECIESREFERENCE_DEPTH);
360  glVertex3f(0.0f, -6.0f, SPECIESREFERENCE_DEPTH);
361  glVertex3f(-2.0f, -3.0f, SPECIESREFERENCE_DEPTH);
362  glEnd();
363  glLineWidth(1.0f);
364  glEnable(GL_DEPTH_TEST);
365  glEndList();
366 
367  // display lists for the shadows of the glyphs
368  glNewList(mDisplayLists + 10, GL_COMPILE);
369  // approximate a quarter circle by a triangle fan with 3 triangles
370  glColor4fv(mShadowColor);
371  glBegin(GL_TRIANGLE_FAN);
372  glVertex3f(0.0f, 0.4f, 0.0f);
373  glVertex3f(0.0f, 0.5f, 0.0f);
374  glVertex3f(0.05f, 0.4866f, 0.0f);
375  glVertex3f(0.0866f, 0.45f, 0.0f);
376  glVertex3f(0.1f, 0.4f, 0.0f);
377  glEnd();
378  glBegin(GL_POLYGON);
379  glVertex3f(0.0f, 0.4f, 0.0f);
380  glVertex3f(0.1f, 0.4f, 0.0f);
381  glVertex3f(0.1f, 0.0f, 0.0f);
382  glVertex3f(0.0f, 0.0f, 0.0f);
383  glEnd();
384  glEndList();
385  // now copy the first call list and mirror the copy at the x-axis
386  glNewList(mDisplayLists + 11, GL_COMPILE);
387  glPushMatrix();
388  glTranslatef(0.0f, 0.5f, 0.0f);
389  glCallList(mDisplayLists + 10);
390  // mirror transformation
391  glMultMatrixf(MIRROR_X);
392  glCallList(mDisplayLists + 10);
393  glPopMatrix();
394  glEndList();
395  // next list is the center piece for the shadow
396  glNewList(mDisplayLists + 12, GL_COMPILE);
397  glColor4fv(mShadowColor);
398  glBegin(GL_POLYGON);
399  glVertex3f(0.0f, 1.0f, 0.0f);
400  glVertex3f(1.0f, 1.0f, 0.0f);
401  glVertex3f(1.0f, 0.0f, 0.0f);
402  glVertex3f(0.0f, 0.0f, 0.0f);
403  glEnd();
404  glEndList();
405 
406  // display list to draw a circle with a triangle fan for the animated species
407  glNewList(mDisplayLists + 13, GL_COMPILE);
408  float lowerBound = 0.5;
409  std::vector<std::pair<float, float> >::const_iterator it = mCirclePoints.begin(), endit = mCirclePoints.end();
410  glBegin(GL_TRIANGLE_FAN);
411  glColor4fv(mAnimatedSpeciesColor);
412  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
413  // on the edge we have 50% of the color value
414  glColor4f(mAnimatedSpeciesColor[0]*lowerBound, mAnimatedSpeciesColor[1]*lowerBound, mAnimatedSpeciesColor[2]*lowerBound, 1.0f);
415 
416  while (it != endit)
417  {
418  glVertex3f(it->first, it->second, SPECIES_DEPTH);
419  ++it;
420  }
421 
422  glEnd();
423  glColor4fv(mFrameColor);
424  glDisable(GL_DEPTH_TEST);
425  it = mCirclePoints.begin();
426  glBegin(GL_LINE_LOOP);
427 
428  while (it != endit)
429  {
430  glVertex3f(it->first, it->second, SPECIES_FRAME_DEPTH);
431  ++it;
432  }
433 
434  glEnd();
435  glEnable(GL_DEPTH_TEST);
436  glEndList();
437 
438  // gray circle with color gradient for deactivated species in animation
439  glNewList(mDisplayLists + 14, GL_COMPILE);
440  lowerBound = 0.5;
441  it = mCirclePoints.begin(), endit = mCirclePoints.end();
442  glBegin(GL_TRIANGLE_FAN);
443  glColor4fv(mInanimatedSpeciesColor);
444  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
445  // on the edge we have 50% of the color value
446  glColor4f(mInanimatedSpeciesColor[0]*lowerBound, mInanimatedSpeciesColor[1]*lowerBound, mInanimatedSpeciesColor[2]*lowerBound, 1.0f);
447 
448  while (it != endit)
449  {
450  glVertex3f(it->first, it->second, SPECIES_DEPTH);
451  ++it;
452  }
453 
454  glEnd();
455  glColor4fv(mFrameColor);
456  glDisable(GL_DEPTH_TEST);
457  it = mCirclePoints.begin();
458  glBegin(GL_LINE_LOOP);
459 
460  while (it != endit)
461  {
462  glVertex3f(it->first, it->second, SPECIES_FRAME_DEPTH);
463  ++it;
464  }
465 
466  glEnd();
467  glEnable(GL_DEPTH_TEST);
468  glEndList();
469 
470  // gray circle for constant nodes in animation
471  glNewList(mDisplayLists + 15, GL_COMPILE);
472  lowerBound = 0.5;
473  it = mCirclePoints.begin(), endit = mCirclePoints.end();
474  glBegin(GL_TRIANGLE_FAN);
475  glColor4fv(mConstantSpeciesColor);
476  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
477  // on the edge we have 50% of the color value
478  glColor4f(mConstantSpeciesColor[0]*lowerBound, mConstantSpeciesColor[1]*lowerBound, mConstantSpeciesColor[2]*lowerBound, 1.0f);
479 
480  while (it != endit)
481  {
482  glVertex3f(it->first, it->second, SPECIES_DEPTH);
483  ++it;
484  }
485 
486  glEnd();
487  glColor4fv(mFrameColor);
488  glDisable(GL_DEPTH_TEST);
489  it = mCirclePoints.begin();
490  glBegin(GL_LINE_LOOP);
491 
492  while (it != endit)
493  {
494  glVertex3f(it->first, it->second, SPECIES_FRAME_DEPTH);
495  ++it;
496  }
497 
498  glEnd();
499  glEnable(GL_DEPTH_TEST);
500  glEndList();
501 
502  // gray circle with transparency for circle shadows
503  glNewList(mDisplayLists + 16, GL_COMPILE);
504  it = mCirclePoints.begin(), endit = mCirclePoints.end();
505  glBegin(GL_TRIANGLE_FAN);
506  glColor4fv(mShadowColor);
507  glVertex3f(0.0f, 0.0f, 0.0f);
508 
509  while (it != endit)
510  {
511  glVertex3f(it->first, it->second, 0.0f);
512  ++it;
513  }
514 
515  glEnd();
516  glEndList();
517 }
518 
520 {
521  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
522  glEnable(GL_DEPTH_TEST);
523  glEnable(GL_BLEND);
524  glEnable(GL_LINE_SMOOTH);
525  glEnable(GL_ALPHA_TEST);
526  // glEnable(GL_POINT_SMOOTH);
527  // glEnable(GL_POLYGON_SMOOTH);
528  glShadeModel(GL_SMOOTH);
529 
530  glGenTextures(1, textureNames);
531  this->initializeDisplayLists();
532  mIsInitialized = true;
533 }
534 
536 {
537  // setup viewport, projection etc.:
538  glViewport(0, 0, (GLint)w, (GLint)h);
539 
540  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
541  glLoadIdentity(); // Reset The Projection Matrix
542  gluOrtho2D((GLdouble)mCurrentPositionX,
543  (GLdouble)(mCurrentPositionX + w / mCurrentZoom),
544  (GLdouble)(mCurrentPositionY + h / mCurrentZoom),
545  (GLdouble)mCurrentPositionY); // y: 0.0 is bottom left instead of top left as in SBML
546  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
547 }
548 
550 {
551  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
552  draw();
553  glFlush();
554 }
555 
557 {
558  const CLPoint& mi = mgraphMin;
559  return mi;
560 }
561 
563 {
564  const CLPoint& ma = mgraphMax;
565  return ma;
566 }
567 
568 // set graph size and reset projection to fit new size
570 {
571  mgraphMin.setX(min.getX());
572  mgraphMin.setY(min.getY());
573  mgraphMax.setX(max.getX());
574  mgraphMax.setY(max.getY());
575 }
576 
578 {
579  glLoadIdentity();
580  drawGraph();
581 }
582 
584 {
585  keyMap.clear();
586  compartmentNodeMap.clear();
587  nodeMap.clear();
588  labelNodeMap.clear();
589  nodeArrowMap.clear();
590  nodeCurveMap.clear();
591  viewerNodes.clear();
592  viewerCurves.clear();
593  viewerLabels.clear();
594  curvesWithArrow.clear();
595  int numberOfInvertedCurves = 0;
596  // copy graph to local variables
598  viewerCompartmentNodes = std::vector<std::string>();
599  unsigned int i;
600 
601  for (i = 0; i < compartmentNodes.size(); i++)
602  {
603  std::string nKey = (*compartmentNodes[i]).getKey();
604  std::string oKey = (*compartmentNodes[i]).getModelObjectKey();
605  viewerCompartmentNodes.push_back(nKey);
606  compartmentNodeMap.insert(std::pair<std::string, CCompartmentGraphNode>
607  (nKey,
608  CCompartmentGraphNode(*compartmentNodes[i])));
609  keyMap.insert(std::pair<std::string, std::string>
610  (oKey, nKey));
611  }
612 
614  nodes = lP->getListOfMetaboliteGlyphs();
615  viewerNodes = std::vector<std::string>();
616 
617  for (i = 0; i < nodes.size(); i++)
618  {
619  std::string nKey = (*nodes[i]).getKey();
620  std::string oKey = (*nodes[i]).getModelObjectKey();
621  viewerNodes.push_back(nKey);
622  nodeMap.insert(std::pair<std::string, CGraphNode>
623  (nKey,
624  CGraphNode(*nodes[i])));
625  keyMap.insert(std::pair<std::string, std::string>
626  (oKey, nKey));
627  }
628 
630  reactions = lP->getListOfReactionGlyphs();
631 
632  //now extract curves to draw from reaction
633  viewerCurves = std::vector<CGraphCurve>();
634 
635  //first get reaction arrow
636  for (i = 0; i < reactions.size(); i++)
637  {
638  CGraphCurve curveR = CGraphCurve((reactions[i])->getCurve());
639  viewerCurves.push_back(curveR);
640 
641  CCopasiVector<CLMetabReferenceGlyph> edgesToNodesOfReaction;
642  edgesToNodesOfReaction = reactions[i]->getListOfMetabReferenceGlyphs();
643  unsigned int j2;
644 
645  for (j2 = 0; j2 < edgesToNodesOfReaction.size(); j2++)
646  {
647  CGraphCurve curve = CGraphCurve(edgesToNodesOfReaction[j2]->getCurve());
648  std::string nodeKey = "";
649 
650  if (edgesToNodesOfReaction[j2]->getMetabGlyph() != NULL) // i.e. there is an associated node
651  {
652  nodeKey = std::string(edgesToNodesOfReaction[j2]->getMetabGlyph()->getKey());
653  std::map<std::string, CGraphNode>::iterator itNode;
654  itNode = nodeMap.find(nodeKey);
655 
656  if (itNode != nodeMap.end())
657  {
658  CLBoundingBox box = (*itNode).second.getBoundingBox();
659 
660  if (this->checkCurve(&curve, curveR, box))
661  numberOfInvertedCurves++;
662  }
663  }
664 
665  CLMetabReferenceGlyph::Role r = edgesToNodesOfReaction[j2]->getRole();
666  curve.setRole(r);
667 
668  if (edgesToNodesOfReaction[j2]->getMetabGlyph() != NULL) // if there is an associated species node look whether an arrow has to be created
669  {
670  // if role is product or sideproduct, create arrow for line
672  {
673  // create arrows just for edges to products or sideproducts
674  std::vector<CLLineSegment> segments = curve.getCurveSegments();
675 
676  if (! segments.empty())
677  {
678 
679  CLLineSegment lastSeg = segments[segments.size() - 1];
680 
682  {
683  lastSeg = segments[0];
684  }
685 
686  CLPoint p = lastSeg.getEnd();
687  CArrow *ar;
688 
689  if (lastSeg.isBezier())
690  {
691  CLPoint to = lastSeg.getBase2();
692  CLPoint p = lastSeg.getEnd();
693 
694  // check if the second base point and the endpoint are identical
695  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
696  {
697  // if yes, take the first basepoint
698  to = lastSeg.getBase1();
699 
700  // if they are still identical take the start point because
701  // it is a straight line
702  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
703  {
704  to = lastSeg.getStart();
705  }
706  }
707 
708  CLLineSegment segForArrow;
709 
711  {
712  segForArrow = CLLineSegment(to, lastSeg.getEnd());
713  }
714  else
715  {
716  segForArrow = CLLineSegment(to, lastSeg.getEnd());
717  }
718 
720  {
721  ar = new CArrow(segForArrow, lastSeg.getEnd().getX(), lastSeg.getEnd().getY(), this->mCurrentZoom);
722  }
723  else
724  {
725  ar = new CArrow(segForArrow, lastSeg.getEnd().getX(), lastSeg.getEnd().getY(), this->mCurrentZoom);
726  }
727  }
728  else
729  {
731  {
732  ar = new CArrow(lastSeg, p.getX(), p.getY(), this->mCurrentZoom);
733  }
734  else
735  {
736  ar = new CArrow(lastSeg, p.getX(), p.getY(), this->mCurrentZoom);
737  }
738  }
739 
740  curve.setArrowP(true);
741  curve.setArrow(*ar);
742  delete ar;
743  }
744  }
745 
746  if (nodeKey != "")
747  {
748  nodeCurveMap.insert(std::pair<std::string, CGraphCurve>
749  (nodeKey,
750  curve));
751  }
752  }
753  else
754  {
755  // if no species node is associated with the curve: just store curve
756  viewerCurves.push_back(curve); // just collect curve in order to be shown within the graph
757  }
758  } // end j
759  } // end i (reactions)
760 
762  labels = lP->getListOfTextGlyphs();
763  viewerLabels = std::vector<CLabel>();
764  std::map<std::string, CGraphNode>::iterator itNode;
765 
766  for (i = 0; i < labels.size(); i++)
767  {
768  labelNodeMap.insert(std::pair<std::string, std::string>
769  (labels[i]->getKey(),
770  labels[i]->getGraphicalObjectKey()));
771  std::string s1 = labels[i]->getKey();
772  std::string s2 = labels[i]->getGraphicalObjectKey();
773  viewerLabels.push_back(CLabel(*labels[i]));
774  itNode = nodeMap.find(labels[i]->getGraphicalObjectKey());
775 
776  if (itNode != nodeMap.end())
777  {
778  (*itNode).second.setLabelText(labels[i]->getText());
779  }
780  }
781 
782  CLPoint p1 = CLPoint(0.0, 0.0);
784  this->setGraphSize(p1, p2);
785 }
786 
787 // decides whether the direction of the curve has to be inverted (meaning the order of the line segments, start and end points and base points have to be inverted
789 {
790  bool inverted = false;
791  // first checks whether the start point or the end point of the curve is closer to the center of the box defining the reactant node
792  CLPoint center; // center of bounding box for node
793  center.setX(box.getPosition().getX() + (box.getDimensions().getWidth() / 2.0));
794  center.setY(box.getPosition().getY() + (box.getDimensions().getHeight() / 2.0));
795 
796  // get start and end point of curve (start point of first segment and end point of last segment)
797  std::vector <CLPoint> points = curve->getListOfPoints();
798 
799  if (points.size() > 1)
800  {
801  // if there are at least 2 points
802  CLPoint s = points[0];
803  CLPoint e = points[points.size() - 1];
804  // now compute the distances from these points to the center
805 
806  C_FLOAT64 dist1 = sqrt(((center.getX() - s.getX()) * (center.getX() - s.getX())) +
807  ((center.getY() - s.getY()) * (center.getY() - s.getY())));
808  C_FLOAT64 dist2 = sqrt(((center.getX() - e.getX()) * (center.getX() - e.getX())) +
809  ((center.getY() - e.getY()) * (center.getY() - e.getY())));
810 
811  if (dist1 < dist2)
812  {
813  // if the start point of the curve is closer to the node than the end point
814  // the curve direction should be TOWARDS the node, not away from it
815  curve->invertOrderOfPoints(); // invert the order of the points in the curve
816  inverted = true;
817  }
818  }
819 
820  return inverted;
821 }
822 
824 {
825  // create OpenGL display list
826  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
827  glLoadIdentity();
828  unsigned int i;
829 
830  if ((pParentLayoutWindow != NULL) && this->mLabelShape == CIRCLE &&
832  {
833  drawColorLegend();
834  } // end color mode
835 
836  // draw curves to (reactant) nodes and arrows and circular nodes when in appropriate mode
837  std::map<std::string, CCompartmentGraphNode>::iterator itCompartmentNode;
838  std::map<std::string, CGraphNode>::iterator itNode;
839  std::multimap<std::string, CGraphCurve>::iterator itCurve;
840  std::multimap<std::string, CArrow>::iterator itArrow;
841  std::pair<std::multimap<std::string, CGraphCurve>::iterator, std::multimap<std::string, CGraphCurve>::iterator> curveRangeIt;;
842  std::pair<std::multimap<std::string, CArrow>::iterator, std::multimap<std::string, CArrow>::iterator> arrowRangeIt;
843 
844  for (i = 0; i < viewerCompartmentNodes.size(); i++)
845  {
846  itCompartmentNode = compartmentNodeMap.find(viewerCompartmentNodes[i]);
847 
848  // draw node as rectangle
849  if (itCompartmentNode != compartmentNodeMap.end())
850  {
851  drawNode((*itCompartmentNode).second);
852  }
853  }
854 
855  for (i = 0; i < viewerNodes.size(); i++)
856  {
857  itNode = nodeMap.find(viewerNodes[i]);
858  // draw curves of node
859  curveRangeIt = nodeCurveMap.equal_range(viewerNodes[i]);
860  itCurve = curveRangeIt.first;
861  glColor4fv(mSpeciesReferenceColor);
862 
863  while (itCurve != curveRangeIt.second)
864  {
865  drawEdge((*itCurve).second);
866  itCurve++;
867  }
868 
869  //draw node as a circle
870  if (itNode != nodeMap.end())
871  {
872  drawNode((*itNode).second);
873  }
874  }
875 
876  glColor4fv(mSpeciesReferenceColor);
877 
878  for (i = 0; i < viewerCurves.size(); i++) // draw edges that do not directly belong to a node (reaction curves)
879  {
881  }
882 
883  // NOW DRAW LABELS
884 
885  if (this->mLabelShape == RECTANGLE)
886  {
887  // debug: print font info
888  this->mf.setPointSize(this->mFontsize);
889  const QFont& mfRef = this->mf;
890  QFontInfo fontInfo = QFontInfo(mfRef);
891 
892  // debug end
893  for (i = 0; i < viewerLabels.size(); i++)
894  {
895  // only draw the text if there actually is text
896  if (!viewerLabels[i].getText().empty())
897  {
898  RG_drawStringAt(viewerLabels[i].getText(), static_cast<C_INT32>(viewerLabels[i].getX()), static_cast<C_INT32>(viewerLabels[i].getY()), static_cast<C_INT32>(viewerLabels[i].getWidth()), static_cast<C_INT32>(viewerLabels[i].getHeight()));
899  }
900  }
901  }
902  else
903  {
904  // draw string next to circle (to the right) or in the center if there is enough space
905  for (i = 0; i < viewerLabels.size(); i++)
906  {
907  if (!viewerLabels[i].getText().empty())
908  {
909  C_FLOAT64 tWid = getTextWidth(viewerLabels[i].getText(), mFontname, static_cast<int>(floor(viewerLabels[i].getHeight())));
910  C_FLOAT64 nDiam = 0.0;
911  C_FLOAT64 x, y;
912 
913  const std::string& nodeKey = viewerLabels[i].getGraphicalObjectKey();
914 
915  if (!nodeKey.empty())
916  {
917  std::map<std::string, CGraphNode>::iterator itNodeObj;
918  itNodeObj = nodeMap.find(nodeKey);
919 
920  if (itNodeObj != nodeMap.end())
921  nDiam = (*itNodeObj).second.getSize();
922 
923  C_INT32 xNdCenter = (C_INT32)((*itNodeObj).second.getX() + ((*itNodeObj).second.getWidth() / 2.0));
924  C_INT32 yNdCenter = (C_INT32)(*itNodeObj).second.getY() + ((*itNodeObj).second.getHeight() / 2.0);
925 
927  {
928  x = xNdCenter + (CVisParameters::DEFAULT_NODE_SIZE / 2.0 * this->mCurrentZoom) + 2.0 - ((viewerLabels[i].getWidth() - tWid) / 2.0); // node center + circle radius + 2.0 - texture window overhead
929  y = yNdCenter + (CVisParameters::DEFAULT_NODE_SIZE / 2.0 * this->mCurrentZoom) + 2.0 - ((viewerLabels[i].getHeight()) / 2.0);
930  }
931  else if ((tWid + 4) > nDiam)
932  {
933  // label wider (+ k=4 to avoid crossing circle borders) than size of circle-> place next to circle
934  x = xNdCenter + (nDiam / 2.0) + 2.0 - ((viewerLabels[i].getWidth() - tWid) / 2.0); // + nDiam / 2.0 - ((labelWWid - (*itNodeObj).second.getWidth()) / 2.0); // node center + circle radius - texture window overhead
935  y = yNdCenter + (nDiam / 2.0) + 2.0 - ((viewerLabels[i].getHeight()) / 2.0);
936  }
937  else
938  {
939  // place in center of circle
940  x = xNdCenter - (viewerLabels[i].getWidth() / 2.0); // - ((labelWWid - (*itNodeObj).second.getWidth()) / 2.0);
941  y = yNdCenter;
942  }
943  }
944  else
945  {
946  // if there is no node associated, just take label position
947  x = viewerLabels[i].getX();
948  y = viewerLabels[i].getY();
949  }
950 
951  RG_drawStringAt(viewerLabels[i].getText(), static_cast<C_INT32>(x), static_cast<C_INT32>(y), static_cast<C_INT32>(viewerLabels[i].getWidth()), static_cast<C_INT32>(viewerLabels[i].getHeight()));
952  }
953  }
954  }
955 }
956 
958 {
959  C_INT32 sx = 40; //start at position (sx,sy)
960  C_INT32 sy = 20;
961  C_INT32 w = 120; // size of legend rectangle w x h
962  C_INT32 h = 15;
963 
964  RG_drawStringAt("MIN", 7, sy + 3, 32, 16);
965  RG_drawStringAt("MAX", 165, sy + 3, 32, 16);
966 
967  // the colors should go from RGB 0,0,0 to RGB 200,0,0 to RGB 200,200,0 to RGB
968  // 255,255,0
969  C_INT16 i;
970  QColor col = QColor();
971  // the color range has 456 steps and the legend has 120 pixels
972  // so the step size is 455/120
973  double ratio = 455 / 120;
974  double val;
975 
976  for (i = 0; i <= w; i++)
977  {
978  val = i * ratio;
979 
980  if (val < 200.0)
981  {
982  col.setRgb(0, 0, (int)val);
983  }
984  else if (val < 400.0)
985  {
986  col.setRgb(0, (int)(val - 200.0), 200);
987  }
988  else
989  {
990  col.setRgb(0, 200 + (int)(val - 400.0), 200 + (int)(val - 400.0));
991  }
992 
993  QGLWidget::qglColor(col);
994  // draw colored line in rectangle
995  glBegin(GL_LINES);
996  glVertex2d(i + sx, sy);
997  glVertex2d(i + sx, sy + h);
998  glEnd();
999  }
1000 }
1001 
1002 // draw compartment node as rectangle
1004 {
1005  float width = n.getWidth();
1006  float height = n.getHeight();
1007  float x = n.getX();
1008  float y = n.getY();
1009  float translateX = 0.0f;
1010  float scaledWidth = width - (0.2f * height);
1011 
1012  if (mDrawShadows == true)
1013  {
1014  // first we draw the shadow which we move a bit to the left and somewhat up
1015  // and into the correct depth
1016  glPushMatrix();
1017  // draw one end
1018  glLoadIdentity();
1020  translateX = x;
1021  glPushMatrix();
1022  // additional translation 0.1*width needed because we mirror the
1023  // element
1024  glTranslatef(translateX + height * 0.1, y, 0.0f);
1025  glScalef(height, height, 1.0f);
1026  // mirror it at the y axis
1027  glMultMatrixf(MIRROR_Y);
1028  glCallList(mDisplayLists + 11);
1029  glPopMatrix();
1030  // draw the center
1031  // scale it to the correct width
1032  translateX += 0.1 * height;
1033  glTranslatef(translateX, y, 0.0f);
1034  // the scaling has to be different
1035  glScalef(scaledWidth, height, 1.0f);
1036  glCallList(mDisplayLists + 12);
1037  // draw the other end
1038  glLoadIdentity();
1040  translateX += scaledWidth;
1041  glTranslatef(translateX, y, 0.0f);
1042  glScalef(height, height, 1.0f);
1043  glCallList(mDisplayLists + 11);
1044  // scale the object the the correct size
1045  glPopMatrix();
1046  }
1047 
1048  // now draw the real glyph
1049  glPushMatrix();
1050  // draw one end
1051  glLoadIdentity();
1052  translateX = x;
1053  glPushMatrix();
1054  // additional translation 0.1*width needed because we mirror the
1055  // element
1056  glTranslatef(translateX + height * 0.1, y, 0.0f);
1057  glScalef(height, height, 1.0f);
1058  // mirror it at the y axis
1059  glMultMatrixf(MIRROR_Y);
1060  glCallList(mDisplayLists + 1);
1061  glPopMatrix();
1062  // draw the center
1063  // scale it to the correct width
1064  translateX += 0.1 * height;
1065  glTranslatef(translateX, y, 0.0f);
1066  // the scaling has to be different
1067  glScalef(scaledWidth, height, 1.0f);
1068  glCallList(mDisplayLists + 2);
1069  // draw the other end
1070  glLoadIdentity();
1071  translateX += scaledWidth;
1072  glTranslatef(translateX, y, 0.0f);
1073  glScalef(height, height, 1.0f);
1074  glCallList(mDisplayLists + 1);
1075  // scale the object the the correct size
1076  glPopMatrix();
1077 }
1078 
1079 // draw node as circle
1080 void CQGLNetworkPainter::drawNode(CGraphNode &n) // draw node as filled circle
1081 {
1082  if (this->mLabelShape == CIRCLE)
1083  {
1084  float scaledValue = CVisParameters::DEFAULT_NODE_SIZE * mCurrentZoom;
1086 
1087  if (pParentLayoutWindow != NULL)
1088  {
1089  mappingMode = pParentLayoutWindow->getMappingMode();
1090 
1091  if ((mappingMode == CVisParameters::SIZE_DIAMETER_MODE) ||
1092  (mappingMode == CVisParameters::SIZE_AREA_MODE))
1093  {
1094  scaledValue = n.getSize(); // change of node size only for size mode
1095  }
1096  }
1097 
1098  glColor4fv(mAnimatedSpeciesColor); // red
1099 
1100  double tx = n.getX() + (n.getWidth() / 2.0);
1101  double ty = n.getY() + (n.getHeight() / 2.0);
1102 
1103  if ((mappingMode == CVisParameters::SIZE_DIAMETER_MODE) ||
1104  (mappingMode == CVisParameters::SIZE_AREA_MODE))
1105  {
1107  {
1109  {
1110  // red as default color for all nodes in non-color modes
1111  // which have a substantial range of values (max - min > epsilon)
1112  // and which are not disabled
1113  if (mDrawShadows == true)
1114  {
1115  glPushMatrix();
1117  glScalef(scaledValue, scaledValue, 1.0f);
1118  glCallList(mDisplayLists + 16);
1119  glPopMatrix();
1120  }
1121 
1122  glPushMatrix();
1123  glTranslatef(tx, ty, 0.0f);
1124  glScalef(scaledValue, scaledValue, 1.0f);
1125  glCallList(mDisplayLists + 13);
1126  glPopMatrix();
1127  }
1128  else
1129  {
1130  scaledValue = CVisParameters::DEFAULT_NODE_SIZE; // default node size
1131 
1132  if (mDrawShadows == true)
1133  {
1134  glPushMatrix();
1136  glScalef(scaledValue, scaledValue, 1.0f);
1137  glCallList(mDisplayLists + 16);
1138  glPopMatrix();
1139  }
1140 
1141  glPushMatrix();
1142  glTranslatef(tx, ty, 0.0f);
1143  glScalef(scaledValue, scaledValue, 1.0f);
1144  glCallList(mDisplayLists + 14);
1145  glPopMatrix();
1146  }
1147  }
1148  else
1149  {
1150  scaledValue = CVisParameters::DEFAULT_NODE_SIZE; // node size for disabled nodes
1151 
1152  if (mDrawShadows == true)
1153  {
1154  glPushMatrix();
1156  glScalef(scaledValue, scaledValue, 1.0f);
1157  glCallList(mDisplayLists + 16);
1158  glPopMatrix();
1159  }
1160 
1161  glPushMatrix();
1162  glTranslatef(tx, ty, 0.0f);
1163  glScalef(scaledValue, scaledValue, 1.0f);
1164  glCallList(mDisplayLists + 15);
1165  glPopMatrix();
1166  }
1167  }
1168  // are not scaled and marked in grey
1169  else
1170  {
1171  // color mapping
1172  GLfloat color[3] = {1.0f, 1.0f, 1.0f};
1173 
1175  {
1177  {
1178  float v = (float)n.getSize() * 455.0f; // there are 456 colors in the current gradient and the node sizes are scaled from 0.0 to 1.0 in color mode
1179 
1180  if (v < 200.0)
1181  {
1182  color[0] = 0.0f;
1183  color[1] = 0.0f;
1184  color[2] = v / 255.0f;
1185  }
1186  else if (v < 400)
1187  {
1188  color[0] = 0.0f;
1189  color[1] = (v - 200.0f) / 255.0f;
1190  color[2] = 200.0f / 255.0f;
1191  }
1192  else
1193  {
1194  color[0] = 0.0f;
1195  color[1] = (200.0f + (v - 400.0f)) / 255.0f;
1196  color[2] = (200.0f + (v - 400.0f)) / 255.0f;
1197  }
1198  }
1199  // is disabled
1200  else
1201  {
1202  // set color to white
1203  color[0] = 1.0f;
1204  color[1] = 1.0f;
1205  color[2] = 1.0f;
1206  }
1207  }
1208  // is constant
1209  else
1210  {
1211  // set color to light gray
1212  color[0] = mConstantSpeciesColor[0];
1213  color[1] = mConstantSpeciesColor[1];
1214  color[2] = mConstantSpeciesColor[2];
1215  }
1216 
1217  if (mDrawShadows == true)
1218  {
1219  glPushMatrix();
1221  glScalef(scaledValue, scaledValue, 1.0f);
1222  glCallList(mDisplayLists + 16);
1223  glPopMatrix();
1224  }
1225 
1226  // use out own circle points to draw the circle instead of using glut
1227  // this makes it easier to have color gradients.
1228  glPushMatrix();
1229  glTranslatef((float)tx, (float)ty, 0.0f);
1230  glScalef(scaledValue, scaledValue, 1.0f);
1231  float lowerBound = 0.5;
1232  std::vector<std::pair<float, float> >::const_iterator it = mCirclePoints.begin(), endit = mCirclePoints.end();
1233  glBegin(GL_TRIANGLE_FAN);
1234  glColor3fv(color);
1235  glVertex3f(0.0f, 0.0f, SPECIES_DEPTH);
1236  // on the edge we have 50% of the color value
1237  glColor4f(color[0]*lowerBound, color[1]*lowerBound, color[2]*lowerBound, 1.0f);
1238 
1239  while (it != endit)
1240  {
1241  glVertex3f(it->first, it->second, SPECIES_DEPTH);
1242  ++it;
1243  }
1244 
1245  glEnd();
1246  glColor4fv(mFrameColor);
1247  glDisable(GL_DEPTH_TEST);
1248  it = mCirclePoints.begin();
1249  glBegin(GL_LINE_LOOP);
1250 
1251  while (it != endit)
1252  {
1253  glVertex3f(it->first, it->second, SPECIES_FRAME_DEPTH);
1254  ++it;
1255  }
1256 
1257  glEnd();
1258  glPopMatrix();
1259  }
1260  }
1261  else
1262  {
1263  float width = n.getWidth();
1264  float height = n.getHeight();
1265  float x = n.getX();
1266  float y = n.getY();
1267  float scaledWidth = width - (0.2f * height);
1268  float translateX = 0.0f;
1269 
1270  // first draw shadow if enabled
1271  if (mDrawShadows == true)
1272  {
1273  // first we draw the shadow which we move a bit to the left and somewhat up
1274  // and into the correct depth
1275  glPushMatrix();
1276  // draw one end
1277  glLoadIdentity();
1279  translateX = x;
1280  glPushMatrix();
1281  // additional translation 0.1*width needed because we mirror the
1282  // element
1283  glTranslatef(translateX + height * 0.1, y, 0.0f);
1284  glScalef(height, height, 1.0f);
1285  // mirror it at the y axis
1286  glMultMatrixf(MIRROR_Y);
1287  glCallList(mDisplayLists + 11);
1288  glPopMatrix();
1289  // draw the center
1290  // scale it to the correct width
1291  translateX += 0.1 * height;
1292  glTranslatef(translateX, y, 0.0f);
1293  // the scaling has to be different
1294  glScalef(scaledWidth, height, 1.0f);
1295  glCallList(mDisplayLists + 12);
1296  // draw the other end
1297  glLoadIdentity();
1299  translateX += scaledWidth;
1300  glTranslatef(translateX, y, 0.0f);
1301  glScalef(height, height, 1.0f);
1302  glCallList(mDisplayLists + 11);
1303  // scale the object the the correct size
1304  glPopMatrix();
1305  }
1306 
1307  // now draw the real glyph
1308  glPushMatrix();
1309  // draw one end
1310  glLoadIdentity();
1311  translateX = x;
1312  glPushMatrix();
1313  // additional translation 0.1*width needed because we mirror the
1314  // element
1315  glTranslatef(translateX + height * 0.1, y, 0.0f);
1316  glScalef(height, height, 1.0f);
1317  // mirror it at the y axis
1318  glMultMatrixf(MIRROR_Y);
1319  glCallList(mDisplayLists + 4);
1320  glPopMatrix();
1321  // draw the center
1322  // scale it to the correct width
1323  translateX += 0.1 * height;
1324  glTranslatef(translateX, y, 0.0f);
1325  // the scaling has to be different
1326  glScalef(scaledWidth, height, 1.0f);
1327  glCallList(mDisplayLists + 5);
1328  // draw the other end
1329  glLoadIdentity();
1330  translateX += scaledWidth;
1331  glTranslatef(translateX, y, 0.0f);
1332  glScalef(height, height, 1.0f);
1333  glCallList(mDisplayLists + 4);
1334  // scale the object the the correct size
1335  glPopMatrix();
1336  }
1337 }
1338 
1339 // draw a curve: at the moment just a line from the start point to the end point (for each segment)
1341 {
1342  glLineWidth(2.0);
1343  // Depth test has to be disabled when drawing lines,
1344  // especially if the lines have a thickness greater 1
1345  // otherwise they look ugly because break appear at the end of the lines and
1346  // they seem to be disconnected.
1347  // This seems to be a feature of OpenGL rather than a bug. (See
1348  // http://www.3dsource.de/faq/rasterization.htm)
1349  glDisable(GL_DEPTH_TEST);
1350  std::vector<CLLineSegment> segments = c.getCurveSegments();
1351  size_t i;
1352 
1353  for (size_t k = 0; k < c.getNumCurveSegments(); k++)
1354  {
1355  CLLineSegment seg = segments[k];
1356 
1357  CLPoint startPoint = seg.getStart();
1358  CLPoint endPoint = seg.getEnd();
1359  // for the moment do not take type of curve into account
1360 
1361  if (seg.isBezier())
1362  {
1363  CLPoint base1 = seg.getBase1();
1364  CLPoint base2 = seg.getBase2();
1365  //now paint Bezier as line strip
1366  // use an evaluator since this is probably a lot more efficient
1367  GLfloat controlPts[4][3] =
1368  {
1369  {static_cast<GLfloat>(startPoint.getX()), static_cast<GLfloat>(startPoint.getY()), SPECIESREFERENCE_DEPTH},
1370  {static_cast<GLfloat>(base1.getX()), static_cast<GLfloat>(base1.getY()), SPECIESREFERENCE_DEPTH},
1371  {static_cast<GLfloat>(base2.getX()), static_cast<GLfloat>(base2.getY()), SPECIESREFERENCE_DEPTH},
1372  {static_cast<GLfloat>(endPoint.getX()), static_cast<GLfloat>(endPoint.getY()), SPECIESREFERENCE_DEPTH}
1373  };
1374  // enable the evaluator to draw the cubic Bezier
1375  glMap1f(GL_MAP1_VERTEX_3, 0.0f, 20.0f, 3, 4, &controlPts[0][0]);
1376  glEnable(GL_MAP1_VERTEX_3);
1377  glBegin(GL_LINE_STRIP);
1378 
1379  for (i = 0; i <= 20; ++i)
1380  {
1381  // evaluate the function
1382  glEvalCoord1f((GLfloat)i);
1383  }
1384 
1385  glEnd();
1386  glDisable(GL_MAP1_VERTEX_3);
1387  }
1388  else
1389  {
1390  // just draw a straight line
1391  glBegin(GL_LINE_STRIP);
1392  glVertex3d(startPoint.getX(), startPoint.getY(), SPECIESREFERENCE_DEPTH);
1393  glVertex3d(endPoint.getX(), endPoint.getY(), SPECIESREFERENCE_DEPTH);
1394  glEnd();
1395  }
1396  }
1397 
1398  glLineWidth(1.0);
1399 
1400  if (c.hasArrowP())
1401  {
1402  drawArrow(c.getArrow(), c.getRole());
1403  }
1404 
1405  glEnable(GL_DEPTH_TEST);
1406 }
1407 
1408 double CQGLNetworkPainter::calculateAngle(const CLPoint& endPoint, const CLPoint& startPoint)
1409 {
1410  double deltaX = endPoint.getX() - startPoint.getX();
1411  double deltaY = endPoint.getY() - startPoint.getY();
1412  double angle = 0.0;
1413 
1414  if (deltaY == 0.0)
1415  {
1416  if (deltaX == 0.0)
1417  {
1418  return std::numeric_limits<double>::quiet_NaN();
1419  }
1420 
1421  // we have a horizontal line
1422  if (deltaX < 0.0)
1423  {
1424  angle = 180.0;
1425  }
1426  }
1427  else if (deltaX == 0.0)
1428  {
1429  if (deltaY == 0.0)
1430  {
1431  return std::numeric_limits<double>::quiet_NaN();
1432  }
1433 
1434  // we have a vertical line
1435  if (deltaX < 0.0)
1436  {
1437  angle = 270.0;
1438  }
1439  else
1440  {
1441  angle = 90.0f;
1442  }
1443  }
1444  else
1445  {
1446  double slope = deltaY / deltaX;
1447  angle = 180.0 * atan(slope) / M_PI;
1448 
1449  if (angle < 0.0)
1450  {
1451  angle += 360.0;
1452  }
1453 
1454  // now we have to find out in which quadrant the angle really is
1455  if (deltaX < 0.0)
1456  {
1457  if (deltaY > 0.0) // 2.quadrant; subtract 180
1458  {
1459  angle -= 180;
1460  }
1461  else // 3. quadrant; add 180
1462  {
1463  angle += 180.0;
1464  }
1465  }
1466  }
1467 
1468  return angle;
1469 }
1470 
1472 {
1473  // here we draw the arrow heads depending on the passed in role, a different
1474  // head is drawn
1475  // Since right now the edge width is fixed to a value of 2.0 and does not
1476  // scale with the rest of the diagram, it probably does not make sense to
1477  // scale the arrow heads.
1478 
1479  // we need to calculate the slope of the line at the attachment point
1480 
1481  // first get the two points defining the line segment (curve)
1482  CLPoint p2 = a.getStartOfLine();
1483  CLPoint p1 = a.getEndOfLine();
1484  // p1 and p2 define a line where the arrow peak can be placed onto,
1485  // peak should be at p1, the arrow peak is just a triangle
1486 
1487  // calculate the angle of the line from the x axis
1488  // since all arrow heads go along the y axis, we have to subtract 90° from
1489  // the angle to get the correct rotation angle
1490  double angle;
1491 
1493  {
1494  angle = calculateAngle(p1, p2);
1495  }
1496  else
1497  {
1498  angle = calculateAngle(p2, p1);
1499  }
1500 
1501  if (angle != angle)
1502  {
1503  return; // we got NaN
1504  }
1505 
1506  angle -= 90.0;
1507 
1508  // so we need to rotate the head by angle degrees and move it to
1509  // p1.getX(),p1.getY()
1510 
1511  // first compute parameters of equation of line and point on line where arrow intersects line
1512  // now draw polygon, using vertices from triangle
1513  // now create triangle;
1514  glPushMatrix();
1515  glColor4fv(mSpeciesReferenceColor);
1516 
1518  {
1519  glTranslatef(p1.getX(), p1.getY(), 0.0f);
1520  glRotatef(angle, 0.0f, 0.0f, 1.0f);
1521  glScalef(3.0f, 3.0f, 1.0f);
1522  glCallList(mDisplayLists + 7);
1523  }
1524  else
1525  {
1526  if (role == CLMetabReferenceGlyph::MODIFIER)
1527  {
1528  glTranslatef(p2.getX(), p2.getY(), 0.0f);
1529  glRotatef(angle, 0.0f, 0.0f, 1.0f);
1530  glScalef(3.0f, 3.0f, 1.0f);
1531  glCallList(mDisplayLists + 9);
1532  }
1533  else if (role == CLMetabReferenceGlyph::ACTIVATOR)
1534  {
1535  glTranslatef(p2.getX(), p2.getY(), 0.0f);
1536  glRotatef(angle, 0.0f, 0.0f, 1.0f);
1537  glScalef(3.0f, 3.0f, 1.0f);
1538  glCallList(mDisplayLists + 6);
1539  }
1540  else if (role == CLMetabReferenceGlyph::INHIBITOR)
1541  {
1542  glTranslatef(p2.getX(), p2.getY(), 0.0f);
1543  glRotatef(angle, 0.0f, 0.0f, 1.0f);
1544  glScalef(3.0f, 3.0f, 1.0f);
1545  glCallList(mDisplayLists + 8);
1546  }
1547  }
1548 
1549  // reset the color since some of the call lists change the color
1550  glColor4fv(mSpeciesReferenceColor);
1551  glPopMatrix();
1552 }
1553 
1554 // uses QT
1555 
1557 {
1558  RGTextureSpec* texSpec = getTextureForText(s, mFontname, h);
1559 
1560  if (texSpec == NULL)
1561  {
1562  return;
1563  }
1564 
1565  glPushMatrix();
1566  glColor4fv(mTextColor);
1567  glEnable(GL_TEXTURE_2D);
1568  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1569  glBindTexture(GL_TEXTURE_2D, textureNames[0]);
1570  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
1571  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
1572  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1573  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1574  glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY8, static_cast<int>(texSpec->textureWidth), static_cast<int>(texSpec->textureHeight), 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texSpec->textureData);
1575  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1576  glTranslated(x, y, 0.5);
1577 
1578  double xOffset = (w - texSpec->textWidth + 2) / 2.0;
1579  double yOffset = (h - texSpec->textHeight + texSpec->textYOffset + 2) / 2.0;
1580  xOffset = (xOffset < 0.0) ? 0.0 : xOffset;
1581  yOffset = (yOffset < 0.0) ? 0.0 : yOffset;
1582  double textureXRatio = ((texSpec->textWidth + 2) / texSpec->textureWidth) / ((w - xOffset) / w) * 1.02;
1583  double textureYRatio = ((texSpec->textHeight + 2) / texSpec->textureHeight) / ((h - 2 * yOffset) / h);
1584 
1585  glBegin(GL_POLYGON);
1586  glTexCoord2f(-xOffset / texSpec->textureWidth, -yOffset / texSpec->textureHeight);
1587  glVertex3f(0.0, 0.0, 0.0);
1588 
1589  glTexCoord2f(textureXRatio, -yOffset / texSpec->textureHeight);
1590  glVertex3f(w, 0.0, 0.0);
1591 
1592  glTexCoord2f(textureXRatio, textureYRatio);
1593  glVertex3f(w, h, 0.0);
1594 
1595  glTexCoord2f(-xOffset / texSpec->textureWidth, textureYRatio);
1596  glVertex3f(0.0, h, 0.0);
1597  glEnd();
1598 
1599  glDisable(GL_TEXTURE_2D);
1600  glPopMatrix();
1601 }
1602 
1603 int CQGLNetworkPainter::getTextWidth(const std::string& text, const std::string& fontName, unsigned int fontSize)
1604 {
1605  QFont font(QString(fontName.c_str()), fontSize);
1606  QFontMetrics fontMetrics = QFontMetrics(font);
1607 
1608  QRect rect = fontMetrics.boundingRect(QString(text.c_str()));
1609  int width = rect.width();
1610 
1611  return width;
1612 }
1613 
1615 {
1616  int exponent = static_cast<int>(ceil(log2(width + 2.0)));
1617 
1618  if (exponent < 6)
1619  {
1620  exponent = 6;
1621  }
1622 
1623  width = static_cast<int>(pow(2.0, exponent + 1));
1624  return width;
1625 }
1626 
1628 {
1629  std::map<std::string, RGTextureSpec*>::iterator it = labelTextureMap.begin(), endit = labelTextureMap.end();
1630 
1631  while (it != endit)
1632  {
1633  delete[] it->second->textureData;
1634  delete it->second;
1635  ++it;
1636  }
1637 
1638  labelTextureMap.clear();
1639  unsigned int i = 0;
1640 
1641  for (i = 0; i < viewerLabels.size(); i++)
1642  {
1643  C_INT32 fontSize = mFontsize;
1644  RGTextureSpec* pTexture = RG_createTextureForText(viewerLabels[i].getText(), mFontname, fontSize);
1645  labelTextureMap.insert(std::pair<std::string, RGTextureSpec*>
1646  (viewerLabels[i].getText(),
1647  pTexture));
1648  }
1649 }
1650 
1651 RGTextureSpec* CQGLNetworkPainter::getTextureForText(const std::string& text, const std::string& fontName, unsigned int fontSize)
1652 {
1653  std::map<std::string, RGTextureSpec*>::iterator it;
1654  it = labelTextureMap.find(text);
1655  RGTextureSpec* texSpec = NULL;
1656 
1657  if (it != labelTextureMap.end())
1658  {
1659  texSpec = ((*it).second);
1660  }
1661  else
1662  {
1663  texSpec = RG_createTextureForText(text, fontName, fontSize);
1664  labelTextureMap.insert(std::pair<std::string, RGTextureSpec*>(text, texSpec));
1665  }
1666 
1667  return texSpec;
1668 }
1669 
1670 RGTextureSpec* CQGLNetworkPainter::RG_createTextureForText(const std::string& text, const std::string& fontName, unsigned int fontSize)
1671 {
1672  QFont font(QString(fontName.c_str()), fontSize);
1673  QFontMetrics fontMetrics = QFontMetrics(font);
1674 
1675  QRect rect = fontMetrics.boundingRect(QString(text.c_str()));
1676  int width = rect.width();
1677  int height = rect.height();
1678  int exponent = static_cast<int>(ceil(log2(width + 2.0)));
1679 
1680  if (exponent < 6)
1681  {
1682  exponent = 6;
1683  }
1684 
1685  width = static_cast<int>(pow(2.0, exponent + 1));
1686  exponent = static_cast<int>(ceil(log2(height + 2.0)));
1687 
1688  if (exponent < 6)
1689  {
1690  exponent = 6;
1691  }
1692 
1693  height = static_cast<int>(pow(2.0, exponent + 1));
1694 
1695  QPixmap pixmap(width, height);
1696  pixmap.fill(QColor(255, 255, 255));
1697  QGraphicsScene scene(0.0, 0.0, width, height);
1698  QGraphicsTextItem* pTextItem = scene.addText(QString(text.c_str()), font);
1699  pTextItem->setDefaultTextColor(QColor(0, 0, 0));
1700  // also move one to the right and one down to generate one column
1701  // and one row of transparent pixels
1702  pTextItem->moveBy(1.0, 1.0);
1703  QPainter painter(&pixmap);
1704  scene.render(&painter);
1705 
1706  RGTextureSpec* texture = new RGTextureSpec();
1707  texture->textureData = new GLubyte[height * width];
1708  texture->textureWidth = width;
1709  texture->textureHeight = height;
1710  texture->textWidth = rect.width();
1711  texture->textHeight = rect.height();
1712  QImage image = pixmap.toImage(); // UR
1713  // write the texture to a file to check if they were created correctly
1714  //bool tmpRes=image.save(text+".png","PNG");
1715  //assert(tmpRes == true);
1716  int i, j;
1717  int firstWhitePixel = height;
1718  char pixelValue;
1719  QRgb pixel;
1720 
1721  for (i = 0; i < height; ++i)
1722  {
1723  for (j = 0; j < width; ++j)
1724  {
1725  pixel = image.pixel(j, i);
1726  pixelValue = static_cast<unsigned char>(255 - (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3);
1727  texture->textureData[i * width + j] = pixelValue;
1728 
1729  if (pixelValue != 0)
1730  {
1731  if (firstWhitePixel == height)
1732  {
1733  firstWhitePixel = i;
1734  }
1735  }
1736  }
1737  }
1738 
1739  texture->textYOffset = firstWhitePixel;
1740  // write the actual texture to a file
1741  //texture->save(text+".tga");
1742  return texture;
1743 }
1744 
1745 void CQGLNetworkPainter::drawStringAt(std::string s, C_FLOAT64 x, C_FLOAT64 y, C_FLOAT64 w, C_FLOAT64 h, QColor bgCol)
1746 {
1747  glColor4fv(mTextColor); // black
1748 
1749  QString str(FROM_UTF8(s));
1750 
1751  QFontMetrics mfm = QFontMetrics(mf);
1752  QRect bbox = mfm.boundingRect(FROM_UTF8(s)); // bounding rectangle for text in certain size
1753 
1754  int w2 = round2powN(bbox.width()); // look for smallest w2 = 2^^k with n > w2
1755  int h2 = round2powN(bbox.height() + 2); // look for smallest h2 = 2^^k with n > h2
1756 
1757  while (h2 > h)
1758  {
1759  // reduce font size in order to avoid problems with size of texture image
1760  this->mFontsize--;
1761  this->mFontsizeDouble = (double) this->mFontsize;
1762  mf.setPointSize(this->mFontsize);
1763  const QFont& mfRef = mf;
1764  QFontMetrics mfm = QFontMetrics(mfRef);
1765  bbox = mfm.boundingRect(FROM_UTF8(s));
1766  w2 = round2powN(bbox.width());
1767  h2 = round2powN(bbox.height() + 2);
1768  }
1769 
1770  QRect c(0, 0, w2, h2);
1771 
1772  QPixmap pm(w2, h2);
1773 
1774  pm.fill(bgCol);
1775  QPainter painter2(&pm);
1776  painter2.setPen(Qt::black);
1777  painter2.setFont(mf);
1778  painter2.drawText(c, Qt::AlignCenter, FROM_UTF8(s));
1779  painter2.end();
1780 
1781  QImage img = pm.toImage();
1782  QImage timg = QGLWidget::convertToGLFormat(img);
1783 
1784  glTexImage2D(GL_TEXTURE_2D, 0, 3, timg.width(), timg.height(), 0,
1785  GL_RGBA, GL_UNSIGNED_BYTE, timg.bits());
1786  double xoff = (w - w2) / 2.0;
1787  double yoff = (h - h2) / 2.0;
1788 
1789  xoff = 0;
1790  yoff = 0;
1791 
1792  glRasterPos2f(x + xoff, y + h - yoff);
1793  glDrawPixels(w2, h2, GL_RGBA, GL_UNSIGNED_BYTE, timg.bits());
1794 }
1795 
1797 {
1798  int n = (int)(ceil(d));
1799  int p = 1;
1800  int maxP = 12; // max size of images 2*12
1801 
1802  while ((p <= maxP) && (n > pow(2.0, p)))
1803  p++;
1804 
1805  return (int)pow(2.0, p);
1806 }
1807 
1808 void CQGLNetworkPainter::setItemAnimated(std::string key, bool animatedP)
1809 {
1810  if (!animatedP)
1811  {
1812  setOfDisabledMetabolites.insert(key);
1813  C_FLOAT64 midValue = (pParentLayoutWindow->getMinNodeSize() + pParentLayoutWindow->getMaxNodeSize()) / 2.0; // node size used here is set to mid between min and max node size (for reactants that are not animated)
1814  setConstantNodeSizeForAllSteps(key, midValue);
1815  }
1816  else
1817  {
1818  setOfDisabledMetabolites.erase(key);
1820  }
1821 
1823 }
1824 
1826 {
1827  unsigned int s; // step number
1828  C_FLOAT64 val, val_new;
1829  setOfConstantMetabolites.clear();
1830 
1831  for (s = 0; s < mDataSets.size(); s++) // for all steps
1832  {
1833  CDataEntity& dataSet = mDataSets[s];
1834  unsigned int i;
1835 
1836  for (i = 0; i < viewerNodes.size(); i++) // iterate over string values (node keys)
1837  {
1838  // get old value
1839  val = dataSet.getValueForSpecies(viewerNodes[i]);
1840  C_FLOAT64 a = 0.0, b = 1.0;
1841 
1842  if (pParentLayoutWindow != NULL)
1843  {
1844  if (scaleMode == CVisParameters::INDIVIDUAL_SCALING)
1845  {
1848  }
1849  else // scaleMode == CVisParameters::GLOBAL_SCALING
1850  {
1853  }
1854  }
1855 
1856  C_FLOAT64 val_orig;
1857 
1858  if ((b - a) > CVisParameters::EPSILON)
1859  {
1860  val_orig = dataSet.getOrigValueForSpecies(viewerNodes[i]); // get original value
1861  // now scale value
1862  val_new = newMin + ((val_orig - a) / (b - a) * (newMax - newMin));
1863  }
1864  else
1865  {
1866  // no scaling if differences are too small, just set mid value
1867  val_new = (newMax + newMin) / 2.0;
1868 
1869  if (s == 0) // only insert once into set
1871  }
1872 
1873  dataSet.putValueForSpecies(viewerNodes[i], val_new);
1874  }
1875  }
1876 
1877  // if there is no time course data, we set all values to 0.0
1878  if (mDataSets.size() == 0)
1879  {
1880  CDataEntity dataSet;
1881  unsigned int i;
1882 
1883  for (i = 0; i < viewerNodes.size(); i++) // iterate over string values (node keys)
1884  {
1885  dataSet.putValueForSpecies(viewerNodes[i], 0.0);
1886  }
1887 
1888  mDataSets.push_back(dataSet);
1889  }
1890 }
1891 
1893 {
1894  // this is called if a species which has been disabled in the animation is reenabled
1895  unsigned int s; // step number
1896  C_FLOAT64 val, val_new;
1897  setOfConstantMetabolites.clear();
1898 
1899  for (s = 0; s < mDataSets.size(); s++) // for all steps
1900  {
1901  CDataEntity& dataSet = mDataSets[s];
1902  // get old value
1903  val = dataSet.getValueForSpecies(key);
1904  C_FLOAT64 a = 0.0, b = 1.0;
1905 
1906  if (pParentLayoutWindow != NULL)
1907  {
1908  if (scaleMode == CVisParameters::INDIVIDUAL_SCALING)
1909  {
1910  a = pSummaryInfo->getMinForSpecies(key);
1911  b = pSummaryInfo->getMaxForSpecies(key);
1912  }
1913  else // scaleMode == CVisParameters::GLOBAL_SCALING
1914  {
1917  }
1918  }
1919 
1920  C_FLOAT64 val_orig;
1921 
1922  if ((b - a) > CVisParameters::EPSILON)
1923  {
1924  val_orig = dataSet.getOrigValueForSpecies(key); // get original value
1925  // now scale value
1926  val_new = newMin + ((val_orig - a) / (b - a) * (newMax - newMin));
1927  }
1928  else
1929  {
1930  // no scaling if differences are too small, just set mid value
1931  val_new = (newMax + newMin) / 2.0;
1932 
1933  if (s == 0) // only insert once into set
1934  setOfConstantMetabolites.insert(key);
1935  }
1936 
1937  dataSet.putValueForSpecies(key, val_new);
1938  }
1939 }
1940 
1942 {
1943  unsigned int s; // step number
1944 
1945  for (s = 0; s < mDataSets.size(); s++) // for all steps
1946  {
1947  CDataEntity& dataSet = mDataSets[s];
1948  dataSet.putValueForSpecies(key, val);
1949  }
1950 }
1951 
1953 {
1954  unsigned int s; // step number
1955  setOfConstantMetabolites.clear();
1956 
1957  for (s = 0; s < mDataSets.size(); s++) // for all steps
1958  {
1959  CDataEntity& dataSet = mDataSets[s];
1960  // get old value
1961  dataSet.putValueForSpecies(key, val);
1962  }
1963 }
1964 
1965 // INFO: to rescale an interval [a..b] to another interval [x..y] the following formula is used: (val_old in [a..b]
1966 // val_new = x + ((val_old - a) * (y - x) / (b - a))
1967 
1969 {
1970  unsigned int s; // step number
1971  C_FLOAT64 val, val_new;
1972  setOfConstantMetabolites.clear();
1973 
1974  for (s = 0; s < mDataSets.size(); s++)
1975  {
1976  CDataEntity& dataSet = mDataSets[s];
1977  unsigned int i;
1978  // try to get VisParameters from parent (CQLayoutMainWindow)
1979  C_FLOAT64 minNodeSize = 10;
1980  C_FLOAT64 maxNodeSize = 100;
1981 
1982  if (pParentLayoutWindow != NULL)
1983  {
1985  {
1986  minNodeSize = 0.0;
1987  maxNodeSize = 1.0; // 456 color values from black to red to yellow
1988  }
1989  else
1990  {
1991  minNodeSize = pParentLayoutWindow->getMinNodeSize();
1992  maxNodeSize = pParentLayoutWindow->getMaxNodeSize();
1993  }
1994  }
1995 
1996  for (i = 0; i < viewerNodes.size(); i++) // iterate over string values (node keys)
1997  {
1998  // get old value
1999  val = dataSet.getValueForSpecies(viewerNodes[i]);
2000 
2001  if ((scaleMode == CVisParameters::INDIVIDUAL_SCALING) &&
2002  (pParentLayoutWindow != NULL))
2003  {
2004  // global mode -> individual mode
2005  // first get to original value
2006  C_FLOAT64 orig_value = dataSet.getOrigValueForSpecies(viewerNodes[i]);
2007 
2008  // recalculation of original value
2010  {
2011  // now rescale
2012  val_new = ((orig_value - pSummaryInfo->getMinForSpecies(viewerNodes[i])) *
2013  (maxNodeSize - minNodeSize) /
2015  + minNodeSize;
2016  }
2017  else
2018  {
2019  val_new = (maxNodeSize + minNodeSize) / 2.0;
2020 
2021  if (s == 0) // only insert once into set
2023  }
2024  }
2025  else
2026  {
2027  // individual mode -> global mode
2028  C_FLOAT64 orig_value = dataSet.getOrigValueForSpecies(viewerNodes[i]);
2029 
2030  // first calculate original value
2032  {
2033  // now rescale
2034  val_new = ((orig_value - pSummaryInfo->getMinOverallConcentration()) *
2035  (maxNodeSize - minNodeSize) /
2037  + minNodeSize;
2038  }
2039  else
2040  val_new = (maxNodeSize + minNodeSize) / 2.0;
2041  }
2042 
2043  dataSet.putValueForSpecies(viewerNodes[i], val_new);
2044  }
2045  }
2046 }
2047 
2048 //tries to load data from time series,
2049 //if this is successful true is returned, else false
2051 {
2052  int counter = 0;
2053  bool loadDataSuccessful = false;
2054  assert(CCopasiRootContainer::getDatamodelList()->size() > 0);
2055 
2056  if ((*CCopasiRootContainer::getDatamodelList())[0] != NULL)
2057  {
2058  CTrajectoryTask *ptask = dynamic_cast< CTrajectoryTask * >((*(*CCopasiRootContainer::getDatamodelList())[0]->getTaskList())["Time-Course"]);
2059  const CTimeSeries* pTimeSer = &ptask->getTimeSeries();
2060  CTimeSeries dummyTimeSeries;
2061 
2062  if (pTimeSer->getRecordedSteps() == 0)
2063  {
2064  // create a dummy time series from the current state
2065  dummyTimeSeries.allocate(1);
2066  assert(CCopasiRootContainer::getDatamodelList()->size() > 0);
2068  assert(pDataModel != NULL);
2069  std::vector<CCopasiContainer*> tmpV;
2070  dummyTimeSeries.compile(tmpV, pDataModel);
2071  dummyTimeSeries.output(COutputInterface::DURING);
2072  assert(dummyTimeSeries.getRecordedSteps() == 1);
2073  pTimeSer = &dummyTimeSeries; // point to the dummy time series
2074  }
2075 
2076  if (pTimeSer->getNumVariables() > 0)
2077  {
2078  mDataSets.clear(); // remove old data sets
2079  pSummaryInfo = new CSimSummaryInfo(pTimeSer->getRecordedSteps(), pTimeSer->getNumVariables(),
2080  pTimeSer->getConcentrationData(pTimeSer->getRecordedSteps() - 1, 0) - pTimeSer->getConcentrationData(0, 0));
2081  unsigned int i;
2082  unsigned int t;
2083  C_FLOAT64 val;
2084  std::string name;
2085  std::string objKey;
2086  std::string ndKey;
2087  C_FLOAT64 minR;
2088  C_FLOAT64 maxR;
2089  C_FLOAT64 maxAll = 0.0;
2090 
2091  // now get some info about the data set such as the maximum concentration values for each reactant
2092  for (i = 0; i < pTimeSer->getNumVariables(); i++) // iterate on reactants
2093  {
2096  name = pTimeSer->getTitle(i);
2097  objKey = pTimeSer->getKey(i);
2098  std::map<std::string, std::string>::iterator iter = keyMap.find(objKey);
2099 
2100  if (iter != keyMap.end())
2101  {
2102  // if there is a node (key)
2103  ndKey = (keyMap.find(objKey))->second;
2104 
2105  for (t = 0; t < pTimeSer->getRecordedSteps(); t++) // iterate on time steps t=0..n
2106  {
2107  val = pTimeSer->getConcentrationData(t, i);
2108 
2109  if (val > maxR)
2110  maxR = val;
2111 
2112  if (val < minR)
2113  minR = val;
2114  }
2115 
2116  pSummaryInfo->storeMax(ndKey, maxR);
2117  pSummaryInfo->storeMin(ndKey, minR);
2118 
2119  if (maxR > maxAll)
2120  maxAll = maxR;
2121  }
2122  }
2123 
2125  // now create data sets for visualization/animation
2126  // try to get VisParameters from parent (CQLayoutMainWindow)
2127  C_FLOAT64 minNodeSize = 10;
2128  C_FLOAT64 maxNodeSize = 100;
2129 
2130  if (pParentLayoutWindow != NULL)
2131  {
2132  minNodeSize = pParentLayoutWindow->getMinNodeSize();
2133  maxNodeSize = pParentLayoutWindow->getMaxNodeSize();
2134  }
2135 
2136  for (t = 0; t < pTimeSer->getRecordedSteps(); t++) // iterate on time steps t=0..n
2137  {
2138  CDataEntity dataSet;
2139 
2140  for (i = 0; i < pTimeSer->getNumVariables(); i++) // iterate on reactants
2141  {
2142  objKey = pTimeSer->getKey(i); // object key os SBML species
2143  std::map<std::string, std::string>::iterator iter = keyMap.find(objKey);
2144 
2145  if (iter != keyMap.end())
2146  {
2147  // if there is a node (key)
2148  ndKey = (keyMap.find(objKey))->second; // key of graphical node
2149  val = pTimeSer->getConcentrationData(t, i); // get concentration of species i at time point t
2150  C_FLOAT64 scaledVal;
2151 
2152  // now scale value;
2154  {
2155  minR = pSummaryInfo->getMinForSpecies(ndKey);
2156  maxR = pSummaryInfo->getMaxForSpecies(ndKey);
2157  }
2158  else
2159  {
2160  // == CVisParameters.GLOBAL_SCALING
2163  }
2164 
2165  if ((maxR - minR) > CVisParameters::EPSILON)
2166  scaledVal = minNodeSize +
2167  (((maxNodeSize - minNodeSize) / (maxR - minR))
2168  * (val - minR));
2169  else
2170  scaledVal = (maxNodeSize + minNodeSize) / 2.0;
2171 
2172  // put scaled value in data entity (collection of scaled values for one step)
2173  dataSet.putValueForSpecies(ndKey, scaledVal);
2174  dataSet.putOrigValueForSpecies(ndKey, val);
2175  }
2176  }
2177 
2178  // now collect data set
2179  mDataSets.push_back(dataSet);
2180  counter++;
2181  }
2182 
2183  loadDataSuccessful = true;
2184  }
2185  else
2186  {
2187  }
2188  }
2189 
2190  this->mDataPresentP = loadDataSuccessful;
2191 
2192  if (loadDataSuccessful)
2193  {
2194  // if loading was successful, parent should create data table to show it in its window
2195  if (!mDataSets.empty())
2196  {
2198  }
2199  }
2200 
2201  return loadDataSuccessful;
2202 }
2203 
2205 {
2206  return mDataSets.size();
2207 }
2208 
2210 {
2211  return this->stepShown;
2212 }
2213 
2215 {
2216  this->mLabelShape = CIRCLE;
2217 
2218  if (mDataSets.empty())
2219  {
2220  this->createDataSets(); // load data if this was not done before
2221  }
2222 
2223  // try to get VisParameters from parent (CQLayoutMainWindow)
2224 
2225  C_INT16 stepsPerSecond = 10;
2226 
2227  if (pParentLayoutWindow != NULL)
2228  {
2230  stepsPerSecond = pParentLayoutWindow->getStepsPerSecond();
2231  }
2232 
2233  regularTimer->setSingleShot(false);
2234  regularTimer->start((int)(1000 / stepsPerSecond)); // emit signal in chosen frame rate
2235 }
2236 
2238 {
2239  size_t numberOfSteps = 100;
2240  bool animationRunning = true;
2241 
2242  if (pParentLayoutWindow != NULL)
2243  {
2244  //check whether animation is running
2245  animationRunning = pParentLayoutWindow->getAnimationRunning();
2246  }
2247 
2248  numberOfSteps = getNumberOfSteps();
2249 
2250  if ((stepShown <= numberOfSteps) &&
2251  (animationRunning))
2252  {
2253  // set value in slider
2254  emit stepChanged((int) stepShown);
2255  this->stepShown++;
2256  }
2257  else
2258  {
2259  regularTimer->stop();
2260  emit endOfAnimationReached();
2261  }
2262 }
2263 
2265 {
2266  CDataEntity* pDataSet = NULL;
2267 
2268  if (stepNumber < mDataSets.size())
2269  {
2270  pDataSet = &(mDataSets[stepNumber]);
2271  }
2272 
2273  return pDataSet;
2274 }
2275 
2276 void CQGLNetworkPainter::showStep(size_t stepNumber)
2277 {
2278  this->stepShown = stepNumber;
2279 
2280  if (this->mLabelShape != CIRCLE)
2281  this->mLabelShape = CIRCLE;
2282 
2283  if (stepNumber < mDataSets.size())
2284  {
2285  CDataEntity dataSet = mDataSets[stepNumber];
2286  unsigned int i;
2287 
2288  for (i = 0; i < viewerNodes.size(); i++)
2289  {
2290  if (pParentLayoutWindow != NULL)
2291  {
2292  double val = dataSet.getOrigValueForSpecies(viewerNodes[i]);
2293  // do the scaling here instead of elsewhere
2294  double a, b;
2295 
2297  {
2300  }
2301  else // mScaleMode == CVisParameters::GLOBAL_SCALING
2302  {
2305  }
2306 
2307  if ((b - a) > CVisParameters::EPSILON)
2308  {
2309  val = (val - a) / (b - a);
2310  }
2311  else
2312  {
2313  // no scaling if differences are too small, just set mid value
2314  val = 0.5;
2316  }
2317 
2319  {
2320  // no color mode
2321 
2323  {
2324  if (isnan(val)) // test for NaN
2325  {
2326  std::map<std::string, CGraphNode>::iterator itNodeObj = nodeMap.find(viewerNodes[i]);
2327 
2328  if (itNodeObj != nodeMap.end())
2329  {
2330  }
2331 
2333  }
2334  else
2335  {
2337  {
2340  setNodeSize(viewerNodes[i], min + val * (max - min));
2341  }
2342  else
2343  {
2345  }
2346  }
2347  }
2348  }
2349  else // COLOR_MODE
2350  {
2351  // TODO the call to setNodeSize should not be necessary since the nodes don't change in size during a color coded
2352  // TODO animation, so the arrow heads don't have to be recalculated
2354 
2356  {
2357  if (isnan(val)) // test for NaN
2358  {
2360  }
2361  else
2362  {
2364  }
2365  }
2366  }
2367  }
2368  }
2369  }
2370 
2371  this->drawGraph();
2372 }
2373 
2375 {
2376  std::map<std::string, CGraphNode>::iterator nodeIt;
2377  nodeIt = nodeMap.find(key);
2378 
2379  if (nodeIt != nodeMap.end())
2380  (*nodeIt).second.setSize(val);
2381 }
2382 
2383 // set node sizes according to data set and change curves (meaning end points of curve segments) to nodes
2384 void CQGLNetworkPainter::setNodeSize(std::string key, C_FLOAT64 val)
2385 {
2386  // curves to nodes are changed, arrows are created newly
2387  nodeArrowMap.clear();
2388  std::map<std::string, CGraphNode>::iterator nodeIt;
2389  nodeIt = nodeMap.find(key);
2390 
2391  if (nodeIt != nodeMap.end())
2392  (*nodeIt).second.setSize(val);
2393 
2394  // now adaptCurves pointing to nodes
2395  std::pair<std::multimap<std::string, CGraphCurve>::iterator, std::multimap<std::string, CGraphCurve>::iterator> curveRangeIt;;
2396  curveRangeIt = nodeCurveMap.equal_range(key);
2397  std::multimap<std::string, CGraphCurve>::iterator curveIt;
2398  curveIt = curveRangeIt.first;
2399 
2400  while (curveIt != curveRangeIt.second)
2401  {
2402  CGraphCurve *pCurve = & (*curveIt).second;
2403 
2404  if (pCurve != NULL && pCurve->getNumCurveSegments() > 0)
2405  {
2406  CLLineSegment* pLastSeg = pCurve->getSegmentAt(pCurve->getNumCurveSegments() - 1); // get pointer to last segment
2407 
2408  // move end point of segment along the line from the circle center(=from) to the current end point of the last segment
2409  // so that it lies on the border of the circle
2410  CLPoint to;
2411 
2412  if (pLastSeg->isBezier())
2413  {
2414  to = pLastSeg->getBase2();
2415  CLPoint p = pLastSeg->getEnd();
2416 
2417  // check if the second base point and the endpoint are identical
2418  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2419  {
2420  // if yes, take the first basepoint
2421  to = pLastSeg->getBase1();
2422 
2423  // if they are still identical take the start point because
2424  // it is a straight line
2425  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2426  {
2427  to = pLastSeg->getStart();
2428  }
2429  }
2430  }
2431  else
2432  {
2433  to = pLastSeg->getStart();
2434  }
2435 
2436  CLPoint from = CLPoint((*nodeIt).second.getX() + ((*nodeIt).second.getWidth() / 2.0), (*nodeIt).second.getY() + ((*nodeIt).second.getHeight() / 2.0)); // center of bounding box and also of circle
2437  C_FLOAT64 distance = sqrt(((to.getX() - from.getX()) * (to.getX() - from.getX())) + ((to.getY() - from.getY()) * (to.getY() - from.getY())));
2438 
2439  C_FLOAT64 circleDist = ((*nodeIt).second.getSize() / 2.0) + 4.0; // near border
2440  C_FLOAT64 newX = from.getX() + ((to.getX() - from.getX()) / distance * circleDist);
2441  C_FLOAT64 newY = from.getY() + ((to.getY() - from.getY()) / distance * circleDist);
2442 
2443  pLastSeg->setEnd(CLPoint(newX, newY));
2444  // now insert new arrow in map
2445  CLPoint p = pLastSeg->getEnd();
2446 
2447  if (pCurve->hasArrowP())
2448  {
2449  CLLineSegment ls(to, pLastSeg->getEnd());
2450  CArrow *ar = new CArrow(ls, p.getX(), p.getY(), this->mCurrentZoom);
2451  nodeArrowMap.insert(std::pair<std::string, CArrow>
2452  (key, *ar));
2453  pCurve->setArrow(*ar);
2454  delete ar;
2455  }
2456  }
2457 
2458  curveIt++;
2459  }
2460 }
2461 
2463 {
2464  this->mLabelShape = RECTANGLE;
2465  nodeArrowMap.clear(); // map is filled with new arrows
2466  std::pair<std::multimap<std::string, CGraphCurve>::iterator, std::multimap<std::string, CGraphCurve>::iterator> rangeCurveIt;
2467  std::multimap<std::string, CGraphCurve>::iterator curveIt;
2468  unsigned int i;
2469 
2470  for (i = 0; i < viewerNodes.size(); i++)
2471  {
2472  rangeCurveIt = nodeCurveMap.equal_range(viewerNodes[i]);
2473  std::map<std::string, CGraphNode>::iterator nodeIt = nodeMap.find(viewerNodes[i]); // find all edges belonging to a node
2474 
2475  if (nodeIt != nodeMap.end())
2476  {
2477  curveIt = rangeCurveIt.first;
2478 
2479  while (curveIt != rangeCurveIt.second)
2480  {
2481  this->adaptCurveForRectangles(curveIt, (*nodeIt).second.getBoundingBox());
2482  curveIt++;
2483  }
2484  }
2485  }
2486 
2487  this->drawGraph(); // this function will draw the bounding box for each node
2488 }
2489 
2491 {
2492  CLPoint onpoint;
2493  CLPoint q = r.getPosition();
2494  q.setX(r.getPosition().getX() + r.getDimensions().getWidth()); // q is now top right point of rectangle
2495  CLPoint center; // center of rectangle
2496  center.setX(r.getPosition().getX() + (r.getDimensions().getWidth() / 2.0));
2497  center.setY(r.getPosition().getY() + (r.getDimensions().getHeight() / 2.0)); //
2498 
2499  C_FLOAT64 qAngle = atan((q.getY() - center.getY()) / (q.getX() - center.getX()));
2500  C_FLOAT64 pAngle = atan((p.getY() - center.getY()) / (p.getX() - center.getX()));
2501 
2502  if (fabs(pAngle) < fabs(qAngle))
2503  {
2504  // intersection point is left or right side
2505  if (p.getX() > center.getX()) // right side
2506  onpoint = CLPoint(q.getX(), center.getY());
2507  else // left side
2508  onpoint = CLPoint(r.getPosition().getX(), center.getY());
2509  }
2510  else
2511  {
2512  //intersection point is top or bottom side
2513  if (p.getY() > center.getY()) // top side
2514  onpoint = CLPoint(center.getX(), r.getPosition().getY() + r.getDimensions().getHeight());
2515  else // bottom side
2516  onpoint = CLPoint(center.getX(), r.getPosition().getY());
2517  }
2518 
2519  return onpoint;
2520 }
2521 
2523 {
2524  this->mLabelShape = CIRCLE;
2525 
2526  nodeArrowMap.clear(); // map is filled with new arrows
2527  std::pair<std::multimap<std::string, CGraphCurve>::iterator, std::multimap<std::string, CGraphCurve>::iterator> rangeCurveIt;
2528  std::multimap<std::string, CGraphCurve>::iterator curveIt;
2529  unsigned int i;
2530 
2531  for (i = 0; i < viewerNodes.size(); i++)
2532  {
2533  rangeCurveIt = nodeCurveMap.equal_range(viewerNodes[i]);
2534  std::map<std::string, CGraphNode>::iterator nodeIt = nodeMap.find(viewerNodes[i]); // find all edges belonging to a node
2535 
2536  if (nodeIt != nodeMap.end())
2537  {
2538  curveIt = rangeCurveIt.first;
2539 
2540  while (curveIt != rangeCurveIt.second)
2541  {
2542  this->adaptCurveForCircle(curveIt, (*nodeIt).second.getBoundingBox());
2543  curveIt++;
2544  }
2545  }
2546  }
2547 
2548  this->drawGraph();
2549 }
2550 
2551 // get Point on Circle border on the line from the center of the given rectangle to the given point p
2553 {
2554  CLPoint center; // center of rectangle
2555  center.setX(r.getPosition().getX() + (r.getDimensions().getWidth() / 2.0));
2556  center.setY(r.getPosition().getY() + (r.getDimensions().getHeight() / 2.0));
2557 
2558  C_FLOAT64 distance = sqrt(((p.getX() - center.getX()) * (p.getX() - center.getX())) + ((p.getY() - center.getY()) * (p.getY() - center.getY())));
2559 
2560  C_FLOAT64 onPointX = center.getX() + ((p.getX() - center.getX()) / distance * CVisParameters::DEFAULT_NODE_SIZE / 2.0);
2561  C_FLOAT64 onPointY = center.getY() + ((p.getY() - center.getY()) / distance * CVisParameters::DEFAULT_NODE_SIZE / 2.0);
2562 
2563  return CLPoint(onPointX, onPointY);
2564 }
2565 
2566 // get Point on the line from the center of the given rectangle to the given point p with the distance d to the circle border
2568 {
2569  CLPoint center; // center of rectangle
2570  center.setX(r.getPosition().getX() + (r.getDimensions().getWidth() / 2.0));
2571  center.setY(r.getPosition().getY() + (r.getDimensions().getHeight() / 2.0));
2572 
2573  C_FLOAT64 distance = sqrt(((p.getX() - center.getX()) * (p.getX() - center.getX())) + ((p.getY() - center.getY()) * (p.getY() - center.getY())));
2574 
2575  C_FLOAT64 onPointX = center.getX() + ((p.getX() - center.getX()) / distance * ((CVisParameters::DEFAULT_NODE_SIZE / 2.0) + d));
2576  C_FLOAT64 onPointY = center.getY() + ((p.getY() - center.getY()) / distance * ((CVisParameters::DEFAULT_NODE_SIZE / 2.0) + d));
2577 
2578  return CLPoint(onPointX, onPointY);
2579 }
2580 
2581 // move one or two points of a curve, so that the end point of the curve ends at the circle given by the center of the bounding box (where the diagonals intersect) that is given in the parameters and that has the default size
2582 void CQGLNetworkPainter::adaptCurveForCircle(std::multimap<std::string, CGraphCurve>::iterator it, CLBoundingBox box)
2583 {
2584  CLLineSegment* pLastSeg = (*it).second.getSegmentAt((*it).second.getNumCurveSegments() - 1);
2585  CLPoint pointOnCircle;
2586 
2587  if (pLastSeg->isBezier())
2588  pointOnCircle = getPointNearCircle(box, pLastSeg->getBase2(), 1);
2589  else
2590  pointOnCircle = getPointNearCircle(box, pLastSeg->getStart(), 1);
2591 
2592  pLastSeg->setEnd(pointOnCircle);
2593 
2594  // create corresponding arrow, if necessary and insert it into map
2595  if (((*it).second).hasArrowP())
2596  {
2597  CLPoint p = pLastSeg->getEnd();
2598  CArrow *ar;
2599 
2600  if (pLastSeg->isBezier())
2601  {
2602  CLPoint to = pLastSeg->getBase2();
2603  CLPoint p = pLastSeg->getEnd();
2604 
2605  // check if the second base point and the endpoint are identical
2606  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2607  {
2608  // if yes, take the first basepoint
2609  to = pLastSeg->getBase1();
2610 
2611  // if they are still identical take the start point because
2612  // it is a straight line
2613  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2614  {
2615  to = pLastSeg->getStart();
2616  }
2617  }
2618 
2619  CLLineSegment segForArrow = CLLineSegment(to, pLastSeg->getEnd());
2620  ar = new CArrow(segForArrow, pLastSeg->getEnd().getX(), pLastSeg->getEnd().getY(), this->mCurrentZoom);
2621  }
2622  else
2623  {
2624  ar = new CArrow(*pLastSeg, p.getX(), p.getY(), this->mCurrentZoom);
2625  }
2626 
2627  nodeArrowMap.insert(std::pair<std::string, CArrow>
2628  ((*it).first, *ar));
2629  ((*it).second).setArrowP(true);
2630  ((*it).second).setArrow(*ar);
2631  }
2632 }
2633 
2634 // move one or two points of a curve, so that the end point of the curve ends at the box given in the parameters
2635 void CQGLNetworkPainter::adaptCurveForRectangles(std::multimap<std::string, CGraphCurve>::iterator it, CLBoundingBox box)
2636 {
2637  // while (it != nodeCurveMap.end()){
2638  CLLineSegment* pLastSeg = (*it).second.getSegmentAt((*it).second.getNumCurveSegments() - 1);
2639  CLPoint pointOnRect;
2640 
2641  if (pLastSeg->isBezier())
2642  pointOnRect = getPointOnRectangle(box, pLastSeg->getBase2());
2643  else
2644  pointOnRect = getPointOnRectangle(box, pLastSeg->getStart());
2645 
2646  pLastSeg->setEnd(pointOnRect);
2647 
2648  // create corresponding arrow, if necessary and insert it into map
2649  CLPoint p = pLastSeg->getEnd();
2650 
2651  if (((*it).second).hasArrowP())
2652  {
2653  CArrow *ar;
2654 
2655  if (pLastSeg->isBezier())
2656  {
2657  CLPoint to = pLastSeg->getBase2();
2658  CLPoint p = pLastSeg->getEnd();
2659 
2660  // check if the second base point and the endpoint are identical
2661  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2662  {
2663  // if yes, take the first basepoint
2664  to = pLastSeg->getBase1();
2665 
2666  // if they are still identical take the start point because
2667  // it is a straight line
2668  if (fabs(p.getX() - to.getX() + p.getY() - to.getY()) < 1e-8)
2669  {
2670  to = pLastSeg->getStart();
2671  }
2672  }
2673 
2674  CLLineSegment segForArrow = CLLineSegment(to, pLastSeg->getEnd());
2675  ar = new CArrow(segForArrow, pLastSeg->getEnd().getX(), pLastSeg->getEnd().getY(), this->mCurrentZoom);
2676  }
2677  else
2678  {
2679  ar = new CArrow(*pLastSeg, p.getX(), p.getY(), this->mCurrentZoom);
2680  }
2681 
2682  nodeArrowMap.insert(std::pair<std::string, CArrow>
2683  ((*it).first, *ar));
2684  ((*it).second).setArrowP(true);
2685  ((*it).second).setArrow(*ar);
2686  }
2687 }
2688 
2689 // looks for the best point to make a line between a given point p and a rectangle r.
2690 // The point to connect to should always lie on the border of the rectangle and, more specifically
2691 // on the middle of one of the border lines
2692 
2694 {
2695  zoomInAction = new QAction("Zoom in", this);
2696  zoomInAction->setShortcut(Qt::CTRL + Qt::Key_P);
2697  connect(zoomInAction, SIGNAL(triggered()), this, SLOT(zoomIn()));
2698 
2699  zoomOutAction = new QAction("Zoom out", this);
2700  zoomOutAction->setShortcut(Qt::CTRL + Qt::Key_M);
2701  connect(zoomOutAction, SIGNAL(triggered()), this, SLOT(zoomOut()));
2702 
2703  setFontSizeAction = new QAction("Set Font Size", this);
2704  setFontSizeAction->setShortcut(Qt::CTRL + Qt::Key_F);
2705  connect(setFontSizeAction, SIGNAL(triggered()), this, SLOT(setFontSize()));
2706 }
2707 
2709 {
2710  FontChooser *fCh = new FontChooser(this);
2711  fCh->exec();
2712 }
2713 
2715 {
2716  emit signalZoomIn();
2717 }
2718 
2720 {
2721  emit signalZoomOut();
2722 }
2723 
2725 {
2726  this->zoom(zoomFactor);
2727 }
2728 
2730 {
2731  this->mCurrentZoom *= zoomFactor;
2732 
2733  CLPoint cMax = CLPoint(this->mgraphMax.getX() * zoomFactor, this->mgraphMax.getY() * zoomFactor);
2734 
2735  if (pParentLayoutWindow != NULL)
2736  {
2741  unsigned int i;
2742 
2743  if ((mDataPresentP) && (pParentLayoutWindow->getMappingMode() != CVisParameters::COLOR_MODE)) // only rescale data set values in size mode and when data to be rescaled is present
2744  {
2746  }
2747 
2748  //scale node sizes if not in color mode
2749  for (i = 0; i < this->viewerNodes.size(); i++)
2750  {
2751  std::map<std::string, CGraphNode>::iterator nodeIt;
2752  nodeIt = nodeMap.find(viewerNodes[i]);
2753 
2754  if (nodeIt != nodeMap.end())
2755  (*nodeIt).second.scale(zoomFactor, (pParentLayoutWindow->getMappingMode() != CVisParameters::COLOR_MODE)); // change position in any way, but size only when not in color mode
2756  }
2757 
2758  //scale curves not directly pointing to a reactant/species node
2759  for (i = 0; i < viewerCurves.size(); i++)
2760  {
2761  this->viewerCurves[i].scale(zoomFactor);
2762  }
2763 
2764  //scale curves that are associated with a reactant/species node (i.e. directly points to it)
2765  for (i = 0; i < viewerNodes.size(); i++)
2766  {
2767  std::pair<std::multimap<std::string, CGraphCurve>::iterator, std::multimap<std::string, CGraphCurve>::iterator> curveRangeIt;
2768  std::multimap<std::string, CGraphCurve>::iterator curveIt;
2769 
2770  curveRangeIt = nodeCurveMap.equal_range(viewerNodes[i]);
2771  curveIt = curveRangeIt.first;
2772 
2773  while (curveIt != curveRangeIt.second)
2774  {
2775  ((*curveIt).second).scale(zoomFactor); // scale curve
2776  curveIt++;
2777  }
2778  }
2779 
2780  // common font name and size for all labels are stored in this class
2781  // each label size is always computed from the labels original size value
2782  // and scaled by currentZoom (which is the product of all zoomFactors applied so far)
2783  this->mFontsizeDouble = this->mFontsizeDouble * zoomFactor;
2784  this->mFontsize = (int)this->mFontsizeDouble;
2785 
2786  for (i = 0; i < viewerLabels.size(); i++)
2787  {
2789  this->viewerLabels[i].scale(mCurrentZoom);
2790  else
2791  {
2792  if ((this->viewerLabels[i].getOrigHeight() * mCurrentZoom) >= MIN_HEIGHT)
2793  this->viewerLabels[i].scale(mCurrentZoom);
2794  else
2795  {
2796  this->mFontsizeDouble = (double) MIN_HEIGHT;
2797  this->mFontsize = MIN_HEIGHT;
2798  this->viewerLabels[i].adaptToHeight(MIN_HEIGHT);
2799  this->viewerLabels[i].scalePosition(zoomFactor);
2800  }
2801  }
2802  }
2803 
2804  for (i = 0; i < viewerNodes.size(); i++)
2805  {
2806  std::pair<std::multimap<std::string, CArrow>::iterator, std::multimap<std::string, CArrow>::iterator> arrowRangeIt;
2807  std::multimap<std::string, CArrow>::iterator arrowIt;
2808  arrowRangeIt = nodeArrowMap.equal_range(viewerNodes[i]);
2809  arrowIt = arrowRangeIt.first;
2810 
2811  while (arrowIt != arrowRangeIt.second)
2812  {
2813  (*arrowIt).second.scale(zoomFactor); //scale arrow
2814  arrowIt++;
2815  }
2816  }
2817  }
2818 
2820  this->drawGraph();
2821 }
2822 
2824 {
2825  this->mFontsizeDouble = fs;
2826  this->mFontsize = (int)this->mFontsizeDouble;
2827 
2828  unsigned int i;
2829 
2830  for (i = 0; i < viewerLabels.size(); i++)
2831  {
2832  this->viewerLabels[i].adaptToHeight(fs);
2833  }
2834 
2836  this->drawGraph();
2837  this->update();
2838 }
2839 
2841 {
2842  return this->grabFrameBuffer();
2843 }
2844 
2845 void CQGLNetworkPainter::contextMenuEvent(QContextMenuEvent *cme)
2846 {
2847  QMenu *pContextMenu = new QMenu();
2848  pContextMenu->addAction(this->zoomInAction);
2849  pContextMenu->addAction(this->zoomOutAction);
2850  pContextMenu->addAction(this->setFontSizeAction);
2851  pContextMenu->popup(cme->globalPos());
2852  delete pContextMenu;
2853 }
2854 
2856 {
2857  glLoadIdentity();
2858  glTranslatef(10.0f, 10.0f, -1.0f);
2859  glBegin(GL_TRIANGLES); // Drawing Using Triangles
2860  glColor3f(0.0f, 0.0f, 1.0f);
2861  glVertex3f(0.0f, 10.0f, 0.0f); // Top
2862  glVertex3f(-10.0f, -10.0f, 0.0f); // Bottom Left
2863  glVertex3f(10.0f, -10.0f, 0.0f); // Bottom Right
2864  glEnd();
2865 
2866  glTranslatef(3.0f, 0.0f, 0.0f);
2867  glBegin(GL_QUADS); // Draw A Quad
2868  glColor3f(1.0f, 0.0f, 0.0f);
2869  glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
2870  glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
2871  glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
2872  glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
2873  glEnd(); // Done Drawing The Quad
2874 
2875  glTranslatef(3.5f, 0.0f, 0.0f);
2876  glBegin(GL_POLYGON); // Ein Polygon (in diesem Falle ein Achteck.)
2877  // jede Ecke bekommt eine andere Farbe
2878  glColor3f(1.0f, 0.0f, 0.0f); // rot
2879  glVertex3f(-0.5f, 1.5f, 0.0f); // obere Ecke links
2880  glVertex3f(0.5f, 1.5f, 0.0f); // obere Ecke rechts
2881 
2882  glColor3f(0.0f, 0.0f, 1.0f); // blau
2883  glVertex3f(1.5f, 0.5f, 0.0f); // rechte Ecke oben
2884  glVertex3f(1.5f, -0.5f, 0.0f); // rechte Ecke unten
2885 
2886  glColor3f(0.0f, 1.0f, 0.0f); // gruen
2887  glVertex3f(0.5f, -1.5f, 0.0f); // untere Ecke rechts
2888  glVertex3f(-0.5f, -1.5f, 0.0f); // untere Ecke links
2889 
2890  glColor3f(1.0f, 1.0f, 0.0f); // gelb
2891  glVertex3f(-1.5f, -0.5f, 0.0f); // linke Ecke unten
2892  glVertex3f(-1.5f, 0.5f, 0.0f); // linke Ecke oben
2893  glEnd(); // Zeichenaktion beenden
2894 }
2895 
2897 {
2898  if (this->mLabelShape == CIRCLE)
2899  {
2900  return true;
2901  }
2902  else
2903  {
2904  return false;
2905  }
2906 }
2907 
2909 {
2911 
2912  mSpeciesColor[0] = 0.824f;
2913  mSpeciesColor[1] = 0.824f;
2914  mSpeciesColor[2] = 0.902f;
2915  mSpeciesColor[3] = 1.0f;
2916 
2917  mCompartmentColor[0] = 0.737f;
2918  mCompartmentColor[1] = 0.792f;
2919  mCompartmentColor[2] = 0.729f;
2920  mCompartmentColor[3] = 1.0f;
2921 
2922  mShadowColor[0] = 0.2f;
2923  mShadowColor[1] = 0.2f;
2924  mShadowColor[2] = 0.2f;
2925  mShadowColor[3] = 0.6f;
2926 
2927  mSpeciesReferenceColor[0] = 0.3f;
2928  mSpeciesReferenceColor[1] = 0.3f;
2929  mSpeciesReferenceColor[2] = 0.3f;
2930  mSpeciesReferenceColor[3] = 1.0f;
2931 
2932  mTextColor[0] = 0.0f;
2933  mTextColor[1] = 0.0f;
2934  mTextColor[2] = 0.0f;
2935  mTextColor[3] = 1.0f;
2936 
2937  mFrameColor[0] = 0.1f;
2938  mFrameColor[1] = 0.1f;
2939  mFrameColor[2] = 0.1f;
2940  mFrameColor[3] = 1.0f;
2941 
2942  mBackgroundColor[0] = 1.0f;
2943  mBackgroundColor[1] = 1.0f;
2944  mBackgroundColor[2] = 1.0f;
2945  mBackgroundColor[3] = 1.0f;
2946 
2947  mAnimatedSpeciesColor[0] = 1.0f;
2948  mAnimatedSpeciesColor[1] = 0.0f;
2949  mAnimatedSpeciesColor[2] = 0.0f;
2950  mAnimatedSpeciesColor[3] = 1.0f;
2951 
2952  mInanimatedSpeciesColor[0] = 0.75f;
2953  mInanimatedSpeciesColor[1] = 0.75f;
2954  mInanimatedSpeciesColor[2] = 1.0f;
2955  mInanimatedSpeciesColor[3] = 1.0f;
2956 
2957  mConstantSpeciesColor[0] = 0.7f;
2958  mConstantSpeciesColor[1] = 0.7f;
2959  mConstantSpeciesColor[2] = 0.7f;
2960  mConstantSpeciesColor[3] = 1.0f;
2961 
2964 
2965  // the species probably need a smaller shadow offset
2966  mSpeciesShadowXOffset = 2.0f;
2967  mSpeciesShadowYOffset = 2.0f;
2968 
2969  mDrawShadows = true;
2970 
2971  mNumCirclePoints = 30;
2973 
2974  std::vector<std::pair<float, float> > mCirclePoints;
2975 
2976  mCurrentZoom = 1.0;
2977  mCurrentPositionX = 0.0;
2978  mCurrentPositionY = 0.0;
2980  mgraphMin = CLPoint(0.0, 0.0);
2981  mgraphMax = CLPoint(250.0, 250.0);
2982  //mFontname = "Helvetica";
2983  mFontname = "Arial";
2984  mFontsize = 12;
2985  mFontsizeDouble = 12.0; // to avoid rounding errors due to zooming in and out
2986  mDataPresentP = false;
2987  preserveMinLabelHeightP = true;
2988 
2989  mf = QFont(FROM_UTF8(mFontname));
2990  mf.setPointSize(this->mFontsize);
2991  const QFont& mfRef = mf;
2992  QFontMetrics mfm = QFontMetrics(mfRef);
2993 
2994  // parent structure: glPainter -> CQGLViewport -> splitter ->
2995  // vbox -> mainWindow
2996  QWidget *ancestor = parent->parentWidget();
2997 
2998  while (ancestor && dynamic_cast<CQLayoutMainWindow*>(ancestor) == NULL)
2999  {
3000  ancestor = ancestor->parentWidget();
3001  }
3002 
3003  assert(ancestor != NULL);
3004  connect(this, SIGNAL(stepChanged(int)), ancestor, SLOT(changeStepValue(int)));
3005  connect(this, SIGNAL(endOfAnimationReached()), ancestor, SLOT(endOfAnimationReached()));
3006  regularTimer = new QTimer(this);
3007  connect(regularTimer, SIGNAL(timeout()), this, SLOT(triggerAnimationStep()));
3008 
3009  CQLayoutMainWindow * tmp = dynamic_cast<CQLayoutMainWindow *>(ancestor);
3010  assert(tmp);
3011 
3012  if (tmp)
3013  pParentLayoutWindow = tmp;
3014  else
3015  pParentLayoutWindow = NULL;
3016 
3017  stepShown = 0;
3018  createActions();
3019 }
3020 
3022 {
3023  std::map<std::string, CGraphNode>::iterator nodeIt;
3024  nodeIt = nodeMap.begin();
3025 
3026  while (nodeIt != nodeMap.end())
3027  {
3028  nodeIt++;
3029  }
3030 }
3031 
3033 {
3034  //std::map<std::string, CGraphNode>::iterator itNodeObj = nodeMap.find(key);
3035 }
3036 
3037 std::string CQGLNetworkPainter::getNameForNodeKey(std::string key)
3038 {
3039  std::string s = "UNKNOWN";
3040  std::map<std::string, CGraphNode>::iterator itNodeObj = nodeMap.find(key);
3041 
3042  if (itNodeObj != nodeMap.end())
3043  s = (*itNodeObj).second.getLabelText();
3044 
3045  return s;
3046 }
3047 
3049 {
3050  if (i < static_cast< int >(viewerNodes.size()))
3051  return viewerNodes[i];
3052  else
3053  return "";
3054 }
3055 
3057 {
3058  QFontDatabase fdb;
3059  QStringList families = fdb.families();
3060 
3061  for (QStringList::Iterator f = families.begin(); f != families.end(); ++f)
3062  {
3063  QString family = *f;
3064  QStringList styles = fdb.styles(family);
3065 
3066  for (QStringList::Iterator s = styles.begin(); s != styles.end(); ++s)
3067  {
3068  QString style = *s;
3069  QString dstyle = "\t" + style + " (";
3070  QList<int> smoothies = fdb.smoothSizes(family, style);
3071 
3072  for (QList<int>::iterator points = smoothies.begin();
3073  points != smoothies.end(); ++points)
3074  {
3075  dstyle += QString::number(*points) + " ";
3076  }
3077 
3078  dstyle = dstyle.left(dstyle.length() - 1) + ")";
3079  }
3080  }
3081 }
3082 
3084 {
3085  if (zoom != this->mCurrentZoom)
3086  {
3087  this->mCurrentZoom = zoom;
3088 
3089  if (this->isVisible())
3090  {
3091  this->updateGL();
3092  }
3093  }
3094 }
3095 
3097 {
3098  return this->mCurrentZoom;
3099 }
3100 
3102 {
3103  if (this->mCurrentPositionX != x || this->mCurrentPositionY != y)
3104  {
3105  this->mCurrentPositionX = x;
3106  this->mCurrentPositionY = y;
3107  this->update();
3108  }
3109 }
3110 
3112 {
3113  if (mIsInitialized)
3114  {
3115  this->resizeGL(this->width(), this->height());
3116 
3117  if (this->isVisible())
3118  {
3119  this->updateGL();
3120  }
3121  }
3122 }
3123 
3125 {
3126  if (this->mCurrentPositionX != x)
3127  {
3128  this->mCurrentPositionX = x;
3129  this->update();
3130  }
3131 }
3132 
3134 {
3135  if (this->mCurrentPositionY != y)
3136  {
3137  this->mCurrentPositionY = y;
3138  this->update();
3139  }
3140 }
3141 
3143 {
3144  return this->mCurrentPositionX;
3145 }
3146 
3148 {
3149  return this->mCurrentPositionY;
3150 }
3151 
3153 {
3154  this->setZoomFactor(1.0);
3155  this->setCurrentPosition(this->getGraphMin().getX(), this->getGraphMin().getY());
3156 }
3157 
3158 /**
3159  * Calculates the ratio that is needed to fit the diagram on the current viewport
3160  * and sets this as the zoom factor.
3161  * The new zoom factor is returned.
3162  */
3164 {
3165  double zoom = 1.0;
3166  const CLPoint& min = this->getGraphMin();
3167  const CLPoint& max = this->getGraphMax();
3168  double width = max.getX() - min.getX();
3169  double height = max.getY() - min.getY();
3170  GLint vp[4];
3171  glGetIntegerv(GL_VIEWPORT, vp);
3172 
3173  double wRatio = (vp[2] - vp[0]) / width;
3174  double hRatio = (vp[3] - vp[1]) / height;
3175 
3176  if (wRatio > hRatio)
3177  {
3178  zoom = hRatio;
3179  }
3180  else
3181  {
3182  zoom = wRatio;
3183  }
3184 
3185  this->setZoomFactor(zoom);
3186  this->setCurrentPosition(this->getGraphMin().getX(), this->getGraphMin().getY());
3187  return zoom;
3188 }
3189 
3191 {
3192  regularTimer->stop();
3193 }
3194 
3195 /**
3196  * Calculates a circle with n points.
3197  * The points are returned as pairs of x,y values in a vector.
3198  * The points are calculated for a circle with diameter 1.
3199  */
3200 std::vector<std::pair<float, float> > CQGLNetworkPainter::calculateCirclePoints(unsigned int n)
3201 {
3202  std::vector<std::pair<float, float> > result;
3203  unsigned int i;
3204  double angle;
3205 
3206  for (i = 0; i <= n; ++i)
3207  {
3208  angle = 2 * M_PI * i / n;
3209  result.push_back(std::pair<float, float>(cos(angle) * 0.5, sin(angle) * 0.5));
3210  }
3211 
3212  return result;
3213 }
3214 
3215 /**
3216  * Sets the scaling mode to either global or individual scaling.
3217  */
3219 {
3220  this->mScaleMode = scaleMode;
3221 }
3222 
3223 /**
3224  * New method for creating a bitmap from the animation window.
3225  * This method uses QPainter, QImage and QGLFrameBufferObject to draw
3226  * into a multisample buffer if availabel and if not, it will be single sample.
3227  * This way the implementation should work on more computers.
3228  * The image is rendered in tiles of size 128x128 which should be OK for even small
3229  * frame buffers and it is a multiple of 2 which is compliant with older versions of OpenGL.
3230  *
3231  * The methods get the region to be drawn and the size of the final image as parameters.
3232  * In addition to that, the user can specify a vectir of frame numbers to be rendered.
3233  * If no frame number is given, nothing is rendered.
3234  * If a frame number is outside the range of valid frame numbers, the last frame is rendered.
3235  * If the rendering was successfull, true is returned, otherwise false is returned.
3236  */
3237 bool CQGLNetworkPainter::export_bitmap(double x, double y, double width, double height, unsigned int imageWidth, unsigned int imageHeight, const QString& filename, const std::vector<size_t> frames)
3238 {
3239  bool result = true;
3240 
3241  if (!filename.isEmpty() && !frames.empty())
3242  {
3243  // set busy cursor
3244  QCursor oldCursor = this->cursor();
3245  this->setCursor(Qt::WaitCursor);
3246  // create a progress bar
3247  QProgressDialog* pProgress = new QProgressDialog("image export", "Cancel", 0, 100, this);
3248  QImage* pImage = NULL;
3249  // make the OpenGL context current
3250  this->makeCurrent();
3251  // draw tiles of size 128x128
3252  const unsigned int tileSize = 128;
3253  unsigned int numColumns = imageWidth / tileSize;
3254  unsigned int numRows = imageHeight / tileSize;
3255  // save state
3256  double oldZoom = this->mCurrentZoom;
3257  this->mCurrentZoom = 1.0;
3258  double oldX = this->mCurrentPositionX;
3259  double oldY = this->mCurrentPositionY;
3260  GLint oldViewport[4];
3261  bool frameSet = false;
3262  // remember the current step
3263  size_t oldStep = this->stepShown;
3264  glMatrixMode(GL_PROJECTION);
3265  glPushMatrix();
3266  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3267  glPushMatrix();
3268  glGetIntegerv(GL_VIEWPORT, oldViewport);
3269  // create QGLFrameBufferObject and QPainter that draws into the framebuffer
3270  QGLFramebufferObject fbo(tileSize, tileSize, QGLFramebufferObject::CombinedDepthStencil);
3271  //QPainter fbopainter;
3272  fbo.bind();
3273 
3274  // create an instance of QImage and a QPainter that draws into the image
3275  if (fbo.isValid() && fbo.isBound())
3276  {
3277  try
3278  {
3279  pImage = new QImage(imageWidth, imageHeight, QImage::Format_RGB888);
3280  }
3281  catch (...)
3282  {
3283  pImage = NULL;
3284  result = false;
3285  }
3286 
3287  if (pImage->isNull())
3288  {
3289  delete pImage;
3290  pImage = NULL;
3291  result = false;
3292  }
3293 
3294  if (pImage != NULL)
3295  {
3296  QPainter p;
3297 
3298  // set the viewport
3299  glViewport(0, 0, (GLint)tileSize, (GLint)tileSize);
3300  //
3301  std::vector<size_t>::const_iterator it = frames.begin(), endit = frames.end();
3302  std::set<size_t> s;
3303  // cap frame
3304  size_t frame;
3305 
3306  while (it != endit)
3307  {
3308 
3309  frame = *it;
3310 
3311  if (frame >= mDataSets.size())
3312  {
3313  if (this->mDataSets.empty())
3314  {
3315  frame = 0;
3316  }
3317  else
3318  {
3319  frame = this->mDataSets.size();
3320  }
3321  }
3322 
3323  s.insert(frame);
3324  ++it;
3325  }
3326 
3327  pProgress->setMaximum(s.size());
3328  pProgress->show();
3329  // loop over the frames
3330  std::set<size_t>::const_iterator sit = s.begin(), sendit = s.end();
3331  unsigned int step = 0;
3332 
3333  while (sit != sendit)
3334  {
3335  // the tile size doesn't change because the texture
3336  // in the framebuffer should by a multiple of 2
3337  // When we copy the pixels for the potential stripes on the right
3338  // and at the bottom, we have to consider this
3339  GLdouble xPos = (GLdouble)x, yPos = (GLdouble)y, w = (GLdouble)(width * (double)tileSize / (double)imageWidth), h = (GLdouble)height * (double)tileSize / (double)imageHeight;
3340 
3341  if (pProgress->wasCanceled())
3342  {
3343  break;
3344  }
3345 
3346  // for each iteration, we need to reset the frameSet flag
3347  frameSet = false;
3348  p.begin(pImage);
3349  frame = *sit;
3350  // loop overs the rows and columns and render the tiles
3351  unsigned int i, j;
3352 
3353  for (i = 0; i < numRows; ++i, yPos += h, xPos = (GLdouble)x)
3354  {
3355  for (j = 0; j < numColumns; ++j)
3356  {
3357  // set the projection
3358  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
3359  glLoadIdentity(); // Reset The Projection Matrix
3360  gluOrtho2D((GLdouble)xPos,
3361  (GLdouble)(xPos + w),
3362  (GLdouble)(yPos + h),
3363  (GLdouble)yPos); // y: 0.0 is bottom left instead of top left as in SBML
3364  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3365 
3366  // create the tile
3367  // If the correct frame has already been set, we only need to redraw, otherwise we
3368  // need to update the frames
3369  if (!frameSet)
3370  {
3371  this->showStep(frame);
3372  frameSet = true;
3373  }
3374  else
3375  {
3376  this->drawGraph();
3377  }
3378 
3379  // render it into the image
3380  p.drawImage(QPoint(j * tileSize, i * tileSize), fbo.toImage());
3381  xPos += w;
3382  }
3383 
3384  if (imageWidth % tileSize != 0)
3385  {
3386  // there is a stripe on the right
3387  // set the projection
3388  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
3389  glLoadIdentity(); // Reset The Projection Matrix
3390  gluOrtho2D((GLdouble)xPos,
3391  (GLdouble)(xPos + w),
3392  (GLdouble)(yPos + h),
3393  (GLdouble)yPos); // y: 0.0 is bottom left instead of top left as in SBML
3394  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3395 
3396  // create the tile
3397  if (!frameSet)
3398  {
3399  this->showStep(frame);
3400  frameSet = true;
3401  }
3402  else
3403  {
3404  this->drawGraph();
3405  }
3406 
3407  // render part of tile into the image
3408  p.drawImage(QPoint(j * tileSize, i * tileSize), fbo.toImage(), QRect(QPoint(0, 0), QSize(imageWidth % tileSize, tileSize)));
3409  }
3410  }
3411 
3412  if ((imageHeight % tileSize) != 0)
3413  {
3414  // create the stripe at the bottom
3415  for (j = 0; j < numColumns; ++j, xPos += w)
3416  {
3417  // set the projection
3418  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
3419  glLoadIdentity(); // Reset The Projection Matrix
3420  gluOrtho2D((GLdouble)xPos,
3421  (GLdouble)(xPos + w),
3422  (GLdouble)(yPos + h),
3423  (GLdouble)yPos); // y: 0.0 is bottom left instead of top left as in SBML
3424  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3425 
3426  // create the tile
3427  if (!frameSet)
3428  {
3429  this->showStep(frame);
3430  frameSet = true;
3431  }
3432  else
3433  {
3434  this->drawGraph();
3435  }
3436 
3437  // render part of tile into the image
3438  p.drawImage(QPoint(j * tileSize, i * tileSize), fbo.toImage(), QRect(QPoint(0, 0), QSize(tileSize, imageHeight % tileSize)));
3439  }
3440 
3441  if (imageWidth % tileSize != 0)
3442  {
3443  // there is a stripe on the right
3444  // set the projection
3445  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
3446  glLoadIdentity(); // Reset The Projection Matrix
3447  gluOrtho2D((GLdouble)xPos,
3448  (GLdouble)(xPos + w),
3449  (GLdouble)(yPos + h),
3450  (GLdouble)yPos); // y: 0.0 is bottom left instead of top left as in SBML
3451  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3452 
3453  // create the tile
3454  if (!frameSet)
3455  {
3456  this->showStep(frame);
3457  frameSet = true;
3458  }
3459  else
3460  {
3461  this->drawGraph();
3462  }
3463 
3464  // render part of tile into the image
3465  p.drawImage(QPoint(j * tileSize, i * tileSize), fbo.toImage(), QRect(QPoint(0, 0), QSize(imageWidth % tileSize, imageHeight % tileSize)));
3466  }
3467  }
3468 
3469  p.end();
3470  // create a new temporary filename
3471  QString tmpfilename;
3472 
3473  if (frames.size() > 1)
3474  {
3475  // add the frame number to the frame name
3476  QFileInfo info(filename);
3477  QString completeBaseName = info.completeBaseName();
3478  QString suffix = info.suffix();
3479  QString path = info.path();
3480  assert(suffix == "png" || suffix == "PNG");
3481  tmpfilename = path;
3482  tmpfilename.append("/");
3483  tmpfilename.append(completeBaseName);
3484  // check how many decimals we need
3485  // the largest number should be at the end of s
3486  int length = ceil(log10((C_FLOAT64) * s.rbegin()) + 1);
3487  tmpfilename.append(QString("%1").arg((uint)frame, (int)length, (int)10, QLatin1Char('0')));
3488  tmpfilename.append(".");
3489  tmpfilename.append(suffix);
3490  }
3491  else
3492  {
3493  tmpfilename = filename;
3494  }
3495 
3496  // save the image
3497  pImage->save(tmpfilename, "PNG");
3498  ++sit;
3499  ++step;
3500  pProgress->setValue(step);
3501  QCoreApplication::processEvents();
3502  }
3503 
3504  fbo.release();
3505  // Reset the state
3506  // First we have to return to the old step
3507  this->mCurrentZoom = oldZoom;
3508  this->mCurrentPositionX = oldX;
3509  this->mCurrentPositionY = oldY;
3510  // reset the projection
3511  glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
3512  glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
3513  glPopMatrix();
3514  glMatrixMode(GL_PROJECTION);
3515  glPopMatrix();
3516 
3517  if (oldStep != frame)
3518  {
3519  this->setVisible(false);
3520  this->showStep(oldStep);
3521  this->setVisible(true);
3522  this->resizeGL(oldViewport[2], oldViewport[3]);
3523  }
3524  else
3525  {
3526  this->resizeGL(oldViewport[2], oldViewport[3]);
3527  }
3528 
3529  delete pImage;
3530  }
3531  else
3532  {
3533  fbo.release();
3534  }
3535  }
3536  else
3537  {
3538  result = false;
3539  }
3540 
3541  // reset cursor
3542  this->setCursor(oldCursor);
3543  pProgress->close();
3544  delete pProgress;
3545  }
3546  else
3547  {
3548  result = false;
3549  }
3550 
3551  // return the result;
3552  return result;
3553 }
const size_t & getRecordedSteps() const
void stepChanged(int)
C_FLOAT64 getConcentrationData(const size_t &step, const size_t &variable) const
bool mIsInitialized
flag that indicates whether OpenGL was initialized
void drawNode(CCompartmentGraphNode &n)
const CCopasiVector< CLTextGlyph > & getListOfTextGlyphs() const
Definition: CLayout.h:120
const C_FLOAT64 & getWidth() const
Definition: CLBase.h:211
CLPoint getPointOnRectangle(CLBoundingBox r, CLPoint p)
virtual bool compile(std::vector< CCopasiContainer * > listOfContainer, const CCopasiDataModel *pDataModel)
const size_t & getNumVariables() const
static const GLfloat MIRROR_X[16]
double textureWidth
Definition: RGTextureSpec.h:31
int getLabelWindowWidth(int width)
#define FROM_UTF8(__x)
Definition: qtUtilities.h:73
static const float SPECIES_SHADOW_DEPTH
static const GLfloat MIRROR_Y[16]
C_FLOAT64 getMinOverallConcentration()
std::vector< CDataEntity > mDataSets
void setX(const C_FLOAT64 &x)
Definition: CLBase.h:91
std::multimap< std::string, CArrow > nodeArrowMap
GLfloat mInanimatedSpeciesColor[4]
void setMaxNodeSize(C_FLOAT64 minNdSize)
CLMetabReferenceGlyph::Role getRole()
Definition: CGraphCurve.h:53
void storeMin(std::string ndKey, C_FLOAT64 minR)
virtual size_t size() const
void storeMax(std::string ndKey, C_FLOAT64 maxR)
std::map< std::string, RGTextureSpec * > labelTextureMap
C_FLOAT64 getSize()
Definition: CGraphNode.h:52
std::vector< CGraphCurve > curvesWithArrow
void setCurrentPosition(C_FLOAT64 x, C_FLOAT64 y)
const C_FLOAT64 & getX() const
unsigned int mNumCirclePoints
const std::vector< CLLineSegment > & getCurveSegments() const
Definition: CLCurve.h:154
const std::string & getTitle(const size_t &variable) const
std::vector< CGraphCurve > viewerCurves
C_FLOAT64 getMinForSpecies(std::string ndKey)
C_FLOAT64 getValueForSpecies(std::string nodeKey)
Definition: CDataEntity.cpp:58
void allocate(const size_t &steps)
Definition: CTimeSeries.cpp:73
C_FLOAT64 getOrigValueForSpecies(std::string nodeKey)
Definition: CDataEntity.cpp:70
CVisParameters::SCALING_MODE mScaleMode
std::vector< std::pair< float, float > > mCirclePoints
double textHeight
Definition: RGTextureSpec.h:30
void setY(const C_FLOAT64 &y)
Definition: CLBase.h:92
void setCurrentPositionY(C_FLOAT64 y)
std::set< std::string > setOfConstantMetabolites
CLPoint getEndOfLine()
Definition: CArrow.h:41
CSimSummaryInfo * pSummaryInfo
static C_INT16 DEFAULT_NODE_SIZE
void setNodeSizeWithoutChangingCurves(std::string key, C_FLOAT64 val)
void setArrow(CArrow ar)
Definition: CGraphCurve.h:51
CQLayoutMainWindow * pParentLayoutWindow
CDataEntity * getDataSetAt(size_t stepNumber)
static const C_INT32 MIN_HEIGHT
C_FLOAT64 getMaxOverallConcentration()
#define C_INT32
Definition: copasi.h:90
void rescaleNode(std::string key, C_FLOAT64 newMin, C_FLOAT64 newMax, CVisParameters::SCALING_MODE scaleMode)
GLubyte * textureData
Definition: RGTextureSpec.h:35
std::multimap< std::string, CGraphCurve > nodeCurveMap
size_t getCurrentStep() const
const CLPoint & getBase1() const
Definition: CLCurve.h:82
static const float COMPARTMENT_FRAME_DEPTH
void setConstantNodeSize(std::string key, C_FLOAT64 val)
const CCopasiVector< CLCompartmentGlyph > & getListOfCompartmentGlyphs() const
Definition: CLayout.h:81
void insertValueTable(CDataEntity dataSet)
C_FLOAT64 getMaxForSpecies(std::string ndKey)
void printNodeInfoForKey(std::string key)
const std::string & getKey(const size_t &variable) const
const CLDimensions & getDimensions() const
Definition: CLBase.h:266
virtual const std::string & getKey() const
static const float COMPARTMENT_SHADOW_DEPTH
static const C_FLOAT64 EPSILON
void createGraph(CLayout *lP)
void setGraphSize(const CLPoint &min, const CLPoint &max)
const CLPoint & getBase2() const
Definition: CLCurve.h:83
bool checkCurve(CGraphCurve *curve, CGraphCurve curveR, CLBoundingBox box)
void rescaleDataSets(CVisParameters::SCALING_MODE scaleMode)
const CLPoint & getEnd() const
Definition: CLCurve.h:75
int getTextWidth(const std::string &text, const std::string &fontName, unsigned int fontSize)
void putOrigValueForSpecies(std::string nodeKey, C_FLOAT64 value)
Definition: CDataEntity.cpp:50
std::map< std::string, std::string > labelNodeMap
std::map< std::string, CCompartmentGraphNode > compartmentNodeMap
void setCurrentPositionX(C_FLOAT64 x)
double textWidth
Definition: RGTextureSpec.h:29
std::map< std::string, std::string > keyMap
const C_FLOAT64 & getX() const
Definition: CLBase.h:83
Definition: CLBase.h:54
const CLPoint & getPosition() const
Definition: CLBase.h:265
void setAnimationRunning(bool animationRunningP)
size_t getNumberOfSteps() const
static double calculateAngle(const CLPoint &endPoint, const CLPoint &startPoint)
static const float SPECIES_FRAME_DEPTH
GLfloat mConstantSpeciesColor[4]
const CCopasiVector< CLMetabGlyph > & getListOfMetaboliteGlyphs() const
Definition: CLayout.h:94
static CCopasiVector< CCopasiDataModel > * getDatamodelList()
Definition: CArrow.h:29
#define C_INT16
Definition: copasi.h:91
std::string getOrigNodeKey()
Definition: CGraphNode.h:61
RGTextureSpec * getTextureForText(const std::string &text, const std::string &fontName, unsigned int fontSize)
CVisParameters::SCALING_MODE getScalingMode()
void setMinNodeSize(C_FLOAT64 minNdSize)
void setScaleMode(CVisParameters::SCALING_MODE scaleMode)
shapeOfLabels mLabelShape
static const float SPECIES_DEPTH
GLfloat mAnimatedSpeciesColor[4]
std::vector< CLPoint > getListOfPoints() const
Definition: CLCurve.cpp:130
std::string getNameForNodeKey(std::string key)
GLclampf mBackgroundColor[4]
const CTimeSeries & getTimeSeries() const
Header file of class BezierCurve.
RGTextureSpec * RG_createTextureForText(const std::string &text, const std::string &fontName, unsigned int fontSize)
CLPoint getPointOnCircle(CLBoundingBox r, CLPoint p)
C_FLOAT64 getCurrentPositionY() const
const C_FLOAT64 & getWidth() const
CLPoint getStartOfLine()
Definition: CArrow.h:40
const C_FLOAT64 & getY() const
Definition: CLBase.h:84
const C_FLOAT64 & getY() const
void setArrowP(bool b)
Definition: CGraphCurve.h:50
void endOfAnimationReached()
bool isBezier() const
Definition: CLCurve.h:90
void setFontSizeForLabels(unsigned int fs)
void RG_drawStringAt(std::string s, C_INT32 x, C_INT32 y, C_INT32 w, C_INT32 h)
Definition: CLabel.h:27
void putValueForSpecies(std::string nodeKey, C_FLOAT64 value)
Definition: CDataEntity.cpp:43
void setNodeSize(std::string key, C_FLOAT64 val)
const C_FLOAT64 & getHeight() const
Definition: CLBase.h:212
#define C_FLOAT64
Definition: copasi.h:92
const CLLineSegment * getSegmentAt(size_t i) const
Definition: CLCurve.h:156
static const float SPECIESREFERENCE_DEPTH
CArrow getArrow()
Definition: CGraphCurve.h:54
void adaptCurveForRectangles(std::multimap< std::string, CGraphCurve >::iterator it, CLBoundingBox box)
bool export_bitmap(double x, double y, double width, double height, unsigned int imageWidth, unsigned int imageHeight, const QString &filename, const std::vector< size_t > frames)
void rescaleDataSetsWithNewMinMax(C_FLOAT64 oldMin, C_FLOAT64 oldMax, C_FLOAT64 newMin, C_FLOAT64 newMax, CVisParameters::SCALING_MODE scaleMode)
const CLPoint & getGraphMax()
void setEnd(const CLPoint &p)
Definition: CLCurve.h:80
void zoomGraph(C_FLOAT64 zoomFactor)
void setRole(CLMetabReferenceGlyph::Role r)
Definition: CGraphCurve.h:52
double textureHeight
Definition: RGTextureSpec.h:32
std::vector< std::string > viewerNodes
std::vector< std::string > viewerCompartmentNodes
void drawEdge(CGraphCurve &c)
CQGLNetworkPainter(const QGLFormat &format, QWidget *parent=0)
std::string getNodeNameEntry(int i)
GLfloat mSpeciesReferenceColor[4]
C_FLOAT64 getCurrentPositionX() const
static const float COMPARTMENT_DEPTH
C_FLOAT64 getZoomFactor() const
bool hasArrowP()
Definition: CGraphCurve.h:49
void zoom(double zoomFactor)
void resizeGL(int w, int h)
void contextMenuEvent(QContextMenuEvent *event)
void initializeGraphPainter(QWidget *viewportWidget)
void setZoomFactor(C_FLOAT64)
static std::vector< std::pair< float, float > > calculateCirclePoints(unsigned int n)
void setItemAnimated(std::string key, bool animatedP)
void drawArrow(CArrow a, CLMetabReferenceGlyph::Role role)
void invertOrderOfPoints()
Definition: CGraphCurve.cpp:62
void drawStringAt(std::string s, C_FLOAT64 x, C_FLOAT64 y, C_FLOAT64 w, C_FLOAT64 h, QColor bgCol)
double textYOffset
Definition: RGTextureSpec.h:33
virtual void output(const Activity &activity)
const C_FLOAT64 & getHeight() const
size_t getNumCurveSegments() const
Definition: CLCurve.h:168
CVisParameters::MAPPING_MODE getMappingMode()
std::vector< CLabel > viewerLabels
#define min(a, b)
Definition: f2c.h:175
const CLPoint & getGraphMin()
std::set< std::string > setOfDisabledMetabolites
const CCopasiVector< CLReactionGlyph > & getListOfReactionGlyphs() const
Definition: CLayout.h:107
void setConstantNodeSizeForAllSteps(std::string key, C_FLOAT64 midValue)
std::map< std::string, CGraphNode > nodeMap
CLPoint getPointNearCircle(CLBoundingBox r, CLPoint p, C_INT16 d)
void setMaxOverallConcentration(C_FLOAT64 max)
const CLDimensions & getDimensions() const
Definition: CLayout.h:76
const CLPoint & getStart() const
Definition: CLCurve.h:74
void adaptCurveForCircle(std::multimap< std::string, CGraphCurve >::iterator it, CLBoundingBox box)
#define max(a, b)
Definition: f2c.h:176